forty-cdk 0.15.0 → 0.17.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 (191) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +1 -1
  3. package/accordion/README.md +5 -1
  4. package/aspect-ratio/README.md +1 -1
  5. package/avatar/README.md +5 -1
  6. package/button/README.md +1 -1
  7. package/calendar/README.md +5 -1
  8. package/carousel/README.md +32 -20
  9. package/checkbox/README.md +9 -6
  10. package/combobox/README.md +25 -18
  11. package/context-menu/README.md +9 -5
  12. package/date-field/README.md +2 -2
  13. package/date-picker/README.md +7 -7
  14. package/date-range-field/README.md +2 -2
  15. package/dialog/README.md +10 -6
  16. package/disclosure/README.md +6 -2
  17. package/drag-drop/README.md +39 -4
  18. package/drawer/README.md +38 -31
  19. package/dropdown-menu/README.md +9 -5
  20. package/fesm2022/forty-cdk-accordion.mjs +38 -7
  21. package/fesm2022/forty-cdk-accordion.mjs.map +1 -1
  22. package/fesm2022/forty-cdk-button.mjs +9 -43
  23. package/fesm2022/forty-cdk-button.mjs.map +1 -1
  24. package/fesm2022/forty-cdk-calendar.mjs +10 -7
  25. package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
  26. package/fesm2022/forty-cdk-carousel.mjs +62 -19
  27. package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
  28. package/fesm2022/forty-cdk-checkbox.mjs +34 -8
  29. package/fesm2022/forty-cdk-checkbox.mjs.map +1 -1
  30. package/fesm2022/forty-cdk-combobox.mjs +60 -171
  31. package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
  32. package/fesm2022/forty-cdk-context-menu.mjs +10 -2
  33. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
  34. package/fesm2022/forty-cdk-core.mjs +434 -117
  35. package/fesm2022/forty-cdk-core.mjs.map +1 -1
  36. package/fesm2022/forty-cdk-date-field.mjs +7 -2
  37. package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
  38. package/fesm2022/forty-cdk-date-picker.mjs +17 -10
  39. package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
  40. package/fesm2022/forty-cdk-date-range-field.mjs +7 -2
  41. package/fesm2022/forty-cdk-date-range-field.mjs.map +1 -1
  42. package/fesm2022/forty-cdk-dialog.mjs +14 -11
  43. package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
  44. package/fesm2022/forty-cdk-disclosure.mjs +8 -5
  45. package/fesm2022/forty-cdk-disclosure.mjs.map +1 -1
  46. package/fesm2022/forty-cdk-drag-drop.mjs +30 -20
  47. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
  48. package/fesm2022/forty-cdk-drawer.mjs +121 -101
  49. package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
  50. package/fesm2022/forty-cdk-dropdown-menu.mjs +7 -6
  51. package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
  52. package/fesm2022/forty-cdk-file-upload.mjs +4 -3
  53. package/fesm2022/forty-cdk-file-upload.mjs.map +1 -1
  54. package/fesm2022/forty-cdk-hover-card.mjs +4 -4
  55. package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
  56. package/fesm2022/forty-cdk-input.mjs +2 -4
  57. package/fesm2022/forty-cdk-input.mjs.map +1 -1
  58. package/fesm2022/forty-cdk-listbox.mjs +18 -17
  59. package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
  60. package/fesm2022/forty-cdk-menu.mjs +39 -24
  61. package/fesm2022/forty-cdk-menu.mjs.map +1 -1
  62. package/fesm2022/forty-cdk-menubar.mjs +156 -63
  63. package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
  64. package/fesm2022/forty-cdk-navigation-menu.mjs +166 -49
  65. package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
  66. package/fesm2022/forty-cdk-number-input.mjs +8 -7
  67. package/fesm2022/forty-cdk-number-input.mjs.map +1 -1
  68. package/fesm2022/forty-cdk-otp-input.mjs +14 -15
  69. package/fesm2022/forty-cdk-otp-input.mjs.map +1 -1
  70. package/fesm2022/forty-cdk-pagination.mjs +10 -7
  71. package/fesm2022/forty-cdk-pagination.mjs.map +1 -1
  72. package/fesm2022/forty-cdk-popover.mjs +16 -12
  73. package/fesm2022/forty-cdk-popover.mjs.map +1 -1
  74. package/fesm2022/forty-cdk-radio-group.mjs +47 -10
  75. package/fesm2022/forty-cdk-radio-group.mjs.map +1 -1
  76. package/fesm2022/forty-cdk-search.mjs +6 -6
  77. package/fesm2022/forty-cdk-search.mjs.map +1 -1
  78. package/fesm2022/forty-cdk-select.mjs +74 -86
  79. package/fesm2022/forty-cdk-select.mjs.map +1 -1
  80. package/fesm2022/forty-cdk-shared.mjs +1 -1
  81. package/fesm2022/forty-cdk-slider.mjs +31 -29
  82. package/fesm2022/forty-cdk-slider.mjs.map +1 -1
  83. package/fesm2022/forty-cdk-stepper.mjs +13 -9
  84. package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
  85. package/fesm2022/forty-cdk-switch.mjs +33 -7
  86. package/fesm2022/forty-cdk-switch.mjs.map +1 -1
  87. package/fesm2022/forty-cdk-table.mjs +52 -18
  88. package/fesm2022/forty-cdk-table.mjs.map +1 -1
  89. package/fesm2022/forty-cdk-tabs.mjs +45 -26
  90. package/fesm2022/forty-cdk-tabs.mjs.map +1 -1
  91. package/fesm2022/forty-cdk-time-field.mjs +7 -2
  92. package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
  93. package/fesm2022/forty-cdk-time-picker.mjs +25 -9
  94. package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
  95. package/fesm2022/forty-cdk-time-range-field.mjs +7 -2
  96. package/fesm2022/forty-cdk-time-range-field.mjs.map +1 -1
  97. package/fesm2022/forty-cdk-toast.mjs +65 -26
  98. package/fesm2022/forty-cdk-toast.mjs.map +1 -1
  99. package/fesm2022/forty-cdk-toggle.mjs +14 -9
  100. package/fesm2022/forty-cdk-toggle.mjs.map +1 -1
  101. package/fesm2022/forty-cdk-toolbar.mjs +13 -6
  102. package/fesm2022/forty-cdk-toolbar.mjs.map +1 -1
  103. package/fesm2022/forty-cdk-tooltip.mjs +4 -4
  104. package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
  105. package/fesm2022/forty-cdk-tree.mjs +4 -3
  106. package/fesm2022/forty-cdk-tree.mjs.map +1 -1
  107. package/fesm2022/forty-cdk-virtualization.mjs +15 -10
  108. package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
  109. package/field/README.md +5 -1
  110. package/fieldset/README.md +5 -1
  111. package/file-upload/README.md +5 -1
  112. package/hover-card/README.md +10 -6
  113. package/input/README.md +6 -5
  114. package/internationalized-date/README.md +23 -23
  115. package/listbox/README.md +13 -13
  116. package/menu/README.md +10 -6
  117. package/menubar/README.md +24 -6
  118. package/meter/README.md +5 -1
  119. package/navigation-menu/README.md +9 -5
  120. package/number-input/README.md +4 -3
  121. package/otp-input/README.md +8 -7
  122. package/package.json +1 -1
  123. package/pagination/README.md +4 -0
  124. package/pane-resizer/README.md +1 -1
  125. package/popover/README.md +13 -9
  126. package/progress/README.md +5 -1
  127. package/radio-group/README.md +2 -2
  128. package/scroll-area/README.md +5 -1
  129. package/search/README.md +5 -3
  130. package/select/README.md +25 -26
  131. package/separator/README.md +1 -1
  132. package/shared/README.md +18 -3
  133. package/signal-forms/README.md +2 -2
  134. package/slider/README.md +22 -23
  135. package/stepper/README.md +6 -2
  136. package/switch/README.md +9 -7
  137. package/table/README.md +21 -957
  138. package/tabs/README.md +5 -1
  139. package/time-field/README.md +2 -2
  140. package/time-picker/README.md +2 -2
  141. package/time-range-field/README.md +2 -2
  142. package/toast/README.md +7 -3
  143. package/toggle/README.md +6 -5
  144. package/toolbar/README.md +5 -1
  145. package/tooltip/README.md +8 -4
  146. package/tree/README.md +5 -1
  147. package/types/forty-cdk-accordion.d.ts +48 -9
  148. package/types/forty-cdk-breakpoints.d.ts +1 -1
  149. package/types/forty-cdk-button.d.ts +1 -1
  150. package/types/forty-cdk-calendar.d.ts +3 -0
  151. package/types/forty-cdk-carousel.d.ts +83 -30
  152. package/types/forty-cdk-checkbox.d.ts +20 -4
  153. package/types/forty-cdk-combobox.d.ts +43 -23
  154. package/types/forty-cdk-context-menu.d.ts +10 -2
  155. package/types/forty-cdk-core.d.ts +406 -113
  156. package/types/forty-cdk-date-field.d.ts +5 -0
  157. package/types/forty-cdk-date-picker.d.ts +18 -11
  158. package/types/forty-cdk-date-range-field.d.ts +5 -0
  159. package/types/forty-cdk-dialog.d.ts +10 -6
  160. package/types/forty-cdk-disclosure.d.ts +5 -1
  161. package/types/forty-cdk-drag-drop.d.ts +11 -10
  162. package/types/forty-cdk-drawer.d.ts +93 -58
  163. package/types/forty-cdk-dropdown-menu.d.ts +3 -2
  164. package/types/forty-cdk-file-upload.d.ts +1 -0
  165. package/types/forty-cdk-hover-card.d.ts +5 -5
  166. package/types/forty-cdk-listbox.d.ts +10 -9
  167. package/types/forty-cdk-menu.d.ts +14 -4
  168. package/types/forty-cdk-menubar.d.ts +99 -14
  169. package/types/forty-cdk-navigation-menu.d.ts +151 -44
  170. package/types/forty-cdk-number-input.d.ts +2 -0
  171. package/types/forty-cdk-otp-input.d.ts +7 -7
  172. package/types/forty-cdk-pagination.d.ts +3 -0
  173. package/types/forty-cdk-popover.d.ts +12 -7
  174. package/types/forty-cdk-radio-group.d.ts +35 -19
  175. package/types/forty-cdk-search.d.ts +15 -14
  176. package/types/forty-cdk-select.d.ts +33 -16
  177. package/types/forty-cdk-shared.d.ts +1 -1
  178. package/types/forty-cdk-slider.d.ts +28 -23
  179. package/types/forty-cdk-stepper.d.ts +8 -4
  180. package/types/forty-cdk-switch.d.ts +23 -7
  181. package/types/forty-cdk-tabs.d.ts +73 -16
  182. package/types/forty-cdk-time-field.d.ts +5 -0
  183. package/types/forty-cdk-time-picker.d.ts +21 -4
  184. package/types/forty-cdk-time-range-field.d.ts +5 -0
  185. package/types/forty-cdk-toast.d.ts +70 -23
  186. package/types/forty-cdk-toggle.d.ts +6 -1
  187. package/types/forty-cdk-toolbar.d.ts +5 -2
  188. package/types/forty-cdk-tooltip.d.ts +5 -5
  189. package/types/forty-cdk-tree.d.ts +1 -0
  190. package/virtualization/README.md +4 -0
  191. package/visually-hidden/README.md +1 -1
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 tutkli
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tutkli
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -98,7 +98,7 @@ The tables below group the primitives by purpose. The link on each name opens th
98
98
  | ------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
