forty-cdk 0.27.0 → 0.28.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 (175) hide show
  1. package/accordion/README.md +2 -2
  2. package/aspect-ratio/README.md +1 -1
  3. package/avatar/README.md +2 -2
  4. package/breadcrumbs/README.md +2 -0
  5. package/button/README.md +1 -1
  6. package/calendar/README.md +11 -4
  7. package/carousel/README.md +41 -15
  8. package/checkbox/README.md +2 -2
  9. package/combobox/README.md +1 -1
  10. package/context-menu/README.md +3 -2
  11. package/date-field/README.md +20 -15
  12. package/date-picker/README.md +3 -1
  13. package/dialog/README.md +47 -12
  14. package/disclosure/README.md +2 -2
  15. package/drag-drop/README.md +41 -1
  16. package/drawer/README.md +17 -7
  17. package/dropdown-menu/README.md +2 -2
  18. package/fesm2022/forty-cdk-avatar.mjs +4 -0
  19. package/fesm2022/forty-cdk-avatar.mjs.map +1 -1
  20. package/fesm2022/forty-cdk-breadcrumbs.mjs +8 -4
  21. package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -1
  22. package/fesm2022/forty-cdk-breakpoints.mjs +4 -1
  23. package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -1
  24. package/fesm2022/forty-cdk-calendar.mjs +7 -1
  25. package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
  26. package/fesm2022/forty-cdk-carousel.mjs +28 -11
  27. package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
  28. package/fesm2022/forty-cdk-combobox.mjs +16 -8
  29. package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
  30. package/fesm2022/forty-cdk-context-menu.mjs +61 -15
  31. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
  32. package/fesm2022/forty-cdk-core-overlay.mjs +172 -100
  33. package/fesm2022/forty-cdk-core-overlay.mjs.map +1 -1
  34. package/fesm2022/forty-cdk-core.mjs +149 -8
  35. package/fesm2022/forty-cdk-core.mjs.map +1 -1
  36. package/fesm2022/forty-cdk-date-field.mjs +26 -12
  37. package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
  38. package/fesm2022/forty-cdk-date-picker.mjs +14 -2
  39. package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
  40. package/fesm2022/forty-cdk-dialog.mjs +142 -8
  41. package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
  42. package/fesm2022/forty-cdk-drag-drop.mjs +11 -4
  43. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
  44. package/fesm2022/forty-cdk-drawer.mjs +90 -7
  45. package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
  46. package/fesm2022/forty-cdk-dropdown-menu.mjs +4 -0
  47. package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -1
  48. package/fesm2022/forty-cdk-field.mjs +47 -2
  49. package/fesm2022/forty-cdk-field.mjs.map +1 -1
  50. package/fesm2022/forty-cdk-hover-card.mjs +11 -6
  51. package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
  52. package/fesm2022/forty-cdk-listbox.mjs +4 -0
  53. package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
  54. package/fesm2022/forty-cdk-menu.mjs +4 -0
  55. package/fesm2022/forty-cdk-menu.mjs.map +1 -1
  56. package/fesm2022/forty-cdk-menubar.mjs +4 -0
  57. package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
  58. package/fesm2022/forty-cdk-navigation-menu.mjs +4 -0
  59. package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
  60. package/fesm2022/forty-cdk-number-input.mjs +4 -0
  61. package/fesm2022/forty-cdk-number-input.mjs.map +1 -1
  62. package/fesm2022/forty-cdk-pagination.mjs +4 -0
  63. package/fesm2022/forty-cdk-pagination.mjs.map +1 -1
  64. package/fesm2022/forty-cdk-popover.mjs +86 -7
  65. package/fesm2022/forty-cdk-popover.mjs.map +1 -1
  66. package/fesm2022/forty-cdk-progress.mjs +7 -3
  67. package/fesm2022/forty-cdk-progress.mjs.map +1 -1
  68. package/fesm2022/forty-cdk-radio-group.mjs +4 -0
  69. package/fesm2022/forty-cdk-radio-group.mjs.map +1 -1
  70. package/fesm2022/forty-cdk-scroll-area.mjs +4 -0
  71. package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -1
  72. package/fesm2022/forty-cdk-search.mjs +8 -4
  73. package/fesm2022/forty-cdk-search.mjs.map +1 -1
  74. package/fesm2022/forty-cdk-select.mjs +10 -2
  75. package/fesm2022/forty-cdk-select.mjs.map +1 -1
  76. package/fesm2022/forty-cdk-slider.mjs +4 -0
  77. package/fesm2022/forty-cdk-slider.mjs.map +1 -1
  78. package/fesm2022/forty-cdk-stepper.mjs +4 -0
  79. package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
  80. package/fesm2022/forty-cdk-table.mjs +3 -1
  81. package/fesm2022/forty-cdk-table.mjs.map +1 -1
  82. package/fesm2022/forty-cdk-tabs.mjs +4 -0
  83. package/fesm2022/forty-cdk-tabs.mjs.map +1 -1
  84. package/fesm2022/forty-cdk-time-field.mjs +26 -12
  85. package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
  86. package/fesm2022/forty-cdk-time-picker.mjs +10 -2
  87. package/fesm2022/forty-cdk-time-picker.mjs.map +1 -1
  88. package/fesm2022/forty-cdk-toast.mjs +76 -21
  89. package/fesm2022/forty-cdk-toast.mjs.map +1 -1
  90. package/fesm2022/forty-cdk-toggle.mjs +4 -0
  91. package/fesm2022/forty-cdk-toggle.mjs.map +1 -1
  92. package/fesm2022/forty-cdk-toolbar.mjs +4 -0
  93. package/fesm2022/forty-cdk-toolbar.mjs.map +1 -1
  94. package/fesm2022/forty-cdk-tooltip.mjs +17 -5
  95. package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
  96. package/fesm2022/forty-cdk-tree.mjs +4 -0
  97. package/fesm2022/forty-cdk-tree.mjs.map +1 -1
  98. package/fesm2022/forty-cdk-virtualization.mjs +41 -2
  99. package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
  100. package/field/README.md +24 -2
  101. package/fieldset/README.md +2 -2
  102. package/file-upload/README.md +2 -2
  103. package/hover-card/README.md +19 -3
  104. package/input/README.md +1 -1
  105. package/listbox/README.md +2 -2
  106. package/menu/README.md +2 -2
  107. package/menubar/README.md +2 -2
  108. package/meter/README.md +2 -2
  109. package/navigation-menu/README.md +2 -2
  110. package/number-input/README.md +1 -1
  111. package/otp-input/README.md +1 -1
  112. package/package.json +1 -1
  113. package/pagination/README.md +1 -1
  114. package/pane-resizer/README.md +1 -1
  115. package/popover/README.md +30 -3
  116. package/progress/README.md +2 -2
  117. package/radio-group/README.md +1 -1
  118. package/scroll-area/README.md +2 -2
  119. package/select/README.md +1 -1
  120. package/separator/README.md +1 -1
  121. package/shared/README.md +27 -1
  122. package/slider/README.md +1 -1
  123. package/stepper/README.md +2 -2
  124. package/switch/README.md +1 -1
  125. package/table/README.md +1 -1
  126. package/tabs/README.md +2 -2
  127. package/time-field/README.md +20 -15
  128. package/toast/README.md +44 -6
  129. package/toggle/README.md +1 -1
  130. package/toolbar/README.md +2 -2
  131. package/tooltip/README.md +19 -3
  132. package/tree/README.md +4 -2
  133. package/types/forty-cdk-avatar.d.ts +5 -1
  134. package/types/forty-cdk-breadcrumbs.d.ts +8 -3
  135. package/types/forty-cdk-breakpoints.d.ts +4 -1
  136. package/types/forty-cdk-calendar.d.ts +16 -4
  137. package/types/forty-cdk-carousel.d.ts +29 -7
  138. package/types/forty-cdk-combobox.d.ts +13 -6
  139. package/types/forty-cdk-context-menu.d.ts +30 -4
  140. package/types/forty-cdk-core-overlay.d.ts +144 -81
  141. package/types/forty-cdk-core.d.ts +92 -6
  142. package/types/forty-cdk-date-field.d.ts +34 -12
  143. package/types/forty-cdk-date-picker.d.ts +13 -2
  144. package/types/forty-cdk-dialog.d.ts +73 -4
  145. package/types/forty-cdk-drag-drop.d.ts +14 -7
  146. package/types/forty-cdk-drawer.d.ts +66 -4
  147. package/types/forty-cdk-dropdown-menu.d.ts +5 -1
  148. package/types/forty-cdk-field.d.ts +35 -1
  149. package/types/forty-cdk-hover-card.d.ts +24 -6
  150. package/types/forty-cdk-listbox.d.ts +5 -1
  151. package/types/forty-cdk-menu.d.ts +5 -1
  152. package/types/forty-cdk-menubar.d.ts +5 -1
  153. package/types/forty-cdk-navigation-menu.d.ts +5 -1
  154. package/types/forty-cdk-number-input.d.ts +5 -1
  155. package/types/forty-cdk-pagination.d.ts +5 -1
  156. package/types/forty-cdk-popover.d.ts +69 -4
  157. package/types/forty-cdk-progress.d.ts +7 -2
  158. package/types/forty-cdk-radio-group.d.ts +5 -1
  159. package/types/forty-cdk-scroll-area.d.ts +5 -1
  160. package/types/forty-cdk-search.d.ts +8 -4
  161. package/types/forty-cdk-select.d.ts +8 -1
  162. package/types/forty-cdk-shared.d.ts +1 -1
  163. package/types/forty-cdk-slider.d.ts +5 -1
  164. package/types/forty-cdk-stepper.d.ts +5 -1
  165. package/types/forty-cdk-tabs.d.ts +5 -1
  166. package/types/forty-cdk-time-field.d.ts +33 -11
  167. package/types/forty-cdk-time-picker.d.ts +8 -1
  168. package/types/forty-cdk-toast.d.ts +78 -21
  169. package/types/forty-cdk-toggle.d.ts +5 -1
  170. package/types/forty-cdk-toolbar.d.ts +5 -1
  171. package/types/forty-cdk-tooltip.d.ts +30 -6
  172. package/types/forty-cdk-tree.d.ts +5 -1
  173. package/types/forty-cdk-virtualization.d.ts +7 -1
  174. package/virtualization/README.md +6 -1
  175. package/visually-hidden/README.md +1 -1
