From 815e144d9a883f3611eaa127f60f8ed4379a686e Mon Sep 17 00:00:00 2001 From: Bertrand Benjamin Date: Mon, 10 Aug 2026 19:17:15 +0200 Subject: [PATCH] =?UTF-8?q?Rendre=20lisibles=20le=20statut=20et=20la=20dat?= =?UTF-8?q?e=20impos=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Statut : quatre boutons peints de leur texture, si bien que le panneau sert enfin de légende. « En cours » se raye et « terminé » redevient vif — la trame les distingue, la teinte n'a plus à le faire. Date imposée : un cadre tireté cerne la forme entière. Les taquets tombaient sous les poignées de redimensionnement et disparaissaient sur une barre courte. Son libellé passe dans le modèle, ce qui la fait apparaître dans la vue par mois, qui la taisait. Co-Authored-By: Claude Opus 5 --- README.md | 23 +++- css/style.css | 257 +++++++++++++++++++++++++++++++++++++++----- docs/decisions.md | 127 ++++++++++++++++++++-- index.html | 59 ++++++++-- js/app.js | 1 + js/detail.js | 115 ++++++++++++++++---- js/model.js | 18 ++++ js/mois.js | 11 +- js/timeline.js | 40 +++---- tests/model.test.js | 21 ++++ 10 files changed, 574 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 7eb5394..8d1b567 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,18 @@ survol de la ligne, sur son bord droit : Il n'y a pas de commande séparée pour créer un **jalon** ni pour marquer une **date imposée** : ce sont deux cases du panneau de la phase, indépendantes l'une de l'autre comme du statut. Cocher -« Jalon » ramène la fin sur le début. Les deux bascules jouent dans les deux sens à tout moment. +« Jalon » ramène la fin sur le début, cocher « Date imposée » encadre les deux champs de date d'un +trait tireté — le même qu'on verra sur la barre. Les deux bascules jouent dans les deux sens à tout +moment. + +Le **statut** se choisit sur quatre boutons, chacun peint de la texture qu'il donne à la barre. Deux +traits suffisent à les lire : c'est **plein** quand rien ne bouge et **rayé** quand quelque chose se +passe ; c'est la **couleur pleine** du projet dès qu'une phase a commencé, et une teinte **diluée** +tant qu'elle n'a pas commencé. D'où : à venir pâle et bordé, en cours plein et rayé, terminé plein et +nu, bloqué rayé de rouge — seule teinte étrangère, parce que c'est la seule chose qui ne parle pas +d'avancement. La couleur reste celle du projet : elle distingue les couloirs, jamais l'avancement. Le +panneau sert ainsi de légende — il n'y en a pas d'autre, et il n'y a rien à apprendre avant de s'en +servir. La colonne se **redimensionne** en glissant la ligne qui la sépare de la frise, en haut à gauche : un double-clic revient à la largeur d'origine. Cette largeur est propre au navigateur, @@ -138,8 +149,10 @@ va. Un projet dont tout est terminé n'offre aucune prise, et son infobulle le d **Ce dont on ne décide pas non plus.** Une phase peut porter une **date imposée** : elle est déléguée à quelqu'un d'autre, ou tenue par un contrat, un créneau, une échéance réglementaire. La -case se coche dans son panneau, sous « Jalon », et la barre reçoit alors deux petits taquets à ses -extrémités — un cercle pour un jalon. Repousser le projet ne la repousse pas : si tout le reste +case se coche dans son panneau, sous « Jalon », et sa forme se cerne alors d'un **trait tireté** — +une barre comme un losange, et jusqu'aux deux champs de date du panneau. À ne pas confondre avec le +contour plein de la phase sélectionnée : celui-ci est dedans et discontinu, celui-là dehors et +franc. Repousser le projet ne la repousse pas : si tout le reste glisse, c'est le créneau qu'on rate, et le chevauchement qui apparaît est justement ce qu'on voulait voir. Elle reste glissable seule, elle : « imposée » veut dire *indépendante du projet*, pas *immuable*. @@ -297,6 +310,10 @@ mois —, chez qui — le projet et ses tags —, puis quoi — l'intitulé de l enfin son statut. Une phase longue n'est **pas** répétée dans les mois qu'elle traverse : elle n'apparaît qu'à son mois de départ, avec sa date de fin écrite en clair. +La petite marque en tête de ligne reprend les formes de la frise — barre pour une tâche, losange pour +un jalon, texture du statut, cadre tireté pour une **date imposée**. C'est ici qu'elle compte le +plus : une liste d'échéances doit dire lesquelles ne vous appartiennent pas. + Les phases qui portent des **notes** le signalent par un triangle en tête de ligne, le même que celui qui plie un projet sur la frise. Un clic **n'importe où sur la ligne** déplie la note en dessous, un second la referme. Elle s'y affiche rendue, comme dans le panneau. Rien n'est affiché diff --git a/css/style.css b/css/style.css index 1b0942b..e698aac 100644 --- a/css/style.css +++ b/css/style.css @@ -804,19 +804,52 @@ button[aria-pressed="true"] { } /* Le statut se lit à la texture de la barre, pas à sa couleur : la couleur - appartient au projet, elle sert à distinguer les couloirs entre eux. */ + appartient au projet, elle sert à distinguer les couloirs entre eux. + + Une seule grammaire pour les quatre, et elle tient en deux traits : + + - **plein** pour un état stable, **rayé** pour ce qui bouge ou coince ; + - la couleur du projet **pleine** dès qu'une phase a commencé, **diluée** tant + qu'elle n'a pas commencé. + + D'où : à venir dilué et bordé, en cours plein et rayé, terminé plein et nu, + bloqué rayé de rouge — seule teinte étrangère, parce que c'est la seule chose + qui ne parle pas d'avancement. + + « Terminé » a longtemps été assourdi, d'abord d'un gris fixe puis d'une + dilution. Les deux disaient « c'est derrière nous » en donnant une frise + morte, où la majorité des barres d'un projet avancé sont éteintes. C'est la + rayure d'« en cours » qui a permis de le rendre vif : la trame distingue + désormais ces deux-là, la teinte n'a plus à s'en charger. */ .barre--todo { background: color-mix(in srgb, var(--couleur-projet) 28%, var(--surface)); color: var(--texte); border: 1px solid var(--couleur-projet); } +/* Rayée, et non pleine : posée à côté d'une barre terminée — un aplat de la même + teinte —, une couleur pleine ne se distinguait pas assez pour qu'on lise + l'avancement d'un couloir en diagonale. La rayure lui donne ce que les trois + autres textures ont déjà : une trame, et pas seulement une nuance. Elle mêle + la couleur du projet à elle-même diluée, là où celle d'une phase bloquée y + mêle du rouge : le motif est le même, la teinte dit lequel des deux on + regarde. */ .barre--doing { - background: var(--couleur-projet); + background: repeating-linear-gradient( + 45deg, + var(--couleur-projet), + var(--couleur-projet) 6px, + color-mix(in srgb, var(--couleur-projet) 50%, var(--surface)) 6px, + color-mix(in srgb, var(--couleur-projet) 50%, var(--surface)) 12px + ); } +/* La couleur du projet, pleine et nue — c'est la seule des quatre à n'avoir + aucune marque, et c'est bien ce qu'on veut dire : il n'y a plus rien à en + suivre. Déclarée quand même, et pas laissée au fond de `.barre` : ce qui vaut + pour un statut doit se lire à l'endroit où on lit les statuts. */ .barre--done { - background: color-mix(in srgb, var(--couleur-projet) 55%, #64748b); + background: var(--couleur-projet); } .barre--blocked { @@ -832,19 +865,37 @@ button[aria-pressed="true"] { /* Une phase dont la date ne nous appartient pas — déléguée, contractuelle, réglementaire (docs/decisions.md, section 28). - Deux **taquets** verticaux à ses extrémités, comme les butées d'une pièce - qu'on ne peut plus faire coulisser. La marque se cumule avec les quatre - textures de statut au lieu de leur disputer la place : c'est bien une phase à - venir, en cours ou bloquée, qui se trouve en plus tenue par le dehors. + La forme est **cerclée**, tout entière : c'est tenu de partout, et le trait + suit le contour quel qu'il soit — une barre, un losange. Deux taquets aux + extrémités disaient la même chose et le disaient mal : sur une barre de deux + jours ils l'avalaient, sur une barre à venir ils se lisaient comme une bordure + épaissie, et ils occupaient la place où le survol révèle les poignées de + redimensionnement — le signe « cette date ne vous appartient pas » tombait + exactement là où l'outil dit « tirez ici pour la changer ». - En `inset`, donc sans toucher à la géométrie : la barre garde exactement la - largeur de ses dates, et l'ombre portée reste déclarée à côté — un - `box-shadow` ne se cumule pas d'une règle à l'autre, il se remplace. */ -.barre--imposee { - box-shadow: - var(--ombre), - inset 3px 0 0 var(--texte), - inset -3px 0 0 var(--texte); + La marque se cumule avec les quatre textures de statut au lieu de leur + disputer la place : c'est bien une phase à venir, en cours ou bloquée, qui se + trouve en plus tenue par le dehors. + + Le trait est **tireté**, et c'est ce qui le sépare du contour de la sélection, + plein. Un cerclage plein avait été essayé d'abord : à la taille réelle des + barres, on ne le distinguait pas d'une phase simplement sélectionnée. Le tiret + dit d'ailleurs ce qu'il faut — ce n'est pas nous qui traçons cette ligne. + + Il vit sur un `::after` plutôt que sur la barre : un élément n'a qu'un + `outline`, et c'est la sélection qui le prend. Les deux marques se superposent + donc sans se gêner sur une phase imposée qu'on vient de sélectionner — un + cadre tireté dedans, un contour plein dehors. */ +.barre--imposee::after, +.jalon--imposee::after { + content: ''; + position: absolute; + inset: 0; + border: 2px dashed var(--texte); + border-radius: inherit; + /* Le losange tourne, son cadre avec lui : le pseudo-élément hérite de la + rotation du jalon, et n'a donc rien à en savoir. */ + pointer-events: none; } /* Poignées de redimensionnement, révélées au survol de la barre. */ @@ -894,23 +945,35 @@ button[aria-pressed="true"] { box-shadow: var(--ombre); } +/* Mêmes textures que les barres, hachures resserrées : le losange fait 14px de + côté, une rayure de 6px n'y tiendrait pas deux fois. */ +.jalon--doing { + background: repeating-linear-gradient( + 45deg, + var(--couleur-projet), + var(--couleur-projet) 3px, + color-mix(in srgb, var(--couleur-projet) 50%, var(--surface)) 3px, + color-mix(in srgb, var(--couleur-projet) 50%, var(--surface)) 6px + ); +} + .jalon--done { - background: color-mix(in srgb, var(--couleur-projet) 55%, #64748b); + background: var(--couleur-projet); } .jalon--blocked { background: var(--danger); } -/* Un losange n'a pas d'extrémités où poser les taquets d'une barre imposée : il - se cercle. Déclarée après `.jalon`, dont elle partage la spécificité — c'est - l'ordre qui la fait gagner, un `box-shadow` se remplaçant en bloc. */ -.jalon--impose { - box-shadow: - var(--ombre), - inset 0 0 0 2px var(--texte); -} +/* Rien ici pour `.jalon--imposee` : son cadre tireté est déclaré avec celui des + barres, plus haut, à la même épaisseur. C'est la même marque, et un losange + n'est qu'une autre forme à cerner — son `::after` s'appuie sur le + `position: absolute` que `.jalon` porte déjà, et tourne avec lui. + Le trait avait été affiné à 1px, au motif que la longueur des tirets suit + l'épaisseur et qu'un losange de 14px n'en porte que quelques-uns. Mais c'est + la lisibilité qui tranche : à 1px sur une forme de cette taille, la marque + s'efface. Mieux vaut peu de tirets qu'on voit que beaucoup qu'on devine. */ .jalon--selectionne { outline: 2px solid var(--texte); outline-offset: 1px; @@ -1167,7 +1230,14 @@ button[aria-pressed="true"] { et le second garde le droit de rétrécir sous la taille de son contenu — sans lui, un flex item refuse de descendre sous sa hauteur intrinsèque et c'est la page entière qui déborderait au lieu du corps qui défile. */ +/* `--couleur-projet` se déclare ici, sur l'élément même que detail.js peint en + style inline à l'ouverture : le style inline l'emporte sur cette règle, et + l'accent ne sert que de repli tant qu'aucune phase n'est ouverte — sans quoi + les `color-mix` des textures de statut se peindraient dans le vide. La + déclarer plus bas, sur le groupe de boutons, ferait l'inverse : une valeur + posée sur un descendant gagne toujours contre celle dont il hérite. */ .panneau__corps { + --couleur-projet: var(--accent); flex: 1; min-height: 0; padding: 16px; @@ -1188,7 +1258,6 @@ button[aria-pressed="true"] { .panneau__corps input[type="text"], .panneau__corps input[type="date"], -.panneau__corps select, .panneau__corps textarea { font: inherit; padding: 6px 8px; @@ -1209,6 +1278,122 @@ button[aria-pressed="true"] { gap: 10px; } +/* Le cerclage de la barre imposée, reporté sur les deux champs que la case + qualifie : cocher « date imposée » n'avait jusque-là aucun effet visible dans + le panneau, alors que la conséquence porte précisément sur ces dates-là. + + Le padding est repris par une marge négative de même valeur : les champs ne + bougent pas d'un pixel quand la case change d'état, seul le contour apparaît. + Il déborde de 8px dans les 16px de padding du corps, donc sans jamais sortir + du panneau. */ +.champ-double--imposee { + padding: 6px 8px; + margin: -6px -8px; + border-radius: var(--rayon); + outline: 2px dashed var(--texte); + outline-offset: -2px; +} + +/* --- statut, en quatre boutons ------------------------------------------- */ + +/* Chacun peint de la texture qu'il donne à la barre sur la frise : voir + index.html pour le pourquoi. La couleur, elle, vient du formulaire — cf. le + repli déclaré sur `.panneau__corps`. */ +.statuts { + margin: 0; + padding: 0; + border: 0; +} + +.statuts legend { + padding: 0 0 4px; + font-size: 12px; + font-weight: 500; + color: var(--texte-doux); +} + +.statuts__choix { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 6px; +} + +/* Le label *est* le bouton. Le radio natif s'efface sans quitter le DOM : c'est + lui qui porte la valeur, réunit le groupe et répond aux flèches. */ +.panneau__corps .statut { + flex-direction: row; + align-items: center; + gap: 8px; + padding: 6px 8px; + border: 1px solid var(--bordure-forte); + border-radius: var(--rayon); + font-weight: 400; + color: var(--texte); + cursor: pointer; +} + +.statut input { + position: absolute; + opacity: 0; + pointer-events: none; +} + +.statut:hover { + background: color-mix(in srgb, var(--accent) 6%, transparent); +} + +.statut:has(input:checked) { + border-color: var(--accent); + background: color-mix(in srgb, var(--accent) 12%, transparent); + font-weight: 600; +} + +/* Le focus se pose sur un radio devenu invisible : c'est au bouton de le + montrer, sans quoi la navigation aux flèches se ferait à l'aveugle. */ +.statut:has(input:focus-visible) { + outline: 2px solid var(--accent); + outline-offset: 1px; +} + +/* Les quatre textures, reprises telles quelles des barres — hachures resserrées + à l'échelle de la marque, comme celles de la vue par mois. */ +.statut__marque { + flex: none; + width: 18px; + height: 9px; + border-radius: 2px; + background: var(--couleur-projet); +} + +.statut--todo .statut__marque { + background: color-mix(in srgb, var(--couleur-projet) 28%, var(--surface)); + border: 1px solid var(--couleur-projet); +} + +.statut--doing .statut__marque { + background: repeating-linear-gradient( + 45deg, + var(--couleur-projet), + var(--couleur-projet) 3px, + color-mix(in srgb, var(--couleur-projet) 50%, var(--surface)) 3px, + color-mix(in srgb, var(--couleur-projet) 50%, var(--surface)) 6px + ); +} + +.statut--done .statut__marque { + background: var(--couleur-projet); +} + +.statut--blocked .statut__marque { + background: repeating-linear-gradient( + 45deg, + var(--couleur-projet), + var(--couleur-projet) 3px, + color-mix(in srgb, var(--danger) 75%, var(--couleur-projet)) 3px, + color-mix(in srgb, var(--danger) 75%, var(--couleur-projet)) 6px + ); +} + .case { flex-direction: row !important; align-items: center; @@ -1964,8 +2149,18 @@ kbd { border: 1px solid var(--couleur-projet); } +.mois-entree--doing .mois-entree__marque { + background: repeating-linear-gradient( + 45deg, + var(--couleur-projet), + var(--couleur-projet) 3px, + color-mix(in srgb, var(--couleur-projet) 50%, var(--surface)) 3px, + color-mix(in srgb, var(--couleur-projet) 50%, var(--surface)) 6px + ); +} + .mois-entree--done .mois-entree__marque { - background: color-mix(in srgb, var(--couleur-projet) 55%, #64748b); + background: var(--couleur-projet); } .mois-entree--blocked .mois-entree__marque { @@ -1990,6 +2185,16 @@ kbd { background: var(--danger); } +/* Le même cadre tireté que sur la frise, à l'échelle de la marque : 1px sur huit + de haut plutôt que 2 sur vingt-six. En `border` et non sur un `::after` — la + marque n'a pas de sélection à qui laisser la place, et `border-box` lui garde + sa taille. Déclaré après les quatre textures de statut, dont il ne prend pas + la place : c'est une phase à venir, en cours ou bloquée qui se trouve en plus + tenue par le dehors. */ +.mois-entree--imposee .mois-entree__marque { + border: 1px dashed var(--texte); +} + /* Chiffres tabulaires : les quantièmes s'alignent d'une ligne à l'autre, ce qui fait de la colonne une échelle lisible plutôt qu'une pile de textes. */ .mois-entree__dates { diff --git a/docs/decisions.md b/docs/decisions.md index ac86fab..b8ee542 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -1150,15 +1150,14 @@ entièrement clos ; il n'a pas eu à changer, seule la liste des phases emporté ### Ce que ça donne à l'œil -Deux **taquets** verticaux aux extrémités de la barre, comme les butées d'une pièce qui ne coulisse -plus ; un **cercle** pour un jalon, qui n'a pas d'extrémités où en poser. La marque se cumule avec -les quatre textures de statut au lieu de leur disputer la place — c'est bien une phase à venir, en -cours ou bloquée, qui se trouve en plus tenue par le dehors. +> Deux **taquets** verticaux aux extrémités de la barre, comme les butées d'une pièce qui ne coulisse +> plus ; un **cercle** pour un jalon, qui n'a pas d'extrémités où en poser. -Elle se pose en `box-shadow` interne, donc sans toucher à la géométrie : la barre garde exactement la -largeur de ses dates. Sur une phase de deux jours, les taquets occupent presque toute la barre et la -texture de statut disparaît ; c'est accepté, une fenêtre de maintenance de deux jours se lit de toute -façon à son infobulle. +C'est ce qui avait été retenu ici, et c'est ce que la section 29 a défait : la marque était trop +faible et mal placée. Elle est aujourd'hui un **cadre tireté** qui cerne la forme entière, barre ou +losange. Le reste du raisonnement tient toujours — elle se cumule avec les quatre textures de statut +au lieu de leur disputer la place, et ne touche pas à la géométrie : la barre garde exactement la +largeur de ses dates. ### Le format passe en version 6 @@ -1176,3 +1175,115 @@ comprenne pourquoi. Au passage, `donneesInitiales` dans `main.go` a été remis d'aplomb : le serveur écrivait encore `version 4` pour un planning vide, deux versions en retard. Sans conséquence — le validateur accepte un fichier plus ancien et le remonte — mais un fichier neuf se déclarait d'avant le cycle de vie. + +## 29. Rendre lisibles le statut et la date imposée + +La section 28 a posé la date imposée ; l'usage a montré que ni elle ni le statut ne se lisaient bien +là où on les cherche. Quatre corrections, de la plus factuelle à la plus discutable. + +### Le libellé d'une phase vit dans le modèle + +« En cours — date imposée, non emportée par un décalage du projet » se recomposait sur quatre sites : +les trois infobulles de la frise, et celle de la vue par mois. La quatrième avait oublié la mention, +si bien que la vue des échéances — celle où une date déléguée compte le plus — taisait ce que la +frise annonçait sur chaque barre. + +D'où `libelleStatut(phase)` dans `model.js`, à côté de la table dont il se sert. Le mettre dans une +vue aurait reposé la même question à la suivante ; le modèle est le seul endroit que les deux vues +connaissent déjà. La vue par mois a reçu du même coup la marque d'une date imposée sur sa puce de +ligne, qui reprend déjà les formes de la frise : le vocabulaire visuel est le même des deux côtés, on +ne l'apprend qu'une fois. + +### Le statut passe de la liste déroulante à quatre boutons + +Une liste déroulante montre une valeur sur quatre, n'en dessine aucune, et demande deux clics. Les +quatre boutons montrent les quatre, chacun peint de la **texture qu'il donne à la barre**, et le +panneau devient ainsi la légende que l'outil n'avait nulle part — au seul moment où on en a besoin, +celui du choix, et sans coûter d'espace permanent au planning. + +Une légende posée sur la frise avait été envisagée d'abord. Elle occupe de la place en permanence +pour une chose qui s'apprend une fois, et elle se lit loin de l'objet qu'elle explique. + +Les textures se peignent dans la **couleur du projet qu'on édite** : `detail.js` pose +`--couleur-projet` sur le formulaire à l'ouverture. Le repli se déclare sur `.panneau__corps`, donc +sur l'élément même que le style inline surcharge — le déclarer sur le groupe de boutons faisait +l'inverse, une valeur posée sur un descendant gagnant toujours contre celle dont il hérite. C'est le +genre de détail qui se paie en minutes de recherche : la teinte du repli est une couleur plausible, +et rien ne signale qu'elle n'est pas celle du projet. + +### Les quatre textures sur une seule échelle + +`doing` était un aplat de la couleur du projet, `done` le même aplat assourdi d'un gris fixe. Deux +nuances de la même teinte, qu'on ne distinguait pas en parcourant un couloir en diagonale — alors que +*ce qui avance / ce qui est fait* est la distinction qu'on lit le plus souvent. + +Deux corrections, et la seconde n'a été possible que grâce à la première. + +**`doing` reçoit une rayure**, comme `blocked` en a une : le motif est le même, la teinte dit lequel +des deux on regarde — du rouge pour une phase bloquée, la couleur du projet mêlée à elle-même diluée +pour une phase qui avance. + +**`done` redevient vif.** Le gris qui l'assourdissait n'était là que pour le séparer de `doing` ; la +rayure s'en charge désormais, et la teinte n'a plus à s'en mêler. C'est un gain net : sur un projet +avancé, la majorité des barres sont terminées, et les éteindre toutes donnait une frise morte où le +travail accompli avait l'air d'un brouillon. Une dilution à mi-chemin avait été essayée d'abord — +même famille de couleur, mais toujours délavée, donc toujours le même reproche. + +La grammaire tient maintenant en deux traits, et c'est cette économie qui la rend lisible : + +- **plein** pour un état stable, **rayé** pour ce qui bouge ou coince ; +- la couleur du projet **pleine** dès qu'une phase a commencé, **diluée** tant qu'elle n'a pas + commencé. + +D'où : à venir dilué et bordé, en cours plein et rayé, terminé plein et nu, bloqué rayé de rouge — +seule teinte étrangère, parce que c'est la seule chose qui ne parle pas d'avancement. Aucune des +quatre ne se lit à une nuance qu'il faudrait comparer à une autre pour la reconnaître. + +Le contraste de la rayure a été resserré au passage — la bande claire descend à 50% de dilution +plutôt que 65% : face à un `done` désormais plein, une rayure trop douce se serait lue comme un aplat +légèrement texturé. + +La barre cumulative garde ses opacités : elle fait neuf pixels de haut, une rayure y serait du bruit, +et elle dit la forme d'ensemble d'un projet, pas le détail d'une phase. + +### La date imposée se marque d'un cadre tireté + +Les deux taquets de la section 28 ne tenaient pas leurs promesses, pour trois raisons qui se +cumulaient : + +- sur une barre de deux jours, ils l'avalaient — la section 28 l'avait prévu et accepté ; à l'usage + ce n'est pas acceptable, une fenêtre de maintenance est *typiquement* courte, c'est le cas central + et non le cas limite ; +- sur une barre à venir, qui porte déjà une bordure, ils se lisaient comme une bordure épaissie aux + bouts, c'est-à-dire comme rien ; +- surtout, ils occupaient exactement la place où le survol révèle les **poignées de + redimensionnement**. Le signe « cette date ne vous appartient pas » tombait là où l'outil dit + « tirez ici pour la changer ». + +La forme se cerne donc maintenant tout entière, d'un trait **tireté** — une barre, un losange, et +jusqu'au bloc des deux champs de date dans le panneau, qui ne montrait rien quand on cochait la case +alors que la conséquence porte précisément sur ces dates-là. Un seul signe partout, là où le losange +se cerclait et la barre se taquetait : c'était deux signes pour une même chose. + +Le tiret n'est pas décoratif. Un cerclage **plein** a été essayé d'abord, et à la taille réelle des +barres on ne le distinguait pas d'une phase simplement sélectionnée. Discontinu, il ne se confond +plus avec rien — et il dit ce qu'il faut : ce n'est pas nous qui traçons cette ligne. + +Sur la frise, le cadre vit sur un `::after` : un élément n'a qu'un `outline`, et c'est la sélection +qui le prend. Les deux marques se superposent donc sans se gêner sur une phase imposée qu'on vient de +sélectionner — un cadre tireté dedans, un contour plein dehors. Le losange l'affine à 1px : la +longueur des tirets suit l'épaisseur du trait, et 2px en posait de trop longs pour 14px de côté. + +Dans le panneau, le padding qui porte le cadre est repris par une marge négative de même valeur : les +champs ne bougent pas d'un pixel quand la case change d'état, seul le trait apparaît. + +### Ce que `detail.js` a appris de la section 28 + +Ajouter `fixed` y avait demandé quatre retouches dispersées — remplissage, application, rafraîchissement, +comparaison — qu'on ne savait liées qu'en les cherchant. Les champs sont maintenant une liste unique +que ces fonctions parcourent. `notes` en reste dehors : son contrôle est bâti par `notes.js` et se +remplit par lui, jamais en écrivant dans le `textarea`, sans quoi l'aperçu markdown ne suivrait pas. + +Le groupe de radios a demandé un détour : `formulaire.status` n'est plus un contrôle mais la liste des +quatre, et n'est donc jamais l'élément focalisé. Sans `aLeFocus()`, le panneau réécrirait le statut +sous les flèches de qui est en train de le choisir. diff --git a/index.html b/index.html index 2ffa064..24e5566 100644 --- a/index.html +++ b/index.html @@ -177,7 +177,13 @@ Jalon (une seule date, affichée en losange) -
+ +
- + +
+ Statut + +
+ + + + +
+