origam 2.17.1 → 2.18.1

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