origam 2.18.16 → 2.18.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +839 -0
  2. package/dist/src/assets/css/main.css +1 -1
  3. package/dist/src/assets/css/tokens/dark.css +150 -26
  4. package/dist/src/assets/css/tokens/light.css +75 -13
  5. package/dist/src/assets/scss/tokens/_dark.scss +150 -26
  6. package/dist/src/assets/scss/tokens/_light.scss +75 -13
  7. package/dist/src/components/Btn/OrigamBtn.vue +39 -2
  8. package/dist/src/components/Btn/OrigamBtnGroup.vue +5 -0
  9. package/dist/src/components/DataTable/OrigamDataTable.vue +1 -1
  10. package/dist/src/components/DataTable/OrigamDataTableColumnCell.vue +3 -3
  11. package/dist/src/components/DataTable/OrigamDataTableGroupHeaderRow.vue +11 -4
  12. package/dist/src/components/DataTable/OrigamDataTableHeaderCell.vue +1 -1
  13. package/dist/src/components/DataTable/OrigamDataTableRow.vue +6 -1
  14. package/dist/src/components/DataTable/OrigamDataTableRows.vue +3 -3
  15. package/dist/src/components/Grid/OrigamGrid.vue +19 -0
  16. package/dist/src/components/List/OrigamListItem.vue +7 -0
  17. package/dist/src/components/List/OrigamListSubheader.vue +5 -0
  18. package/dist/src/components/Pagination/OrigamPagination.vue +64 -2
  19. package/dist/src/components/Toolbar/OrigamToolbar.vue +58 -0
  20. package/dist/src/composables/Commons/defaults.composable.d.ts +51 -0
  21. package/dist/src/composables/DataTable/headers.composable.d.ts +4 -4
  22. package/dist/src/consts/EmptyState/empty-state.const.d.ts +2 -16
  23. package/dist/src/interfaces/Commons/installed-theme.interface.cjs +1 -0
  24. package/dist/src/interfaces/Commons/installed-theme.interface.d.ts +26 -0
  25. package/dist/src/interfaces/Commons/installed-theme.interface.js +0 -0
  26. package/dist/src/interfaces/Commons/semantic-tree.interface.cjs +1 -0
  27. package/dist/src/interfaces/Commons/semantic-tree.interface.d.ts +30 -0
  28. package/dist/src/interfaces/Commons/semantic-tree.interface.js +0 -0
  29. package/dist/src/interfaces/Commons/theme.interface.d.ts +1 -1
  30. package/dist/src/interfaces/EmptyState/empty-state.interface.d.ts +13 -0
  31. package/dist/src/interfaces/index.cjs +22 -0
  32. package/dist/src/interfaces/index.d.ts +2 -0
  33. package/dist/src/interfaces/index.js +2 -0
  34. package/dist/src/types/Commons/installed-theme.type.d.ts +2 -27
  35. package/dist/src/types/Commons/semantic-tree.type.d.ts +0 -29
  36. package/dist/src/types/tokens.type.d.ts +1 -1
  37. package/dist/src/utils/Commons/apply-theme.util.d.ts +1 -1
  38. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -18,6 +18,845 @@ This project follows [Semantic Versioning](https://semver.org).
18
18
 
19
19
  ## [Unreleased]
20
20
 
21
+ ## [2.18.17] - 2026-09-29
22
+
23
+ Dix-huit commits, dont la réparation qui débloquait `Type-check (vue-tsc)` sur
24
+ `develop` et bloquait donc toute PR. Deux des quatre PR du lot ne sont **pas**
25
+ dans le tag, faute de toucher le paquet publié : #986 (barrière e2e du catalogue)
26
+ et #989 (commentaire seul).
27
+
28
+ ### Fixed — #597 : 32 canaux de thème morts résorbés sur Btn, DataTable et Grid, zéro changement de rendu
29
+
30
+ Le périmètre annoncé par le ticket — « 55 tokens morts » — n'a pas résisté à la
31
+ mesure : il en restait **48**. List avait déjà été traitée en PR #586 (il n'en
32
+ reste que les 3 laissées délibérément) et Btn en portait 17, pas 16.
33
+
34
+ Et ces 48 n'avaient pas une cause mais **trois**, établies avant d'écrire une
35
+ ligne — ce qui interdisait le traitement uniforme que la campagne #550 suggère :
36
+
37
+ | famille | entrées | cause réelle |
38
+ |---|---|---|
39
+ | DataTable | 16 | moitié manquante de la migration #503 : le 1er échelon nomme la grammaire par sous-composant, que seules 3 familles déclaraient |
40
+ | Grid | 12 | les vars sont écrites **en style inline par instance**, repli mot-clé CSS constant — personne ne les déclarait, simplement |
41
+ | Btn | 17 | aucun jumeau ; 7 replis littéraux, les 10 autres relatifs à l'élément (`currentColor`, `calc()` de densité) donc indéclarables sans changer le rendu |
42
+
43
+ **31 noms déclarés** à la valeur exacte du repli déjà rendu, dans les 4 feuilles
44
+ et dans `TTokenName` ; **16 entrées laissées en baseline**, chacune avec sa raison
45
+ écrite **à son site de lecture**. Deux lectures sont renommées au passage parce
46
+ que leur nom ne désignait aucun composant réel
47
+ (`--origam-data-table-sortable---cursor` → `-header-cell--sortable---cursor`), et
48
+ un échelon intermédiaire inatteignable est retiré du SCSS plutôt que déclaré.
49
+
50
+ Compteur de la garde, **recompté sur la baseline elle-même** et non repris d'un
51
+ message : `token-var-channels` dead **299 → 267**, dormant **460 → 460**, le diff
52
+ de la baseline ne contenant que des suppressions.
53
+
54
+ ⚠️ Le corps de `e83a1fbf1` annonce « dead 438 → 406 ». C'est une mesure
55
+ **pré-rebase**, prise sur une branche qui portait Bracket + Card mais pas encore
56
+ Chart ; elle ne décrit aucun état de l'historique final. Les chiffres retenus
57
+ ci-dessus sont ceux des fichiers de baseline aux tags concernés.
58
+
59
+ Preuve de non-régression, en trois instruments plutôt qu'un :
60
+ **1 927 584 lectures** de longhands calculés (20 stories, tous leurs Variants,
61
+ 2 modes, `::before` / `::after` inclus) rejouées avant/après — hors les couples
62
+ `(élément, propriété)` d'animations en vol, **1 926 702 lectures comparées,
63
+ `diff` exit 0, zéro ligne**. Le contrôle négatif est ce qui donne sa valeur au
64
+ résultat : deux passes sur le **même** bundle produisent 148 lignes de diff sur
65
+ exactement le même ensemble de couples — ce bruit est l'instant
66
+ d'échantillonnage, pas un changement de style. S'y ajoutent une sonde
67
+ d'équivalence de 83 expressions `var()` × 2 modes (une seule différence, `0` →
68
+ `0px`, valeur calculée identique) et 28 captures dont 27 byte-identiques.
69
+
70
+ ### Changed — #597 : les 13 alias DataTable sont renommés, pas dupliqués
71
+
72
+ Correction portée sur la première passe du lot lui-même. Elle déclarait 13
73
+ tokens DataTable pour éteindre la garde **en laissant en place leur jumeau
74
+ historique** lu en repli : 13 doublons introduits par la PR, et un second
75
+ échelon que la chaîne ne consultait plus jamais.
76
+
77
+ Deux mesures établissent qu'il s'agit de purs alias : chaque nom historique a
78
+ **exactement 1 site de lecture** — la position de repli de sa chaîne, aucun autre
79
+ consommateur — et les deux échelons portent la **même valeur** en clair comme en
80
+ sombre (26 comparaisons, 0 divergence). Les chaînes sont donc effondrées
81
+ (`var(NEUF, var(HISTORIQUE, T))` → `var(NEUF, T)`) et les déclarations
82
+ historiques retirées des 4 feuilles : **net nul**, 13 ajoutés et 13 retirés, au
83
+ lieu de +13. Ensemble émetteur 3142 → 3129, soit exactement −13.
84
+
85
+ Le principe du lot, jusque-là nulle part écrit, est consigné : on déclare
86
+ l'échelon 1 quand l'échelon 2 est un token **statique** de feuille ; on refuse et
87
+ on documente quand l'échelon 2 est écrit **par instance** (Grid en style inline,
88
+ List par le parent) ou relatif à `currentColor` (Btn ghost), parce qu'une
89
+ déclaration `:root` y figerait le rendu.
90
+
91
+ ### Fixed — `develop` était rouge : un import cassé par la scission des interfaces
92
+
93
+ `Type-check (vue-tsc)` échouait sur `develop`, donc bloquait toute PR :
94
+ `theme.interface.ts(3,15) TS2305`, `IThemeVars` introuvable. Le commit
95
+ `4fc7fcd92` avait déplacé le symbole vers
96
+ `interfaces/Commons/semantic-tree.interface.ts` sans mettre à jour ce seul import
97
+ resté en arrière — `apply-theme.util.ts` pointait déjà au bon endroit.
98
+ `type-check` passe de `exit 2` à **exit 0**.
99
+
100
+ Même cause pour la garde `comment-format`, qui rougissait à **30/31** : les blocs
101
+ de commentaire ont suivi les symboles, et trois chemins neufs se sont retrouvés
102
+ sans allocation. La baseline est re-dérivée, conservation vérifiée à l'unité sur
103
+ les trois paires concernées, et les quatre autres lignes ne font que **baisser**
104
+ (−14, exactement le delta du total) : la baseline sort plus **stricte**, jamais
105
+ plus laxiste.
106
+
107
+ ### Fixed — #953 : le catalogue de composants entre dans la barrière CI, et l'assertion qui l'en empêchait était fausse
108
+
109
+ `components.spec.ts` existait depuis longtemps sans être exécuté par **aucun
110
+ job**. `/components` — la vitrine du DS — pouvait donc rendre une page **vide**
111
+ sans une seule ligne rouge : `useReferenceCatalog` déclare `default: () => []`,
112
+ et « 0 famille sur 0 catégorie » passe pour un succès.
113
+
114
+ ⛔ Le dernier défaut qui bloquait la promotion était **dans le test**, pas dans le
115
+ produit. L'assertion de la section « Design tokens » comptait des `tbody tr` — or
116
+ cette section n'a **jamais** contenu de `<table>` : c'est un `<dl class="prop-list">`
117
+ dont chaque ligne est un `<div class="prop-list__item">`. Elle rendait donc
118
+ toujours 0, quelle que soit la donnée, et masquait une fonctionnalité qui
119
+ marchait. Les assertions voisines du même fichier utilisaient déjà le bon
120
+ sélecteur ; seule celle-ci divergeait.
121
+
122
+ Mesure sur un seed frais (2733 entrées) : `btn` rend 8 lignes de tokens, et
123
+ **127 des 218** composants en ont au moins une. Barrière complète
124
+ `MARKETING_GREEN_ONLY` : **109 passed** contre 80 avant promotion.
125
+
126
+ ⚠️ #960 n'est pas fermé par là : il porte sur le **contenu** du seed (notation
127
+ DTCG à accolades, chemins pointés de l'ancien format), pas sur la forme du DOM.
128
+
129
+ ### Changed — #544 : la conclusion du ticket est inversée, `useDefaults` reste
130
+
131
+ #544 classait `useDefaults` « doublon du résolveur ADR-005, plus aucun appelant,
132
+ à retirer ». **Sa prémisse factuelle est juste, sa conclusion est fausse** — et
133
+ l'entrée est ici parce que le composable a été à deux doigts d'être supprimé.
134
+
135
+ Juste : zéro appelant interne, les 40 derniers partis sous #363. Faux : ce n'est
136
+ pas un doublon. `installThemePropsResolver` a une limitation **structurelle**,
137
+ déjà documentée dans son propre fichier et qui n'est pas un oubli mais le
138
+ séquencement de Vue — le hook écrit dans `beforeCreate`, qui s'exécute **après**
139
+ le retour de `setup()`. Un prop lu une fois, synchroniquement, au top level de
140
+ `setup()` capture donc la valeur d'**avant** le patch : le thème ne l'atteint
141
+ jamais et rien n'avertit. `useDefaults` n'a pas cette lacune, le getter de son
142
+ `computed` s'évaluant à l'accès `.value`. C'est le **seul échappatoire** pour un
143
+ prop qui doit être lu tôt.
144
+
145
+ La preuve est exécutable et **existait avant le ticket** — une paire appariée
146
+ dans `TU/origam/setup-level-prop-reads.spec.ts` dont les deux titres disent
147
+ exactement la différence. Deux faits que le ticket demandait de vérifier et que
148
+ personne n'avait mesurés : la surface publique est **réelle** (page de doc dédiée
149
+ `docs/composables/useDefaults.md`, plus deux citations), et **35 fichiers** sous
150
+ `packages/tests/TU` le référencent — pas « 3 specs, 23 tests », chiffre de
151
+ mémoire qu'une note de session avançait.
152
+
153
+ ### Documentation
154
+
155
+ - `CLAUDE.md` présentait **#964 et #966 comme vivants** alors que les deux sont
156
+ fermés, et l'un contredisait la note écrite deux lignes plus haut. Le
157
+ paragraphe #964 était de surcroît **charcuté** — une note française insérée au
158
+ milieu d'une phrase anglaise, ressemant l'affirmation fausse dans la moitié
159
+ restée en anglais. Recousu, et la leçon qui survit au correctif est conservée :
160
+ une garde qui énumère le disque mesure la machine, pas le dépôt.
161
+ - Compteurs de gardes remis à jour, **sixième fois** : `guards` 31/31,
162
+ `guards:self` 25/25. Le 17 → 25 n'est **pas** huit tests neufs mais un
163
+ sous-comptage corrigé — un glob sur `lib/` seul en rate six.
164
+ - Deux justifications C2 fausses **en prémisse** corrigées, conclusions
165
+ inchangées : les jumeaux `ghost` de Btn ne portent pas « une couleur
166
+ d'intention opaque » (`action--ghost---bg` vaut `rgba(0,0,0,0)`), et l'exception
167
+ Grid ne tient pas à « un repli mot-clé constant » mais à **qui le repli
168
+ désigne** — `column-gap` / `row-gap` lisent le token du voisin, les déclarer
169
+ ferait cesser d'agir la prop `gap`.
170
+
171
+ ### Tooling
172
+
173
+ - La transition `min-height` du subheader de liste est sanctionnée comme
174
+ **exception assumée** et non comme défaut : `transform` ne fait pas refluer les
175
+ frères, et `grid-template-rows` suppose un conteneur grid quand le subheader est
176
+ en `display: flex`. 6 composants du DS animent une propriété de layout, dont 2
177
+ exactement `min-height` — le second emploie la construction **identique**.
178
+ L'ignore est porté au seul fichier, et la config dit explicitement que c'est un
179
+ jugement d'agent, non une décision validée.
180
+
181
+
182
+ ## [2.18.16] - 2026-09-27
183
+
184
+ ⚠️ Ce tag couvre **deux merges**, pas un — dérogation assumée à la règle « un
185
+ ticket = un merge = un tag ». Les PR #983 (#592 Bracket, #593 Card) et #984
186
+ (#591 Chart) sont le même lot de campagne, mergées à une heure d'intervalle, et
187
+ #984 a dû être rebasée sur #983 pour un conflit sur un artefact généré.
188
+
189
+ ### Fixed — #591, #592, #593 : 221 canaux de thème rendus au DS, zéro changement de rendu
190
+
191
+ `token-var-channels` (direction *dead*) **520 → 299**, direction *dormant*
192
+ **460 → 460** — inchangée dans les deux sens. Chiffres recomptés sur les fichiers
193
+ de baseline aux tags `v2.18.15` et `v2.18.16`, pas repris des messages de commit.
194
+
195
+ | famille | avant | après | cause réelle |
196
+ |---|---|---|---|
197
+ | Chart | 147 | 8 | le SCSS lisait **97 noms que personne ne déclarait** |
198
+ | Bracket | 65 | 24 | direction dormante à 0 : aucun jumeau à renommer, les lectures nommaient des tokens jamais écrits nulle part |
199
+ | Card | 42 | 1 | second échelon d'une chaîne de replis dont le premier est déclaré — l'alias plat est **inatteignable** |
200
+
201
+ ⛔ **Rien n'a été supprimé**, et les trois familles avaient **trois causes
202
+ différentes** dont aucune n'était celle qu'annonce la campagne #550 (le désaccord
203
+ de grammaire). C'est le point de l'entrée : traiter les trois de la même façon
204
+ aurait cassé quelque chose à chaque fois.
205
+
206
+ Côté Card, 33 des 42 entrées sont le second échelon d'une chaîne à deux niveaux
207
+ dont le premier est bien déclaré. Mesure en navigateur : **29 des 33** tokens
208
+ externes résolvent une valeur non vide, leur échelon interne ne peut donc jamais
209
+ être consulté ; les 4 qui lisent vide sont exactement ceux déclarés `inherit`.
210
+ Ces 33 échelons sont donc du **code mort retiré du SCSS** plutôt que déclaré —
211
+ déclarer l'alias plat aurait créé un second nom public perpétuellement masqué
212
+ par le nom BEM canonique.
213
+
214
+ Les tokens déclarés le sont **à la valeur exacte du repli déjà rendu**, littéral
215
+ pour un littéral et référence de token pour une référence, afin que le mode
216
+ sombre continue de suivre. C'est ce qui garantit l'absence de changement visuel.
217
+
218
+ ⚠️ Trois pièges mesurés plutôt que supposés, et chacun aurait été une régression :
219
+
220
+ - **Déclarer un échelon d'une cascade de replis détruit le bouton général** — une
221
+ custom property est substituée sur l'élément qui la déclare, donc une
222
+ déclaration `:root` figerait les quatre côtés à la valeur racine. Vérifié :
223
+ régler le seul bouton général agit toujours sur les 4 côtés.
224
+ - **`--origam-chart-path---length` n'est pas un token** : c'est la longueur
225
+ *mesurée* de chaque tracé, écrite en inline par instance. La déclarer aurait
226
+ été inerte **et** aurait cassé l'animation de dessin partout.
227
+ - **`inherit` en repli ne peut pas être gravé tel quel** dans la feuille : un
228
+ mot-clé CSS-wide rend la custom property *guaranteed-invalid*, donc le token
229
+ serait non déclaré **tout en ayant l'air corrigé**. Les deux tokens concernés
230
+ passent à `currentColor`, et l'équivalence sur la propriété `color` est mesurée
231
+ en navigateur, pas supposée.
232
+
233
+ Non-régression : Bracket + Card, **13 272 lectures** de propriétés calculées
234
+ comparées avant/après (2 modes, 12 Variants, pseudo-éléments inclus) → **0
235
+ différence**, 14 paires de captures dont 12 byte-identiques. Chart, **31 mesures
236
+ absolues** sur 7 variantes → **31/31 identiques**, 7 captures byte-identiques, et
237
+ l'assertion « les tokens réconciliés sont déclarés par la feuille » vérifiée
238
+ **rouge sur le commit parent**.
239
+
240
+ 8 entrées Chart restent en baseline délibérément, dont
241
+ `--origam-chart__axis-label---font-size` (7 fichiers) parce que les replis **ne
242
+ s'accordent pas** — `0.75rem` sur cinq fichiers, `0.6875rem` sur trois : une
243
+ déclaration unique changerait le rendu d'un des deux camps. C'est un choix de
244
+ design, remonté plutôt que tranché en passant.
245
+
246
+ ### Fixed — #855 : l'état « introuvable » des 8 pages de détail était structurellement mort
247
+
248
+ `useReferenceDoc` levait un `createError({ fatal: true })` sur **toute** erreur,
249
+ 404 comprise. Les huit pages de détail portent pourtant chacune un bloc
250
+ `v-if="!catalogEntry"` avec son `data-cy` dédié — et ce bloc ne pouvait jamais
251
+ s'afficher, le composable levant avant que la page rende quoi que ce soit.
252
+
253
+ Sur un slug inconnu : `/components/<inconnu>` et `/types/<inconnu>` rendaient
254
+ **HTTP 500**. Après, les **huit** familles — components, types, enums,
255
+ interfaces, consts, composables, utils, directives — rendent **HTTP 200** avec
256
+ leur état not-found, **8/8**.
257
+
258
+ ⛔ Les autres statuts continuent de lever. Un 500 ou un 503 (base absente) n'est
259
+ pas « cette entrée n'existe pas », c'est une panne — la masquer derrière un joli
260
+ « introuvable » mentirait au visiteur comme au développeur. C'est d'ailleurs ce
261
+ 503 qui donnait un 500 au lieu d'un 404 sans base.
262
+
263
+ Vérifié sur un **build de production** et non en mode dev : le worker vite-node
264
+ du serveur de dev mourait en boucle sur `IPC connection closed` (famille #853),
265
+ et trois tentatives de mesure ont été perdues avant le changement d'outil.
266
+
267
+ ### Fixed — contraste WCAG du libellé des métadonnées de tokens (4,24:1 → conforme sur 16/16)
268
+
269
+ axe le levait sur `/components/btn` : `.component-tokens__meta-label`, `#7e5fb0`
270
+ sur `#efe8fc`, **4,24:1** en 12 px **gras**. Douze pixels gras restent du « texte
271
+ normal » au sens de WCAG 1.4.3 — le seuil est 4,5:1, pas 3:1.
272
+
273
+ ⚠️ Le token n'était **pas** mort, contrairement au premier diagnostic :
274
+ `geek.theme.ts` déclare bien `tertiary: '#7e5fb0'`, émis à l'exécution par la
275
+ matrice de thème. C'est sa **valeur** qui ne passe pas, pas son nom.
276
+
277
+ Mesure des deux encres candidates contre le fond **réel** du panneau
278
+ (`surface---sunken` **composite** sur `surface---default`), 8 identités × 2 modes :
279
+ `tertiary` échoue **6 fois sur 16** (pire cas apple sombre à 2,75) ; `secondary`
280
+ **0 fois sur 16**, minimum 5,42.
281
+
282
+ ⛔ La composition n'est pas un détail : sans elle, `glass` sortait 1,58 et 1,30 —
283
+ des chiffres absurdes, son `sunken` étant à 3 % d'alpha. Composite, il donne 8,91
284
+ et 14,56. La conclusion inverse — « `secondary` est pire » — a failli être tirée.
285
+
286
+ ⚠️ `--origam-color__text---tertiary` reste lu à ~72 endroits du marketing et
287
+ échoue sur 6 configurations partout où il sert de texte lisible. Hors périmètre
288
+ ici, noté dans le code.
289
+
290
+ ### Tooling
291
+
292
+ - Le job `test-e2e-marketing` **n'avait pas de base** (#953) : `/api/reference/*`
293
+ répondait 503 en CI, et toute page lisant le catalogue rendait du **vide** sans
294
+ qu'aucun check ne rougisse. Trois étapes copiées de `docs-fixtures.yml` où
295
+ elles tournent déjà — service `postgres:16`, `db:migrate`, `docs:seed`. Recette
296
+ vérifiée en local avant d'être écrite : 2733 entrées semées,
297
+ `/api/reference/component` = 218, `/components/btn` = 200 contre 500 sans base.
298
+ La seconde moitié de #953 — l'assertion qui rougit — arrive en 2.18.17.
299
+
300
+
301
+ ## [2.18.15] - 2026-09-26
302
+
303
+ Un correctif de DS et deux de marketing. Le correctif DS est la moitié
304
+ **invisible** d'un défaut signalé sur capture : la puce se voyait, l'indentation
305
+ de 40px qui l'accompagnait non.
306
+
307
+ ### Fixed — `OrigamGrid` neutralise la puce et l'indentation que le navigateur pose sur un `ul`
308
+
309
+ Signalé sur capture : un petit cercle apparaissait à gauche de chaque élément de
310
+ grille. La cause est **dans le DS**, pas dans la page : le thème de base
311
+ (`themes/origam.theme.ts`) impose `tag: 'ul'` aux grilles et `tag: 'li'` aux
312
+ items — choix sémantique volontaire — mais une grille n'est pas une liste à
313
+ puces, et le navigateur appliquait quand même ses styles de `ul`.
314
+
315
+ | | avant | après |
316
+ |---|---|---|
317
+ | `list-style-type` | `circle` sur **9** items visibles | **0** |
318
+ | `padding-inline-start` | **40px** sur 3 des 7 grilles de la page | **0** |
319
+
320
+ ⚠️ L'indentation de 40px **n'était pas dans le signalement** — trouvée en
321
+ mesurant. C'est la moitié invisible du même défaut. Et les autres pages du site
322
+ ne montraient rien parce qu'elles remettaient `list-style: none` **à la main,
323
+ page par page** : exactement le signal que le manque était dans le DS.
324
+
325
+ ⛔ `:where()` n'est pas décoratif, il met la spécificité à **zéro**. Une règle
326
+ scopée ordinaire vaudrait (0,2,0) et **confisquerait le canal `padding`**, les
327
+ classes utilitaires émises par `usePadding` valant (0,1,0) — c'est le défaut de
328
+ #950, corrigé sur 21 composants dans la 2.18.14 juste avant. Le réintroduire
329
+ dans la version suivante aurait été absurde.
330
+
331
+ Vérifié sur la CSS **réellement servie**, pas sur l'intention :
332
+ `:where(.origam-grid[data-v-711d86d8]) { list-style: none; padding-inline-start: 0 }`.
333
+ La règle bat l'origine navigateur et perd contre tout ce qu'un consommateur écrit.
334
+
335
+ ⚠️ Note de méthode conservée parce qu'elle a failli produire un faux défaut : une
336
+ première sonde ajoutant `.origam--p-6` à la grille rendue lisait `0px` et
337
+ suggérait une confiscation. Faux — un `div` neuf avec la même classe reçoit bien
338
+ 24px, et l'énumération de règles utilisée renvoyait **0 règle au total**, les
339
+ feuilles cross-origin étant avalées par un `try/catch`. L'artefact était dans la
340
+ mesure, pas dans le code.
341
+
342
+ ### Fixed — #955 : le menu du site lit enfin son canal de thème (et le retrait seul aurait tout cassé)
343
+
344
+ `padding: 4px` en dur sur `.appbar-menu .origam-menu__content` écrasait
345
+ `--origam-menu__content---padding`, que sept thèmes déclarent : quatre en
346
+ demandaient 6px et obtenaient 4px.
347
+
348
+ ⛔ Mais le simple **retrait** du littéral — ce que le ticket prescrivait — ne rend
349
+ pas la main au thème. Mesure, 8 identités × 2 modes : le panneau tombe à **0px
350
+ partout**. La régression a failli être livrée.
351
+
352
+ | | résultat |
353
+ |---|---|
354
+ | avant | 4px partout — 4 thèmes sur 7 ignorés |
355
+ | retrait seul | **0px partout** — régression |
356
+ | après | **16/16 conformes**, zéro débordement |
357
+
358
+ La cause, trouvée en énumérant les règles qui matchent l'élément : **aucune** ne
359
+ pose de padding sur `.origam-menu__content`. Le DS applique
360
+ `var(--origam-menu__content---padding, 4px)` sur `.origam-menu__list`, un élément
361
+ rendu **uniquement dans le repli du slot par défaut**. Dès qu'un consommateur
362
+ fournit son contenu — ce que fait toute la navigation du site — l'élément
363
+ n'existe pas et le panneau ne reçoit aucun padding du DS. **Le token porte donc
364
+ le nom d'un élément et peint sur un autre, qui n'est presque jamais rendu.**
365
+
366
+ Le vrai défaut reste côté DS et n'est pas corrigé ici : le corriger changerait la
367
+ géométrie de tous les consommateurs d'`origam-menu`, ce qui n'est pas une PR
368
+ marketing. Consigné dans le commentaire, à côté du code.
369
+
370
+ ### Fixed — wireframe pricing : les CTA alignés en bas quelle que soit la longueur du contenu
371
+
372
+ Deux causes distinctes, et la seconde ne s'est vue **qu'en regardant le rendu**.
373
+
374
+ 1. La chaîne d'étirement était rompue au milieu. La grille étire bien les trois
375
+ cartes à la même hauteur (184px chacune), mais `.origam-card__content` est en
376
+ `display: block` et ne remplit pas la carte (159px dans une carte de 184) : le
377
+ `block-size: 100%` de l'inner résolvait donc sur 159. Corrigé en chaînant
378
+ `flex: 1` + `min-block-size: 0` sur toute la profondeur, au lieu de demander
379
+ « 100% » à un parent qui ne fait pas 100%.
380
+ 2. ⛔ Le premier correctif a produit des **chiffres parfaits et un rendu faux**.
381
+ Écart entre les bas de boutons : 0. Et pourtant les CTA des cartes courtes
382
+ faisaient **51px** de haut contre **28px** pour celui du milieu — ils
383
+ s'étiraient. Cause : `origam-btn` avec la prop `block` pose `flex-grow: 1`,
384
+ donc en colonne flex le bouton absorbe l'espace libre.
385
+
386
+ ⚠️ Deux manques du DS notés au passage, non corrigés : une carte à hauteur
387
+ imposée dont le contenu ne s'étire pas, et `block` qui implique `flex-grow: 1` —
388
+ un comportement qui surprendra quiconque pose un `origam-btn block` dans une
389
+ colonne flex.
390
+
391
+
392
+ ## [2.18.14] - 2026-09-26
393
+
394
+ Un seul correctif, sur deux props que **21 composants** ignoraient en silence
395
+ depuis toujours — la classe était émise, et rien ne bougeait.
396
+
397
+ ### Fixed — #950 : `:where(&)` sur les défauts `padding` / `margin`, 21 composants, 93 déclarations
398
+
399
+ La forme d'échelle de `padding` / `margin` (`padding="6"`) n'appliquait **rien**
400
+ sur 21 composants : la classe utilitaire était bien émise, et le longhand calculé
401
+ ne bougeait pas d'un pixel.
402
+
403
+ **La cause n'est pas celle que le ticket nommait d'abord.** La spécificité et
404
+ « longhand > raccourci » sont vraies mais ne discriminent rien — elles valent
405
+ pour toutes les familles d'utilitaires. Le filtre décisif est l'absence de
406
+ *companion inline* sur le chemin tokenisé : `usePadding` / `useMargin` sautent le
407
+ rung inline, quand `rounded`, `elevation`, `border`, `bg` et `fg` en émettent un,
408
+ et l'inline bat toujours une règle scopée. Ces deux familles n'avaient donc plus
409
+ aucun canal capable de gagner la cascade.
410
+
411
+ **93 déclarations** passent sous `:where(&)`, soit 31 couples (composant × prop)
412
+ sur **21 composants**. Les deux chiffres sont vérifiés sur le diff : 21 fichiers
413
+ de composant touchés, et 85 déclarations *longhand* plus 8 *raccourcis* = 93,
414
+ avec autant de lignes retirées qu'ajoutées — un déplacement pur, ce qui est
415
+ cohérent avec l'absence de changement de rendu.
416
+
417
+ Vérifié via `@vue/compiler-sfc` : le compilateur scopé insère l'attribut **à
418
+ l'intérieur** de la pseudo-classe, `:where(.origam-sheet[data-v-abc123])` =
419
+ (0,0,0).
420
+
421
+ ⛔ **Seuls les défauts descendent.** Les 495 déclarations modificatrices ou d'état
422
+ (`:hover` à (0,3,0), `--variant-flat` à (0,2,0), `+ .origam-row` à (0,3,0)) sur
423
+ 154 composants gardent leur poids — c'est précisément ce qui a fait écarter
424
+ `@layer`, et trois contrôles au navigateur le confirment (`--compact` 8px,
425
+ `--inset` 16px, `--variant-minimal` 0px peignent toujours). La réserve de la
426
+ décision — « une règle interne comptant sur l'ordre source peut changer de
427
+ gagnant » — est levée et vérifiée au compilateur : aucun gagnant ne change nulle
428
+ part.
429
+
430
+ ⚠️ **Trois composants restent morts, sciemment** : Btn, Chip et Stepper. Chez eux
431
+ un modificateur **toujours présent** (`--size-default`, `--density-default`)
432
+ déclare la propriété à (0,2,0) ; il bat l'utilitaire quoi qu'on fasse au défaut,
433
+ et Chip n'a même aucune déclaration racine à abaisser. Les ressusciter exigerait
434
+ d'abaisser une règle modificatrice, ce que la décision interdit. Mais la prop
435
+ n'est pas morte pour autant — seule sa forme d'**échelle en chaîne** l'est :
436
+ `:padding="6"` (nombre), `padding="6px 8px"` (custom) et les formes
437
+ directionnelles peignent.
438
+
439
+ Partiels et nommés dans le code : Blockquote (`--variant-default` garde
440
+ `padding-inline-start`) et Row (la gouttière `+ .origam-row` garde
441
+ `margin-block-start`).
442
+
443
+ Épinglé par `utility-cascade-padding-margin-950.spec.ts` — 34 tests, dont 3 de
444
+ non-régression qui vérifient qu'un modificateur continue de battre l'utilitaire.
445
+ A/B fait : **34 failed à `HEAD~1`, 34 passed à `HEAD`**.
446
+
447
+ Au passage, story Sheet/Design : `style="padding: 16px"` remplacé par
448
+ `padding: '4'`. Ce hatch rendait le contrôle « Padding » de la variante
449
+ définitivement inerte — `OrigamSheet` reverse son binding `:style` dans une règle
450
+ générée portant l'**id** de l'élément, soit (1,0,0), qui battait l'utilitaire, la
451
+ règle scopée, et le retrait de l'attribut `style` lui-même.
452
+
453
+
454
+ ## [2.18.13] - 2026-09-26
455
+
456
+ Deux correctifs, dont la cause racine du journal de 5,5 Go de #853. Dans les deux
457
+ cas la **mesure a corrigé le périmètre du ticket** — en le réduisant de 15 à 12
458
+ composants pour l'un, en l'étendant d'une surface à quatre pour l'autre.
459
+
460
+ ### Fixed — #916 : `inheritAttrs` sur 12 racines fragment/teleport — pas 15, et 4 auraient été cassés
461
+
462
+ Vue ne peut pas fusionner les attributs de *fallthrough* sur une racine qui n'est
463
+ ni élément ni composant — fragment, texte, ou `<teleport>`. En dev il logue alors
464
+ « Extraneous non-props attributes », et la trace de ce warning **sérialise les
465
+ props de chaque ancêtre**, dont le `vnode` de `RouteProvider` : **~4,4 Mo par
466
+ occurrence**. C'est la cause racine de #853, le journal de 5,5 Go qui finissait en
467
+ worker mort et 500 sur toutes les routes.
468
+
469
+ ⛔ **Le ticket annonçait 15 composants. Il y en avait 12, et 4 de ses entrées
470
+ étaient fausses** — `OrigamChart`, `OrigamNumberField`, `OrigamSliderField`,
471
+ `OrigamSwitch`, mesurés à **0 avertissement**. Les « réparer » aurait fabriqué le
472
+ défaut de #492 quatre fois : sur une racine à élément unique,
473
+ `inheritAttrs: false` avale **en silence** tous les attributs du consommateur.
474
+
475
+ La différence vient de la méthode, et c'est l'enseignement du lot. Le relevé du
476
+ ticket comptait les **balises de profondeur 0** ; ici la question est posée au
477
+ **compilateur Vue** (forme du `codegenNode` racine), puis confirmée au runtime en
478
+ montant chaque composant avec un attribut non déclaré et un écouteur non émis :
479
+
480
+ | méthode | verdict |
481
+ |---|---|
482
+ | comptage à profondeur 0 | 15 candidats, **4 faux, 2 manques** |
483
+ | `codegenNode` du compilateur | **12**, tous reproduits au runtime |
484
+
485
+ Une paire `<template v-if>` / `<template v-else>` compte pour deux nœuds et en
486
+ rend **un** : le fallthrough marche, et il n'y a pas de défaut.
487
+
488
+ Effet mesuré : **34 avertissements → 0**, sur 27 montages, chacun dans la
489
+ configuration qui **atteint** sa branche fragment — un montage par défaut aurait
490
+ donné un faux vert sur les 4 racines mixtes.
491
+
492
+ ⚠️ Aucun changement d'API. Les attributs atterrissent exactement où ils
493
+ atterrissaient : table identique ligne pour ligne sur les 8 branches, et chacun
494
+ des 7 `v-bind` retiré un par un produit un rouge, donc chacun est porteur. Où ils
495
+ vont a été décidé composant par composant — forward explicite sur la racine
496
+ visible (5), drapeau seul là où le forward existait déjà (2), et **nulle part,
497
+ délibérément**, pour les 5 dont toutes les branches sont des fragments.
498
+
499
+ ⚠️ Non touché volontairement : le `<tr>` `--loading` de `DataTableRows`. Il
500
+ ressemble à son jumeau `--no-data` mais n'est **pas** une racine — le compilateur
501
+ donne `COND{ FRAGMENT | COND{ element(tr) | FRAGMENT } }` — donc y lier `$attrs`
502
+ aurait introduit un comportement nouveau sous couvert d'un correctif de warning.
503
+ L'asymétrie est préexistante et désormais mesurée.
504
+
505
+ Une nouvelle garde ferme la récidive, parce que **le symptôme est un fichier de
506
+ journal, pas un test rouge** : aucune autre vérification du dépôt ne le voit, et
507
+ #853 a vécu des mois. Ses 26 fixtures couvrent le rappel (les 12 formes réelles)
508
+ et la précision — les 4 faux positifs sont des **témoins négatifs**, les poser en
509
+ violation enverrait quelqu'un « réparer » quatre composants sains. Un contrôle de
510
+ **cécité** exige qu'un template que le compilateur refuse nomme sa raison au lieu
511
+ de se lire « propre ». Sa portée est **statique** et le dit : elle prouve que
512
+ l'option est déclarée, jamais que les attributs atteignent encore le bon élément —
513
+ c'est la moitié dangereuse, et elle vit dans le spec TU.
514
+
515
+ ⚠️ `v-bind="{ ...$attrs }"` et non `v-bind="$attrs"` sur les 5 forwards ajoutés,
516
+ et ce n'est pas cosmétique : la garde `id-forwarding` crédite un `v-bind="$attrs"`
517
+ littéral comme preuve que la prop `id` atteint le DOM. L'inférence est fausse dès
518
+ que `id` est une prop **déclarée**, puisque Vue la retire de `$attrs`. Avec la
519
+ forme nue, la garde annonçait `SnackbarGroup:no-id-reach` comme « déjà corrigée »
520
+ et demandait sa suppression, sur un composant où l'`id` du consommateur n'atteint
521
+ toujours aucun nœud — supprimer cette entrée aurait masqué un défaut vivant.
522
+
523
+ Un test de #550 est renversé en l'assumant : il affirmait que « Vue **avertit** au
524
+ lieu d'avaler » et présentait cet avertissement comme « tout le gain ». Le
525
+ raisonnement tenait tant qu'on ignorait son coût. Il assert désormais les **deux**
526
+ moitiés — plus d'avertissement (#916) **et** toujours pas de classe sur ces lignes
527
+ (#550, inchangé).
528
+
529
+ ### Fixed — #924 : sous `cartoon` clair, l'anneau de focus était invisible sur toutes ses surfaces claires
530
+
531
+ `cartoon` clair déclarait `border.focus: '#ff8fa3'` — le rose de
532
+ `action.primary.bg`, qui ne se détache d'**aucune** de ses surfaces claires. Le
533
+ ticket en nommait une ; la mesure en trouve **quatre**, toutes sous le seuil de
534
+ 3:1 de WCAG 2.1 SC 1.4.11 :
535
+
536
+ | surface | contraste |
537
+ |---|---|
538
+ | page | 2,07:1 |
539
+ | `surface.raised` | 2,16:1 |
540
+ | `overlay` / `sunken` | 1,96:1 |
541
+ | `feedback.danger.bgSubtle` | **1,53:1** |
542
+
543
+ Le token est **global** : 38 des 96 familles de composants peignent leur anneau
544
+ avec — 10 le lisent directement, 28 y accèdent via Btn / Field /
545
+ SelectionControl. **L'identité entière était inutilisable au clavier.**
546
+
547
+ Correctif : `border.focus` → `#c0174a`, qui est `action.primary.fgSubtle` de
548
+ cette même palette — la réponse que cartoon donne déjà à « le rose, à une valeur
549
+ qui se lit sur un fond clair ». **Valeur dérivée, pas choisie à l'œil.** L'anneau
550
+ passe à **4,30–6,06:1** sur ces quatre surfaces, dans la bande des 7 autres
551
+ identités (3,82–12,56).
552
+
553
+ La couche retenue est la **sémantique** (`vars.color.border.focus`) et non les
554
+ props : vérifié, aucun composant du DS ne déclare de prop exprimant la couleur de
555
+ l'anneau — elle n'est lue que comme `var(--origam-color__border---focus)` depuis
556
+ le SCSS. Les props ne peuvent pas l'exprimer ; les `cssVars` seraient un
557
+ contournement d'un tier qui nomme déjà la valeur.
558
+
559
+ ⚠️ Le bloc **sombre** de cartoon garde `#ff8fa3` **à dessein** : mesuré conforme
560
+ (6,09–8,05:1). Corriger les deux par symétrie est l'erreur de #829.
561
+
562
+ Un anneau se mesure contre **ce qu'il entoure** : `outline-offset` est positif sur
563
+ Btn et SelectionControl, donc la couleur adjacente est le fond du **conteneur** —
564
+ une carte, un menu, une alerte — pas « le fond de la page ». L'outil est élargi en
565
+ conséquence, de un composant sur un fond à **8 identités × 2 modes × 6 fonds
566
+ sémantiques × 5 familles focusables**, soit 448 lignes. A/B du spec, vrai `$?`
567
+ hors pipe : sans le correctif **1 échec / 17 verts**, l'échec étant
568
+ `cartoon/light … = 2.07:1` ; avec, **18/18**.
569
+
570
+ ### Tooling
571
+
572
+ - **La garde 30 balayait le disque, pas l'index git** (#966), donc
573
+ `packages/marketing/public/stories/` — 35 Mo de bundle Histoire, **zéro fichier
574
+ suivi par git**. La pollution allait dans les **deux sens**, ce qui rend le
575
+ symptôme dangereux : le bundle *déclare* des `--origam-*`, donc il entrait aussi
576
+ dans l'ensemble émetteur et des lectures réellement mortes apparaissaient
577
+ corrigées. Une seule déclaration dans un faux fichier de trois lignes faisait
578
+ passer **8 entrées de baseline** en « déjà corrigée » : un mainteneur suivant le
579
+ message de la garde aurait supprimé 8 lignes décrivant de **vrais défauts**.
580
+ L'énumération passe par `git ls-files --cached --others --exclude-standard` — la
581
+ source de vérité connaît déjà les artefacts, là où une liste de noms ne peut pas
582
+ contenir le nom du **prochain** artefact. ⛔ La variante minimale du ticket
583
+ (exclure `public/`) aurait été fausse : ce répertoire porte 25 fichiers suivis.
584
+ - **`guards:self` ne découvrait qu'un niveau sur trois** (#964) : cinq self-tests
585
+ vivent au niveau `guards/` et n'étaient invoqués par **rien** — ni le runner, ni
586
+ la CI, ni un script npm. Leurs seules « références » étaient les lignes
587
+ `Run: node …` de leurs propres en-têtes, dont celui de `token-var-channels`, la
588
+ garde la plus sollicitée du dépôt. **Lancés à froid avant tout câblage, les cinq
589
+ passent** : aucune garde muette en production. Un **sixième orphelin** que le
590
+ ticket n'avait pas recensé a été trouvé par le contrôle anti-récidive
591
+ lui-même. Compteur **17/17 → 24/24**, et la récidive est fermée par un balayage
592
+ qui fait échouer le runner **avant exécution** sur tout `*.selftest.mjs` que la
593
+ découverte raterait.
594
+ - **#902 est invalidé par la mesure, dans l'autre sens.** Le ticket annonçait
595
+ 5 faux positifs de la garde 29 ; la sonde montre qu'elle ne les **signale** pas,
596
+ elle les **rate** — aucun n'est dans la baseline. Et ils doivent continuer à
597
+ être ratés, car les 5 sont **porteurs** : le `!important` protège la barre de
598
+ transport contre un thème posant `density: 'comfortable'` sur chaque bouton, et
599
+ les 4 `calc(24px / 2)` sont **dérivés** et non dupliqués — l'égalité avec `12px`
600
+ n'est vraie qu'à l'échelon comfortable, et retirer les lignes casserait
601
+ `gutter="none"`. La correction prescrite par le ticket, appliquée telle
602
+ qu'écrite, produit **2 FAIL** sur les deux fixtures étiquetées « faux positif ».
603
+ Les deux exclusions deviennent **explicites et testées** au lieu d'être
604
+ accidentelles — aujourd'hui `'0px !important' !== '0px'` sauve la garde par
605
+ hasard, et un accident n'est pas une décision.
606
+
607
+ ### Documentation
608
+
609
+ - Le commentaire de `_shared.css` justifiait un override **vivant** par une
610
+ prémisse fausse (#961). ⚠️ Et le ticket se trompait aussi : il affirmait que
611
+ `--origam-radius---btn` n'était « déclaré nulle part », conclusion tirée d'un
612
+ grep sur `light.css` seul. Il est déclaré dans `_shared.css`. **Une seule des
613
+ deux prémisses était fausse, pas deux** ; le code reste, le commentaire dit
614
+ désormais la vraie raison.
615
+
616
+
617
+ ## [2.18.12] - 2026-09-25
618
+
619
+ Un seul des 19 commits touche `packages/ds/src`, donc le **code publié** : le
620
+ reste est marketing, outillage et documentation. Deux autres touchent bien
621
+ `packages/ds/` mais sous `scripts/`, que le tarball n'embarque pas. Les
622
+ compteurs de gardes cités dans les corps de commit de ce lot divergent (29/29
623
+ puis 30/30) : la garde 30 est **arrivée au milieu du lot**.
624
+
625
+ ### Fixed — #957 : `OrigamChip` n'ouvrait aucune gouttière pour `prepend` / `append`
626
+
627
+ `__close` possédait ses deux canaux de marge depuis toujours ; `__prepend` et
628
+ `__append` n'en avaient **aucun**. Une puce avec icône rendait donc son icône
629
+ collée au texte — mesuré **0 px**, `margin-inline-end: 0px`, `gap: normal`, sur
630
+ les 8 identités — et **aucun thème ne pouvait corriger l'espacement puisque le
631
+ canal n'avait jamais été ouvert**.
632
+
633
+ La valeur par défaut, 6 px, n'est pas un nouveau chiffre de design : c'est celle
634
+ de `--origam-chip__close---margin-inline-start`, le précédent du composant
635
+ lui-même.
636
+
637
+ ⛔ La gouttière est **conditionnelle au libellé non vide**, et c'est le cœur du
638
+ correctif. `__content` n'a pas de `v-if` : une puce icône-seule rend quand même
639
+ un `<div class="…__content">` portant le nœud texte vide que Vue émet pour
640
+ `{{ text }}`. Une marge inconditionnelle y accrocherait un **fantôme de 6 px**.
641
+ Mesuré en Chromium, ce qui fait de `:empty` le bon discriminant :
642
+
643
+ | contenu de `__content` | `:empty` | gouttière |
644
+ |---|---|---|
645
+ | `"texte"` | false | 6px |
646
+ | `<div></div>` | true | 0px |
647
+ | nœud texte vide (le cas Vue) | true | **0px** |
648
+ | `" "` (une espace) | false | 6px |
649
+
650
+ `__prepend` dépend de son frère **suivant**, d'où `:has(+ …)` ; `__append` de son
651
+ frère **précédent**, d'où un simple `+`. Les deux gardent la déclaration sur
652
+ l'élément que le token **nomme**. Là où `:has()` manque, la gouttière prepend est
653
+ simplement absente — le rendu d'aujourd'hui, pas une régression.
654
+
655
+ ⚠️ **Effet visuel assumé** : toute puce portant un libellé **et** un
656
+ `prepend-icon` / `append-icon` s'élargit de 6 px (mesuré 105 → 111 px). Les puces
657
+ icône-seule et sans affixe ne bougent pas.
658
+
659
+ ⚠️ Le ticket — et le corps du commit après lui — chiffrent l'exposition à
660
+ **« 217 emplacements »**. Ce nombre n'a pas pu être reproduit : huit méthodes de
661
+ comptage sur l'arbre au tag `v2.18.12` donnent 145, 233, 241, 272, 373 ou 413
662
+ selon qu'on compte les lignes ou les occurrences et selon le périmètre de
663
+ fichiers, jamais 217. L'ordre de grandeur — plusieurs centaines de sites d'appel
664
+ — est juste ; le chiffre exact reste **non établi**, et il n'entre dans aucune
665
+ décision du correctif.
666
+
667
+ Les deux nouveaux noms sont déclarés dans `light.css` / `dark.css` (bloc
668
+ explicite **et** bloc `prefers-color-scheme`) et leurs jumeaux SCSS, et ajoutés à
669
+ `tokens.type.ts`. Aucun changement d'API — ni prop, ni emit, ni slot.
670
+
671
+ ### Fixed — #958 : 151 lectures de deux tokens qui n'existent pas
672
+
673
+ Le site lisait `--origam-font-family---mono` (87×) et `--origam-font-size---base`
674
+ (64×) — **151 lectures dans 28 fichiers**, sur deux noms qu'aucune feuille ne
675
+ déclare. La grammaire du dépôt sépare le bloc BEM par un **double tiret bas** ; le
676
+ marketing écrivait un tiret simple. Chaque lecture portait un repli littéral, donc
677
+ **rien ne cassait à l'écran** : le repli peignait toujours, et le canal de thème
678
+ était mort sans qu'aucun test, aucun lint, aucune console ne le dise.
679
+
680
+ ⛔ **L'échelon retenu n'est pas celui que le ticket proposait, et c'est une mesure
681
+ qui l'a décidé.** Le ticket suggérait `--origam-font__size---md`. Or
682
+ `primitive.css` pose `md: 0.875rem`, tandis que le repli effectivement rendu par
683
+ les 64 lectures était `1rem`, c'est-à-dire l'échelon `lg`. Substituer `md` aurait
684
+ **rétréci le texte de 12,5 %** dans 27 fichiers en croyant faire un renommage.
685
+
686
+ 151 lignes ajoutées, 151 retirées, toutes des déclarations CSS dans un bloc
687
+ `<style>` : aucun `.ts`, aucun template, aucune clé i18n touchés.
688
+
689
+ **La vraie demande du ticket était la garde.** `token-var-channels` pose exactement
690
+ la bonne question depuis #435, mais ne balayait que `packages/ds/`. Le site
691
+ marketing, plus gros consommateur de tokens du dépôt et sa vitrine, était regardé
692
+ par **personne**. La nouvelle garde délègue son **verdict** à l'analyse existante
693
+ — aucun doublon de la logique de décision — et seule la **collecte** est neuve,
694
+ parce que trois différences interdisaient d'élargir simplement le périmètre :
695
+ l'ensemble émetteur est **double** (le marketing déclare légitimement ses propres
696
+ `--origam-…`), la portée de ces déclarations est **globale** et non locale au
697
+ fichier, et les consommateurs ne sont pas que des `.vue`.
698
+
699
+ La direction « dormant » n'est **pas** reprise, délibérément : « ce token du DS
700
+ n'est lu par personne » est un défaut dans une bibliothèque publiée et le cas
701
+ **normal** chez un consommateur. Reporter les ~2 900 autres serait du bruit, et
702
+ une garde bruyante se fait désactiver.
703
+
704
+ ⚠️ **L'extension révèle 50 autres noms morts**, laissés en baseline et **non
705
+ corrigés** — recensement remis pour arbitrage plutôt que corrigé en silence :
706
+ 841 lectures mortes / 52 noms distincts / 292 paires, dont **44 sans aucun
707
+ repli** — la déclaration entière est alors jetée par le navigateur, donc rendu
708
+ réellement cassé et pas seulement canal mort.
709
+
710
+ ### Fixed — #922 : les résidus du pipeline supprimé que la première passe avait laissés
711
+
712
+ Complément à une première correction qui avait traité les 4 valeurs de locale
713
+ nommées par le ticket mais laissé trois résidus. Les appels `t(clé, fallback)`
714
+ portaient encore le texte d'origine mot pour mot — invisibles à l'écran
715
+ puisque la clé existe, mais lisibles dans un dépôt public, **ce qui est
716
+ précisément l'argument du ticket**.
717
+
718
+ Un **chiffre gravé dans une locale** est retiré : `218 components` figurait dans
719
+ deux valeurs. Recompté à 218 le jour même, mais un nombre dans une locale se
720
+ périme tout seul — reformulé sans chiffre, sur la propriété vérifiée qui porte
721
+ vraiment l'argument (« no per-component wiring », ADR-005).
722
+
723
+ Deux commentaires citaient des chemins **supprimés le 2026-08-31**. Pour l'un,
724
+ l'affirmation restait **vraie** et seule sa source était morte : les 10 barreaux
725
+ ont été re-vérifiés un à un contre `primitive.css`, tous identiques, commentaire
726
+ repointé.
727
+
728
+ ⚠️ La ligne « Pipeline » du détail de composant est **masquée, pas corrigée** —
729
+ décision du propriétaire en attendant #960. La donnée vit **en base** : 129
730
+ entrées, dont **125 vendent encore** « Built with Style Dictionary v4 ». Rendue,
731
+ cette ligne contredisait le paragraphe juste au-dessus, sur **chaque** composant
732
+ du catalogue. Restaurer la ligne fait partie de #960, et le commentaire laissé
733
+ dans le template le dit.
734
+
735
+ Non touché volontairement, et c'est ce qui rend le lot honnête : les entrées de
736
+ changelog des versions qui ont **réellement** livré avec Style Dictionary, et la
737
+ note sur la conformité DTCG de PrimeVue, qui est vraie.
738
+
739
+ ### Fixed — #951, #944, #946, #294 : quatre défauts de menu mesurés sur les 8 identités
740
+
741
+ - **`/why-origam` lisait `---fg` comme une teinte.** Trois déclarations peignaient
742
+ une surface **neutre** avec `--origam-color__feedback--{success,warning}---fg`.
743
+ `fg` n'est pas la teinte de l'intention : c'est le premier plan posé **sur** son
744
+ aplat, et hors de cet aplat il rend un neutre. Avant, `fg` ne rendait une teinte
745
+ **nulle part** sur les 16 configurations ; 9 sur 16 passaient sous 1,6:1, et
746
+ `glass` sombre à **1,00** — exactement la couleur de son fond. Après : **0
747
+ configuration sous 3:1**, les 16 entre 4,38:1 et 13,22:1. Le point 3 du ticket
748
+ (la nomenclature invite à l'erreur, une garde vaudrait mieux qu'un quatrième
749
+ correctif) **n'est pas traité** et #951 reste ouvert dessus.
750
+ - **Le menu déroulant portait la forme de tiroir M3** — 28px de rayon sur un
751
+ conteneur et des lignes de 48px de haut, donc une pilule. Vérifié **dans les
752
+ sources de tokens Material**, pas de mémoire : un conteneur de menu déroulant
753
+ est `corner-extra-small` = 4px, un item de liste `corner-none` = 0, et
754
+ `corner-extra-large` = 28px est la forme d'une destination de **tiroir**.
755
+ ⛔ Au passage, un fait qui circulait est corrigé : **il n'y a pas de rognage**.
756
+ La ligne ne déborde jamais de son conteneur (`scrollWidth === clientWidth`), et
757
+ forcer `overflow: visible` ne change pas un pixel — deux captures du coin à
758
+ `deviceScaleFactor` 8 sont identiques. Deux arcs de **même rayon** laissaient un
759
+ croissant très fin qui se lit comme une coupe à l'échelle 1×.
760
+ - **Conteneurs de `glass` et `cartoon` alignés sur leur rung `sm`**, et la valeur
761
+ **vient du thème** et non de l'œil : ces deux identités déclarent `sm`, `md`,
762
+ `lg` mais **pas** `xs`, donc `rounded: 'xs'` retomberait sur le primitif DS à
763
+ 2px, une valeur qu'aucune des deux n'a choisie. Après : **0 vignette au-delà de
764
+ 18px contre 6 avant**.
765
+ - **Le dropdown d'`origam-select` ne suivait pas**, et le mécanisme est correct :
766
+ `rounded` sur `origam-menu` est un **défaut de composant** que trois surfaces
767
+ héritent, tandis que `menuProps` est un binding **explicite** qui bat le défaut
768
+ de thème. Canal séparé, donc correction séparée — il fallait régler la valeur là
769
+ où elle est écrite, pas retirer le canal. Recensement complet fait pour ne pas y
770
+ revenir une troisième fois : **quatre** thèmes portent un `menuProps`, dont deux
771
+ déjà alignés qui servent de **témoins négatifs**. Le **champ** garde
772
+ `rounded: 'lg'` : c'est un contrôle de formulaire, pas un menu.
773
+ - **La taille du texte des menus suit enfin celle de l'activateur.** Les items
774
+ rendaient 16px quand le lien qui les ouvre en rend 13px, sur **7 identités sur
775
+ 8**. `geek` était la seule alignée, et pour une raison qui n'avait rien à voir :
776
+ une règle posée pour sa police monospace fixait aussi `font-size: 0.8125rem`.
777
+ L'exception cachée devient la règle générale, portée par **une seule**
778
+ déclaration que l'activateur et les items lisent tous deux. ⛔ Déclarée sur
779
+ `:root` et non sur la barre : le contenu d'`origam-menu` est **téléporté** hors
780
+ d'elle, une variable posée sur la barre ne l'atteindrait jamais.
781
+ - **La liste du menu `material` avait 8px de `padding-inline` en trop.** ⛔ Corrigé
782
+ par la **prop**, pas la cssVar, et ce n'est pas un goût : sur un
783
+ `origam-list nav`, `OrigamList` **re-déclare** le token sur l'élément lui-même
784
+ depuis sa règle scopée, et une cssVar de thème posée à la racine est **héritée**
785
+ — elle ne peut pas battre une déclaration locale.
786
+
787
+ ### Changed — /roadmap refondue sur wireframe validé, et 8 affirmations fausses corrigées
788
+
789
+ Page ramenée de **11 892 à 6 868 px** en desktop 1440 (**−42,2 %**) et de 20 058 à
790
+ 11 279 px en mobile 390 (**−43,8 %**), mesuré sur la même machine et un serveur
791
+ isolé. La section des phases, qui pesait 4 203 px à elle seule, tombe à 1 326 px
792
+ (**−68,5 %**) **sans qu'aucun item soit coupé ni raccourci** : les 38 chantiers
793
+ gardent titre et description complets, seulement repliés.
794
+
795
+ Chaque chiffre de la page a été **re-mesuré, jamais recopié** : npm 2.18.8 →
796
+ **2.18.11**, tests 7 187/563 → **7 192/564**, specs e2e 254 → **256**, monorepo
797
+ « 6 paquets » → **5**. La barrière CI était annoncée par deux textes qui **se
798
+ contredisaient** — « 81 de 254 » d'un côté, « 58 de 229 » de l'autre, tous deux
799
+ faux ; un seul survit, **81 de 256**, compté en interrogeant Playwright lui-même
800
+ plutôt qu'en recomptant les littéraux de la liste blanche. Un composant annoncé
801
+ sous le nom `OrigamSound` **n'a jamais été un export** : la page décrivait un
802
+ import qui échouerait.
803
+
804
+ ⛔ Et la page annonçait « CHANGELOG à jour ». **Faux**, passé à `done: false` — le
805
+ document s'arrêtait alors à 2.18.8 quand npm servait 2.18.11. C'est le même trou
806
+ que la présente entrée comble, et il avait déjà été identifié ici.
807
+
808
+ La section « livré » annonçait « 15 components & features » au-dessus de 15 tuiles
809
+ écrites à la main : un lecteur en déduisait que le DS ne livre que 15 composants,
810
+ alors que **tout** le catalogue est publié. Elle rend désormais le catalogue
811
+ complet **lu en direct** sur les mêmes endpoints que `/components`. ⛔ **Aucun
812
+ compte n'est gravé**, nulle part — ni titre, ni fallback, ni commentaire présenté
813
+ comme un fait : la taille du catalogue **dépend de l'environnement**, mesuré à
814
+ 218 entrées en local contre **194** sur l'env déployé. Un littéral corrigé aurait
815
+ donc déjà été faux sur l'un des deux.
816
+
817
+ Cinq constats **internes** que la page publiait sont retirés, dont « la CI ne
818
+ barre que 81 des 256 specs » et « pas encore de communauté » — la comptabilité
819
+ interne d'un dépôt n'a pas à être publiée, et l'un des cinq était déjà devenu
820
+ faux.
821
+
822
+ ⚠️ Trois défauts du DS trouvés **en implémentant**, signalés et non corrigés là :
823
+ `<origam-sheet padding="6">` est une **prop morte** (cause de #950, corrigé en
824
+ 2.18.14), `feedback--success---fg` vaut `#000000` sous `geek`, et les liens
825
+ d'ancrage atterrissaient **sous** l'app bar fixe.
826
+
827
+ ### Tooling
828
+
829
+ - `marketing-primary-nav.spec.ts` existait de longue date **sans être exécuté par
830
+ aucun job** : il était inscrit dans la baseline `spec-coverage` comme non
831
+ exécuté, donc la garde restait verte et rien ne le signalait. Avant de le
832
+ brancher, la stabilité du **fichier entier** a été mesurée et pas seulement
833
+ celle des cas ajoutés — brancher un fichier qui contient des cas instables
834
+ rendrait la CI rouge en permanence, ce qui est **pire** que pas de garde :
835
+ 110/110 en `--repeat-each=5 --retries=0`, machine au repos, serveur isolé.
836
+ - Quatre captures d'écran committées par accident à la racine sont retirées. Elles
837
+ étaient arrivées dans un commit qui n'avait rien à voir, balayées par un
838
+ `git add`, et **déjà supprimées du disque sans que la suppression soit
839
+ enregistrée** — l'arbre restait donc sale et un `git add -A` distrait les aurait
840
+ re-committées. `/*.png` rejoint `.gitignore` : une image ne se relit pas dans
841
+ une revue, elle passe.
842
+
843
+ ### Documentation
844
+
845
+ - La règle « pas de tag pour un lot qui ne touche pas `packages/ds/` » est
846
+ consignée, avec sa raison **mécanique** : `release.yml` publie depuis
847
+ `packages/ds/` exclusivement, donc un lot hors de ce dossier produit un tarball
848
+ **byte-identique**. ⚠️ Le paragraphe disait auparavant l'**inverse** — « when in
849
+ doubt, tag: a redundant patch costs nothing ». C'était faux : un patch redondant
850
+ coûte un numéro de version qui ne veut rien dire, sur un registre où une version
851
+ publiée ne se retire jamais.
852
+ - Deux pièges de mesure ajoutés, tous deux payés sur le terrain : **le serveur
853
+ Nuxt ne recharge pas les thèmes** (imports de `nuxt.config.ts`, hors du graphe
854
+ HMR), donc une remesure après édition rend les **anciennes** valeurs et
855
+ ressemble à une prop morte ; et **une sonde qui clique en aveugle** dans la barre
856
+ d'actions tombe sur la bascule de mode et mesure le mode **opposé** — chiffres
857
+ inversés, cohérents entre eux, parfaitement crédibles.
858
+
859
+
21
860
  ## [2.18.11] - 2026-09-24
22
861
 
23
862
  ### Fixed — #935 : l'anneau `focus-visible` du Switch débordait de la piste, peint sur un frère non clippé