99
99
  | [Calendar](calendar) | A single-date calendar grid (APG Grid) over a pluggable date adapter, with roving-tabindex navigation. |
100
100
  | [Date Field](date-field) | A segmented date (and optional time) input — each part a spinbutton with locale-driven order and clamping. |
101
- | [Date Picker](date-picker) | A trigger that opens a floating calendar to pick a date, composing Calendar inside a dismissable popover. |
101
+ | [Date Picker](date-picker) | A trigger that opens a floating calendar to pick a date, composing Calendar inside a dismissible popover. |
102
102
  | [Date Range Field](date-range-field) | Two labelled spinbutton endpoints (start / end) sharing locale, granularity and bounds. |
103
103
  | [Time Field](time-field) | A segmented time-of-day input with 12 / 24-hour cycles, optional seconds and min / max clamping. |
104
104
  | [Time Picker](time-picker) | A trigger that opens a floating listbox of generated time slots over a pluggable date adapter. |
@@ -120,7 +120,7 @@ export class DemoFaq {
120
120
 
121
121
  ## Styling
122
122
 
123
- forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed per piece in the [API](#api) section.
123
+ forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed per piece in the [API](#api) section.
124
124
 
125
125
  ```css
126
126
  .trigger-chevron {
@@ -131,3 +131,7 @@ forty-cdk ships no styles. Add your own class to each piece — the `for*` selec
131
131
  transform: rotate(180deg);
132
132
  }
133
133
  ```
134
+
135
+ ## Wrapping in a design system
136
+
137
+ Subclassing the root is the supported pattern; the subclass must spread `provideForAccordion(MyRoot)` into its own `providers` because Angular does not inherit a directive's `providers`, and every projected piece resolves its context through it. Re-providing `FOR_ACCORDION_CONTEXT` by hand is not enough: the root also provides an unexported registration token the wrapper cannot name. See [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md).
@@ -87,7 +87,7 @@ export class DemoAspectRatio {}
87
87
 
88
88
  ## Styling
89
89
 
90
- forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). This primitive is purely structural: its only host effect is the native `aspect-ratio` style, so it reflects no `data-*` attributes and writes no CSS custom properties. Style the host through your own class on `[forAspectRatio]`.
90
+ forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). This primitive is purely structural: its only host effect is the native `aspect-ratio` style, so it reflects no `data-*` attributes and writes no CSS custom properties. Style the host through your own class on `[forAspectRatio]`.
91
91
 
92
92
  ## Behavior notes
93
93
 
package/avatar/README.md CHANGED
@@ -98,7 +98,7 @@ The directive does not impose a `role`. Pair the avatar with visible name text o
98
98
 
99
99
  ## Styling
100
100
 
101
- forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed per piece in the [API](#api) section.
101
+ forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed per piece in the [API](#api) section.
102
102
 
103
103
  ```css
104
104
  .avatar-image:not([data-status='loaded']) {
@@ -115,3 +115,7 @@ forty-cdk ships no styles. Add your own class to each piece — the `for*` selec
115
115
  - **Multiple images per avatar are not supported.** Each `[forAvatar]` expects exactly one `[forAvatarImage]`. If you need cascading sources (CDN → fallback URL → fallback content), swap `src` on a single image.
116
116
  - **`alt` is consumer territory.** Because `<img>` is the host element, the consumer keeps full control of `alt` — set `""` for purely decorative avatars next to a name, or describe the person if the avatar stands alone.
117
117
  - **The image stays in the DOM.** Hide it via CSS `[data-status="loading"], [data-status="error"] { display: none }` if your consumer-side styling needs it gone. The fallback uses `@if`, so it only mounts when needed.
118
+
119
+ ## Wrapping in a design system
120
+
121
+ Subclassing the root is the supported pattern; the subclass must re-provide `FOR_AVATAR_CONTEXT` because Angular does not inherit a directive's `providers`, and every projected piece resolves its context through it. See [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md).
package/button/README.md CHANGED
@@ -81,7 +81,7 @@ Implements the [WAI-ARIA Button pattern](https://www.w3.org/WAI/ARIA/apg/pattern
81
81
 
82
82
  ## Styling
83
83
 
84
- forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed per piece in the [API](#api) section.
84
+ forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed per piece in the [API](#api) section.
85
85
 
86
86
  ```css
87
87
  [forButton][data-disabled] {
@@ -438,7 +438,7 @@ Implements the [WAI-ARIA Grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/
438
438
 
439
439
  ## Styling
440
440
 
441
- forty-cdk ships no styles. Add your own class to each piece — the `forCalendar*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed under [Data attributes](#data-attributes).
441
+ forty-cdk ships no styles. Add your own class to each piece — the `forCalendar*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed under [Data attributes](#data-attributes).
442
442
 
443
443
  ```css
444
444
  .calendar-cell {
@@ -481,3 +481,7 @@ export class DatePage {
481
481
  }
482
482
  }
483
483
  ```
484
+
485
+ ## Wrapping in a design system
486
+
487
+ Subclassing the root is the supported pattern; the subclass must re-provide `FOR_CALENDAR_CONTEXT` because Angular does not inherit a directive's `providers`, and every projected piece resolves its context through it. See [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md).
@@ -146,17 +146,22 @@ don't use it.
146
146
 
147
147
  ### CSS contract
148
148
 
149
- The directive publishes `--for-carousel-drag` (a raw px value) on the viewport
150
- host during the gesture. Compose it with `--for-carousel-offset` on the track
151
- transform:
149
+ The directive publishes the live displacement as `--for-carousel-swipe-movement-x`
150
+ (horizontal carousels) or `--for-carousel-swipe-movement-y` (vertical), a raw px
151
+ value on the viewport host; only the primary-axis property is written. Compose it
152
+ with `--for-carousel-offset` on the track transform:
152
153
 
153
154
  ```css
154
155
  [forCarouselTrack] {
155
- transform: translateX(calc(var(--for-carousel-offset) + var(--for-carousel-drag, 0px)));
156
+ transform: translateX(
157
+ calc(var(--for-carousel-offset) + var(--for-carousel-swipe-movement-x, 0px))
158
+ );
156
159
  transition: transform 300ms ease;
157
160
  }
158
161
  [forCarousel][data-orientation='vertical'] [forCarouselTrack] {
159
- transform: translateY(calc(var(--for-carousel-offset) + var(--for-carousel-drag, 0px)));
162
+ transform: translateY(
163
+ calc(var(--for-carousel-offset) + var(--for-carousel-swipe-movement-y, 0px))
164
+ );
160
165
  }
161
166
  /* Kill the settle transition while the finger is down so the track follows 1:1 */
162
167
  [forCarouselViewport][data-dragging] [forCarouselTrack] {
@@ -169,20 +174,22 @@ transform:
169
174
 
170
175
  ### RTL
171
176
 
172
- `--for-carousel-drag` is always the **physical** finger displacement, so compose
173
- it **without** the `-1` factor the consumer may apply to `--for-carousel-offset`
174
- in RTL:
177
+ `--for-carousel-swipe-movement-x` is always the **physical** finger displacement,
178
+ so compose it **without** the `-1` factor the consumer may apply to
179
+ `--for-carousel-offset` in RTL:
175
180
 
176
181
  ```css
177
182
  [dir='rtl'] [forCarouselTrack] {
178
- transform: translateX(calc(-1 * var(--for-carousel-offset) + var(--for-carousel-drag, 0px)));
183
+ transform: translateX(
184
+ calc(-1 * var(--for-carousel-offset) + var(--for-carousel-swipe-movement-x, 0px))
185
+ );
179
186
  }
180
187
  ```
181
188
 
182
189
  ### Reduced motion
183
190
 
184
191
  Under `prefers-reduced-motion: reduce` the directive does **not** publish
185
- `--for-carousel-drag` (no live track motion). The gesture still snaps
192
+ `--for-carousel-swipe-movement-x` / `-y` (no live track motion). The gesture still snaps
186
193
  `activeIndex` on release — only the continuous live offset is suppressed.
187
194
 
188
195
  ### Cross-axis / touch
@@ -319,7 +326,7 @@ Implements the [WAI-ARIA Carousel pattern](https://www.w3.org/WAI/ARIA/apg/patte
319
326
 
320
327
  ## Styling
321
328
 
322
- forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). The directive publishes geometry as CSS custom properties on the root element so they cascade to the track. The consumer applies the transform and transition.
329
+ forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). The directive publishes geometry as CSS custom properties on the root element so they cascade to the track. The consumer applies the transform and transition.
323
330
 
324
331
  ```css
325
332
  [forCarouselViewport] {
@@ -349,15 +356,16 @@ forty-cdk ships no styles. Add your own class to each piece — the `for*` selec
349
356
  The following properties are set on the `[forCarousel]` host and cascade to
350
357
  children, unless noted otherwise:
351
358
 
352
- | Property | Host | Value | Notes |
353
- | -------------------------------- | ----------------------- | ------------- | --------------------------------------------------------------------------------------------------------------- |
354
- | `--for-carousel-offset` | `[forCarousel]` | e.g. `-100%` | Pure arithmetic from `activeIndex`, `slidesPerView`, `align`. |
355
- | `--for-carousel-active-index` | `[forCarousel]` | integer | Current `activeIndex`. |
356
- | `--for-carousel-slide-count` | `[forCarousel]` | integer | Total registered slides. |
357
- | `--for-carousel-slides-per-view` | `[forCarousel]` | integer | From the `slidesPerView` input. |
358
- | `--for-carousel-viewport-width` | `[forCarousel]` | e.g. `640px` | Measured via `ResizeObserver`. Absent on the server and before first measurement. |
359
- | `--for-carousel-viewport-height` | `[forCarousel]` | e.g. `400px` | Same as above, for the block axis. |
360
- | `--for-carousel-drag` | `[forCarouselViewport]` | e.g. `-128px` | Live px offset along the primary axis during a drag; absent at rest and under `prefers-reduced-motion: reduce`. |
359
+ | Property | Host | Value | Notes |
360
+ | --------------------------------- | ----------------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
361
+ | `--for-carousel-offset` | `[forCarousel]` | e.g. `-100%` | Pure arithmetic from `activeIndex`, `slidesPerView`, `align`. |
362
+ | `--for-carousel-active-index` | `[forCarousel]` | integer | Current `activeIndex`. |
363
+ | `--for-carousel-slide-count` | `[forCarousel]` | integer | Total registered slides. |
364
+ | `--for-carousel-slides-per-view` | `[forCarousel]` | integer | From the `slidesPerView` input. |
365
+ | `--for-carousel-viewport-width` | `[forCarousel]` | e.g. `640px` | Measured via `ResizeObserver`. Absent on the server and before first measurement. |
366
+ | `--for-carousel-viewport-height` | `[forCarousel]` | e.g. `400px` | Same as above, for the block axis. |
367
+ | `--for-carousel-swipe-movement-x` | `[forCarouselViewport]` | e.g. `-128px` | Live px displacement along the primary axis during a swipe. Only the axis matching `orientation` is written; the other is absent, as is both at rest and under `prefers-reduced-motion: reduce`. |
368
+ | `--for-carousel-swipe-movement-y` | `[forCarouselViewport]` | e.g. `-128px` | Live px displacement along the primary axis during a swipe. Only the axis matching `orientation` is written; the other is absent, as is both at rest and under `prefers-reduced-motion: reduce`. |
361
369
 
362
370
  ### Autoplay styling hooks
363
371
 
@@ -415,3 +423,7 @@ in RTL is the consumer's CSS concern. For example, to flip the translate sign in
415
423
  ```
416
424
 
417
425
  The example CSS above is LTR-only by default.
426
+
427
+ ## Wrapping in a design system
428
+
429
+ Subclassing the root is the supported pattern; the subclass must spread `provideForCarousel(MyRoot)` into its own `providers` because Angular does not inherit a directive's `providers`, and every projected piece resolves its context through it. Re-providing `FOR_CAROUSEL_CONTEXT` by hand is not enough: the root also provides an unexported registration token the wrapper cannot name. See [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md).
@@ -136,23 +136,26 @@ Optional styling slot inside a `[forCheckbox]`. Mirrors the parent's `data-state
136
136
 
137
137
  ## Keyboard
138
138
 
139
- | Key | Action |
140
- | ------- | ----------------------------------------------------------------------------------------------- |
141
- | `Space` | Toggle the checkbox. The only key APG mandates. |
142
- | `Enter` | Also toggles — the directive sits on a `<button>`, a documented superset, not an APG violation. |
139
+ | Key | Action |
140
+ | ------- | ----------------------------------------------------------- |
141
+ | `Space` | Toggle the checkbox. The only key APG mandates. |
142
+ | `Enter` | Also toggles — a documented superset, not an APG violation. |
143
143
 
144
144
  Activating an indeterminate checkbox clears `indeterminate` and toggles `checked` (matches native `<input type="checkbox">`).
145
145
 
146
+ Both keys work on any host element. On a `<button>` they come from native button behavior; on any other host (`<div>`, `<span>`, or a `hostDirectives` wrapper's own host) the directive adds `tabindex="0"` and synthesizes the same activation, so a styled-from-scratch checkbox is never announced as a checkbox it is impossible to operate. `Space` keydown always blocks page scrolling; the toggle fires on its keyup.
147
+
146
148
  ## Accessibility
147
149
 
148
150
  Implements the [WAI-ARIA Checkbox pattern](https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/).
149
151
 
150
152
  - **Provide an accessible name.** Wrap the button in a `<label>`, or set `aria-labelledby` / `aria-label`. Without one, the control is announced as just "checkbox" with no purpose.
153
+ - **Any host element works.** A `<button>` is the recommended host (the directive forces `type="button"` through a host binding, so it never submits a surrounding form even if you write `type="submit"` yourself), but a non-button host gets `tabindex="0"` and synthesized `Space` / `Enter` activation, so it is keyboard-operable too. A non-button host gets no `type` attribute at all — `type` is not valid on a `<div>` / `<span>`, and there is no form submission to protect against.
151
154
  - **`role="checkbox"`** with `aria-checked="mixed"` is the canonical tri-state contract. Some legacy screen readers handle "mixed" differently — test with your target SRs.
152
155
 
153
156
  ## Styling
154
157
 
155
- forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed per piece in the [API](#api) section.
158
+ forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed per piece in the [API](#api) section.
156
159
 
157
160
  ```css
158
161
  .checkbox-indicator[data-state='unchecked'] {
@@ -166,4 +169,4 @@ forty-cdk ships no styles. Add your own class to each piece — the for\* select
166
169
 
167
170
  ## Wrapping in a design system
168
171
 
169
- Both supported wrapper patterns — `hostDirectives` with the exported `FOR_CHECKBOX_HOST_DIRECTIVE_INPUTS` / `FOR_CHECKBOX_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../../../docs/wrapping-form-primitives.md).
172
+ Both supported wrapper patterns — `hostDirectives` with the exported `FOR_CHECKBOX_HOST_DIRECTIVE_INPUTS` / `FOR_CHECKBOX_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../docs/wrapping-form-primitives.md).
@@ -2,7 +2,7 @@
2
2
 
3
3
  An editable input paired with a filterable listbox popup, supporting single or multi selection with chips.
4
4
 
5
- > New to overlays in forty-cdk? [Your first overlay](../../../../../docs/your-first-overlay.md) walks a Popover from empty markup to styled-and-animated and explains the `@if` / open-state model and the portal → global CSS rule.
5
+ > New to overlays in forty-cdk? [Your first overlay](../../../docs/your-first-overlay.md) walks a Popover from empty markup to styled-and-animated and explains the `@if` / open-state model and the portal → global CSS rule.
6
6
 
7
7
  Headless: `role="combobox"` on the input, `role="listbox"` on the surface, `role="option"` on items, plus `aria-activedescendant` so DOM focus stays in the input. Implements the `FormValueControl<readonly T[]>` interface from `@angular/forms/signals`.
8
8
 
@@ -232,7 +232,7 @@ runtime. This is the "editable + list" shape (no `[forComboboxTrigger]` needed).
232
232
  <input forComboboxInput placeholder="Search…" />
233
233
  @if (combobox.open()) {
234
234
  <div forComboboxContent>
235
- <button forComboboxAction (action)="createNew(query())">Create "{{ query() }}"</button>
235
+ <button forComboboxAction (activate)="createNew(query())">Create "{{ query() }}"</button>
236
236
  <div forComboboxList>
237
237
  @for (it of filtered; track it.id) {
238
238
  <div forComboboxOption [value]="it.id" [label]="it.label">{{ it.label }}</div>
@@ -247,7 +247,7 @@ An action:
247
247
 
248
248
  - **never touches `value` / `options()`.** It registers in a collection separate
249
249
  from options, so `options()`, `aria-setsize`, and `aria-posinset` are
250
- unaffected and activation emits `(action)` instead of mutating `[(value)]`. The
250
+ unaffected and activation emits `(activate)` instead of mutating `[(value)]`. The
251
251
  consumer decides what happens and whether to close the popup afterwards.
252
252
  - **is `role="button"`, not `role="option"`.** Assistive tech announces it as an
253
253
  action, not as one of N choices.
@@ -270,7 +270,7 @@ a bottom-pinned option cannot guarantee under infinite scroll.
270
270
  Because focus is trapped in the input↔actions ring while open, **Escape** (or an
271
271
  outside pointer) is how you leave: Escape from an action closes the popup and
272
272
  returns focus to the input (editable anatomy) or the `[forComboboxTrigger]`
273
- (picker anatomy). Activation is **click / Enter / Space** and routes to `(action)`
273
+ (picker anatomy). Activation is **click / Enter / Space** and routes to `(activate)`
274
274
  only. With no action registered, Tab keeps its default "close and let Tab flow on"
275
275
  behaviour, so existing comboboxes are unchanged.
276
276
 
@@ -283,11 +283,12 @@ outside-focus dismissal checks, exactly like the input.
283
283
  | Member | Type | Notes |
284
284
  | ------------ | -------------- | ---------------------------------------------------------------------------------------------------------- |
285
285
  | `[disabled]` | `boolean` | Drops the action out of the focus ring (`tabindex` removed), reflects `aria-disabled`, ignores activation. |
286
- | `(action)` | `output<void>` | Fired on click / Enter / Space. Never mutates `[(value)]`. |
286
+ | `(activate)` | `output<void>` | Fired on click / Enter / Space. Never mutates `[(value)]`. |
287
287
 
288
- `[forComboboxAction]` host-binds `role="button"`, `type="button"`, a
289
- primitive-managed `tabindex`, `aria-disabled` (when disabled), and reflects
290
- `data-highlighted` while it holds DOM focus + `data-disabled` when disabled.
288
+ `[forComboboxAction]` host-binds `role="button"`, `type="button"` (on a native
289
+ `<button>` host only — any other element gets no `type`), a primitive-managed
290
+ `tabindex`, `aria-disabled` (when disabled), and reflects `data-highlighted`
291
+ while it holds DOM focus + `data-disabled` when disabled.
291
292
 
292
293
  > **Out of scope (v1):** grouped action clusters / multiple action zones,
293
294
  > submenu-style nested actions, and actions that mutate `value` (use a plain
@@ -380,7 +381,13 @@ Chips are intentionally **out of the Tab cycle** — Tab from outside lands on t
380
381
 
381
382
  In RTL the chip cluster lays out right-to-left, so **ArrowRight** moves to the visually-next chip (DOM-previous) and **ArrowLeft** moves to the visually-previous one (DOM-next, hopping to the input at the leftmost visual edge).
382
383
 
383
- `[forComboboxChipRemove]` is a click-only target (also out of Tab cycle) with auto-generated `aria-label="Remove <chip label>"`.
384
+ `[forComboboxChipRemove]` is a click-only target (also out of Tab cycle) with auto-generated `aria-label="Remove <chip label>"`. The name is computed per chip, so the piece takes no `[ariaLabel]` input and ignores a static `aria-label` attribute — localize it centrally by overriding the scope's builder:
385
+
386
+ ```ts
387
+ @Component({
388
+ providers: [provideForComboboxDefaults({ chipRemoveLabel: (label) => `Quitar ${label}` })],
389
+ })
390
+ ```
384
391
 
385
392
  ### Multi-mode Backspace heuristic
386
393
 
@@ -440,11 +447,11 @@ Real apps usually have richer option models — `{ id, label, ... }` — where t
440
447
 
441
448
  Three inputs configure the object behaviour. Defaults make string mode work unchanged:
442
449
 
443
- | Input | Default | Purpose |
444
- | ---------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
445
- | `[isItemEqualToValue]` | `(a, b) => a === b` | How two items compare. Override for object values so selection / removal locate by id (or any stable key). |
446
- | `[itemToStringLabel]` | `(item) => String(item)` | Render an item as a string. Drives `commitOnSelect` writes into the input and the chip-label fallback. |
447
- | `[itemToFormValue]` | `(item) => typeof item === 'string' ? item : JSON.stringify(item)` | Serialize an item for the hidden input. Override to emit a per-item id (or any wire format your backend wants). |
450
+ | Input | Default | Purpose |
451
+ | --------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
452
+ | `[compareWith]` | `(a, b) => a === b` | How two items compare. Override for object values so selection / removal locate by id (or any stable key). |
453
+ | `[itemToStringLabel]` | `(item) => String(item)` | Render an item as a string. Drives `commitOnSelect` writes into the input and the chip-label fallback. |
454
+ | `[itemToFormValue]` | `(item) => typeof item === 'string' ? item : JSON.stringify(item)` | Serialize an item for the hidden input. Override to emit a per-item id (or any wire format your backend wants). |
448
455
 
449
456
  ```html
450
457
  @let q = query().toLowerCase(); @let filtered = cities().filter((c) =>
@@ -455,7 +462,7 @@ c.name.toLowerCase().includes(q));
455
462
  [(query)]="query"
456
463
  [(value)]="value"
457
464
  [(open)]="open"
458
- [isItemEqualToValue]="byId"
465
+ [compareWith]="byId"
459
466
  [itemToStringLabel]="toName"
460
467
  name="city"
461
468
  [itemToFormValue]="toId"
@@ -617,7 +624,7 @@ Implements the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patte
617
624
 
618
625
  ## Styling
619
626
 
620
- forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
627
+ forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
621
628
 
622
629
  ### CSS custom properties
623
630
 
@@ -631,7 +638,7 @@ forty-cdk ships no styles. Add your own class to each piece — the for\* select
631
638
  | `--for-available-height` | px | Space available along the block axis — clamp with `max-height`. |
632
639
  | `--for-content-transform-origin` | `<origin>` keywords | `transform-origin` matching the resolved side / align, so a `scale` enter animation pivots from the input. |
633
640
 
634
- > `[forComboboxContent]` is portaled to `document.body`, so it lives outside your component's view-encapsulated styles. Style it with global CSS (or a class you pass through) and the shared positioner properties above. See [Styling floating content](../../../../../docs/styling-floating-content.md) for the full positioner-variable list and the portal styling rules.
641
+ > `[forComboboxContent]` is portaled to `document.body`, so it lives outside your component's view-encapsulated styles. Style it with global CSS (or a class you pass through) and the shared positioner properties above. See [Styling floating content](../../../docs/styling-floating-content.md) for the full positioner-variable list and the portal styling rules.
635
642
 
636
643
  ```css
637
644
  .option[data-highlighted] {
@@ -645,4 +652,4 @@ forty-cdk ships no styles. Add your own class to each piece — the for\* select
645
652
 
646
653
  ## Wrapping in a design system
647
654
 
648
- Both supported wrapper patterns — `hostDirectives` with the exported `FOR_COMBOBOX_HOST_DIRECTIVE_INPUTS` / `FOR_COMBOBOX_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../../../docs/wrapping-form-primitives.md).
655
+ Both supported wrapper patterns — `hostDirectives` with the exported `FOR_COMBOBOX_HOST_DIRECTIVE_INPUTS` / `FOR_COMBOBOX_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../docs/wrapping-form-primitives.md).
@@ -4,7 +4,7 @@ A menu opened by right-click or long-press, anchored to the pointer position.
4
4
 
5
5
  Opened via the `contextmenu` event (right-click, long-press on touch) and via the keyboard activators `Shift+F10` and the dedicated `ContextMenu` key. The native browser context menu is suppressed. Pointer activations anchor the menu at the cursor; keyboard activations anchor it at the bounding rect of the focused element so screen-reader / keyboard-only users get the menu next to whatever they're working on. Floating-ui's virtual element handles either case — placement, flip, and shift middleware still apply, so the menu is repositioned to stay on-screen automatically.
6
6
 
7
- > New to overlays in forty-cdk? [Your first overlay](../../../../../docs/your-first-overlay.md) walks a Popover from empty markup to styled-and-animated and explains the `@if` / open-state model and the portal → global CSS rule.
7
+ > New to overlays in forty-cdk? [Your first overlay](../../../docs/your-first-overlay.md) walks a Popover from empty markup to styled-and-animated and explains the `@if` / open-state model and the portal → global CSS rule.
8
8
 
9
9
  ## Anatomy
10
10
 
@@ -113,7 +113,7 @@ Angular resolves `ng-template` DI at the template's **declaration** site, not wh
113
113
  | `dismissible` | `input<boolean>` | When `false`, Escape and outside interactions don't close.<br>**Default:** `true` |
114
114
  | `returnFocus` | `input<boolean>` | When `true`, focus returns to the trigger element on close.<br>**Default:** `true` |
115
115
  | `ariaLabel` | `input<string \| null>` | Accessible name reflected as `aria-label` on `[forMenuContent]`. The root's only name hook for a context menu — the right-click region is never used as an `aria-labelledby` target.<br>**Default:** `null` |
116
- | `escapeKeyDown` | `output<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape pressed while the menu is the topmost dismissable layer.<br>**Default:** — |
116
+ | `escapeKeyDown` | `output<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape pressed while the menu is the topmost dismissible layer.<br>**Default:** — |
117
117
  | `pointerDownOutside` | `output<VetoableNativeEvent<PointerEvent>>` | Output. Pointer-down on a target outside content + trigger.<br>**Default:** — |
118
118
  | `focusOutside` | `output<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside content + trigger.<br>**Default:** — |
119
119
  | `interactOutside` | `output<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite — fires alongside the two above (and shares their veto state).<br>**Default:** — |
@@ -122,7 +122,7 @@ Angular resolves `ng-template` DI at the template's **declaration** site, not wh
122
122
 
123
123
  Same vetoable dismiss API as DropdownMenu. Call `preventDefault()` on the emitted veto to suppress the directive's default action; the original DOM event, when present, is on `.event`.
124
124
 
125
- `(autoFocusOnOpen)` / `(autoFocusOnClose)` are output-shape because ContextMenu always routes close transitions through `[(open)]` (via the implicit `openChange` emitter). See [CLAUDE.md › Auto-focus hook shape](../../../../../CLAUDE.md#auto-focus-hook-shape) for why Dialog uses callback-shape inputs instead.
125
+ `(autoFocusOnOpen)` / `(autoFocusOnClose)` are output-shape because ContextMenu always routes close transitions through `[(open)]` (via the implicit `openChange` emitter). See [Conventions › Auto-focus hook shape](../../../.claude/rules/conventions.md#auto-focus-hook-shape) for why Dialog uses callback-shape inputs instead.
126
126
 
127
127
  ### Data attributes
128
128
 
@@ -141,9 +141,9 @@ Same vetoable dismiss API as DropdownMenu. Call `preventDefault()` on the emitte
141
141
 
142
142
  ## Styling
143
143
 
144
- forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
144
+ forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
145
145
 
146
- > The menu content (`[forMenuContent]`, from the [`menu/`](../menu/README.md) folder) portals to `document.body`, so it sits outside the trigger's DOM subtree — descendant selectors won't reach it. Style it with **global CSS** or a class on the content element. The content host also exposes the shared positioner custom properties (`--for-anchor-width` / `--for-anchor-height`, `--for-available-width` / `--for-available-height`, `--for-content-transform-origin`); see [Styling floating content](../../../../../docs/styling-floating-content.md) for the full list and the animation rules.
146
+ > The menu content (`[forMenuContent]`, from the [`menu/`](../menu/README.md) folder) portals to `document.body`, so it sits outside the trigger's DOM subtree — descendant selectors won't reach it. Style it with **global CSS** or a class on the content element. The content host also exposes the shared positioner custom properties (`--for-anchor-width` / `--for-anchor-height`, `--for-available-width` / `--for-available-height`, `--for-content-transform-origin`); see [Styling floating content](../../../docs/styling-floating-content.md) for the full list and the animation rules.
147
147
 
148
148
  ```css
149
149
  .context-menu-trigger[data-state='open'] {
@@ -167,3 +167,7 @@ forty-cdk ships no styles. Add your own class to each piece — the for\* select
167
167
  ```
168
168
 
169
169
  - **Mount equals open.** Same convention as the rest of the library — wrap `[forMenuContent]` in `@if (open())` and use `animate.enter` / `animate.leave` for transitions.
170
+
171
+ ## Wrapping in a design system
172
+
173
+ Subclassing the root is the supported pattern; the subclass must re-provide `FOR_MENU_CONTEXT` because Angular does not inherit a directive's `providers`, and every projected piece resolves its context through it. See [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md).
@@ -216,7 +216,7 @@ Composes the [WAI-ARIA Spinbutton pattern](https://www.w3.org/WAI/ARIA/apg/patte
216
216
 
217
217
  ## Styling
218
218
 
219
- forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
219
+ forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
220
220
 
221
221
  ```css
222
222
  .date-field-segment[data-placeholder] {
@@ -233,4 +233,4 @@ forty-cdk ships no styles. Add your own class to each piece — the for\* select
233
233
 
234
234
  ## Wrapping in a design system
235
235
 
236
- Both supported wrapper patterns — `hostDirectives` with the exported `FOR_DATE_FIELD_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_FIELD_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../../../docs/wrapping-form-primitives.md).
236
+ Both supported wrapper patterns — `hostDirectives` with the exported `FOR_DATE_FIELD_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_FIELD_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../docs/wrapping-form-primitives.md).
@@ -1,10 +1,10 @@
1
1
  # DatePicker
2
2
 
3
- A trigger that opens a floating calendar to pick a date, composing ForCalendar inside a dismissable popover with min / max bounds and per-date availability.
3
+ A trigger that opens a floating calendar to pick a date, composing ForCalendar inside a dismissible popover with min / max bounds and per-date availability.
4
4
 
5
5
  Reinterpreted idiomatically for modern Angular: a focusable trigger that opens a floating surface wrapping a projected [`ForCalendar`](../calendar/README.md).
6
6
 
7
- `ForDatePicker` is the root **and** the form value — it implements `FormValueControl<D | null>` from `@angular/forms/signals`, so it auto-wires with `[formField]`. The trigger is the focusable control that carries `name` / `disabled` / `invalid`; selection state flows root → projected calendar via `[(value)]`. The library reuses its existing overlay stack (trigger-anchored Popover positioning, dismissable layer, return-focus) rather than re-implementing positioning, dismissal, or focus return — and the modal opt-in routes through the shared modal shell (focus trap + inert background + scroll lock).
7
+ `ForDatePicker` is the root **and** the form value — it implements `FormValueControl<D | null>` from `@angular/forms/signals`, so it auto-wires with `[formField]`. The trigger is the focusable control that carries `name` / `disabled` / `invalid`; selection state flows root → projected calendar via `[(value)]`. The library reuses its existing overlay stack (trigger-anchored Popover positioning, dismissible layer, return-focus) rather than re-implementing positioning, dismissal, or focus return — and the modal opt-in routes through the shared modal shell (focus trap + inert background + scroll lock).
8
8
 
9
9
  ## Date adapter
10
10
 
@@ -318,7 +318,7 @@ readonly booking = form(this.model, (p) => required(p.stay));
318
318
  - **Native submission.** When `name` is set, two hidden inputs `<name>-start` / `<name>-end` mirror the committed endpoints as ISO `YYYY-MM-DD` for native `<form>` posts.
319
319
  - **Bounds naming.** `minDate` / `maxDate` (not `min` / `max`) for the same reason as `ForDatePicker` — and additionally because `FormUiControl.min` / `max` are typed `NonNullable<TValue>` (the range object itself), which is meaningless as a bound.
320
320
 
321
- Defaults are configured with `provideForDateRangePickerDefaults` (`sideOffset` / `collisionPadding`), and both wrapper patterns work via the exported `FOR_DATE_RANGE_PICKER_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_RANGE_PICKER_HOST_DIRECTIVE_OUTPUTS` tuples — see [Wrapping form primitives](../../../../../docs/wrapping-form-primitives.md).
321
+ Defaults are configured with `provideForDateRangePickerDefaults` (`sideOffset` / `collisionPadding`), and both wrapper patterns work via the exported `FOR_DATE_RANGE_PICKER_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_RANGE_PICKER_HOST_DIRECTIVE_OUTPUTS` tuples — see [Wrapping form primitives](../../../docs/wrapping-form-primitives.md).
322
322
 
323
323
  ## Keyboard
324
324
 
@@ -335,15 +335,15 @@ Implements the [WAI-ARIA Date Picker Dialog pattern](https://www.w3.org/WAI/ARIA
335
335
 
336
336
  - **`aria-haspopup="dialog"`** on the trigger, with `aria-expanded` reflecting `open()` and `aria-controls` pointing at the surface while open.
337
337
  - **`role="dialog"`** on the surface, named by `[ariaLabel]` (or `aria-labelledby` the trigger when no label is set). `aria-modal="true"` only in modal mode (truthy-only).
338
- - **Form-control ARIA** (`aria-disabled` / `aria-readonly` / `aria-required` / `aria-invalid` / `aria-busy`) is reflected on the focusable trigger so assistive tech announces validity on the element that takes focus.
338
+ - **Form-control ARIA** (`aria-readonly` / `aria-required` / `aria-invalid` / `aria-busy`) is reflected on the focusable trigger so assistive tech announces validity on the element that takes focus. The disabled state is the exception: it reflects through the native `disabled` attribute alone (plus `data-disabled`), never `aria-disabled` — one channel per #561 D2.
339
339
  - **Focus management**: focus enters the surface on open (the calendar's roving cell in non-modal mode) and returns to the trigger on close, both vetoable via `(autoFocusOnOpen)` / `(autoFocusOnClose)`.
340
340
  - **Dismissal**: Escape (`(escapeKeyDown)`) and outside-pointer (`(pointerDownOutside)` / `(interactOutside)`) close the surface, each vetoable.
341
341
 
342
342
  ## Styling
343
343
 
344
- forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed under [Data attributes](#data-attributes).
344
+ forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed under [Data attributes](#data-attributes).
345
345
 
346
- > `[forDatePickerContent]` is portaled to `document.body`, so it lives outside your component's view-encapsulated styles. Style it with **global CSS** (or a class you pass through) rather than component-scoped rules — see [Styling floating content](../../../../../docs/styling-floating-content.md). In non-modal (anchored) mode the surface also exposes the shared positioner custom properties (`--for-anchor-width` / `--for-anchor-height`, `--for-available-width` / `--for-available-height`, `--for-content-transform-origin`); that same guide tabulates the full set.
346
+ > `[forDatePickerContent]` is portaled to `document.body`, so it lives outside your component's view-encapsulated styles. Style it with **global CSS** (or a class you pass through) rather than component-scoped rules — see [Styling floating content](../../../docs/styling-floating-content.md). In non-modal (anchored) mode the surface also exposes the shared positioner custom properties (`--for-anchor-width` / `--for-anchor-height`, `--for-available-width` / `--for-available-height`, `--for-content-transform-origin`); that same guide tabulates the full set.
347
347
 
348
348
  ```css
349
349
  .date-picker-trigger .date-picker-value[data-placeholder] {
@@ -360,4 +360,4 @@ forty-cdk ships no styles. Add your own class to each piece — the `for*` selec
360
360
 
361
361
  ## Wrapping in a design system
362
362
 
363
- Both supported wrapper patterns — `hostDirectives` with the exported `FOR_DATE_PICKER_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_PICKER_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../../../docs/wrapping-form-primitives.md).
363
+ Both supported wrapper patterns — `hostDirectives` with the exported `FOR_DATE_PICKER_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_PICKER_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../docs/wrapping-form-primitives.md).
@@ -246,8 +246,8 @@ Each segment implements the [WAI-ARIA Spinbutton pattern](https://www.w3.org/WAI
246
246
 
247
247
  ## Styling
248
248
 
249
- forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
249
+ forty-cdk ships no styles. Add your own class to each piece — the for\* selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected data-\* attributes listed under [Data attributes](#data-attributes).
250
250
 
251
251
  ## Wrapping in a design system
252
252
 
253
- Both supported wrapper patterns — `hostDirectives` with the exported `FOR_DATE_RANGE_FIELD_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_RANGE_FIELD_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../../../docs/wrapping-form-primitives.md).
253
+ Both supported wrapper patterns — `hostDirectives` with the exported `FOR_DATE_RANGE_FIELD_HOST_DIRECTIVE_INPUTS` / `FOR_DATE_RANGE_FIELD_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing — are documented in [Wrapping form primitives](../../../docs/wrapping-form-primitives.md).
package/dialog/README.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  A modal window overlaid on the page, with a focus trap, scroll lock and Escape / dismiss handling. Also openable imperatively through ForDialogManager.
4
4
 
5
- > New to overlays in forty-cdk? [Your first overlay](../../../../../docs/your-first-overlay.md) walks a Popover from empty markup to styled-and-animated and explains the `@if` / open-state model and the portal → global CSS rule.
5
+ > New to overlays in forty-cdk? [Your first overlay](../../../docs/your-first-overlay.md) walks a Popover from empty markup to styled-and-animated and explains the `@if` / open-state model and the portal → global CSS rule.
6
6
 
7
7
  ## Two flows, one engine
8
8
 
9
- The same focus trap, scroll lock, portal, and dismissable-layer behaviors run under both APIs. Pick the one that fits the call site.
9
+ The same focus trap, scroll lock, portal, and dismissible-layer behaviors run under both APIs. Pick the one that fits the call site.
10
10
 
11
11
  ### Declarative — `[forDialog]`
12
12
 
@@ -204,7 +204,7 @@ Set them once for a scope with `provideForDialogDefaults({ animateEnter, animate
204
204
  | `initialFocus` | — | `'first'` (first focusable inside) or `'container'` (the dialog host).<br>**Default:** `'first'` |
205
205
  | `ariaLabel` | — | Manual `aria-label` if no `[forDialogTitle]` is rendered.<br>**Default:** `null` |
206
206
  | `dismiss` | `OutputEmitterRef<ForDialogCloseReason>` | Output. Dialog wants to be unmounted. Reasons: `'escape'`, `'backdrop'`, `'pointerDownOutside'`, `'focusOutside'`, `'closeButton'`, `'programmatic'`.<br>**Default:** — |
207
- | `escapeKeyDown` | `OutputEmitterRef<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape while this dialog is the topmost dismissable layer.<br>**Default:** — |
207
+ | `escapeKeyDown` | `OutputEmitterRef<VetoableNativeEvent<KeyboardEvent>>` | Output. Escape while this dialog is the topmost dismissible layer.<br>**Default:** — |
208
208
  | `pointerDownOutside` | `OutputEmitterRef<VetoableNativeEvent<PointerEvent>>` | Output. Pointer-down outside the dialog.<br>**Default:** — |
209
209
  | `focusOutside` | `OutputEmitterRef<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside the dialog.<br>**Default:** — |
210
210
  | `interactOutside` | `OutputEmitterRef<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite: fires alongside both of the above (and shares their veto state).<br>**Default:** — |
@@ -249,7 +249,7 @@ Keep `dismissible: true` (the default) so Escape still closes, and veto only the
249
249
 
250
250
  ### Inputs — focus callbacks
251
251
 
252
- The auto-focus pair is bound as **function references** (input callbacks), not as event listeners. Each callback receives a `VetoableEvent` whose `preventDefault()` suppresses the directive's default focus action. This shape mirrors `ForDialogManager`'s `config.autoFocusOn*` callbacks and guarantees the `autoFocusOnClose` callback fires reliably on every close path — including a direct `open.set(false)` that bypasses the `(dismiss)` output. See [CLAUDE.md › Auto-focus hook shape](../../../../../CLAUDE.md#auto-focus-hook-shape) for why Dialog uses callback-shape inputs while trigger-anchored overlays (Popover, DropdownMenu, ContextMenu, Menu sub, Select) use output-shape.
252
+ The auto-focus pair is bound as **function references** (input callbacks), not as event listeners. Each callback receives a `VetoableEvent` whose `preventDefault()` suppresses the directive's default focus action. This shape mirrors `ForDialogManager`'s `config.autoFocusOn*` callbacks and guarantees the `autoFocusOnClose` callback fires reliably on every close path — including a direct `open.set(false)` that bypasses the `(dismiss)` output. See [Conventions › Auto-focus hook shape](../../../.claude/rules/conventions.md#auto-focus-hook-shape) for why the free-floating overlays (Dialog, Drawer) use callback-shape inputs while the trigger-anchored ones use output-shape.
253
253
 
254
254
  | Property | Type | Description |
255
255
  | ------------------ | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -351,7 +351,7 @@ Implements the [WAI-ARIA Modal Dialog pattern](https://www.w3.org/WAI/ARIA/apg/p
351
351
 
352
352
  ## Styling
353
353
 
354
- forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed per piece in the [API](#api) section.
354
+ forty-cdk ships no styles. Add your own class to each piece — the `for*` selectors are the behavior API, not a styling contract (see [Styling forty-cdk](../../../docs/styling.md)). Key your CSS off the reflected `data-*` attributes listed per piece in the [API](#api) section.
355
355
 
356
356
  > **This dialog portals to `document.body`.** CSS scoped to ancestors of `[forDialog]` (or `[forDialogBackdrop]`) will not apply once the surface is moved to the body. Style it with **global CSS** or a class. Declaratively you write the surface yourself, so add the class directly (`<div forDialog class="my-dialog">`); for programmatically opened instances pass `class` / `classList` on the `ForDialogManager.open()` config — they land on the same `[forDialog]` host that carries `data-state` / `role` / `aria-modal`, merged and never clobbering them.
357
357
 
@@ -401,4 +401,8 @@ Pass `[container]` to portal the dialog surface into a specific element instead
401
401
  - **Inert siblings**. When `modal`, every direct child of `document.body` other than the dialog box (and its backdrop) gets `inert` and `aria-hidden="true"` while open, and is restored on close. This is what `aria-modal="true"` alone is missing — Safari + VoiceOver and several other AT pairings still announce siblings of an aria-modal node otherwise. Stacking is order-safe: when a second modal opens on top, the first becomes inert; closing the top dialog re-activates the underlying one.
402
402
  - **Vetoable dismissals**. Each of `(escapeKeyDown)`, `(pointerDownOutside)`, `(focusOutside)`, `(interactOutside)` fires before the corresponding `(dismiss)`. Call `preventDefault()` on the event to keep the dialog open (e.g. to ask "are you sure?" first).
403
403
  - **The close button** (`[forDialogClose]`) always requests close, regardless of `dismissible`. Reason emitted is `'closeButton'`.
404
- - **Both flows share the same engine** — the focus trap, scroll lock, dismissable layer, and portal in `ForDialogManager.open()` use the same `_internal/` utilities as the directive. Behavior is identical.
404
+ - **Both flows share the same engine** — the focus trap, scroll lock, dismissible layer, and portal in `ForDialogManager.open()` use the same `_internal/` utilities as the directive. Behavior is identical.
405
+
406
+ ## Wrapping in a design system
407
+
408
+ Subclassing the root is the supported pattern; the subclass must re-provide `FOR_DIALOG_CONTEXT` because Angular does not inherit a directive's `providers`, and every projected piece resolves its context through it. See [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md).