origam 2.17.0 → 2.18.0

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 (213) hide show
  1. package/CHANGELOG.md +744 -1
  2. package/dist/src/assets/css/main.css +1 -1
  3. package/dist/src/assets/css/tokens/dark.css +125 -24
  4. package/dist/src/assets/css/tokens/light.css +49 -12
  5. package/dist/src/assets/locales/en.json +21 -6
  6. package/dist/src/assets/locales/fr.json +21 -6
  7. package/dist/src/assets/scss/main.scss +10 -2
  8. package/dist/src/assets/scss/tokens/_dark.scss +2846 -12
  9. package/dist/src/assets/scss/tokens/_light.scss +49 -12
  10. package/dist/src/components/Alert/OrigamAlert.vue +6 -6
  11. package/dist/src/components/Avatar/OrigamAvatar.vue +15 -2
  12. package/dist/src/components/Badge/OrigamBadge.vue +7 -7
  13. package/dist/src/components/Breadcrumb/OrigamBreadcrumbItem.vue +23 -4
  14. package/dist/src/components/Btn/OrigamBtn.vue +21 -3
  15. package/dist/src/components/Btn/OrigamBtnGroup.vue +1 -1
  16. package/dist/src/components/Calendar/OrigamCalendar.vue +1 -1
  17. package/dist/src/components/Card/OrigamCard.vue +2 -0
  18. package/dist/src/components/Card/OrigamCardHeader.vue +7 -7
  19. package/dist/src/components/Carousel/OrigamCarousel.vue +14 -1
  20. package/dist/src/components/Chart/OrigamChartBoxPlot.vue +1 -1
  21. package/dist/src/components/Chart/OrigamChartBullet.vue +3 -2
  22. package/dist/src/components/Chart/OrigamChartCandlestick.vue +2 -2
  23. package/dist/src/components/Chart/OrigamChartCartesian.vue +49 -4
  24. package/dist/src/components/Chart/OrigamChartLegend.vue +39 -18
  25. package/dist/src/components/Chart/OrigamChartPareto.vue +7 -1
  26. package/dist/src/components/Chart/OrigamChartPictorial.vue +2 -2
  27. package/dist/src/components/Chart/OrigamChartRadar.vue +3 -1
  28. package/dist/src/components/Chart/OrigamChartSparkline.vue +3 -1
  29. package/dist/src/components/Chart/OrigamChartStreamgraph.vue +2 -1
  30. package/dist/src/components/Checkbox/OrigamCheckbox.vue +2 -0
  31. package/dist/src/components/Checkbox/OrigamCheckboxGroup.vue +36 -11
  32. package/dist/src/components/Chip/OrigamChip.vue +19 -4
  33. package/dist/src/components/Code/OrigamCode.vue +20 -3
  34. package/dist/src/components/ColorPickerField/OrigamColorPickerField.vue +43 -18
  35. package/dist/src/components/CommandPalette/OrigamCommandPalette.vue +3 -3
  36. package/dist/src/components/ConfirmWrapper/OrigamConfirmWrapper.vue +6 -6
  37. package/dist/src/components/ContextualMenu/OrigamContextualMenu.vue +0 -4
  38. package/dist/src/components/DataList/OrigamDataList.vue +2 -0
  39. package/dist/src/components/DataList/OrigamDataText.vue +6 -6
  40. package/dist/src/components/DataList/OrigamDataTitle.vue +6 -6
  41. package/dist/src/components/DataTable/OrigamDataTableHeaderCell.vue +1 -1
  42. package/dist/src/components/DataTable/OrigamDataTableHeadersCell.vue +4 -1
  43. package/dist/src/components/DatePicker/OrigamDatePicker.vue +2 -0
  44. package/dist/src/components/DatePicker/OrigamDatePickerHeader.vue +6 -6
  45. package/dist/src/components/DatePickerField/OrigamDatePickerField.vue +34 -14
  46. package/dist/src/components/Dialog/OrigamDialog.vue +25 -0
  47. package/dist/src/components/Dialog/OrigamDialogConfirmation.vue +6 -0
  48. package/dist/src/components/ExpansionPanel/OrigamExpansionPanel.vue +2 -0
  49. package/dist/src/components/ExpansionPanel/OrigamExpansionPanelHeader.vue +6 -6
  50. package/dist/src/components/Field/OrigamField.vue +83 -28
  51. package/dist/src/components/FileField/OrigamFileField.vue +20 -15
  52. package/dist/src/components/Icon/OrigamSvgIcon.vue +3 -1
  53. package/dist/src/components/Img/OrigamImg.vue +9 -2
  54. package/dist/src/components/InfiniteScroll/OrigamInfiniteScroll.vue +34 -18
  55. package/dist/src/components/InlineEdit/OrigamInlineEdit.vue +46 -5
  56. package/dist/src/components/Input/OrigamInput.vue +6 -6
  57. package/dist/src/components/List/OrigamListGroup.vue +4 -0
  58. package/dist/src/components/List/OrigamListItem.vue +68 -4
  59. package/dist/src/components/Masonry/OrigamMasonry.vue +19 -3
  60. package/dist/src/components/Menu/OrigamMenu.vue +79 -9
  61. package/dist/src/components/NumberField/OrigamNumberField.vue +26 -16
  62. package/dist/src/components/OtpInputField/OrigamOtpInputField.vue +48 -24
  63. package/dist/src/components/PasswordField/OrigamPasswordField.vue +20 -15
  64. package/dist/src/components/Radio/OrigamRadio.vue +2 -0
  65. package/dist/src/components/Radio/OrigamRadioGroup.vue +36 -11
  66. package/dist/src/components/RatingField/OrigamRatingField.vue +58 -7
  67. package/dist/src/components/RatingField/OrigamRatingFieldItem.vue +78 -30
  68. package/dist/src/components/Select/OrigamSelect.vue +57 -34
  69. package/dist/src/components/SliderField/OrigamSliderField.vue +20 -10
  70. package/dist/src/components/Snackbar/OrigamSnackbarGroup.vue +1 -1
  71. package/dist/src/components/Switch/OrigamSwitch.vue +2 -0
  72. package/dist/src/components/Switch/OrigamSwitchTrack.vue +20 -0
  73. package/dist/src/components/Tabs/OrigamTab.vue +7 -4
  74. package/dist/src/components/Tabs/OrigamTabPanel.vue +4 -2
  75. package/dist/src/components/Tabs/OrigamTabs.vue +7 -5
  76. package/dist/src/components/TextField/OrigamTextField.vue +20 -15
  77. package/dist/src/components/TextareaField/OrigamTextareaField.vue +20 -15
  78. package/dist/src/components/Transition/OrigamExpandX.vue +19 -3
  79. package/dist/src/components/Transition/OrigamExpandY.vue +19 -3
  80. package/dist/src/components/Transition/OrigamTranslateScale.vue +26 -4
  81. package/dist/src/components/Video/OrigamVideo.vue +3 -2
  82. package/dist/src/components/VirtualScroll/OrigamVirtualScroll.vue +1 -1
  83. package/dist/src/components/Window/OrigamWindow.vue +20 -3
  84. package/dist/src/composables/Commons/accessibleCommand.composable.cjs +26 -0
  85. package/dist/src/composables/Commons/accessibleCommand.composable.d.ts +78 -0
  86. package/dist/src/composables/Commons/accessibleCommand.composable.js +20 -0
  87. package/dist/src/composables/Commons/activator.composable.cjs +10 -1
  88. package/dist/src/composables/Commons/activator.composable.js +11 -2
  89. package/dist/src/composables/Commons/adjacent.composable.cjs +17 -0
  90. package/dist/src/composables/Commons/adjacent.composable.d.ts +12 -0
  91. package/dist/src/composables/Commons/adjacent.composable.js +17 -0
  92. package/dist/src/composables/Commons/adjacentInner.composable.cjs +20 -0
  93. package/dist/src/composables/Commons/adjacentInner.composable.d.ts +24 -5
  94. package/dist/src/composables/Commons/adjacentInner.composable.js +20 -0
  95. package/dist/src/composables/Commons/audio.composable.cjs +14 -1
  96. package/dist/src/composables/Commons/audio.composable.js +14 -1
  97. package/dist/src/composables/Commons/backButton.composable.cjs +3 -1
  98. package/dist/src/composables/Commons/backButton.composable.js +3 -1
  99. package/dist/src/composables/Commons/border.composable.d.ts +13 -0
  100. package/dist/src/composables/Commons/colorEffect.composable.cjs +58 -46
  101. package/dist/src/composables/Commons/colorEffect.composable.d.ts +10 -5
  102. package/dist/src/composables/Commons/colorEffect.composable.js +70 -46
  103. package/dist/src/composables/Commons/createLayout.composable.cjs +21 -12
  104. package/dist/src/composables/Commons/createLayout.composable.js +21 -12
  105. package/dist/src/composables/Commons/delay.composable.d.ts +16 -0
  106. package/dist/src/composables/Commons/dimension.composable.d.ts +11 -0
  107. package/dist/src/composables/Commons/elevation.composable.cjs +6 -2
  108. package/dist/src/composables/Commons/elevation.composable.d.ts +37 -1
  109. package/dist/src/composables/Commons/elevation.composable.js +7 -2
  110. package/dist/src/composables/Commons/goTo.composable.cjs +4 -2
  111. package/dist/src/composables/Commons/goTo.composable.js +5 -3
  112. package/dist/src/composables/Commons/group.composable.cjs +14 -14
  113. package/dist/src/composables/Commons/group.composable.js +14 -14
  114. package/dist/src/composables/Commons/hotkey.composable.d.ts +18 -7
  115. package/dist/src/composables/Commons/hydration.composable.d.ts +8 -0
  116. package/dist/src/composables/Commons/link.composable.d.ts +20 -0
  117. package/dist/src/composables/Commons/locationStrategies.composable.d.ts +17 -0
  118. package/dist/src/composables/Commons/message.composable.d.ts +19 -5
  119. package/dist/src/composables/Commons/position.composable.d.ts +17 -6
  120. package/dist/src/composables/Commons/refs.composable.d.ts +11 -5
  121. package/dist/src/composables/Commons/rounded.composable.d.ts +11 -1
  122. package/dist/src/composables/Commons/scrolling.composable.cjs +15 -12
  123. package/dist/src/composables/Commons/scrolling.composable.js +15 -12
  124. package/dist/src/composables/Commons/size.composable.d.ts +15 -0
  125. package/dist/src/composables/Commons/ssrBoot.composable.cjs +9 -1
  126. package/dist/src/composables/Commons/ssrBoot.composable.js +10 -2
  127. package/dist/src/composables/Commons/stack.composable.cjs +13 -1
  128. package/dist/src/composables/Commons/stack.composable.js +13 -1
  129. package/dist/src/composables/Commons/theme-props-resolver.composable.cjs +41 -32
  130. package/dist/src/composables/Commons/theme-props-resolver.composable.js +41 -32
  131. package/dist/src/composables/Commons/validation.composable.cjs +3 -3
  132. package/dist/src/composables/Commons/validation.composable.js +3 -3
  133. package/dist/src/composables/Commons/virtual.composable.cjs +24 -8
  134. package/dist/src/composables/Commons/virtual.composable.js +24 -8
  135. package/dist/src/composables/Masonry/masonry.composable.cjs +18 -11
  136. package/dist/src/composables/Masonry/masonry.composable.js +18 -11
  137. package/dist/src/composables/Parallax/parallax.composable.cjs +1 -1
  138. package/dist/src/composables/Parallax/parallax.composable.js +1 -1
  139. package/dist/src/composables/index.cjs +36 -0
  140. package/dist/src/composables/index.d.ts +3 -0
  141. package/dist/src/composables/index.js +3 -0
  142. package/dist/src/consts/Chart/chart-cartesian.const.cjs +5 -2
  143. package/dist/src/consts/Chart/chart-cartesian.const.d.ts +42 -0
  144. package/dist/src/consts/Chart/chart-cartesian.const.js +3 -0
  145. package/dist/src/consts/Commons/border.const.cjs +2 -1
  146. package/dist/src/consts/Commons/border.const.d.ts +19 -0
  147. package/dist/src/consts/Commons/border.const.js +15 -0
  148. package/dist/src/consts/Commons/elevation.const.cjs +5 -1
  149. package/dist/src/consts/Commons/elevation.const.d.ts +52 -2
  150. package/dist/src/consts/Commons/elevation.const.js +4 -0
  151. package/dist/src/consts/Commons/ripple.const.cjs +13 -3
  152. package/dist/src/consts/Commons/ripple.const.js +13 -3
  153. package/dist/src/consts/RatingField/rating-field.const.cjs +10 -0
  154. package/dist/src/consts/RatingField/rating-field.const.d.ts +36 -0
  155. package/dist/src/consts/RatingField/rating-field.const.js +12 -0
  156. package/dist/src/consts/index.cjs +11 -0
  157. package/dist/src/consts/index.d.ts +1 -0
  158. package/dist/src/consts/index.js +1 -0
  159. package/dist/src/directives/ClickOutside/clickOutside.directive.cjs +2 -0
  160. package/dist/src/directives/ClickOutside/clickOutside.directive.js +2 -0
  161. package/dist/src/directives/Ripple/ripple.directive.cjs +2 -0
  162. package/dist/src/directives/Ripple/ripple.directive.js +2 -0
  163. package/dist/src/enums/Commons/color.enum.cjs +0 -1
  164. package/dist/src/enums/Commons/color.enum.d.ts +14 -5
  165. package/dist/src/enums/Commons/color.enum.js +0 -1
  166. package/dist/src/enums/Sheet/sheet.enum.d.ts +1 -1
  167. package/dist/src/interfaces/Chart/chart-cartesian.interface.d.ts +6 -2
  168. package/dist/src/interfaces/Commons/activator.interface.d.ts +3 -2
  169. package/dist/src/interfaces/Commons/adjacent.interface.d.ts +29 -0
  170. package/dist/src/interfaces/Commons/group.interface.d.ts +13 -2
  171. package/dist/src/interfaces/Commons/ripple.interface.d.ts +16 -0
  172. package/dist/src/interfaces/Commons/scroll.interface.d.ts +3 -2
  173. package/dist/src/interfaces/DataTable/data-table-header.interface.d.ts +2 -2
  174. package/dist/src/interfaces/DatePicker/date-picker-month.interface.d.ts +2 -1
  175. package/dist/src/interfaces/Field/field.interface.d.ts +21 -1
  176. package/dist/src/interfaces/Menu/menu.interface.d.ts +28 -1
  177. package/dist/src/interfaces/OtpInputField/otp-input-field.interface.d.ts +21 -1
  178. package/dist/src/interfaces/RatingField/rating-field-item.interface.d.ts +16 -1
  179. package/dist/src/interfaces/index.cjs +11 -0
  180. package/dist/src/interfaces/index.d.ts +1 -0
  181. package/dist/src/interfaces/index.js +1 -0
  182. package/dist/src/themes/origam.theme.cjs +8 -14
  183. package/dist/src/themes/origam.theme.js +6 -8
  184. package/dist/src/types/Commons/color.type.d.ts +4 -3
  185. package/dist/src/types/Commons/commons.type.d.ts +33 -0
  186. package/dist/src/types/Commons/theme.type.d.ts +2 -1
  187. package/dist/src/types/Sheet/sheet.type.d.ts +2 -1
  188. package/dist/src/types/index.cjs +22 -0
  189. package/dist/src/types/index.d.ts +2 -0
  190. package/dist/src/types/index.js +2 -0
  191. package/dist/src/types/tokens.type.d.ts +1 -1
  192. package/dist/src/utils/Commons/a11y.util.cjs +35 -0
  193. package/dist/src/utils/Commons/a11y.util.d.ts +63 -0
  194. package/dist/src/utils/Commons/a11y.util.js +33 -0
  195. package/dist/src/utils/Commons/clickOutside.util.cjs +5 -1
  196. package/dist/src/utils/Commons/clickOutside.util.js +5 -1
  197. package/dist/src/utils/Commons/color.util.cjs +10 -5
  198. package/dist/src/utils/Commons/color.util.d.ts +34 -5
  199. package/dist/src/utils/Commons/color.util.js +9 -5
  200. package/dist/src/utils/Commons/goTo.util.cjs +31 -14
  201. package/dist/src/utils/Commons/goTo.util.d.ts +1 -1
  202. package/dist/src/utils/Commons/goTo.util.js +31 -16
  203. package/dist/src/utils/Commons/location.util.cjs +18 -3
  204. package/dist/src/utils/Commons/location.util.js +18 -3
  205. package/dist/src/utils/Commons/ripple.util.cjs +5 -1
  206. package/dist/src/utils/Commons/ripple.util.js +5 -1
  207. package/dist/src/utils/index.cjs +11 -0
  208. package/dist/src/utils/index.d.ts +1 -0
  209. package/dist/src/utils/index.js +1 -0
  210. package/package.json +13 -2
  211. package/dist/src/types/Commons/locale.type.cjs +0 -1
  212. package/dist/src/types/Commons/locale.type.d.ts +0 -0
  213. package/dist/src/types/Commons/locale.type.js +0 -0