@@ -175,7 +175,7 @@ A disabled item cannot be toggled and is skipped by the arrow keys, while stayin
175
175
 
176
176
  ## Styling
177
177
 
178
- 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.
178
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [API](#api), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
179
179
 
180
180
  ```css
181
181
  .chevron {
@@ -189,4 +189,4 @@ forty-cdk ships no styles. Add your own class to each piece. The `for*` selector
189
189
 
190
190
  ## Wrapping in a design system
191
191
 
192
- Subclassing the root is the supported pattern; the subclass must re-provide `FOR_ACCORDION_CONTEXT` with `useExisting` pointing at itself, because Angular does not inherit a directive's `providers` and every projected piece resolves its context through that token. See [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md).
192
+ Subclass the root and re-provide `FOR_ACCORDION_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`; [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.
@@ -76,7 +76,7 @@ Set `ratio` to `1` to keep a box perfectly square at any width. That is handy fo
76
76
 
77
77
  ## Styling
78
78
 
79
- 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]`.
79
+ forty-cdk ships no styles: put your own class on the host rather than styling the `for*` selector ([Styling forty-cdk](../../../docs/styling.md) explains why). 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.
80
80
 
81
81
  ## Behavior notes
82
82
 
package/avatar/README.md CHANGED
@@ -102,7 +102,7 @@ The directive does not impose a `role`. Pair the avatar with visible name text o
102
102
 
103
103
  ## Styling
104
104
 
105
- 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.
105
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [API](#api), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
106
106
 
107
107
  ```css
108
108
  .avatar-image:not([data-status='loaded']) {
@@ -122,4 +122,4 @@ forty-cdk ships no styles. Add your own class to each piece. The `for*` selector
122
122
 
123
123
  ## Wrapping in a design system
124
124
 
125
- 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).
125
+ Subclass the root and re-provide `FOR_AVATAR_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`; [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.
@@ -100,6 +100,8 @@ bootstrapApplication(App, {
100
100
  });
101
101
  ```
102
102
 
103
+ For a language the app sets or switches after bootstrap, pass `label` as a function and the overrides as a factory, as [Localizing default text](../shared/README.md#localizing-default-text) shows.
104
+
103
105
  ## API
104
106
 
105
107
  ### `ForBreadcrumbs`
package/button/README.md CHANGED
@@ -115,7 +115,7 @@ Implements the [WAI-ARIA Button pattern](https://www.w3.org/WAI/ARIA/apg/pattern
115
115
 
116
116
  ## Styling
117
117
 
118
- 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.
118
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [API](#api), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
119
119
 
120
120
  ```css
121
121
  [forButton][data-disabled] {
@@ -220,7 +220,7 @@ Click the heading button to cycle from day → month → year view. Click a mont
220
220
  | `min` | `input<D \| null>` | Minimum selectable date (inclusive). Earlier dates are unavailable.<br>**Default:** `null` |
221
221
  | `max` | `input<D \| null>` | Maximum selectable date (inclusive). Later dates are unavailable.<br>**Default:** `null` |
222
222
  | `isDateUnavailable` | `input<(date: D) => boolean>` | Per-date predicate marking a date unavailable (present but not selectable).<br>**Default:** `() => false` |
223
- | `dateLabel` | `input<CalendarDateLabelFormatter<D>>` | Formats each gridcell's `aria-label` (full accessible date).<br>**Default:** localized full date, outside-month days suffixed |
223
+ | `dateLabel` | `input<CalendarDateLabelFormatter<D>>` | Formats each gridcell's `aria-label` (full accessible date).<br>**Default:** localized full date, outside-month days through the scope's `outsideMonthLabel` |
224
224
  | `disabled` | `input<boolean>` | Disables the whole calendar (no focus movement, no selection). Reflected as `data-disabled`.<br>**Default:** — |
225
225
  | `readonly` | `input<boolean>` | Read-only: dates stay focusable, selection is blocked. Reflected as `data-readonly`.<br>**Default:** — |
226
226
  | `firstDayOfWeek` | `input<number \| null>` | First column's weekday, **0-6** (`0` = Sunday).<br>**Default:** `null` → the adapter's value (or `provideForCalendarDefaults`) |
@@ -538,9 +538,16 @@ Auto-disabled when the entire previous / next page would be outside `[min, max]`
538
538
  import { provideForCalendarDefaults } from 'forty-cdk/calendar';
539
539
 
540
540
  // app config or a component's providers — Monday-first weeks for this scope
541
- providers: [provideForCalendarDefaults({ firstDayOfWeek: 1 })];
541
+ providers: [
542
+ provideForCalendarDefaults({
543
+ firstDayOfWeek: 1,
544
+ outsideMonthLabel: (formattedDate) => `${formattedDate} (fuera del mes)`,
545
+ }),
546
+ ];
542
547
  ```
543
548
 
549
+ `outsideMonthLabel` builds the `aria-label` of a padding day outside the visible month from its formatted full date (default `"<date> (outside month)"`), so assistive tech can tell it apart from the month on screen. A calendar bound to its own `[dateLabel]` formatter ignores it.
550
+
544
551
  ## Keyboard
545
552
 
546
553
  LTR (horizontal arrows mirror under `dir="rtl"`):
@@ -569,7 +576,7 @@ Implements the [WAI-ARIA Grid pattern](https://www.w3.org/WAI/ARIA/apg/patterns/
569
576
 
570
577
  ## Styling
571
578
 
572
- 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).
579
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [Data attributes](#data-attributes), not off the `forCalendar*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
573
580
 
574
581
  ```css
575
582
  .calendar-cell {
@@ -615,4 +622,4 @@ export class DatePage {
615
622
 
616
623
  ## Wrapping in a design system
617
624
 
618
- 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).
625
+ Subclass the root and re-provide `FOR_CALENDAR_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`; [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.
@@ -74,7 +74,8 @@ import {
74
74
 
75
75
  interface Slide {
76
76
  readonly id: number;
77
- readonly label: string;
77
+ readonly title: string;
78
+ readonly summary: string;
78
79
  }
79
80
 
80
81
  @Component({
@@ -98,7 +99,7 @@ interface Slide {
98
99
  loop
99
100
  orientation="horizontal"
100
101
  align="start"
101
- ariaLabel="Featured slides"
102
+ ariaLabel="Featured stories"
102
103
  >
103
104
  <div class="car-controls-row">
104
105
  <button forCarouselPrevious class="car-btn" aria-label="Previous slide">
@@ -131,7 +132,8 @@ interface Slide {
131
132
  <div forCarouselTrack class="car-track">
132
133
  @for (slide of slides; track slide.id; let i = $index) {
133
134
  <div forCarouselSlide class="car-slide" [class]="'car-slide--' + (i + 1)">
134
- <span class="car-slide-label">{{ slide.label }}</span>
135
+ <p class="car-slide-title">{{ slide.title }}</p>
136
+ <p class="car-slide-summary">{{ slide.summary }}</p>
135
137
  </div>
136
138
  }
137
139
  </div>
@@ -151,11 +153,27 @@ interface Slide {
151
153
  })
152
154
  export class CarouselDefaultExample {
153
155
  protected readonly slides: readonly Slide[] = [
154
- { id: 1, label: 'Slide 1' },
155
- { id: 2, label: 'Slide 2' },
156
- { id: 3, label: 'Slide 3' },
157
- { id: 4, label: 'Slide 4' },
158
- { id: 5, label: 'Slide 5' },
156
+ {
157
+ id: 1,
158
+ title: 'The coast road north',
159
+ summary: 'Four days, three ferries and one small car.',
160
+ },
161
+ {
162
+ id: 2,
163
+ title: 'Markets before dawn',
164
+ summary: 'Where the city buys its fish, flowers and coffee.',
165
+ },
166
+ {
167
+ id: 3,
168
+ title: 'A week without screens',
169
+ summary: 'What changed, and the one habit that stuck.',
170
+ },
171
+ { id: 4, title: 'Bread, slowly', summary: 'A starter, a schedule and a forgiving first loaf.' },
172
+ {
173
+ id: 5,
174
+ title: 'Mapping the old river',
175
+ summary: 'Tracing a buried stream through five neighbourhoods.',
176
+ },
159
177
  ];
160
178
 
161
179
  protected readonly activeIndex = signal(0);
@@ -339,9 +357,11 @@ captured, so page scrolling on the perpendicular axis is unaffected.
339
357
 
340
358
  Each slide's default `aria-label` is the positional `"N of M"` string, each
341
359
  indicator's is `"Go to slide N"`, and the rotation control's swaps between
342
- `"Start automatic slide show"` and `"Stop automatic slide show"`. Localize them
343
- all centrally with `provideForCarouselDefaults` instead of setting `ariaLabel` on
344
- every slide and indicator:
360
+ `"Start automatic slide show"` and `"Stop automatic slide show"`. The root and
361
+ each slide also carry an `aria-roledescription` (`"carousel"` / `"slide"`), which
362
+ a screen reader speaks in place of the `group` role. Localize them all centrally
363
+ with `provideForCarouselDefaults` instead of setting `ariaLabel` on every slide
364
+ and indicator:
345
365
 
346
366
  <!-- snippet: fragment -->
347
367
 
@@ -352,6 +372,8 @@ providers: [
352
372
  indicatorLabel: (position) => `Ir a la diapositiva ${position}`,
353
373
  rotationStartLabel: 'Iniciar la presentación',
354
374
  rotationStopLabel: 'Detener la presentación',
375
+ roleDescription: 'carrusel',
376
+ slideRoleDescription: 'diapositiva',
355
377
  }),
356
378
  ];
357
379
  ```
@@ -360,7 +382,10 @@ providers: [
360
382
  merge with the parent scope, so you can localize just the labels and inherit the
361
383
  rest of the defaults. A per-element `ariaLabel` on `[forCarouselSlide]` /
362
384
  `[forCarouselIndicator]` still takes precedence over the localized default, as do
363
- `[startLabel]` / `[stopLabel]` on `[forCarouselRotationControl]`.
385
+ `[startLabel]` / `[stopLabel]` on `[forCarouselRotationControl]`. For a language
386
+ the app sets or switches after bootstrap, pass the text keys as functions and the
387
+ overrides as a factory, as
388
+ [Localizing default text](../shared/README.md#localizing-default-text) shows.
364
389
 
365
390
  ## Indicators map 1:1 to slides
366
391
 
@@ -455,7 +480,8 @@ Implements the [WAI-ARIA Carousel pattern](https://www.w3.org/WAI/ARIA/apg/patte
455
480
  should describe the carousel's purpose without using the word "carousel" (APG guidance).
456
481
  - Each slide carries `role="group"`, `aria-roledescription="slide"`, and
457
482
  `aria-label="N of M"` by default. Override per slide with the `ariaLabel` input,
458
- or localize the default format app-wide with `provideForCarouselDefaults` (see
483
+ or localize the default format and both role descriptions app-wide with
484
+ `provideForCarouselDefaults` (see
459
485
  [Localizing the default labels](#localizing-the-default-labels)).
460
486
  - Off-view slides receive `aria-hidden="true"` and `inert` to remove them from the
461
487
  accessibility tree and focus order.
@@ -473,7 +499,7 @@ Implements the [WAI-ARIA Carousel pattern](https://www.w3.org/WAI/ARIA/apg/patte
473
499
 
474
500
  ## Styling
475
501
 
476
- 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.
502
+ forty-cdk ships no styles: put your own class on each piece rather than styling the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why). 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.
477
503
 
478
504
  ```css
479
505
  [forCarouselViewport] {
@@ -577,4 +603,4 @@ The example CSS above is LTR-only by default.
577
603
 
578
604
  ## Wrapping in a design system
579
605
 
580
- Subclassing the root is the supported pattern; the subclass must re-provide `FOR_CAROUSEL_CONTEXT` with `useExisting` pointing at itself, because Angular does not inherit a directive's `providers` and every projected piece resolves its context through that token. See [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md).
606
+ Subclass the root and re-provide `FOR_CAROUSEL_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`; [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.
@@ -188,7 +188,7 @@ Implements the [WAI-ARIA Checkbox pattern](https://www.w3.org/WAI/ARIA/apg/patte
188
188
 
189
189
  ## Styling
190
190
 
191
- 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.
191
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [API](#api), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
192
192
 
193
193
  ```css
194
194
  .cb-check {
@@ -213,4 +213,4 @@ forty-cdk ships no styles. Add your own class to each piece. The for\* selectors
213
213
 
214
214
  ## Wrapping in a design system
215
215
 
216
- Both supported wrapper patterns are documented in [Wrapping form primitives](../../../docs/wrapping-form-primitives.md): `hostDirectives` with the exported `FOR_CHECKBOX_HOST_DIRECTIVE_INPUTS` / `FOR_CHECKBOX_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing.
216
+ [Wrapping form primitives](../../../docs/wrapping-form-primitives.md) documents both supported wrapper patterns: `hostDirectives` with the exported `FOR_CHECKBOX_HOST_DIRECTIVE_INPUTS` / `FOR_CHECKBOX_HOST_DIRECTIVE_OUTPUTS` name tuples, and subclassing.
@@ -772,7 +772,7 @@ Implements the [WAI-ARIA Combobox pattern](https://www.w3.org/WAI/ARIA/apg/patte
772
772
 
773
773
  ## Styling
774
774
 
775
- 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).
775
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [Data attributes](#data-attributes), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
776
776
 
777
777
  ### CSS custom properties
778
778
 
@@ -122,7 +122,7 @@ Same vetoable dismiss API as DropdownMenu. Call `preventDefault()` on the emitte
122
122
 
123
123
  ## Styling
124
124
 
125
- 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).
125
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [Data attributes](#data-attributes), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
126
126
 
127
127
  > The menu content (`[forMenuContent]`, from the [`menu/`](../menu/README.md) folder) portals to `document.body`, so it sits outside the trigger's DOM subtree and 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-floating-anchor-width` / `--for-floating-anchor-height`, `--for-floating-available-width` / `--for-floating-available-height`, `--for-floating-content-transform-origin`); see [Styling floating content](../../../docs/styling-floating-content.md) for the full list and the animation rules.
128
128
 
@@ -138,6 +138,7 @@ forty-cdk ships no styles. Add your own class to each piece. The for\* selectors
138
138
  - **Virtual anchor.** Right-click captures a 0×0 rect at the pointer location. `Shift+F10` and `ContextMenu` snapshot the bounding rect of the focused element (or the trigger if focus is on it directly), so the menu floats off the element under attention. Both forms feed floating-ui's `flip` and `shift` middleware, so corners and screen edges work without special-casing.
139
139
  - **Keyboard activators only fire while focus is inside the trigger.** Keyboard events dispatch to the focused element, so `Shift+F10` / `ContextMenu` anywhere outside the trigger goes to the browser default. The trigger is focusable by default (host-bound `tabindex="-1"`), so this works out of the box and focus can return to it programmatically when the menu closes. Your own `tabindex` wins over that default: use `tabindex="0"` if you want the region itself reachable via Tab.
140
140
  - **Native menu suppressed.** The trigger calls `event.preventDefault()` on `contextmenu` and on the keyboard activators. Set `disabled` to let the browser's native menu surface for that region.
141
+ - **An open that renders nothing closes again.** When a right-click, `Shift+F10`, the `ContextMenu` key or a long-press opens a menu whose template renders no `[forMenuContent]`, the trigger closes it after the next render with reason `'programmatic'`, and dev mode logs `FORCDK-CONTEXT-MENU-002` once. The native menu cannot come back for that gesture, because it was already suppressed, so bind `[disabled]` to the same condition that renders the content: `[disabled]="!hasActions(col)"` beside `@if (hasActions(col))`.
141
142
  - **Touch long-press.** The trigger runs its own long-press timer (a `touch` `pointerdown` held ~500 ms, without lifting or moving past a small tolerance, opens the menu at the touch point). This is required because iOS Safari never fires the `contextmenu` event a long-press synthesizes elsewhere; where the browser does synthesize it (Android, desktop touch emulation) the two paths stay mutually exclusive, so the menu opens exactly once. For the press to survive on iOS, suppress the native callout / text-selection on the trigger with CSS, since otherwise the OS gesture cancels the press:
142
143
 
143
144
  ```css
@@ -201,4 +202,4 @@ Both triggers carry `[menuPositioning]`, a partial `{ side, align, sideOffset, a
201
202
 
202
203
  ## Wrapping in a design system
203
204
 
204
- 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).
205
+ Subclass the root and re-provide `FOR_MENU_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`; [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.
@@ -149,17 +149,17 @@ With a time-capable adapter, a `granularity` coarser than `'day'` appends time s
149
149
 
150
150
  ### `ForDateField`
151
151
 
152
- | Property | Type | Description |
153
- | ------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
154
- | `value` | `model<D \| null>` | Two-way bindable entered date, or `null` while any segment is empty. The `FormValueControl` backing.<br>**Default:** `null` |
155
- | `minDate` | `input<D \| null>` | Minimum date (inclusive). A composed value below it is clamped up. Named `minDate` (see note below).<br>**Default:** `null` |
156
- | `maxDate` | `input<D \| null>` | Maximum date (inclusive). A composed value above it is clamped down.<br>**Default:** `null` |
157
- | `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision. `'day'` is date-only; coarser-than-day appends time segments. See below.<br>**Default:** `'day'` |
158
- | `hourCycle` | `input<12 \| 24 \| null>` | 12/24-hour cycle for the time segments. `null` → locale. 12-hour adds the AM/PM segment.<br>**Default:** `null` |
159
- | `locale` | `input<string \| null>` | BCP 47 locale driving segment order, separators, and month name. `null` → runtime locale.<br>**Default:** `null` |
160
- | `placeholder` | `input<Partial<Record<SegmentType, string>>>` | Per-segment placeholder while empty. Unspecified parts fall back to `dd` / `mm` / `yyyy` / `hh` / `mm` / `ss` / `--`.<br>**Default:** `{}` |
161
- | `ariaLabel` | `input<string \| null>` | Accessible name for the group. Emits no `aria-label` while `null`.<br>**Default:** `null` |
162
- | `dir` | `input<'ltr' \| 'rtl' \| null>` | Writing direction. `null` resolves the ambient direction; mirrors ArrowLeft / ArrowRight segment navigation.<br>**Default:** `null` |
152
+ | Property | Type | Description |
153
+ | ------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
154
+ | `value` | `model<D \| null>` | Two-way bindable entered date, or `null` while any segment is empty. The `FormValueControl` backing.<br>**Default:** `null` |
155
+ | `minDate` | `input<D \| null>` | Minimum date (inclusive). A composed value below it is clamped up. Named `minDate` (see note below).<br>**Default:** `null` |
156
+ | `maxDate` | `input<D \| null>` | Maximum date (inclusive). A composed value above it is clamped down.<br>**Default:** `null` |
157
+ | `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision. `'day'` is date-only; coarser-than-day appends time segments. See below.<br>**Default:** `'day'` |
158
+ | `hourCycle` | `input<12 \| 24 \| null>` | 12/24-hour cycle for the time segments. `null` → locale. 12-hour adds the AM/PM segment.<br>**Default:** `null` |
159
+ | `locale` | `input<string \| null>` | BCP 47 locale driving segment order, separators, and month name. `null` → runtime locale.<br>**Default:** `null` |
160
+ | `placeholder` | `input<Partial<Record<SegmentType, string>>>` | Per-segment placeholder while empty. Unspecified parts fall back to the scope's `placeholder`, then to `dd` / `mm` / `yyyy` / `hh` / `mm` / `ss` / `--`.<br>**Default:** `{}` |
161
+ | `ariaLabel` | `input<string \| null>` | Accessible name for the group. Emits no `aria-label` while `null`.<br>**Default:** `null` |
162
+ | `dir` | `input<'ltr' \| 'rtl' \| null>` | Writing direction. `null` resolves the ambient direction; mirrors ArrowLeft / ArrowRight segment navigation.<br>**Default:** `null` |
163
163
 
164
164
  Plus the shared `FormUiControl` members from `@angular/forms/signals`: `disabled`, `readonly`, `required`, `invalid`, `name`, `errors`, `touched` (bound automatically by `[formField]`).
165
165
 
@@ -207,18 +207,23 @@ On the AM/PM segment, `a` / `p` set the period and ArrowUp / ArrowDown toggle it
207
207
  ```ts
208
208
  import { provideForDateFieldDefaults } from 'forty-cdk/date-field';
209
209
 
210
- // app config or a component's providers — localize segment labels and the
211
- // empty-segment announcement for every nested [forDateField].
210
+ // app config or a component's providers — localize segment labels, the
211
+ // empty-segment announcement and the placeholders for every nested [forDateField].
212
212
  providers: [
213
213
  provideForDateFieldDefaults({
214
214
  emptySegmentText: 'Vacío',
215
215
  segmentLabels: { day: 'día', month: 'mes', year: 'año', dayPeriod: 'AM/PM' },
216
+ placeholder: { day: 'dd', month: 'mm', year: 'aaaa' },
216
217
  }),
217
218
  ];
218
219
  ```
219
220
 
220
221
  `segmentLabels` supplies each segment's default `aria-label`, keyed by part type. Unset keys keep the library default (the part name, and `'AM/PM'` for the `dayPeriod` segment), so overriding a single key never wipes the rest. A segment's own `[ariaLabel]` still wins over the scope default.
221
222
 
223
+ `placeholder` is the text an empty segment shows, keyed the same way. A field's own `[placeholder]` wins for the parts it names and only those, and a part neither names keeps the letter-repeat default. `provideForDateRangeFieldDefaults` takes the same three keys for `[forDateRangeField]`.
224
+
225
+ For a language the app sets or switches after bootstrap, pass the text keys as functions and the overrides as a factory, as [Localizing default text](../shared/README.md#localizing-default-text) shows.
226
+
222
227
  ## Range selection — `ForDateRangeField`
223
228
 
224
229
  For a date range use the dedicated `ForDateRangeField` root (selector `[forDateRangeField]`), shipped from this same entry point. It is the keyboard-first, form-capable counterpart to [DateRangePicker](../date-picker/README.md): two labelled `role="group"` endpoints (start / end) nested inside one outer `role="group"`. Each endpoint holds a row of spinbutton segments built on the same machinery as `ForDateField`. It implements `FormValueControl<DateRange<D> | null>`, the **same** contract as `ForDateRangePicker`, so the committed range auto-wires with `[formField]`. The value stays `null` until **both** endpoints are fully entered and ordered (`start <= end`).
@@ -271,7 +276,7 @@ readonly booking = form(this.model);
271
276
  | `granularity` | `input<'day' \| 'hour' \| 'minute' \| 'second'>` | Date-time precision shared by both endpoints. `'day'` is date-only; coarser-than-day appends time segments.<br>**Default:** `'day'` |
272
277
  | `hourCycle` | `input<12 \| 24 \| null>` | 12/24-hour cycle for the time segments. `null` → locale. 12-hour adds the AM/PM segment.<br>**Default:** `null` |
273
278
  | `locale` | `input<string \| null>` | BCP 47 locale driving segment order, separators, and month name. `null` → runtime locale.<br>**Default:** `null` |
274
- | `placeholder` | `input<Partial<Record<SegmentType, string>>>` | Per-segment placeholder while empty, applied to both endpoints.<br>**Default:** `{}` |
279
+ | `placeholder` | `input<Partial<Record<SegmentType, string>>>` | Per-segment placeholder while empty, applied to both endpoints. Unspecified parts fall back to the scope's `placeholder`.<br>**Default:** `{}` |
275
280
  | `ariaLabel` | `input<string \| null>` | Accessible name for the whole range field group. Emits no `aria-label` while `null`.<br>**Default:** `null` |
276
281
  | `dir` | `input<'ltr' \| 'rtl' \| null>` | Writing direction. `null` resolves the ambient direction; mirrors ArrowLeft / ArrowRight segment navigation.<br>**Default:** `null` |
277
282
 
@@ -323,7 +328,7 @@ Composes the [WAI-ARIA Spinbutton pattern](https://www.w3.org/WAI/ARIA/apg/patte
323
328
 
324
329
  The library is styleless, so style the boolean `data-*` hooks yourself: `[data-highlighted]` (the focused/roving segment), `[data-placeholder]` (empty), `[data-disabled]` and `[data-readonly]` on the segments, and `[data-empty]` / `[data-disabled]` / `[data-readonly]` on the root group.
325
330
 
326
- 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).
331
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [Data attributes](#data-attributes), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
327
332
 
328
333
  ```css
329
334
  .date-field-segment[data-placeholder] {
@@ -288,6 +288,8 @@ Set `granularity` to `'hour'`, `'minute'`, or `'second'` to turn the picker into
288
288
 
289
289
  Bind the calendar **and** the time field **one-way** to `picker.value()` (not `[(value)]`). The picker is the single source of truth: when one-way bound to a timed value the calendar preserves the time-of-day on its own selection, and the picker re-grafts the previously entered time as a defensive fallback for the case where the calendar value was null or midnight (reading its own value, which the one-way children never clobber); a time-field edit emits a full date-time the picker mirrors in. A date-time picker never closes on a calendar selection, so the user can go on to set the time.
290
290
 
291
+ The content is a field boundary. Inside a [`[forField]`](../field/README.md#how-the-control-connects) the time field does not register with the field, which keeps reflecting the picker (its label target, `invalid` state and errors) while the panel is open.
292
+
291
293
  ```html
292
294
  <div
293
295
  forDatePicker
@@ -403,7 +405,7 @@ Implements the [WAI-ARIA Date Picker Dialog pattern](https://www.w3.org/WAI/ARIA
403
405
 
404
406
  ## Styling
405
407
 
406
- 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).
408
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [Data attributes](#data-attributes), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
407
409
 
408
410
  > `[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-floating-anchor-width` / `--for-floating-anchor-height`, `--for-floating-available-width` / `--for-floating-available-height`, `--for-floating-content-transform-origin`); that same guide tabulates the full set.
409
411
 
package/dialog/README.md CHANGED
@@ -168,9 +168,10 @@ Open a component imperatively and await its result. The manager mounts it under
168
168
  | `focusOutside` | `OutputEmitterRef<VetoableNativeEvent<FocusEvent>>` | Output. Focus moves outside the dialog.<br>**Default:** — |
169
169
  | `interactOutside` | `OutputEmitterRef<VetoableNativeEvent<PointerEvent \| FocusEvent>>` | Output. Composite: fires alongside both of the above (and shares their veto state).<br>**Default:** — |
170
170
 
171
- | Data attribute | Values |
172
- | -------------- | ----------------------------------------------------------------------------- |
173
- | `data-state` | `open` (always: the host is only mounted while open, so it is never `closed`) |
171
+ | Data attribute | Values |
172
+ | -------------- | ----------------------------------------------------------------------------------------------------------------------- |
173
+ | `data-state` | `open` (always: the host is only mounted while open, so it is never `closed`) |
174
+ | `data-depth` | `0` for the first mounted dialog, one above the deepest dialog still mounted otherwise. Fixed for the dialog's lifetime |
174
175
 
175
176
  ### Inputs — focus callbacks
176
177
 
@@ -194,6 +195,7 @@ The auto-focus pair is bound as **function references** (input callbacks), not a
194
195
  | -------------------------- | ---------------------------------------------------------------------------------------- |
195
196
  | `data-state` | `open` (always, since it is mounted alongside the dialog) |
196
197
  | `data-for-dialog-backdrop` | present (stable marker; portaled alongside the dialog, so use it to select the backdrop) |
198
+ | `data-depth` | its dialog's `data-depth` |
197
199
 
198
200
  ### `ForDialogClose`
199
201
 
@@ -201,6 +203,20 @@ The auto-focus pair is bound as **function references** (input callbacks), not a
201
203
  | -------------- | --------------------------------------------------------- |
202
204
  | `data-state` | `open` (always, since it is mounted alongside the dialog) |
203
205
 
206
+ ### `ForDialogInitialFocus`
207
+
208
+ `[forDialogInitialFocus]` marks the element that receives focus when the enclosing dialog opens, in place of the one `initialFocus` picks. It takes no inputs, and it works inside a component opened with `ForDialogManager.open()` as well, since the marker lives in the content rather than in the caller. When the marked element is missing, disabled or hidden at mount, focus falls back to `initialFocus`; a vetoed `autoFocusOnOpen` still skips the move. Mark one element per dialog: a second marker warns in dev mode and only the newest is used.
209
+
210
+ ```html
211
+ <div forDialog (dismiss)="open.set(false)">
212
+ <h2 forDialogTitle>Delete view</h2>
213
+ <button forDialogClose aria-label="Close">×</button>
214
+ <p>This cannot be undone.</p>
215
+ <button forDialogClose forDialogInitialFocus>Cancel</button>
216
+ <button (click)="deleteView()">Delete</button>
217
+ </div>
218
+ ```
219
+
204
220
  ## Programmatic API
205
221
 
206
222
  ```ts
@@ -284,12 +300,12 @@ this.dialogs.open(ConfirmDialog, {
284
300
 
285
301
  Set them once for a scope with `provideForDialogDefaults({ animateEnter, animateLeave })`; a per-`open()` value always wins over the scope default.
286
302
 
287
- | Symbol | Description |
288
- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
289
- | `ForDialogManager` | Injectable. `open(component, config?)` returns a `ForDialogRef<R>`. |
290
- | `ForDialogRef<R>` | `close(result?)`, `closed: Promise<{ reason: ForDialogCloseReason; result: R \| undefined }>`, `result: Signal<R \| undefined>`, `isClosed: Signal<boolean>`. |
291
- | `FOR_DIALOG_DATA` | Token for the `data` payload. Inject in the opened component. |
292
- | `injectDialogData<T>()` | Typed accessor for `FOR_DIALOG_DATA`. Returns `T \| null` (`null` when `open()` got no `data`). |
303
+ | Symbol | Description |
304
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
305
+ | `ForDialogManager` | Injectable. `open(component, config?)` returns a `ForDialogRef<R>`. `closeAll(result?)` closes every open dialog, topmost first, with reason `'programmatic'`; exit animations still play and focus ends where the bottom dialog returns it. `openCount` is a `Signal<number>` of the dialogs still open, counting a closed one until its exit animation finishes. |
306
+ | `ForDialogRef<R>` | `close(result?)`, `closed: Promise<{ reason: ForDialogCloseReason; result: R \| undefined }>`, `result: Signal<R \| undefined>`, `isClosed: Signal<boolean>`. |
307
+ | `FOR_DIALOG_DATA` | Token for the `data` payload. Inject in the opened component. |
308
+ | `injectDialogData<T>()` | Typed accessor for `FOR_DIALOG_DATA`. Returns `T \| null` (`null` when `open()` got no `data`). |
293
309
 
294
310
  ### `ForDialogOpenConfig`
295
311
 
@@ -326,14 +342,15 @@ The four dismiss callbacks mirror the declarative `(escapeKeyDown)` / `(pointerD
326
342
 
327
343
  Implements the [WAI-ARIA Modal Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/).
328
344
 
329
- - Always provide an accessible name: render a `[forDialogTitle]` (sets `aria-labelledby`) or pass `ariaLabel`.
345
+ - Always provide an accessible name: render a `[forDialogTitle]` (sets `aria-labelledby`) or pass `ariaLabel`. A dialog that mounts with neither logs a dev-mode warning (`FORCDK-CORE-011`) after its first render, because a WCAG-tagged audit does not report the missing name.
346
+ - Initial focus follows the APG guidance by default (the first focusable element). When the step is hard to undo, or focusing the first control would scroll the start of the content out of view, mark the least destructive action or a static heading carrying `tabindex="-1"` with [`[forDialogInitialFocus]`](#fordialoginitialfocus).
330
347
  - `[forDialogDescription]` is optional. Use it for non-title supporting copy (the question of a confirm, the rationale of an alert).
331
348
  - `alert: true` interrupts assistive tech aggressively, so use it only for genuine alerts (lost connection, unsaved changes warning), not for general confirms.
332
349
  - Don't put interactive overlays (popovers, menus) outside the focus trap while a modal dialog is open, because they won't be reachable. For a non-modal floating surface anchored to a trigger, use `[forPopover]` instead.
333
350
 
334
351
  ## Styling
335
352
 
336
- 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.
353
+ forty-cdk ships no styles: put your own class on each piece and key your CSS off the `data-*` attributes listed under [API](#api), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
337
354
 
338
355
  > **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.
339
356
 
@@ -355,6 +372,24 @@ forty-cdk ships no styles. Add your own class to each piece. The `for*` selector
355
372
  }
356
373
  ```
357
374
 
375
+ **Stacked dialogs.** Each surface and its backdrop publish their stacking position as `data-depth` and `--for-dialog-depth` (`0` for the first dialog, `1` above it, …), shared by declarative and managed dialogs. One rule then covers every level, with each backdrop one step below its own dialog and above every dialog underneath it:
376
+
377
+ ```css
378
+ .my-dialog {
379
+ z-index: calc(1010 + var(--for-dialog-depth) * 10);
380
+ }
381
+
382
+ .my-backdrop {
383
+ z-index: calc(1009 + var(--for-dialog-depth) * 10);
384
+ }
385
+ ```
386
+
387
+ ### CSS custom properties
388
+
389
+ | Property | Meaning |
390
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
391
+ | `--for-dialog-depth` | Written on `[forDialog]` and `[forDialogBackdrop]`. The dialog's stacking position as a unitless integer, `0` for the first mounted dialog. Fixed for the dialog's lifetime, so a level is never reused. |
392
+
358
393
  ## Behavior notes
359
394
 
360
395
  - **The `(dismiss)` contract: the consumer owns unmount.** `(dismiss)` reports the dialog's **intent** to be unmounted; it does not flip the consumer's signal. The consumer must call `open.set(false)` (or equivalent) inside the handler. If the handler is omitted or does not update the signal, Escape, backdrop-click, and outside-pointer-down all emit `(dismiss)` but the dialog stays mounted.
@@ -471,4 +506,4 @@ Pass `[container]` to portal the dialog surface into a specific element instead
471
506
 
472
507
  ## Wrapping in a design system
473
508
 
474
- 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).
509
+ Subclass the root and re-provide `FOR_DIALOG_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`; [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.
@@ -120,7 +120,7 @@ Implements the [WAI-ARIA Disclosure pattern](https://www.w3.org/WAI/ARIA/apg/pat
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: put your own class on each piece and key your CSS off the `data-*` attributes listed under [API](#api), not off the `for*` selectors ([Styling forty-cdk](../../../docs/styling.md) explains why).
124
124
 
125
125
  ```css
126
126
  .dis-trigger .chevron {
@@ -133,4 +133,4 @@ forty-cdk ships no styles. Add your own class to each piece. The `for*` selector
133
133
 
134
134
  ## Wrapping in a design system
135
135
 
136
- Subclassing the root is the supported pattern; the subclass must re-provide `FOR_DISCLOSURE_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).
136
+ Subclass the root and re-provide `FOR_DISCLOSURE_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`; [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.
@@ -178,6 +178,23 @@ Arrow direction follows the list's `orientation` and respects RTL via `dir`. In
178
178
 
179
179
  Keyboard lifting, stepping, dropping, and cancellation are announced via ARIA live regions. Override the default messages at any injector scope via `provideForDragDropDefaults` (see Announcement customisation below). Free-drag is pointer-only. There is no WAI-ARIA pattern for "reposition an element", so `[forFreeDrag]` owns no role or ARIA state; the consumer is responsible for keeping the moved element fully usable at its default position.
180
180
 
181
+ ### Role description
182
+
183
+ Each `[forDraggable]` that can be lifted carries `aria-roledescription`, `"sortable"` by default.
184
+ An item that cannot be lifted, through `[dragDisabled]` or a disabled `[forDropList]`, emits none.
185
+ A screen reader that honors the attribute speaks it in place of the element's role name, so
186
+ change it, or turn it off with an empty string, through `itemRoleDescription` at any injector
187
+ scope:
188
+
189
+ <!-- snippet: fragment -->
190
+
191
+ ```ts
192
+ providers: [provideForDragDropDefaults({ itemRoleDescription: 'movable item' })];
193
+ ```
194
+
195
+ `[forTableColumnReorder]` turns it off for its header cells, which keep the `columnheader` role
196
+ name and leave "sortable" to `aria-sort`.
197
+
181
198
  ### Focus after a keyboard drop
182
199
 
183
200
  Applying the move in `(dragDrop)` destroys or re-inserts the lifted element, which would otherwise
@@ -244,6 +261,29 @@ unaffected.
244
261
  </li>
245
262
  ```
246
263
 
264
+ ### Nested controls
265
+
266
+ A control inside a draggable item keeps its press from starting a drag by calling
267
+ `preventDefault()` on its own `pointerdown`. The item stands down on the first pointer move, so
268
+ the press stays with the control; lifting with the keyboard is unaffected.
269
+
270
+ ```html
271
+ <li forDraggable [dragData]="item">
272
+ {{ item.label }}
273
+ <button type="button" (pointerdown)="$event.preventDefault()" (click)="remove(item)">
274
+ Remove
275
+ </button>
276
+ </li>
277
+ ```
278
+
279
+ - The check lives in the pointer session every drag surface shares, so it holds for
280
+ `[forDraggable]`, `[forFreeDrag]`, `[forListboxReorder]`, `[forTableRowReorder]`,
281
+ `[forTreeNodeDrag]` and `[forVirtualReorder]` alike.
282
+ - A nested control that runs a drag of its own still starts it: the item around it is what
283
+ stands down. That is how a `[forTableColumnResizer]` resizes inside a reorderable header cell.
284
+ - A canceled `pointerdown` suppresses the compatibility mouse events of that press (`mousedown`,
285
+ `mouseup`), so the control must not rely on them. `click` still fires.
286
+
247
287
  ### Custom preview & placeholder
248
288
 
249
289
  Place `<ng-template forDragPreview>` and/or `<ng-template forDragPlaceholder>` as direct
@@ -591,4 +631,4 @@ append gap counts).
591
631
 
592
632
  ## Wrapping in a design system
593
633
 
594
- Subclassing the root is the supported pattern; the subclass must re-provide `FOR_DROP_LIST_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).
634
+ Subclass the root and re-provide `FOR_DROP_LIST_CONTEXT` with `useExisting` pointing at the subclass, since Angular does not inherit a directive's `providers`; [Wrapping non-form roots](../../../docs/wrapping-non-form-roots.md) walks the pattern.