diff --git a/css/style.css b/css/style.css index 1cd702d..fad8a57 100644 --- a/css/style.css +++ b/css/style.css @@ -21,6 +21,10 @@ --accent: #3b82f6; --danger: #dc2626; --aujourdhui: #ef4444; + /* Réservé à ce qui réclame un arbitrage sans être une erreur : un horizon + dépassé n'est pas une faute, c'est une décision à reprendre. D'où une teinte + distincte de `--danger`, qui signale ce qui ne va pas. */ + --attention: #b45309; --hauteur-barre: 26px; /* Barre cumulative d'un projet, et fantôme qui la double. Ils sont frères, @@ -53,6 +57,7 @@ --texte: #f1f5f9; --texte-doux: #94a3b8; --ombre: 0 1px 2px rgb(0 0 0 / 0.3); + --attention: #fbbf24; } } @@ -209,6 +214,68 @@ button[aria-pressed="true"] { white-space: nowrap; } +/* --- filtre par état ------------------------------------------------------ */ + +/* Même bandeau que le filtre par tags, mais des boutons carrés et non des + pastilles rondes : les deux filtres jouent en sens contraire — cocher un tag + resserre, cocher un état élargit — et rien ne serait plus trompeur que deux + commandes identiques aux effets opposés. */ +.etats { + display: flex; + align-items: center; + gap: 10px; + padding: 7px 16px; + background: var(--surface); + border-bottom: 1px solid var(--bordure); + flex-shrink: 0; +} + +.etats__liste { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.etat-filtre { + font-size: 12px; + padding: 3px 10px; + border: 1px solid var(--bordure-forte); + border-radius: var(--rayon); + background: var(--surface); + color: var(--texte-doux); + cursor: pointer; + white-space: nowrap; +} + +/* Un état affiché est plein, un état masqué est creux. La différence se lit au + remplissage et pas seulement à la teinte : la barre en porte quatre côte à + côte, et la couleur seule n'y suffirait pas. */ +.etat-filtre[aria-pressed="true"] { + background: color-mix(in srgb, var(--accent) 12%, var(--surface)); + border-color: var(--accent); + color: var(--accent); + font-weight: 500; +} + +.etat-filtre:hover:not(:disabled) { + border-color: var(--accent); +} + +/* Le dernier état affiché ne se décoche pas : tout masquer reviendrait à vider + la frise sans qu'aucun geste ne l'ait demandé. */ +.etat-filtre:disabled { + cursor: default; + opacity: 0.75; +} + +/* Les écartés se distinguent des trois états vivants, cochés ou non : c'est le + seul dont l'ouverture change la nature de ce qu'on regarde. */ +.etat-filtre[data-etat="discarded"][aria-pressed="true"] { + background: color-mix(in srgb, var(--attention) 12%, var(--surface)); + border-color: var(--attention); + color: var(--attention); +} + /* --- tags ----------------------------------------------------------------- */ /* Chaque pastille porte sa teinte dans `--teinte-tag`, calculée par hachage du @@ -539,6 +606,48 @@ button[aria-pressed="true"] { color: #16a34a; } +/* Un projet envisagé dont l'horizon est passé — ou qui n'en a jamais eu. Même + forme que la dérive, dont c'est le pendant : l'une dit qu'un projet engagé a + glissé, l'autre qu'un projet envisagé attend qu'on tranche. + Volontairement sobre : ce signal doit rester lisible sur une colonne qui en + porte plusieurs, sans devenir l'alerte qu'on apprend à ignorer. */ +.libelle-projet__adater { + flex: 0 0 auto; + font-size: 11px; + font-weight: 500; + padding: 1px 5px; + border-radius: 10px; + white-space: nowrap; + background: color-mix(in srgb, var(--attention) 15%, transparent); + color: var(--attention); + cursor: default; +} + +/* --- états du cycle de vie ------------------------------------------------ */ + +/* Un projet terminé **reste** sur la frise : elle est notre mémoire, et l'en + retirer perdrait le bilan (décision 27). Il s'éteint donc au lieu de + disparaître — assez pour que l'œil aille d'abord à ce qui est vivant, pas + assez pour qu'on ne puisse plus le lire. + L'écarté va plus loin : il n'est visible que si on l'a demandé au filtre, et + le gris dit qu'il ne se passera plus rien. */ +.libelle-projet[data-etat="completed"], +.couloir--projet[data-etat="completed"] { + opacity: 0.65; +} + +.libelle-projet[data-etat="discarded"], +.couloir--projet[data-etat="discarded"] { + opacity: 0.5; + filter: grayscale(0.85); +} + +/* Le nom d'un projet écarté se barre : l'opacité seule dirait « en retrait », + là où il faut lire « on ne le fera pas ». */ +.libelle-projet[data-etat="discarded"] .libelle-projet__nom { + text-decoration: line-through; +} + .plier { border: 0; background: none; @@ -731,10 +840,17 @@ button[aria-pressed="true"] { background: rgb(255 255 255 / 0.5); } -.barre:hover .poignee { +.barre:hover .poignee, +.barre-horizon:hover .poignee { opacity: 1; } +/* Sur fond pâle et hachuré, un voile blanc à demi transparent ne se voit pas : + les poignées d'un horizon prennent la teinte de son projet. */ +.barre-horizon .poignee { + background: color-mix(in srgb, var(--couleur-projet) 55%, transparent); +} + .poignee--debut { left: 0; border-radius: 4px 0 0 4px; @@ -858,6 +974,67 @@ button[aria-pressed="true"] { height: 4px; } +/* --- horizon d'un projet envisagé ---------------------------------------- */ + +/* La barre d'un projet qui n'a pas encore de phase. Tout, ici, sert à dire que + les dates ne sont pas fermes (décision 27) : + + - les **bords fondus** (`mask-image`), qui suppriment le trait net qu'on + pourrait prendre pour une date de début ou de fin ; + - les **hachures obliques**, qui la distinguent au premier coup d'œil d'une + barre de phase, y compris pour qui ne perçoit pas la nuance de teinte. + + Elle se glisse et se redimensionne comme une barre de phase, mais accrochée au + **mois** plutôt qu'au lundi : le geste est possible, il ne permet simplement + pas de poser une date qu'on n'a pas. + + Sa largeur porte à elle seule l'incertitude : un mois pour un projet mûr, une + large plage pour un projet vague. */ +.barre-horizon { + position: absolute; + top: calc((var(--hauteur-ligne) - var(--hauteur-barre)) / 2); + height: var(--hauteur-barre); + border-radius: 4px; + color: var(--texte-doux); + font-size: 12px; + font-style: italic; + line-height: var(--hauteur-barre); + cursor: grab; + user-select: none; + z-index: 2; + background: + repeating-linear-gradient( + -45deg, + color-mix(in srgb, var(--couleur-projet) 26%, transparent), + color-mix(in srgb, var(--couleur-projet) 26%, transparent) 5px, + transparent 5px, + transparent 10px + ), + color-mix(in srgb, var(--couleur-projet) 12%, var(--surface)); + mask-image: linear-gradient( + 90deg, + transparent, + #000 14px, + #000 calc(100% - 14px), + transparent + ); +} + +/* Un horizon dépassé se teinte, en écho à la pastille « à dater » de la colonne. + La marque est portée deux fois — dans la colonne et sur la frise — parce que + les deux se lisent séparément : on parcourt l'une, on balaie l'autre. */ +.barre-horizon--depasse { + background: + repeating-linear-gradient( + -45deg, + color-mix(in srgb, var(--attention) 30%, transparent), + color-mix(in srgb, var(--attention) 30%, transparent) 5px, + transparent 5px, + transparent 10px + ), + color-mix(in srgb, var(--attention) 12%, var(--surface)); +} + /* --- panneau de détail --------------------------------------------------- */ /* Non modal : aucun voile derrière lui, la frise reste manipulable pendant @@ -1011,6 +1188,106 @@ button[aria-pressed="true"] { margin-top: 4px; } +/* Les deux actes du cycle de vie, côte à côte et à parts égales : aucun des deux + n'est plus courant que l'autre, et les mettre l'un sous l'autre suggérerait + une progression qui n'existe pas — on termine *ou* on écarte. */ +.panneau__actions--cycle { + display: flex; + gap: 8px; +} + +.panneau__actions--cycle button { + flex: 1 1 0; + white-space: nowrap; +} + +/* --- cycle de vie dans le panneau ---------------------------------------- */ + +/* Où en est le projet. Une ligne de texte et non un sélecteur : l'état se + déduit des données (décision 27), il n'y a rien à y choisir. */ +.panneau__etat { + margin: 0; + font-size: var(--taille-annotation); + font-weight: 600; + letter-spacing: 0.02em; + color: var(--texte-doux); +} + +.panneau__etat[data-etat="discarded"] { + color: var(--attention); +} + +.horizon { + border: 1px solid var(--bordure); + border-radius: var(--rayon); + padding: 8px 10px 10px; + margin: 0; + display: flex; + flex-direction: column; + gap: 8px; +} + +.horizon > legend { + font-size: var(--taille-annotation); + color: var(--texte-doux); + padding: 0 4px; +} + +/* Les deux bornes côte à côte : c'est un intervalle, et le lire sur deux lignes + ferait perdre ce que sa largeur veut dire. */ +.horizon__intervalle { + display: flex; + gap: 8px; +} + +.horizon__intervalle label { + flex: 1 1 0; + display: flex; + flex-direction: column; + gap: 3px; + font-size: var(--taille-annotation); + color: var(--texte-doux); +} + +.horizon__crans { + display: flex; + gap: 6px; +} + +.horizon__crans button { + flex: 1 1 0; + padding: 4px 6px; + font-size: var(--taille-annotation); + border: 1px solid var(--bordure-forte); + border-radius: var(--rayon); + background: var(--surface); + color: var(--texte); + cursor: pointer; +} + +.horizon__crans button:hover:not(:disabled) { + border-color: var(--accent); + color: var(--accent); +} + +.horizon__crans button:disabled { + opacity: 0.45; + cursor: default; +} + +.horizon__aide { + margin: 0; + font-size: var(--taille-annotation); + line-height: 1.35; + color: var(--texte-doux); +} + +/* Un horizon passé le dit ici comme il le dit sur la frise et dans la colonne : + c'est le même signal, vu depuis les trois endroits où l'on travaille. */ +.horizon__aide[data-alerte="true"] { + color: var(--attention); +} + /* --- bloc de notes -------------------------------------------------------- */ /* Le bloc prend toute la hauteur que les champs au-dessus lui laissent. C'est @@ -1791,6 +2068,48 @@ button.mois-entree__plier { color: var(--texte-doux); } +/* --- ligne d'un projet envisagé ------------------------------------------ */ + +/* Mêmes colonnes qu'une ligne de phase — la liste reste une grille qu'on lit en + diagonale —, mais tout y dit que rien n'est arrêté : l'italique de l'intitulé + et les hachures de la marque, qui reprennent celles de la barre sur la frise. + Ce qu'on a appris à lire d'un côté vaut de l'autre. */ +.mois-entree--envisage .mois-entree__nom { + font-style: italic; + color: var(--texte-doux); +} + +.mois-entree--envisage .mois-entree__marque { + background: repeating-linear-gradient( + -45deg, + color-mix(in srgb, var(--couleur-projet) 60%, transparent), + color-mix(in srgb, var(--couleur-projet) 60%, transparent) 3px, + transparent 3px, + transparent 6px + ); + border: 1px solid color-mix(in srgb, var(--couleur-projet) 45%, transparent); +} + +.mois-entree__statut[data-statut="considered"], +.mois-entree__statut[data-statut="discarded"] { + font-style: italic; +} + +/* Un projet écarté qui n'a jamais été engagé passe aussi par cette ligne — il + n'a que son horizon pour se situer. Il s'y lit comme sur la frise : éteint et + barré, pour qu'on ne le confonde pas avec ce qui va arriver. */ +.mois-entree--envisage[data-etat="discarded"] { + opacity: 0.55; +} + +.mois-entree--envisage[data-etat="discarded"] .mois-entree__projet { + text-decoration: line-through; +} + +.mois-entree--envisage[data-etat="discarded"] .mois-entree__statut { + color: var(--attention); +} + .mois__vide { max-width: 1080px; margin: 48px auto; diff --git a/index.html b/index.html index 499a891..1dcdfa9 100644 --- a/index.html +++ b/index.html @@ -54,6 +54,22 @@

+ + + @@ -215,11 +236,48 @@
+ +

+ + + +
Couleur du couloir
@@ -245,6 +303,16 @@ + +
+ + +
+