package/CHANGELOG.md CHANGED
@@ -16,7 +16,750 @@ This project follows [Semantic Versioning](https://semver.org).
16
16
 
17
17
  ---
18
18
 
19
- ## [Unreleased]
19
+ ## [2.18.0] - 2026-09-19
20
+
21
+ 80 commits depuis `v2.17.1`. L'essentiel est du **correctif** : des tokens qui
22
+ ne peignaient rien, des props déclarées et inertes, et une famille de défauts
23
+ d'accessibilité où le clavier n'actionnait pas ce que la souris actionnait.
24
+
25
+ > ### ⛔ DÉROGATION, LA DEUXIÈME — cette version MINEURE porte 2 ruptures d'API
26
+ >
27
+ > Comme la `2.17.0` avant elle, cette version mineure embarque des ruptures :
28
+ > `BG_FG_ROLE.DISABLED` et le retrait du sous-chemin `origam/services`, toutes
29
+ > deux détaillées ci-dessous. Le versioning sémantique imposerait une
30
+ > **majeure**. C'est de nouveau une **décision explicite du mainteneur** —
31
+ > le numéro `3.0.0` est réservé à la séparation en modules.
32
+ >
33
+ > Ce bandeau ne demande aucune permission : **il documente**. Un consommateur
34
+ > épinglé en `^2.17` reçoit ces deux ruptures **sans les avoir demandées**, et
35
+ > il doit pouvoir le lire ici plutôt que de le découvrir à l'exécution.
36
+ >
37
+ > Les deux recettes de migration sont dans leurs entrées. Elles sont vides
38
+ > toutes les deux — rien ne pouvait peindre, rien ne pouvait résoudre — mais
39
+ > l'une resserre un type pour de bon, et l'autre retire une porte déclarée.
40
+ >
41
+ > Le décompte cumulé est désormais de **10 ruptures entrées par deux versions
42
+ > mineures**. La note rétrospective plus bas les énumère une par une.
43
+ >
44
+ > Suivi de remédiation : **#717** (ouvert, non tranché).
45
+
46
+ ### ⚠️ BREAKING — `BG_FG_ROLE.DISABLED` removed (#823)
47
+
48
+ `BG_FG_ROLE` is part of the published surface — it is re-exported by
49
+ `origam/enums` (and reachable directly as `origam/enums/Commons/color.enum`
50
+ through the `./*` catch-all). Verified on the BUILT artefact, not inferred
51
+ from the export map: `import { BG_FG_ROLE } from 'origam/enums'` now yields
52
+ `['default', 'hover', 'active']`. The derived `TBgFgRole` (`origam/types`)
53
+ narrows with it, so a consumer annotating a variable `TBgFgRole` and
54
+ assigning `'disabled'` stops compiling.
55
+
56
+ **No rendering changes.** The member was declared and **never passed**:
57
+ `useColorEffect` and `useStateEffect` both derive `bgRole` from `isHover` /
58
+ `isActive` alone, and every other call site passes the literal `'default'`.
59
+ Measured in Chromium against the prebuilt Histoire — 70 story/variant
60
+ captures, 801 computed `background-color` / `color` readings across the ten
61
+ component families that consume those composables — the before and after
62
+ dumps are byte-identical, with a hover positive control that moves
63
+ (`rgb(124,58,237)` → `rgb(109,40,217)`) in both.
64
+
65
+ **Why remove rather than declare the eight missing tokens.** The `disabled`
66
+ rung resolved `--origam-color__feedback--{success,warning,danger,info}---bgDisabled`
67
+ and `---fgDisabled` — eight names **no sheet declares**, with **no fallback**.
68
+ `docs/integrations/theming-authoring.md` already documents `feedback.*` as
69
+ having no `*Disabled` slot; only `action.*` does. A `var()` that fails at
70
+ computed-value time does not yield: the declaration has already won the
71
+ cascade, becomes `unset`, and **erases** the surface (the #813 / #568
72
+ mechanism). The member was therefore an armed trap — the first caller to
73
+ pass `'disabled'`, which the `tokenStylesForIntent(intent, role)` signature
74
+ openly invited, would have wiped eight surfaces with every guard green.
75
+ Disabled is an opacity veil in this DS, never a token swap.
76
+
77
+ Guard 28 `ts-token-refs` drops from **20** baselined dead-channel entries to
78
+ **12**. Negative control run: pre-fix code against the trimmed baseline
79
+ reports exactly those 8 as NEW and exits 1.
80
+
81
+ **Migration.** There is none to write, because nothing could have been
82
+ painting: any consumer passing `'disabled'` was already emitting undeclared
83
+ `var()` references. Use the disabled opacity veil the components already
84
+ apply, or declare the eight `feedback.*` slots in your own theme and pass a
85
+ bespoke `bgColor`.
86
+
87
+ ### ⚠️ BREAKING — le sous-chemin `origam/services` est retiré de la map `exports`
88
+
89
+ **Il ne résolvait déjà plus.** C'est une rupture de *contrat déclaré*, pas de
90
+ comportement : la map annonçait une porte qui ne menait nulle part, et elle
91
+ cesse de l'annoncer.
92
+
93
+ `packages/ds/package.json` déclarait
94
+ `"./services": "./dist/src/services/index.js"` alors que
95
+ `packages/ds/src/services/` n'existe plus. Le répertoire a été **renommé en
96
+ `classes/`** par `63120a402` (`refactor(ds)!: services/ -> classes/`, le
97
+ 2026-08-19) : les trois fichiers sont devenus
98
+ `src/classes/Commons/{box,circular-buffer,date-adapter}.class.ts` et
99
+ `src/services/index.ts` a été supprimé. **L'entrée `exports`, elle, est
100
+ restée** — et a donc été publiée morte dans `2.16.0`, `2.17.0` et `2.17.1`.
101
+
102
+ Mesuré contre le `dist` construit, avant retrait : `import 'origam/services'`
103
+ lève `MODULE_NOT_FOUND` sur `dist/src/services/index.js`. Le `build` le
104
+ signalait d'ailleurs à chaque passage, dans un avertissement que personne ne
105
+ lisait : `Potential missing package.json files: … dist/src/services/index.js`.
106
+ Cet avertissement **disparaît** avec l'entrée — c'est le témoin du retrait,
107
+ et il est passé de 1 occurrence à 0 sur le build de cette release.
108
+
109
+ **Migration.** Aucune, au sens strict : un `import 'origam/services'` échouait
110
+ déjà, donc aucun code en état de marche ne peut en dépendre. Vérifié sur le
111
+ dépôt entier avant le retrait — `packages/marketing`, `stories`, `docs` et
112
+ `tests` compris : **zéro import réel**. La seule référence restante était un
113
+ alias Vite inerte dans le `nuxt.config.ts` du site vitrine, qui pointait lui
114
+ aussi sur le répertoire disparu ; il est retiré dans le même commit.
115
+
116
+ ⚠️ Et il faut être précis sur ce qui change, parce que l'attente naturelle est
117
+ fausse : **le code d'erreur ne change pas**. On pourrait croire qu'un
118
+ sous-chemin retiré d'`exports` lève désormais `ERR_PACKAGE_PATH_NOT_EXPORTED` ;
119
+ mesuré, ce n'est pas le cas — le catch-all `"./*"` **reprend** le sous-chemin
120
+ et le renvoie vers `dist/src/services`. Le consommateur voit donc toujours
121
+ `MODULE_NOT_FOUND`, seule la cible résolue change :
122
+
123
+ | | sous-chemin résolu vers | code |
124
+ |---|---|---|
125
+ | `2.17.1` | `dist/src/services/index.js` (l'entrée déclarée) | `MODULE_NOT_FOUND` |
126
+ | `2.18.0` | `dist/src/services` (le catch-all) | `MODULE_NOT_FOUND` |
127
+
128
+ C'est exactement le comportement observé sur `themes-all`, retiré en `2.17.0`.
129
+ Ce qui est rompu ici est donc le **contrat déclaré**, rien d'autre : le paquet
130
+ cesse d'annoncer une porte qu'il n'a pas.
131
+
132
+ ⚠️ **À savoir, et non corrigé ici** : la surface qui a remplacé `services/`
133
+ n'est pas exportée non plus. `origam/classes` ne résout pas — le catch-all
134
+ l'envoie vers `dist/src/classes`, un **répertoire**, et l'ESM refuse l'import
135
+ de répertoire (`ERR_UNSUPPORTED_DIR_IMPORT`). Seul `origam/classes/index.js`,
136
+ explicite, fonctionne. Déclarer proprement `"./classes"` est un **ajout** de
137
+ surface, pas un retrait : ça ne relève pas de cette release.
138
+
139
+ ### ⛔ NOTE RÉTROSPECTIVE — écrite le 2026-09-19 : les 10 ruptures d'API entrées par des versions MINEURES
140
+
141
+ Cette note est ajoutée **après coup**, et ne modifie aucune entrée passée.
142
+
143
+ **Ce qu'il faut dire d'emblée : la `2.17.0` n'aurait pas dû être une mineure.**
144
+ Elle a embarqué **8 ruptures d'API**, et la `2.18.0` en ajoute **deux** — la
145
+ neuvième et la dixième. Un consommateur épinglé en `^2.16` a reçu les huit
146
+ premières **automatiquement, sans action de sa part**, et son build a pu casser
147
+ sans avertissement. Les deux dérogations sont documentées et assumées (note en
148
+ tête de chaque section, ticket de remédiation **#717**) — mais une dérogation
149
+ documentée reste une dérogation, et un `^2.16` qui casse reste un `^2.16` qui
150
+ casse.
151
+
152
+ Le détail complet de chaque rupture, avec ses mesures navigateur, vit dans la
153
+ section `[2.17.0]` plus bas. Ce qui suit est l'**index vérifié** : chacune a été
154
+ recontrôlée sur le code de cette release (`HEAD` = `2aca4db6d`) avant d'être
155
+ écrite ici, parce que plusieurs tickets de ce dépôt portaient des chiffres
156
+ périmés.
157
+
158
+ | # | Rupture | Vérifiée sur le code de 2.18.0 |
159
+ |---|---|---|
160
+ | 1 | `aspect-ratio` CSS remplace le hack `__sizer` | confirmée |
161
+ | 2 | `inline` retirée d'`IResponsiveProps` | confirmée |
162
+ | 3 | `label` retirée d'`IValidationProps` | confirmée |
163
+ | 4 | échelle `max-width` d'`OrigamContainer` réalignée | confirmée |
164
+ | 5 | 88 paires typographiques mortes retirées | confirmée, le 88 se recompte |
165
+ | 6 | props plates `hover*` / `active*` retirées | confirmée, **titre d'origine inexact** |
166
+ | 7 | `origam/tokens/{css,scss}/themes-all` retirés | confirmée sur le `dist` construit |
167
+ | 8 | 206 custom properties retirées des feuilles livrées | confirmée, **octets d'origine faux** |
168
+ | 9 | `BG_FG_ROLE.DISABLED` retiré (2.18.0) | confirmée sur le `dist` construit |
169
+ | 10 | `origam/services` retiré d'`exports` (2.18.0) | confirmée sur le `dist` construit |
170
+
171
+ **1. `aspect-ratio` CSS remplace le hack `__sizer`.**
172
+ L'enfant `__sizer` (`padding-block-end` en pourcentage) et la marge de rappel
173
+ sur `__content` ont disparu de `OrigamResponsive`, `OrigamImg`,
174
+ `OrigamCarouselItem` et `OrigamVideo` ; `aspect-ratio` résout désormais l'axe
175
+ manquant **dans les deux sens**. *Ce que vous devez faire* : si vous posez une
176
+ `height` explicite et que vous vouliez garder la pleine largeur, posez aussi
177
+ `width` (`width="100%"`) — sinon la largeur est maintenant contrainte par le
178
+ ratio.
179
+
180
+ **2. `inline` retirée d'`IResponsiveProps`** (donc de `<OrigamResponsive>`,
181
+ `<OrigamImg>`, `<OrigamCarouselItem>`). *Ce que vous devez faire* : rien dans le
182
+ cas courant. L'attribut résiduel retombe dans `$attrs` et atterrit inerte sur la
183
+ racine. La prop réduisait la largeur à `0` : elle ne peignait rien qu'on puisse
184
+ regretter.
185
+
186
+ **3. `label` retirée d'`IValidationProps`** (donc de `<OrigamInput>`).
187
+ *Ce que vous devez faire* : rien pour Checkbox, Switch, TextField, RatingField,
188
+ SliderField, RadioGroup — ils gardent leur `label`. Si vous passiez `label` à un
189
+ `<origam-input>` nu, c'était déjà un no-op (zéro `<label>` rendu) : déplacez-le
190
+ sur le contrôle placé dans le slot.
191
+
192
+ **4. Échelle `max-width` d'`OrigamContainer` réalignée sur les tokens.**
193
+ `900/1200/1800/2400px` → `768/992/1280/1440px`, aux mêmes seuils de viewport
194
+ (`960/1280/1920/2560px`). *Ce que vous devez faire* : c'est un **changement
195
+ visible de largeur**, pas un no-op. Si vos maquettes reposaient sur l'ancienne
196
+ échelle, surchargez `--origam-container---max-width-{md,lg,xl,xxl}`.
197
+
198
+ **5. 88 paires (composant, prop) typographiques mortes retirées sur 40
199
+ interfaces.** `fontFamily` / `fontWeight` / `lineHeight` / `letterSpacing` là où
200
+ aucune règle CSS ne les relisait. `fontSize` n'a pas été touché.
201
+ *Ce que vous devez faire* : le rendu ne bouge pas (ces props ne peignaient rien),
202
+ mais **le type se resserre** — un `<origam-btn font-family="…">` cesse de
203
+ compiler. Passez par les tokens de typographie, ou par `:style`.
204
+ *Recompté pour cette note* : 84 retraits déclarés en direct + 4 hérités par
205
+ `IOtpInputFieldProps` (passé à `Omit<…>`) = **88**. Le total de paires
206
+ typographiques déclarées passe de **212 à 129**. Rien n'est revenu depuis.
207
+
208
+ **6. Props plates par état retirées.** ⚠️ **Le titre de l'entrée `[2.17.0]` est
209
+ inexact** et cette note le corrige plutôt que de réécrire l'entrée : il liste
210
+ `hoverBgColor` **deux fois** et omet `hoverColor`. Les quatre props réellement
211
+ retirées sont `hoverColor`, `hoverBgColor`, `activeColor`, `activeBgColor` — le
212
+ corps de l'entrée, lui, les cite bien toutes les quatre.
213
+ ⚠️ Deuxième correction : le corps annonce `Pagination`, `Field`, `DataText`,
214
+ `DataTitle` comme « les derniers déclarants » — il y en avait **cinq** :
215
+ `ISelectionControlProps` déclarait `activeColor` / `activeBgColor` en `2.16.0`
216
+ et n'est pas mentionné. *Ce que vous devez faire* : passez l'override comme clé
217
+ des props objet `hover` / `active`.
218
+
219
+ **7. `origam/tokens/css/themes-all` et `origam/tokens/scss/themes-all`
220
+ retirés.** *Ce que vous devez faire* : importez les feuilles nommées
221
+ (`origam/tokens/css/light`, `.../dark`, `.../primitive`, `.../utilities`) ou
222
+ `origam/styles`, qui les agrège. ⚠️ Le catch-all `"./*"` de la map `exports` ne
223
+ rattrape pas ces sous-chemins : il les envoie vers `dist/src/tokens/…`, qui
224
+ n'existe pas. Vérifié par résolution réelle contre le `dist` construit de cette
225
+ release : les deux lèvent `MODULE_NOT_FOUND` sur
226
+ `dist/src/tokens/{css,scss}/themes-all`.
227
+
228
+ **8. 206 custom properties retirées des feuilles de tokens livrées.**
229
+ Ces propriétés venaient de 12 fichiers `tokens/component/*.json` qu'aucun
230
+ composant ne lisait. ⚠️ **C'est bien une rupture pour un consommateur**, et non
231
+ un remaniement de sources internes : mesuré nom par nom, **206/206 étaient
232
+ déclarées dans `light.css` et dans `dark.css` en `2.16.0`, et 0/206 le sont
233
+ aujourd'hui** — or ces deux feuilles sont dans le paquet publié. *Ce que vous
234
+ devez faire* : si vous surchargiez l'une d'elles, la déclaration n'a plus de
235
+ base ; redéclarez-la dans votre propre thème.
236
+ ⚠️ Troisième correction : les tailles d'octets annoncées dans l'entrée
237
+ `[2.17.0]` (`light.css 187293 → 171736`, `dark.css 380189 → 348663`) **ne se
238
+ reproduisent pas**. Remesuré : `2.16.0` = 187 468 / 380 549 octets ; aujourd'hui
239
+ = 193 455 / 376 376. `light.css` a même **grossi** depuis, d'autres travaux
240
+ ayant touché les feuilles entre-temps. Le chiffre qui tient est 206/206 → 0/206,
241
+ pas les octets.
242
+
243
+ **9. `BG_FG_ROLE.DISABLED` retiré** — la neuvième, livrée par cette `2.18.0`.
244
+ Détail complet en tête de cette section. *Ce que vous devez faire* : rien au
245
+ rendu ; retirez `'disabled'` de toute variable annotée `TBgFgRole`.
246
+ ⚠️ Le numéro **#823 cité par le commit est indirect** : ce ticket porte sur les
247
+ références `var()` assemblées en TypeScript qu'aucune garde ne voyait. Le
248
+ retrait de ce membre est une **trouvaille** de la garde livrée sous ce ticket,
249
+ pas son objet. Aucun ticket dédié n'existe pour cette rupture.
250
+
251
+ **10. `origam/services` retiré de la map `exports`** — la dixième, livrée par
252
+ cette `2.18.0`. Détail complet en tête de section. *Ce que vous devez faire* :
253
+ rien. Le sous-chemin ne résolvait déjà pas, et le dépôt entier a été balayé
254
+ avant le retrait — zéro import réel. C'est le contrat déclaré qui est rompu,
255
+ pas un comportement.
256
+
257
+ **Ce que cette note ne fait pas.** Elle ne publie pas de guide de migration
258
+ `v2.16 → v2.18` consolidé, ne déprécie pas `2.17.0` sur npm, et ne mesure pas le
259
+ nombre de consommateurs externes réellement exposés. Ces trois options sont
260
+ listées dans **#717**, qui reste **ouvert** et attend une décision.
261
+
262
+ ### Added
263
+
264
+ - **Cinq symboles publics redeviennent importables** (#844). `useHotkey` et
265
+ `useAudioPlayer` depuis `origam/composables`, `IMessageProps` depuis
266
+ `origam/interfaces`, `TOrigamAvatarGroup` et `TOrigamSystemBar` depuis
267
+ `origam/types`. Ils existaient dans les sources mais aucun barrel ne les
268
+ réexportait : un `import { useHotkey } from 'origam/composables'` échouait en
269
+ `MISSING_EXPORT` au bundling, et les trois types en `TS2724` / `TS2305`.
270
+ Vérifié par import réel contre le `dist` construit, pas par lecture de la map
271
+ `exports`.
272
+ Le sixième réexport que demandait le ticket, `IHotkeyOptions`, n'a **pas** été
273
+ ajouté : il est atteignable depuis `origam/interfaces` depuis 2025, l'ajouter
274
+ aurait dupliqué un export existant.
275
+ ⚠️ Au passage, le fichier vide `types/Commons/locale.type.ts` (1 octet, zéro
276
+ symbole) a été supprimé : le sous-chemin profond
277
+ `origam/types/Commons/locale.type` ne résout plus. Il n'exportait rien, donc
278
+ aucun symbole ne disparaît.
279
+
280
+ - **Nouveau type public `TAnyString`** (`origam/types`), qui remplace l'idiome
281
+ `(string & {})` sur 6 unions exportées (`data-table-header`, `theme.type`,
282
+ `sheet.type`, `activator` ×2, `scroll`, `date-picker-month`). Purement
283
+ nominatif : la sémantique de l'union est identique, l'autocomplétion des
284
+ littéraux nommés est préservée.
285
+
286
+ - **`<OrigamRatingFieldItem>` émet `change` et `keydown`**, deux emits publics
287
+ nouveaux, nécessaires pour que la navigation clavier native atteigne le modèle
288
+ (#812).
289
+
290
+ - **La référence des composables couvre enfin `Commons`** (#599, #600, #601).
291
+ Trois lots, 80 symboles documentés sur 39 pages : thème / couleur / style,
292
+ dimension / espacement / forme, puis état / interaction / cycle de vie. Chaque
293
+ table est **mesurée** sur les symboles réels, jamais recopiée d'une bannière —
294
+ ce qui a mis au jour des pièges d'API que la documentation annonçait à
295
+ l'envers, et qui valent pour tout consommateur :
296
+ `useRounded` — `rounded-top-left="0"` est un **no-op** (la chaîne `"0"` n'a pas
297
+ d'unité), `rounded="4px 8px"` n'émet rien, et l'ordre à 4 valeurs est
298
+ TL/TR/BL/BR — ni celui de CSS, ni celui de `useMargin` ;
299
+ `useMargin` / `usePadding` — `margin="1.5rem"`, `margin="auto"` et
300
+ `margin="8px 16px 24px"` n'émettent **rien**, en silence, alors que
301
+ `margin-top` accepte les trois ;
302
+ `useBorder` — la forme tableau émet une classe portant la virgule
303
+ d'`Array.toString` et aucune largeur ;
304
+ `useSize` — `useSize(ref(24))` est inerte, `size="zzz"` émet `width: zzz`
305
+ sans avertir.
306
+ Ces comportements ne changent pas dans cette release : ils sont désormais
307
+ **écrits**. La documentation n'est pas livrée dans le paquet npm.
308
+
309
+ ### Fixed
310
+
311
+ #### Thème et tokens — des déclarations qui ne peignaient rien
312
+
313
+ - **Le bundle publié n'avait aucun mode sombre automatique** (#794).
314
+ `dark.css` portait bien son bloc `@media (prefers-color-scheme: dark)`, mais
315
+ son jumeau `_dark.scss` ne l'avait pas — or `origam/styles` résout vers
316
+ `main.css`, **compilé depuis le SCSS**. Un consommateur qui importait
317
+ `origam/styles` et n'épinglait rien restait en clair sur un système en
318
+ sombre, pendant que la documentation affirmait le contraire. Le bloc est
319
+ désormais dans les deux sources, et une garde (`token-twins`) échoue
320
+ maintenant sur toute divergence CSS/SCSS. Mesuré sur le `dist` de cette
321
+ release : `main.css` contient
322
+ `@media(prefers-color-scheme: dark){:root:not([data-theme]):not([data-mode]){…}}`,
323
+ et ce bloc porte **2 731 déclarations** — exactement autant que le bloc
324
+ explicite. C'est un thème sombre complet, pas un sous-ensemble.
325
+ Le correctif hérité a été **resserré** en cours de route : `:root:not([data-theme])`
326
+ seul repeignait en sombre une page ayant épinglé le clair sur l'axe
327
+ `data-mode`, c'est-à-dire la forme exacte que produit `useTheme()`. D'où le
328
+ second `:not([data-mode])`.
329
+
330
+ - **`data-mode="dark"` seul peint désormais le thème sombre** (#807).
331
+ Les feuilles statiques n'émettaient **aucune** règle `[data-mode]` : seule la
332
+ matrice de thème injectée en JS par `createOrigam()` en produisait. Un
333
+ consommateur appelant `useTheme().setMode('dark')` sans thème de marque
334
+ enregistré obtenait `<html data-mode="dark">` et **restait en clair**.
335
+ Le sélecteur du bloc `[data-theme="dark"]` est élargi à
336
+ `:root:not([data-theme])[data-mode="dark"]` — même spécificité, disjoint sur
337
+ l'attribut, donc jamais en concurrence avec le bloc `@media`.
338
+ `data-theme` reste prioritaire dès qu'il est posé : `data-theme="light"` +
339
+ `data-mode="dark"` reste clair. Mesuré en Chromium, `rgb(255,255,255)` →
340
+ `rgb(10,10,10)`. Vérifié à nouveau sur le `dist` de cette release : la règle
341
+ est là, et elle porte **2 731 déclarations** de tokens — le jeu sombre
342
+ complet, obtenu en élargissant la liste de sélecteurs plutôt qu'en dupliquant
343
+ un troisième bloc de 170 Ko.
344
+
345
+ - **`elevation="2xl"` et `"3xl"` n'effacent plus l'ombre du composant** (#813).
346
+ `ORIGAM_SHADOW_RUNGS` accepte huit échelons, les feuilles n'en déclarent que
347
+ six : ces deux-là émettaient un `var()` **nu** sur un token inexistant. Un
348
+ `var()` qui échoue au calcul ne cède pas la main — la déclaration a déjà gagné
349
+ la cascade, devient `unset`, et **efface** l'ombre du composant. Une prop
350
+ censée renforcer l'ombre détruisait celle qui existait : `none` →
351
+ `rgba(0,0,0,.2) 0 11px 15px -7px…`, `md` et `xl` inchangés comme témoins.
352
+ ⚠️ **Contrat assumé** : `2xl` et `3xl` rendent désormais **exactement comme
353
+ `xl`**. Ils cessent d'effacer, ils ne deviennent pas deux échelons
354
+ supplémentaires. Le repli deviendra inerte de lui-même le jour où ces deux
355
+ tokens seront déclarés.
356
+
357
+ - **Un `0` sans unité ne jette plus la déclaration qui le contient** (#568,
358
+ #800). `calc(36px + 0)` est **invalide** en CSS — le zéro nu est un `<number>`,
359
+ pas une longueur — et quand la valeur transite par un `var()`, la déclaration
360
+ gagne la cascade puis devient `unset` : elle **écrase** au lieu de céder.
361
+ Mesuré en Chromium, avant → après : hauteur d'`<OrigamBtn>` et
362
+ d'`<OrigamBtnGroup>` `0px` → `36px` ; `max-width` du label de champ `none` →
363
+ `100%` (largeur rendue `1000px` → `300px`) ; `row-gap` du field `normal` →
364
+ `8px`. Le cas du label **n'était pas latent** : son `max-width` était mort
365
+ pour tous les champs du catalogue.
366
+ ⚠️ Le ticket #800 annonçait **trois** champs vivant de cette erreur ; la mesure
367
+ en trouve **un** (`<OrigamOtpInputField>`, `0px` → `4px`). `ColorPickerField`
368
+ et `NumberField` tirent leur `padding` d'une règle sans `max()`, où un zéro nu
369
+ est valide. Le plancher de dégagement des coins devient un canal explicite
370
+ (`--origam-field---corner-clearance`) plutôt qu'un effet de bord.
371
+
372
+ - **L'en-tête collant de `<OrigamDataTable>` colle** (#840). Le calcul du `top`
373
+ lisait `--origam-table-header-height`, déclaré nulle part et sans repli : le
374
+ `var()` nu **effaçait** le `top`, donc `sticky` n'accrochait rien. La hauteur
375
+ est désormais dérivée du remplissage sensible à la densité — mesuré
376
+ `compact` 32 px, `default` 44 px, `comfortable` 56 px — avec un repli explicite.
377
+ À savoir : `sticky` n'agit que si `<OrigamDataTable>` reçoit sa propre
378
+ `height` ou `maxHeight`, ce qui est désormais documenté.
379
+
380
+ - **`--origam-color__text---primary` résout la valeur qu'il annonce** (#615).
381
+ Il rendait `#0a0a0a` alors que toutes ses déclarations pointaient
382
+ `neutral-900` (`#171717`) : la déclaration gagnante n'était pas une feuille,
383
+ mais le thème runtime injecté en JS. Ce n'était pas un simple écart de
384
+ documentation — sous `<OrigamThemeProvider theme="light">`, le sous-arbre ne
385
+ matche plus `:root`, et **le même thème peignait deux couleurs de texte**
386
+ (`#0a0a0a` à la racine, `#171717` dans le sous-arbre). Sur les 2 892 tokens
387
+ comparés entre thème runtime et feuilles, 268 divergeaient ; ce seul correctif
388
+ en règle 89.
389
+
390
+ - **Cinq paires couleur de texte / fond d'intention repassent au-dessus de
391
+ 4,5:1** (#789), et dans **les deux sources** — feuilles `origam/styles` **et**
392
+ bloc runtime injecté par `createOrigam()` : n'en corriger qu'une n'aurait rien
393
+ changé pour l'autre moitié des consommateurs. Mesuré, avant → après :
394
+ `success` 2,78 / 3,30 → 5,02 ; `warning` 2,37 / 3,19 → 5,02 ; `info` 3,12 →
395
+ 5,17 ; `primary` en sombre 4,23 → 5,70 ; `primary | bgHover` en sombre 2,72 →
396
+ 7,10 — cette dernière n'était signalée nulle part.
397
+ ⚠️ L'intention `ghost` (3,69 annoncé) est un **artefact de la sonde**, pas un
398
+ défaut : son fond est `rgba(0,0,0,0)` et une mesure sans composition alpha le
399
+ prend pour du noir opaque. Sa vraie valeur est 5,70, conforme. Elle n'a donc
400
+ **pas** été « corrigée ».
401
+ Contrepartie mesurée et assumée : `primary | bgHover` en sombre descend à 2,79
402
+ en contraste **non textuel** (WCAG 1.4.11), seule paire sous 3:1, sur un état
403
+ transitoire.
404
+
405
+ - **`<OrigamAvatar>` dérive son texte de son `bgColor` d'intention** (#819).
406
+ `useStateEffect` appliquait une couleur de premier plan fixe, issue du thème,
407
+ sans égard au `bgColor` réel — or `bgColor` est justement la prop qu'un avatar
408
+ fait varier par instance. Mesuré sur 8 marques × 2 modes × 8 intentions :
409
+ **3 combinaisons** tombaient entre 1,25:1 et 1,86:1, c'est-à-dire **texte
410
+ invisible**. Les trois passent AA ; les 91 autres sont **identiques au bit
411
+ près**.
412
+ ⚠️ #819 reste **ouvert** : 4 échecs résiduels sont des paires de tokens sous AA
413
+ indépendamment d'Avatar, hors périmètre de ce correctif.
414
+
415
+ - **`<OrigamListItem>` : la ligne fait autorité sur sa densité** (#571). Le
416
+ composant émettait des classes `origam-list-item--density-*` qu'aucune règle
417
+ ni token ne lisait : la hauteur ne venait que de la liste, alors que la
418
+ documentation annonçait « hérité de la liste parente **sauf si défini** » —
419
+ la moitié « sauf si défini » était fausse. Matrice mesurée après correctif
420
+ (liste `compact` / liste `comfortable` / hors liste) : ligne sans densité
421
+ 48 / 64 / 56 px, ligne `compact` 48 / 48 / 48 px, ligne `comfortable`
422
+ 64 / 64 / 64 px. Le défaut `'origam-list-item': { density: 'compact' }` a été
423
+ retiré des deux blocs du thème livré : une fois la ligne prioritaire, un
424
+ défaut posé sur **chaque** ligne battrait le `density` qu'un consommateur pose
425
+ sur sa **liste**.
426
+
427
+ #### Accessibilité — le clavier actionne enfin ce que la souris actionne
428
+
429
+ - **`<OrigamRatingField>` est opérable au clavier** (#812). Mesuré avant : huit
430
+ `Tab` d'affilée laissaient `activeElement` sur `<body>` à chaque fois ; ni les
431
+ flèches, ni `Home`/`End`, ni `Espace`, ni `Entrée` ne bougeaient quoi que ce
432
+ soit. Après : un seul arrêt de tabulation pour tout le groupe, entrée sur la
433
+ radio cochée, flèches avec bouclage, `Home`/`End`, `Espace`. Trois causes
434
+ distinctes — un `tabindex="-1"` qui rendait les radios focusables mais hors
435
+ tabulation, un modèle qui n'écoutait que le `click` du `<div>` étoile alors
436
+ que la navigation native émet sur l'`<input>`, et un focus qui n'aurait rien
437
+ éclairé (`height: 0; width: 0; opacity: 0`). L'anneau de focus est peint en
438
+ CSS pur via `:has(:focus-visible)`.
439
+ ⚠️ **Changement de rendu** : la **radio fantôme** `value=0` — sixième radio de
440
+ taille nulle, jamais cochée, atteignable aux flèches et annoncée « Rating 0 of
441
+ 5 » comme une option qui n'existe pas — n'est plus rendue. Le composant rend
442
+ **5** radios natives, pas 6.
443
+
444
+ - **`<OrigamRatingField>` nomme son groupe** (#810) : sur le rendu **par
445
+ défaut**, sans aucun `id` consommateur, le `<label for>` ne résolvait vers
446
+ rien, la racine n'avait pas de rôle, et **aucun groupe n'existait dans l'arbre
447
+ ARIA**. Passage à `role="radiogroup"` + `aria-labelledby`.
448
+
449
+ - **`<OrigamCheckboxGroup>` et `<OrigamRadioGroup>` nomment leur groupe**
450
+ (#814). Le défaut le plus grave n'était pas celui du ticket : avec le slot
451
+ `#label` surchargé et sans prop `label` — le cas nominal de la variante
452
+ « Slots - Label » livrée dans les deux stories — **le groupe n'avait aucun nom
453
+ accessible du tout**. `aria-labelledby` vise désormais un conteneur dédié en
454
+ `display: contents` (boîtes englobantes identiques au pixel sur les
455
+ 4 variantes), et l'`id` dupliqué disparaît.
456
+ ⚠️ Le relevé du ticket annonçait le même `id` sur **trois** éléments ; la mesure
457
+ en trouve **deux**.
458
+
459
+ - **`<OrigamTabs>` : le focus suit les flèches** (#786). Il suivait en réalité
460
+ **avec un cran de retard** — il refocalisait l'onglet qu'on venait de quitter
461
+ — parce que le gestionnaire relisait la sélection après l'avoir changée, or
462
+ sous `v-model` le getter rend encore l'ancien identifiant.
463
+
464
+ - **`InlineEdit` : Confirmer et Annuler sont atteignables et actionnables**
465
+ (#614). ⚠️ **Le ticket se trompait de cause** : ce n'est pas un motif
466
+ `nested-interactive`, c'est qu'un seul `Tab` déclenchait le `blur`, lequel
467
+ **démontait** les deux boutons avant que le focus les atteigne. Deux défauts
468
+ voisins corrigés au passage : `Entrée` sur **Annuler** confirmait, et `Espace`
469
+ sur l'un ou l'autre ne faisait rien. Le focus revient désormais sur
470
+ l'affordance d'affichage au lieu de retomber sur `<body>`.
471
+
472
+ - **Le DS n'émet plus un rôle ARIA qu'il ne peut pas nommer** (#747, #660,
473
+ #653) : 54 nœuds en violation `aria-command-name` (impact `serious`, WCAG
474
+ 4.1.2) sur **17 composants**, où le DS posait `role="button"` et un `tabindex`
475
+ sur des éléments dont il n'avait aucun nom. Un nouveau composable arbitre :
476
+ nom disponible → rôle + tabulation + `aria-label` ; pas de nom → **aucun rôle,
477
+ aucun arrêt de tabulation**, plus un avertissement en développement nommant la
478
+ prop à ajouter. Aucun libellé n'est fabriqué, et le `@click` continue de
479
+ partir à la souris. `<OrigamSvgIcon>` était la seule feuille de la famille
480
+ Icon à n'avoir jamais appelé `useIconAccessibility()` : `aria-hidden="true"`
481
+ en dur, à vie.
482
+ ⚠️ **Changement de rendu** : une zone `prepend` / `append` sans nom n'émet plus
483
+ de rôle. Voir la section `Changed`.
484
+
485
+ - **Trois violations `axe` réelles corrigées sur le catalogue** (#777) :
486
+ le sous-titre de `<OrigamCardHeader>` passe de **1,37:1 à 5,69:1** — il était
487
+ figé sur un neutre absolu pendant que le titre héritait du premier plan résolu
488
+ par la carte, soit du gris sur du violet saturé, pour **tout** consommateur
489
+ passant un `bgColor` d'intention ; la page courante d'un fil d'Ariane passe de
490
+ 3,69:1 à 19,79:1 (elle était marquée « désactivée », donc à `opacity: .5`, et
491
+ l'exemption WCAG 1.4.3 ne couvre pas un contenu) ; et la légende de graphique
492
+ redevient une vraie liste. Le niveau `serious` devient bloquant dans la suite
493
+ d'accessibilité.
494
+
495
+ - **Neuf emplacements d'accessibilité supplémentaires** mesurés un par un
496
+ (#781), dont des `aria-label` construits à partir d'un nom de série absent —
497
+ un graphique radar annonçait littéralement « undefined, x: 1 ». ⚠️ #781 reste
498
+ **ouvert** : c'est un lot partiel d'une famille bien plus large.
499
+
500
+ - **La prop `id` du consommateur atteint le DOM** sur **2 des 5** composants
501
+ signalés (#790) : `<OrigamOtpInputField>`, dont la racine ne portait aucun
502
+ `:id` — un `getElementById` ou un `aria-describedby` externe était mort — et
503
+ `<OrigamDataTableHeadersCell>`, où chaque ligne recevait un `id` **dérivé**, si
504
+ bien que la valeur exacte demandée n'existait nulle part.
505
+ ⚠️ Contrairement à ce qu'annonçait le titre du ticket, **`<OrigamInput>` n'est
506
+ pas concerné** : mesuré, il remet l'`id` exact à son slot. `<OrigamSnackbarGroup>`
507
+ ne l'est pas non plus — son `id` est une clé logique de pile, pas un `id` DOM.
508
+ Les trois cas ont été mesurés non défectueux ou hors de portée, et c'est dit
509
+ plutôt que corrigé à tort.
510
+
511
+ #### Comportement des composants
512
+
513
+ - **La frappe clavier atteint enfin le modèle** sur `<OrigamColorPickerField>`
514
+ et `<OrigamDatePickerField>` (#859). Les deux portaient le même défaut par
515
+ copier-coller : aucun `v-model` ni événement entre le champ texte interne et
516
+ le modèle, un `handleChange` en talon vide, et `modelValue` exclu du
517
+ forwarding. **Seul le popover écrivait le modèle** ; tout ce qui était tapé
518
+ restait dans le DOM. La valeur est validée avant d'être écrite, pour ne jamais
519
+ committer une saisie partielle (`#ab` n'est pas une couleur, `09/1` n'est pas
520
+ une date). Portée volontairement limitée au mode date unique — `range` et
521
+ `multiple` restent pilotés par le popover.
522
+ Défaut latent corrigé au passage : après un `Clear` suivant une saisie
523
+ clavier, le champ couleur affichait **littéralement**
524
+ `{ "h": 0, "s": 0, "v": 0, "a": 1 }`, la constante « pas de couleur » étant un
525
+ objet, donc `truthy`. La constante elle-même reste inchangée — c'est un export
526
+ public.
527
+
528
+ - **`<OrigamRatingField>` respecte un parent qui contrôle et refuse la valeur**
529
+ (#827). Cliquer une étoile laissait `:checked` avancer dans le DOM pendant que
530
+ le modèle restait en arrière — et la radio correctement cochée se retrouvait
531
+ **décochée par le navigateur**, sans le moindre événement pour le signaler.
532
+ Vue ne re-patche une prop DOM que si sa valeur calculée diffère ; quand le
533
+ parent refuse, elle ne diffère pas. Les radios sont désormais resynchronisées
534
+ sur le modèle après tout `click` / `change`. ⚠️ #827 reste **ouvert**.
535
+
536
+ - **`<OrigamSelect>` et `<OrigamMenu>` défilent au lieu de déborder** (#742).
537
+ Le plafond de hauteur était sur une boîte et l'`overflow` sur une autre — or
538
+ **une boîte ne défile que si les deux sont sur elle**. Mesuré avant, 30 options,
539
+ molette à fond : les trois boîtes à `scrollTop` 0, 20 options inatteignables.
540
+ Après, la 30ᵉ option est atteignable dans tous les cas.
541
+ ⚠️ Effet de bord assumé : le contenu passe de `inline-block` à `block`, ce qui
542
+ **retire la bande transparente de 5 px** que le descendeur de ligne imposait à
543
+ chaque panneau. A/B géométrique sur 9 panneaux : 8 rectangles identiques au
544
+ pixel, seul le menu long change (`596×580` → `596×310` — le plafond est enfin
545
+ tenu).
546
+
547
+ - **`<OrigamDialog>` : la boîte qui porte le plafond porte le défilement**
548
+ (#563). Le contenu placé hors de la zone de contenu de la carte était
549
+ inatteignable, aucun ancêtre ne défilant : le pied tombait 1 112 px sous la
550
+ fenêtre. Une seule déclaration ajoutée.
551
+ ⚠️ Deux affirmations du ticket **mesurées fausses** et corrigées dans la
552
+ documentation : la zone de contenu de la carte ne défilait pas non plus, et le
553
+ slot `#asset` n'est pas concerné.
554
+
555
+ - **Un attribut booléen nu résout à `true`** (#644) — et la portée réelle
556
+ dépasse largement le titre du ticket : **aucun** attribut booléen nu ne
557
+ résolvait à `true` sur **aucune** prop qu'un thème enregistré nomme.
558
+ `<origam-card flat>` ne valait pas `flat="true"`, tandis que `:flat="true"`
559
+ fonctionnait. Le mécanisme : un attribut nu compile en `flat: ''` et c'est
560
+ **Vue** qui le transforme en `true` pendant la normalisation — le résolveur de
561
+ thème rendait l'instantané brut d'avant cette normalisation, et défaisait donc
562
+ le casting de Vue. Ce qui rendait le défaut illisible, c'est qu'il ne frappe
563
+ que les props qu'un thème **nomme**, que la forme attribut nu, et qu'il est
564
+ indépendant du thème actif — il suffit qu'un thème soit enregistré.
565
+
566
+ - **Déclarer `class` comme prop ne tue plus le fallthrough** (#620) : relevé
567
+ refait au résolveur plutôt qu'au `grep` — 192 composants déclarent `class`
568
+ transitivement, 189 la re-bindent, **3** ne le faisaient pas.
569
+ `<OrigamChartBullet>` et `<OrigamChartStreamgraph>` perdaient la `class`,
570
+ `<OrigamDialogConfirmation>` perdait **la `class` et le `style`**, ce que le
571
+ ticket n'annonçait pas.
572
+
573
+ - **Aucun travail asynchrone ne survit à son propriétaire** (#719, #753, #779).
574
+ Des `requestAnimationFrame` et `setTimeout` non annulés au démontage, sur une
575
+ vingtaine de sites : `useAudio`, `scrollTo`, `useLocationStrategies`,
576
+ `OtpInputField`, `Menu`, les transitions `ExpandX` / `ExpandY` /
577
+ `TranslateScale`, `clickOutside`, `ripple`, `Carousel`, `parallax`,
578
+ `activator`, `stack`, `InfiniteScroll`, `Select`, `Field`, `useSsrBoot`,
579
+ `useVirtual`, `useMasonry`, `Img`. Une boucle qui se reprogramme ne s'annule
580
+ pas, elle s'**arrête** : d'où le couple systématique drapeau + annulation au
581
+ démontage. Mesuré en A/B contre `develop` : 11 des 24 tests rouges sur le
582
+ parent, verts après ; les 13 autres sont les témoins.
583
+ Sur #779, 6 sites ont été relevés un par un : **4 corrigés, 2 écartés avec la
584
+ mesure qui le justifie**.
585
+ ⚠️ Un lot intermédiaire avait introduit une **coalescence** de frames non
586
+ mesurée ; elle a été retirée dans le même chantier. Chaque portée suit
587
+ désormais l'ensemble de ses frames armées : le correctif ajoute l'annulation
588
+ au démontage et **ne change rien d'autre**.
589
+
590
+ - **La validation redéclenche ses règles sur un modèle devenu nullish sans
591
+ focus** (#702). Une des trois branches du `watch` était **vide** : les
592
+ messages d'erreur restaient périmés et `isValid` restait `true`. Dans l'autre
593
+ sens, un champ en erreur vidé par le code gardait son message. L'agrégat du
594
+ formulaire parent est touché aussi — il restait valide sur un champ vide. Le
595
+ report au `blur` **quand le champ a le focus** est délibéré et conservé.
596
+
597
+ - **Quinze chaînes en dur passent par `t()`** (#764), dans 9 composants —
598
+ `<OrigamVideo>` (`Playback error`, mot pour mot le défaut qui avait fait
599
+ naître #567 sur `<OrigamAudio>`, la clé existait déjà dans les deux locales et
600
+ le composant ne la lisait pas), `CommandPalette`, `SliderField`, et cinq
601
+ familles de graphiques. `<OrigamChartCartesian>` portait **déjà** la prop et
602
+ ses deux traductions : seul le texte visible ne les consultait pas, si bien
603
+ qu'en français le bouton affichait « Reset zoom » pendant que son propre
604
+ `aria-label` disait « Réinitialiser le zoom ». La pilule de ce bouton était un
605
+ rectangle de largeur codée en dur, dimensionnée pour l'anglais : mesuré, le
606
+ texte français débordait de 34 px. La boîte a été élargie — **la traduction
607
+ n'a pas été raccourcie**.
608
+
609
+ - **Quatorze remontées critiques de qualité de code** corrigées dans le DS
610
+ (#771). Aucun changement de comportement attendu, et c'est **prouvé** là où le
611
+ risque était réel : la refonte de `useColorEffect`, qui alimente 101 des 216
612
+ composants, a été comparée à l'ancienne implémentation sur **6 272 cas**
613
+ (28 couleurs × 28 fonds × 8 combinaisons d'état) — **zéro divergence**, avec
614
+ un harnais vérifié par mutation.
615
+
616
+ - **Deux divergences d'hydratation**, de deux causes distinctes (#741). Un
617
+ compteur d'identifiants vivait **au niveau module**, jamais remis à zéro, sous
618
+ un commentaire affirmant que cet identifiant n'atteignait jamais le DOM — il
619
+ l'atteignait, via `<OrigamTab>` et `<OrigamTabPanel>`. Sur un processus SSR
620
+ persistant, le serveur rendait `id="origam-tab-285"` et le client
621
+ `id="origam-tab-9"`. Mesuré : 1 erreur console / 1 erreur d'hydratation /
622
+ 4 avertissements → 0 / 0 / 0.
623
+ ⚠️ **Changement de DOM** : l'attribut `data-origam-tab-id` est supprimé. Il
624
+ portait le même compteur, et un `data-*` échappe à la rectification que Vue
625
+ applique en production — il divergeait donc en silence.
626
+
627
+ ### Changed
628
+
629
+ ⚠️ Aucune de ces entrées n'est une rupture de type — rien ne cesse de compiler.
630
+ Toutes changent en revanche ce qui **sort dans le DOM ou à l'écran**, et méritent
631
+ d'être relues avant de monter de version.
632
+
633
+ - **Un repère ARIA anonyme n'est plus annoncé comme un repère.** `<OrigamWindow>`
634
+ et `<origam-carousel>` sans nom accessible cessent d'émettre `role="region"` et
635
+ `aria-roledescription` (#781) ; une zone `prepend` / `append` sans nom cesse
636
+ d'émettre `role="button"` et son arrêt de tabulation (#747, #660, #653). C'est
637
+ volontaire : un repère qu'on ne peut pas nommer n'est pas navigable, et un
638
+ `role` sans nom est une violation `serious`. Si vous comptiez sur l'un de ces
639
+ rôles, passez un nom (`aria-label`, ou la prop que l'avertissement de
640
+ développement vous indique).
641
+ Au passage, `aria-roledescription` était une **chaîne anglaise en dur**, lue
642
+ telle quelle par les lecteurs d'écran ; elle passe par une clé de traduction.
643
+
644
+ - **`<OrigamRatingField>` rend 5 radios natives et non 6** (#812) — la sixième,
645
+ de taille nulle, était annoncée comme une option inexistante.
646
+
647
+ - **`elevation="2xl"` et `"3xl"` rendent comme `xl`** (#813), au lieu
648
+ d'effacer l'ombre. Voir `Fixed`.
649
+
650
+ - **L'attribut `data-origam-tab-id` disparaît du DOM** (#741). Voir `Fixed`.
651
+
652
+ - **`IGroupProvide.prev()` / `.next()` renvoient l'identifiant retenu**
653
+ (`() => number | undefined`) au lieu de `void` (#786). Additif : les cinq
654
+ autres appelants ignorent la valeur.
655
+
656
+ - **Montées de dépendances** : `typeorm` 0.3.31 → 1.1.1, `pg` 8.22.0 → 8.23.0,
657
+ et 6 outils de développement (PR #559, #558, #539 — ce sont des numéros de
658
+ *pull request*, pas de tickets).
659
+ ⚠️ **Aucune n'atteint le paquet publié.** Mesuré : entre `v2.17.1` et cette
660
+ release, `packages/ds/package.json` ne change que dans son bloc `scripts` —
661
+ ni `dependencies`, ni `peerDependencies`, ni la map `exports` ne bougent.
662
+ `typeorm` n'est déclaré que par le site vitrine, qui est `private`.
663
+
664
+ ### Security
665
+
666
+ - **Les 19 avis de `pnpm audit` sont corrigés par montée de version, sans aucun
667
+ waiver** (#718, #796).
668
+ `pnpm audit --prod` : 9 vulnérabilités (2 `moderate`, 7 `high`, 2 ignorées) →
669
+ `No known vulnerabilities found`. `pnpm audit` sur l'arbre **complet** :
670
+ 10 vulnérabilités (4 `moderate`, 6 `high`) → idem. Codes de sortie `1` → `0`,
671
+ capturés **hors pipe**. Remesuré sur cette release : les deux répondent encore
672
+ `No known vulnerabilities found`, exit `0`.
673
+ Le bloc `pnpm.auditConfig.ignoreGhsas` est **supprimé**, pas élargi, et la
674
+ dérogation accordée à `image-size` est **révoquée** : son critère de sortie —
675
+ la publication d'un correctif — avait joué, et le waiver avait survécu un mois
676
+ à son propre correctif. Vérifié dans le **code** des paquets et non dans la
677
+ plage `semver`, l'avis annonçant toujours `first_patched_version: null`.
678
+ Aucune montée de majeur. `docs/security-waivers.md` passe à « aucun waiver
679
+ actif ».
680
+ ⚠️ **Le paquet npm `origam` n'était pas concerné.** Ces avis vivaient dans
681
+ l'arbre de **build et de développement** du dépôt, et les 6 paquets de
682
+ production incriminés n'arrivaient que par le site vitrine, qui est `private`.
683
+ Les dépendances déclarées du paquet publié n'ont pas changé depuis `2.17.1` :
684
+ un consommateur n'était pas exposé, et ne gagne ici qu'une chaîne de
685
+ construction propre.
686
+
687
+ ### Internal
688
+
689
+ **42 des 80 commits ne touchent pas `packages/ds/src/`** et sont invisibles pour
690
+ un consommateur du paquet. Quatre thèmes :
691
+
692
+ - **Site vitrine** — 117 pages sorties du 404 (`/privacy` et `/contact`
693
+ créées), 34 liens de navigation et de pied de page qui suivent enfin la langue
694
+ du visiteur, polices auto-hébergées (plus aucun hôte tiers sur les 19 pages),
695
+ une fuite mémoire du worker SSR de développement (~3,7 Mo par rendu), et une
696
+ garde contre une migration de base destructive.
697
+ - **Fiabilité des tests e2e** — la garde de couverture a mesuré que **177 specs
698
+ sur 242 n'étaient exécutées par aucun job de CI**, sans le moindre signal ;
699
+ une spec rouge 5 fois sur 5 était repeinte en vert par les `retries` ; treize
700
+ attentes codées en dur ont été remplacées par des attentes d'état.
701
+ - **Gardes d'architecture** — de 17 à **28 gardes**, et **15 auto-tests qui
702
+ s'exécutent enfin** (rien ne les lançait). Parmi les nouveaux : `ts-token-refs`
703
+ (garde 28), qui rejoue chaque gabarit `var()` assemblé en TypeScript un chemin
704
+ d'exécution à la fois — **158 références qu'aucune garde ne regardait**, et
705
+ c'est par là que #813 était passé.
706
+ - **Outillage de documentation** — le générateur de référence des composables
707
+ tronquait 56 signatures sur 179, puis aplatissait 20 types de retour objet :
708
+ ces deux défauts sont à zéro.
709
+
710
+ ## [2.17.1] - 2026-09-14
711
+
712
+ Hotfix. Quatre correctifs, **aucune rupture d'API**. Trois des quatre ont
713
+ invalidé le diagnostic du ticket qui les demandait — les causes réelles sont
714
+ consignées ci-dessous, parce qu'elles sont plus instructives que les correctifs.
715
+
716
+ ### Fixed
717
+
718
+ - **`border` / `outlined` sur les champs ne coupe plus le label flottant** (#726).
719
+ La hiérarchie DOM est l'inverse de ce que les noms suggèrent : `origam-input`
720
+ est l'élément EXTÉRIEUR, `origam-field` est à l'intérieur. `useBorder` peignait
721
+ donc un rectangle SANS encoche dont le bord haut traversait le label.
722
+ `IFieldProps` étend désormais `IBorderProps` et route largeur / style / couleur
723
+ vers le canal de tokens que lisent les trois pattes `__outline` — l'encoche
724
+ s'ouvre d'elle-même. Mesure de l'aire d'intersection label × bande de bordure,
725
+ Chromium : TextField `61,41 px² → 0`, Select sans aucune prop `35,59 px² → 0`,
726
+ Select `border="thick"` `71,19 px² → 0`.
727
+ ⚠️ `OrigamSelect` déclare `border: true` dans son propre `withDefaults` : il
728
+ était donc cassé PAR DÉFAUT, sans qu'un consommateur passe quoi que ce soit.
729
+ Les 12 consommateurs d'`OrigamInput` ont été audités : 4 emboîtent un field
730
+ (corrigés), 8 n'en ont aucun et gardent `useBorder` sur leur racine.
731
+ Les valeurs directionnelles (`border="top"`, `borderBlock`…) ne peignent plus
732
+ rien sur les champs à encoche : un trait horizontal y couperait le label.
733
+
734
+ - **Le track de `OrigamSwitch` a de nouveau une bordure par défaut** (#727).
735
+ Les deux tokens étaient déclarés et LUS PAR PERSONNE — ils figuraient dans la
736
+ baseline `token-var-channels-dormant`. Changer leurs valeurs seules n'aurait
737
+ rien produit ; il fallait ajouter la déclaration qui les lit. Défaut :
738
+ `1px solid var(--origam-color__border---default)`, donc le switch suit le thème
739
+ actif. Mesure : `0px / none → 1px / solid`. La surcharge reste entière
740
+ (`border="thick"` → 2px, `border="top"` ramène les autres côtés à 0).
741
+
742
+ - **`OrigamMasonry` patche ses enfants au lieu de les reconstruire** (#735).
743
+ `:is="{ render: () => child }"` reconstruisait un objet littéral à CHAQUE passe
744
+ de rendu : Vue voyait un type différent et détruisait / recréait tout le
745
+ sous-arbre. Mesure Chromium sur UN SEUL redimensionnement, 9 enfants :
746
+ 117 nœuds ajoutés, 117 retirés, 0 enfant conservant son nœud DOM. Les enfants
747
+ perdaient focus, scroll, lecture média, transitions et état de composant, et
748
+ rejouaient leurs `onMounted`. Corrigé par `:is="child"` — branche `isVNode` de
749
+ `createVNode`. Survivants `0/9 → 9/9`. Présent depuis la naissance du
750
+ composant (2026-05-15), ce n'est pas une régression.
751
+
752
+ - **`OrigamVirtualScroll` rend de nouveau** (#736).
753
+ L'erreur `$setup.convertToUnit is not a function` désignait la victime, pas le
754
+ coupable : `convertToUnit` était bien importé ET exposé. `items?` était
755
+ optionnelle SANS défaut, `useVirtual` lit `items.value.length` synchroniquement
756
+ dans le `setup()`, le setup tombait, Vue n'assemblait jamais `__returned__`, et
757
+ le template échouait sur le premier symbole rencontré. Corrigé par
758
+ `items: () => []`. La doc annonçait déjà ce défaut — elle dit enfin vrai.
759
+ Balayage : 0 composant sur 218 appelle un symbole absent de son setup ;
760
+ sur la famille réelle (prop optionnelle lue sans garde), 35 → 34, les 34
761
+ restants étant 18 refus délibérés et 16 props `required: true`.
762
+
20
763
 
21
764
  ## [2.17.0] - 2026-09-14
22
765