forty-cdk 0.5.0 → 0.7.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 (171) hide show
  1. package/accordion/README.md +82 -71
  2. package/aspect-ratio/README.md +24 -22
  3. package/avatar/README.md +50 -33
  4. package/breadcrumbs/README.md +34 -8
  5. package/breakpoints/README.md +16 -12
  6. package/button/README.md +55 -18
  7. package/calendar/README.md +105 -83
  8. package/carousel/README.md +153 -112
  9. package/checkbox/README.md +60 -37
  10. package/combobox/README.md +106 -95
  11. package/context-menu/README.md +64 -37
  12. package/date-field/README.md +110 -60
  13. package/date-picker/README.md +90 -77
  14. package/date-range-field/README.md +142 -64
  15. package/dialog/README.md +90 -74
  16. package/disclosure/README.md +65 -59
  17. package/drag-drop/README.md +69 -13
  18. package/drawer/README.md +127 -99
  19. package/dropdown-menu/README.md +62 -49
  20. package/fesm2022/forty-cdk-accordion.mjs +30 -11
  21. package/fesm2022/forty-cdk-accordion.mjs.map +1 -1
  22. package/fesm2022/forty-cdk-avatar.mjs +4 -3
  23. package/fesm2022/forty-cdk-avatar.mjs.map +1 -1
  24. package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -1
  25. package/fesm2022/forty-cdk-calendar.mjs +1 -1
  26. package/fesm2022/forty-cdk-calendar.mjs.map +1 -1
  27. package/fesm2022/forty-cdk-carousel.mjs +13 -2
  28. package/fesm2022/forty-cdk-carousel.mjs.map +1 -1
  29. package/fesm2022/forty-cdk-combobox.mjs +88 -56
  30. package/fesm2022/forty-cdk-combobox.mjs.map +1 -1
  31. package/fesm2022/forty-cdk-context-menu.mjs +62 -5
  32. package/fesm2022/forty-cdk-context-menu.mjs.map +1 -1
  33. package/fesm2022/forty-cdk-core.mjs +593 -123
  34. package/fesm2022/forty-cdk-core.mjs.map +1 -1
  35. package/fesm2022/forty-cdk-date-field.mjs +12 -0
  36. package/fesm2022/forty-cdk-date-field.mjs.map +1 -1
  37. package/fesm2022/forty-cdk-date-picker.mjs +17 -10
  38. package/fesm2022/forty-cdk-date-picker.mjs.map +1 -1
  39. package/fesm2022/forty-cdk-date-range-field.mjs +26 -5
  40. package/fesm2022/forty-cdk-date-range-field.mjs.map +1 -1
  41. package/fesm2022/forty-cdk-dialog.mjs +47 -11
  42. package/fesm2022/forty-cdk-dialog.mjs.map +1 -1
  43. package/fesm2022/forty-cdk-drag-drop.mjs +50 -7
  44. package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -1
  45. package/fesm2022/forty-cdk-drawer.mjs +38 -18
  46. package/fesm2022/forty-cdk-drawer.mjs.map +1 -1
  47. package/fesm2022/forty-cdk-field.mjs +51 -30
  48. package/fesm2022/forty-cdk-field.mjs.map +1 -1
  49. package/fesm2022/forty-cdk-file-upload.mjs +53 -10
  50. package/fesm2022/forty-cdk-file-upload.mjs.map +1 -1
  51. package/fesm2022/forty-cdk-hover-card.mjs +32 -14
  52. package/fesm2022/forty-cdk-hover-card.mjs.map +1 -1
  53. package/fesm2022/forty-cdk-listbox.mjs +43 -3
  54. package/fesm2022/forty-cdk-listbox.mjs.map +1 -1
  55. package/fesm2022/forty-cdk-menu.mjs +191 -218
  56. package/fesm2022/forty-cdk-menu.mjs.map +1 -1
  57. package/fesm2022/forty-cdk-menubar.mjs +36 -28
  58. package/fesm2022/forty-cdk-menubar.mjs.map +1 -1
  59. package/fesm2022/forty-cdk-navigation-menu.mjs +10 -2
  60. package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -1
  61. package/fesm2022/forty-cdk-number-input.mjs +32 -10
  62. package/fesm2022/forty-cdk-number-input.mjs.map +1 -1
  63. package/fesm2022/forty-cdk-otp-input.mjs +3 -0
  64. package/fesm2022/forty-cdk-otp-input.mjs.map +1 -1
  65. package/fesm2022/forty-cdk-pagination.mjs +20 -8
  66. package/fesm2022/forty-cdk-pagination.mjs.map +1 -1
  67. package/fesm2022/forty-cdk-pane-resizer.mjs +18 -2
  68. package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -1
  69. package/fesm2022/forty-cdk-radio-group.mjs +28 -14
  70. package/fesm2022/forty-cdk-radio-group.mjs.map +1 -1
  71. package/fesm2022/forty-cdk-scroll-area.mjs +18 -3
  72. package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -1
  73. package/fesm2022/forty-cdk-select.mjs +58 -12
  74. package/fesm2022/forty-cdk-select.mjs.map +1 -1
  75. package/fesm2022/forty-cdk-slider.mjs +20 -4
  76. package/fesm2022/forty-cdk-slider.mjs.map +1 -1
  77. package/fesm2022/forty-cdk-stepper.mjs +8 -1
  78. package/fesm2022/forty-cdk-stepper.mjs.map +1 -1
  79. package/fesm2022/forty-cdk-table.mjs +474 -31
  80. package/fesm2022/forty-cdk-table.mjs.map +1 -1
  81. package/fesm2022/forty-cdk-time-field.mjs +12 -0
  82. package/fesm2022/forty-cdk-time-field.mjs.map +1 -1
  83. package/fesm2022/forty-cdk-time-range-field.mjs +26 -5
  84. package/fesm2022/forty-cdk-time-range-field.mjs.map +1 -1
  85. package/fesm2022/forty-cdk-toast.mjs +46 -23
  86. package/fesm2022/forty-cdk-toast.mjs.map +1 -1
  87. package/fesm2022/forty-cdk-toggle.mjs +15 -0
  88. package/fesm2022/forty-cdk-toggle.mjs.map +1 -1
  89. package/fesm2022/forty-cdk-tooltip.mjs +70 -34
  90. package/fesm2022/forty-cdk-tooltip.mjs.map +1 -1
  91. package/fesm2022/forty-cdk-tree.mjs +109 -16
  92. package/fesm2022/forty-cdk-tree.mjs.map +1 -1
  93. package/fesm2022/forty-cdk-virtualization.mjs +351 -6
  94. package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
  95. package/field/README.md +67 -23
  96. package/fieldset/README.md +35 -15
  97. package/file-upload/README.md +43 -14
  98. package/hover-card/README.md +103 -82
  99. package/input/README.md +81 -47
  100. package/listbox/README.md +215 -194
  101. package/menu/README.md +55 -29
  102. package/menubar/README.md +65 -39
  103. package/meter/README.md +67 -57
  104. package/navigation-menu/README.md +128 -115
  105. package/number-input/README.md +86 -68
  106. package/otp-input/README.md +78 -65
  107. package/package.json +1 -1
  108. package/pagination/README.md +57 -9
  109. package/pane-resizer/README.md +68 -51
  110. package/popover/README.md +110 -97
  111. package/progress/README.md +47 -39
  112. package/radio-group/README.md +81 -58
  113. package/scroll-area/README.md +97 -39
  114. package/search/README.md +39 -10
  115. package/select/README.md +141 -118
  116. package/separator/README.md +28 -26
  117. package/slider/README.md +95 -64
  118. package/stepper/README.md +102 -89
  119. package/switch/README.md +61 -40
  120. package/table/README.md +185 -53
  121. package/tabs/README.md +87 -70
  122. package/time-field/README.md +109 -57
  123. package/time-picker/README.md +74 -51
  124. package/time-range-field/README.md +145 -63
  125. package/toast/README.md +113 -77
  126. package/toggle/README.md +125 -104
  127. package/toolbar/README.md +97 -43
  128. package/tooltip/README.md +151 -131
  129. package/tree/README.md +121 -81
  130. package/types/forty-cdk-accordion.d.ts +28 -8
  131. package/types/forty-cdk-calendar.d.ts +5 -17
  132. package/types/forty-cdk-carousel.d.ts +1 -0
  133. package/types/forty-cdk-combobox.d.ts +54 -26
  134. package/types/forty-cdk-context-menu.d.ts +22 -1
  135. package/types/forty-cdk-core.d.ts +338 -45
  136. package/types/forty-cdk-date-field.d.ts +8 -1
  137. package/types/forty-cdk-date-picker.d.ts +14 -12
  138. package/types/forty-cdk-date-range-field.d.ts +23 -10
  139. package/types/forty-cdk-dialog.d.ts +57 -8
  140. package/types/forty-cdk-drag-drop.d.ts +1 -0
  141. package/types/forty-cdk-drawer.d.ts +44 -14
  142. package/types/forty-cdk-dropdown-menu.d.ts +1 -0
  143. package/types/forty-cdk-field.d.ts +20 -13
  144. package/types/forty-cdk-file-upload.d.ts +13 -1
  145. package/types/forty-cdk-hover-card.d.ts +11 -1
  146. package/types/forty-cdk-internationalized-date.d.ts +1 -0
  147. package/types/forty-cdk-listbox.d.ts +17 -0
  148. package/types/forty-cdk-menu.d.ts +87 -49
  149. package/types/forty-cdk-menubar.d.ts +16 -9
  150. package/types/forty-cdk-navigation-menu.d.ts +2 -1
  151. package/types/forty-cdk-number-input.d.ts +15 -3
  152. package/types/forty-cdk-pagination.d.ts +24 -2
  153. package/types/forty-cdk-pane-resizer.d.ts +10 -0
  154. package/types/forty-cdk-popover.d.ts +1 -0
  155. package/types/forty-cdk-radio-group.d.ts +24 -12
  156. package/types/forty-cdk-scroll-area.d.ts +16 -1
  157. package/types/forty-cdk-select.d.ts +38 -0
  158. package/types/forty-cdk-slider.d.ts +10 -0
  159. package/types/forty-cdk-stepper.d.ts +7 -0
  160. package/types/forty-cdk-table.d.ts +160 -9
  161. package/types/forty-cdk-tabs.d.ts +1 -0
  162. package/types/forty-cdk-time-field.d.ts +8 -1
  163. package/types/forty-cdk-time-picker.d.ts +1 -0
  164. package/types/forty-cdk-time-range-field.d.ts +23 -10
  165. package/types/forty-cdk-toast.d.ts +36 -3
  166. package/types/forty-cdk-toggle.d.ts +10 -0
  167. package/types/forty-cdk-toolbar.d.ts +1 -0
  168. package/types/forty-cdk-tooltip.d.ts +57 -15
  169. package/types/forty-cdk-tree.d.ts +17 -1
  170. package/types/forty-cdk-virtualization.d.ts +86 -3
  171. package/virtualization/README.md +52 -20
@@ -1,56 +1,22 @@
1
1
  # Accordion
2
2
 
3
- Headless implementation of the [WAI-ARIA Accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/).
4
- A vertical stack of collapsible sections, each with a header button and a panel.
5
-
6
- ## Pieces
7
-
8
- | Class | Selector | Role |
9
- | --------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------- |
10
- | `ForAccordion` | `[forAccordion]` | Root. Owns the open `value`, the single/multiple mode, and the keyboard navigation between triggers. |
11
- | `ForAccordionItem` | `[forAccordionItem]` | One section. Requires a unique `value` string. |
12
- | `ForAccordionTrigger` | `[forAccordionTrigger]` | Header button. Wires ARIA + click + keyboard. |
13
- | `ForAccordionContent` | `[forAccordionContent]` | Panel. Adds `role="region"` + `aria-labelledby` automatically. |
14
-
15
- ## Inputs / outputs
16
-
17
- ### `ForAccordion`
18
-
19
- | API | Type | Description |
20
- | ------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
21
- | `value` | `model<readonly string[]>` | Currently open item values. In single mode the array has 0 or 1 element. |
22
- | `multiple` | `input<boolean>` | When true, multiple items can be open simultaneously. Defaults to `false`. |
23
- | `collapsible` | `input<boolean>` | Single mode only: when true, the open item can be collapsed by clicking it. Defaults to `false` — once any item is open, exactly one stays open. |
24
- | `orientation` | `input<'horizontal' \| 'vertical'>` | Layout direction of the trigger list. Defaults to `'vertical'`; in horizontal mode ArrowLeft/Right replace ArrowUp/Down. |
25
- | `dir` | `input<'ltr' \| 'rtl'>` | Writing direction. Only relevant in horizontal mode — swaps the meaning of Left/Right arrows. |
26
-
27
- ### `ForAccordionItem`
28
-
29
- | API | Type | Description |
30
- | ---------- | ------------------------ | ---------------------------------------------------------------------------------- |
31
- | `value` | `input.required<string>` | Unique identifier within the accordion. Required. |
32
- | `disabled` | `input<boolean>` | When true, the trigger ignores clicks and exposes the native `disabled` attribute. |
33
-
34
- The host gets `data-state="open" \| "closed"` and `data-disabled` for CSS hooks.
35
-
36
- ### `ForAccordionTrigger`
37
-
38
- Reflects on its host: `id`, `aria-expanded`, `aria-controls`, `aria-disabled` (when collapse is disallowed), `disabled` (real, when item is disabled), `data-state`. Toggles on click. Handles `ArrowDown` / `ArrowUp` / `Home` / `End` for navigation between triggers.
39
-
40
- `aria-controls` is emitted only while the item is expanded — mirroring the overlay triggers' open-only gating — so the reference never dangles at an unmounted panel under the recommended `@if (item.expanded())` mount pattern.
41
-
42
- Wrap it in a heading element (`<h2>`–`<h6>`) — APG requires that for landmark navigation. Use a real `<button type="button">` so Enter / Space activation comes for free.
43
-
44
- ### `ForAccordionContent`
45
-
46
- Reflects on its host: `id`, `role="region"`, `aria-labelledby` (the trigger's id), `data-state`, `aria-hidden` (when closed), `inert` (when closed).
47
-
48
- The directive does **not** apply `[hidden]`. Two patterns work:
49
-
50
- - **Mount/unmount with `@if (item.expanded())`** — the panel is absent from the DOM while closed, which is the cleanest path for `animate.enter` / `animate.leave`.
51
- - **Leave it mounted** — preserve internal state or run CSS-only transitions off `data-state`. While closed, the directive sets `aria-hidden="true"` and `inert` on the host so the panel is removed from the accessibility tree and focus order. Add `display: none` (or your own collapse animation) keyed on `[data-state="closed"]` to also hide it visually.
3
+ A stack of collapsible sections, optionally allowing multiple panels open at once.
4
+
5
+ ## Anatomy
6
+
7
+ ```html
8
+ <div forAccordion>
9
+ <div forAccordionItem value="item-1">
10
+ <h3>
11
+ <button type="button" forAccordionTrigger>Trigger</button>
12
+ </h3>
13
+ <div forAccordionContent>Panel content</div>
14
+ </div>
15
+ <!-- repeat forAccordionItem per section -->
16
+ </div>
17
+ ```
52
18
 
53
- ## Example
19
+ ## Examples
54
20
 
55
21
  ```ts
56
22
  import { Component, signal } from '@angular/core';
@@ -86,22 +52,75 @@ export class DemoFaq {
86
52
  }
87
53
  ```
88
54
 
89
- ## Styling
55
+ ## API
56
+
57
+ ### `ForAccordion`
90
58
 
91
- 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 below.
59
+ | Property | Type | Description |
60
+ | ------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
61
+ | `value` | `model<readonly string[]>` | Currently open item values. In single mode the array has 0 or 1 element.<br>**Default:** — |
62
+ | `multiple` | `input<boolean>` | When true, multiple items can be open simultaneously.<br>**Default:** `false` |
63
+ | `collapsible` | `input<boolean>` | Single mode only: when true, the open item can be collapsed by clicking it. Otherwise once any item is open, exactly one stays open.<br>**Default:** `false` |
64
+ | `disabled` | `input<boolean>` | When true, disables every item — each trigger reflects the native `disabled` attribute and cannot toggle. Composes with a per-item `[disabled]`.<br>**Default:** `false` |
65
+ | `orientation` | `input<'horizontal' \| 'vertical'>` | Layout direction of the trigger list. In horizontal mode ArrowLeft/Right replace ArrowUp/Down.<br>**Default:** `'vertical'` |
66
+ | `dir` | `input<'ltr' \| 'rtl'>` | Writing direction. Only relevant in horizontal mode — swaps the meaning of Left/Right arrows.<br>**Default:** — |
92
67
 
93
- ### Data attributes
68
+ | Data attribute | Values |
69
+ | ------------------ | -------------------------- |
70
+ | `data-orientation` | `horizontal` \| `vertical` |
71
+ | `data-disabled` | present \| absent |
94
72
 
95
- | Piece | Attribute | Values |
96
- | ----------------------- | ------------------ | -------------------------- |
97
- | `[forAccordion]` | `data-orientation` | `horizontal` \| `vertical` |
98
- | `[forAccordionItem]` | `data-state` | `open` \| `closed` |
99
- | `[forAccordionItem]` | `data-disabled` | present \| absent |
100
- | `[forAccordionItem]` | `data-orientation` | `horizontal` \| `vertical` |
101
- | `[forAccordionTrigger]` | `data-state` | `open` \| `closed` |
102
- | `[forAccordionTrigger]` | `data-orientation` | `horizontal` \| `vertical` |
103
- | `[forAccordionContent]` | `data-state` | `open` \| `closed` |
104
- | `[forAccordionContent]` | `data-orientation` | `horizontal` \| `vertical` |
73
+ ### `ForAccordionItem`
74
+
75
+ | Property | Type | Description |
76
+ | ---------- | ------------------------ | ---------------------------------------------------------------------------------------------------- |
77
+ | `value` | `input.required<string>` | Unique identifier within the accordion. Required.<br>**Default:** — |
78
+ | `disabled` | `input<boolean>` | When true, the trigger ignores clicks and exposes the native `disabled` attribute.<br>**Default:** — |
79
+
80
+ | Data attribute | Values |
81
+ | ------------------ | -------------------------- |
82
+ | `data-state` | `open` \| `closed` |
83
+ | `data-disabled` | present \| absent |
84
+ | `data-orientation` | `horizontal` \| `vertical` |
85
+
86
+ ### `ForAccordionTrigger`
87
+
88
+ | Data attribute | Values |
89
+ | ------------------ | -------------------------- |
90
+ | `data-state` | `open` \| `closed` |
91
+ | `data-orientation` | `horizontal` \| `vertical` |
92
+
93
+ ### `ForAccordionContent`
94
+
95
+ | Data attribute | Values |
96
+ | ------------------ | -------------------------- |
97
+ | `data-state` | `open` \| `closed` |
98
+ | `data-orientation` | `horizontal` \| `vertical` |
99
+
100
+ ## Keyboard
101
+
102
+ | Key | Action |
103
+ | -------------------------------------------- | -------------------------------------------------------------------------------------------------- |
104
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> | Toggle the focused trigger (native button). |
105
+ | <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | Move focus between triggers (vertical, default). Wrap-around, skips disabled. |
106
+ | <kbd>ArrowLeft</kbd> / <kbd>ArrowRight</kbd> | Move focus between triggers (horizontal — flipped under `dir='rtl'`). Wrap-around, skips disabled. |
107
+ | <kbd>Home</kbd> | Jump to the first trigger. |
108
+ | <kbd>End</kbd> | Jump to the last trigger. |
109
+
110
+ ## Accessibility
111
+
112
+ - **Heading wrapper is your job.** The library does not render a heading around the trigger — wrap it in the heading level (`<h2>`–`<h6>`) appropriate to your document outline. Without it, screen-reader landmark navigation is broken.
113
+ - **Use a real `<button type="button">` for the trigger.** Native Enter / Space activation and focus come for free; the directive does not synthesize them.
114
+ - **`role="region"`** is added to every panel automatically. APG recommends suppressing it on accordions with 6+ panels to avoid landmark proliferation. An opt-out input will be added to `ForAccordionContent` if this surfaces in real usage.
115
+ - **Closed panels leave the accessibility tree.** While closed, `ForAccordionContent` sets `aria-hidden="true"` and `inert` on the panel, removing it from both the accessibility tree and the focus order. The directive does **not** apply `[hidden]`, so pick how to hide it visually:
116
+ - **Mount / unmount with `@if (item.expanded())`** — the panel is absent from the DOM while closed; the cleanest path for `animate.enter` / `animate.leave`. The trigger emits `aria-controls` only while expanded, so the reference never dangles at an unmounted panel.
117
+ - **Leave it mounted** — preserve internal state or run CSS-only transitions off `data-state`. Add `display: none` (or your own collapse animation) keyed on `[data-state="closed"]` to also hide it visually.
118
+ - **`aria-disabled`** is applied to the open trigger only when single mode is active and `collapsible=false`, indicating the user cannot collapse it from this trigger.
119
+ - **A truly disabled item (`[disabled]` on `[forAccordionItem]`) uses the native `disabled` attribute on the trigger, by design.** This is the sanctioned exception in [rule #561](https://github.com/tutkli/forty-cdk/issues/561): the trigger is a real single-purpose `<button>`, not a roving-tabindex collection item (each trigger stays independently in the Tab order; arrow-key navigation is the APG-optional enhancement on top). The disabled trigger leaves the Tab order and the arrow-key navigation (which already skips it), but stays in the accessibility tree so screen readers announce it as unavailable in browse mode. The [APG Accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/) does not require disabled headers to remain focusable.
120
+
121
+ ## Styling
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.
105
124
 
106
125
  ```css
107
126
  .trigger-chevron {
@@ -112,11 +131,3 @@ forty-cdk ships no styles. Add your own class to each piece — the `for*` selec
112
131
  transform: rotate(180deg);
113
132
  }
114
133
  ```
115
-
116
- ## Accessibility notes
117
-
118
- - **Heading wrapper is your job.** The library does not render a heading around the trigger — wrap it in the heading level appropriate to your document outline. Without it, screen-reader landmark navigation is broken.
119
- - **`role="region"`** is added to every panel automatically. APG recommends suppressing it on accordions with 6+ panels to avoid landmark proliferation. An opt-out input will be added to `ForAccordionContent` if this surfaces in real usage.
120
- - **Keyboard**: Enter and Space toggle the focused trigger (native button). ArrowDown / ArrowUp (vertical, default) or ArrowLeft / ArrowRight (horizontal — flipped under `dir='rtl'`) move focus between triggers (wrap-around, skip disabled). Home / End jump to the first/last trigger.
121
- - **`aria-disabled`** is applied to the open trigger only when single mode is active and `collapsible=false`, indicating the user cannot collapse it from this trigger.
122
- - **A truly disabled item (`[disabled]` on `[forAccordionItem]`) uses the native `disabled` attribute on the trigger, by design.** This is the sanctioned exception in [rule #561](https://github.com/tutkli/forty-cdk/issues/561): the trigger is a real single-purpose `<button>`, not a roving-tabindex collection item (each trigger stays independently in the Tab order; arrow-key navigation is the APG-optional enhancement on top). The disabled trigger leaves the Tab order and the arrow-key navigation (which already skips it), but stays in the accessibility tree so screen readers announce it as unavailable in browse mode. The [APG Accordion pattern](https://www.w3.org/WAI/ARIA/apg/patterns/accordion/) does not require disabled headers to remain focusable.
@@ -1,22 +1,18 @@
1
1
  # AspectRatio
2
2
 
3
- Pure visual utility — locks an element's box to a fixed `width / height` ratio via the native CSS `aspect-ratio` property.
3
+ A container that keeps its content at a fixed width-to-height ratio.
4
4
 
5
- No ARIA semantics. Use it to reserve space for media before it loads (preventing layout shift), to keep cards on a grid uniform, or to wrap responsive iframes.
5
+ Pure visual utility — it locks an element's box via the native CSS `aspect-ratio` property, with no ARIA semantics. Reach for it to reserve space for media before it loads (preventing layout shift), keep cards on a grid uniform, or wrap responsive iframes.
6
6
 
7
- ## Pieces
7
+ ## Anatomy
8
8
 
9
- | Class | Selector | Role |
10
- | ---------------- | ------------------ | --------------------------------------------------------- |
11
- | `ForAspectRatio` | `[forAspectRatio]` | Single attribute directive. Applies `style.aspect-ratio`. |
12
-
13
- ## Inputs
14
-
15
- | API | Type | Description |
16
- | ------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
- | `ratio` | `input<number>` | Width / height ratio (e.g. `16 / 9`, `4 / 3`, `1`). Accepts both numeric expressions and string attributes. Defaults to `1`. Non-positive or non-finite values fall back to `1`. |
9
+ ```html
10
+ <div forAspectRatio [ratio]="16 / 9">
11
+ <!-- your content fills the box -->
12
+ </div>
13
+ ```
18
14
 
19
- ## Usage
15
+ ## Examples
20
16
 
21
17
  ```ts
22
18
  import { Component } from '@angular/core';
@@ -58,19 +54,25 @@ import { ForAspectRatio } from 'forty-cdk/aspect-ratio';
58
54
  export class DemoAspectRatio {}
59
55
  ```
60
56
 
61
- ## Notes
57
+ ## API
62
58
 
63
- - **Browser support.** Native `aspect-ratio` is in Baseline 2021 (Chrome 88+, Firefox 89+, Safari 15+) — same target as Angular 20+, so no polyfill is needed.
64
- - **Width still on you.** The directive only sets `aspect-ratio`; you decide width / max-width / display. The height is computed from the ratio.
65
- - **Children fill the box.** Use `width: 100%; height: 100%; object-fit: cover` on inner media to fill without distortion. The directive imposes no styles on children.
66
- - **No role, no a11y.** This is a layout utility. The element it sits on keeps whatever semantics you give it (`<div>`, `<figure>`, `<a>`, …).
59
+ ### `ForAspectRatio`
60
+
61
+ | Property | Type | Description |
62
+ | -------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
63
+ | `ratio` | `input<number>` | Width / height ratio (e.g. `16 / 9`, `4 / 3`, `1`). Accepts both numeric expressions and string attributes. Non-positive or non-finite values fall back to `1`.<br>**Default:** `1` |
64
+
65
+ | Data attribute | Values |
66
+ | ------------------ | ------- |
67
+ | `[forAspectRatio]` | present |
67
68
 
68
69
  ## Styling
69
70
 
70
71
  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]`.
71
72
 
72
- ### Data attributes
73
+ ## Behavior notes
73
74
 
74
- | Piece | Attribute | Values |
75
- | ------------------ | --------- | ------ |
76
- | `[forAspectRatio]` | _(none)_ | — |
75
+ - **Browser support.** Native `aspect-ratio` is in Baseline 2021 (Chrome 88+, Firefox 89+, Safari 15+) — same target as Angular 20+, so no polyfill is needed.
76
+ - **Width still on you.** The directive only sets `aspect-ratio`; you decide width / max-width / display. The height is computed from the ratio.
77
+ - **Children fill the box.** Use `width: 100%; height: 100%; object-fit: cover` on inner media to fill without distortion. The directive imposes no styles on children.
78
+ - **No role, no a11y.** This is a layout utility. The element it sits on keeps whatever semantics you give it (`<div>`, `<figure>`, `<a>`, …).
package/avatar/README.md CHANGED
@@ -1,29 +1,20 @@
1
1
  # Avatar
2
2
 
3
- Headless avatar that tracks the load lifecycle of an `<img>` and lets the consumer choose what to show during loading or after an error.
3
+ A user image with a graceful fallback across its loading lifecycle.
4
4
 
5
- There is no WAI-ARIA pattern for "avatar" — it is a presentational composition. The directive does not impose a `role`; pair the avatar with visible name text or `aria-label` on the surrounding element when identity matters.
5
+ Headless and presentational — it tracks the load lifecycle of an `<img>` and lets you choose what to show while loading or after an error. There is no WAI-ARIA pattern for avatars, so the directive imposes no `role` of its own.
6
6
 
7
- ## Pieces
7
+ ## Anatomy
8
8
 
9
- | Class | Selector | Role |
10
- | ------------------- | --------------------- | ---------------------------------------------------------------------- |
11
- | `ForAvatar` | `[forAvatar]` | Root. Owns `status`, `shouldShowFallback`, and `fallbackDelayMs`. |
12
- | `ForAvatarImage` | `img[forAvatarImage]` | Observes the `<img>` and reports `idle \| loading \| loaded \| error`. |
13
- | `ForAvatarFallback` | `[forAvatarFallback]` | Marker for fallback content. Reflects `data-status`. |
14
-
15
- ## Inputs / outputs / models
16
-
17
- | API | Type | Owner | Description |
18
- | --------------------- | ------------------------- | ---------------- | ----------------------------------------------------------------------------------------- |
19
- | `fallbackDelayMs` | `input<number>` | `ForAvatar` | ms to wait before `shouldShowFallback()` flips to `true` while idle/loading. Default `0`. |
20
- | `status` | `Signal<ForAvatarStatus>` | `ForAvatar` | Read-only current status. |
21
- | `shouldShowFallback` | `Signal<boolean>` | `ForAvatar` | `true` when the consumer should render the fallback. Drives `@if`. |
22
- | `(loadStatusChanged)` | `output<ForAvatarStatus>` | `ForAvatarImage` | Emits whenever the lifecycle transitions. |
23
-
24
- The host element of every piece carries `data-status="idle" \| "loading" \| "loaded" \| "error"`.
9
+ ```html
10
+ <span forAvatar #avatar="forAvatar">
11
+ <img forAvatarImage [src]="src" [alt]="name" />
12
+ <!-- rendered only when avatar.shouldShowFallback() is true -->
13
+ <span forAvatarFallback>{{ initials }}</span>
14
+ </span>
15
+ ```
25
16
 
26
- ## Usage
17
+ ## Examples
27
18
 
28
19
  ```ts
29
20
  import { Component, signal } from '@angular/core';
@@ -71,24 +62,43 @@ export class DemoAvatar {
71
62
  }
72
63
  ```
73
64
 
74
- ## Notes
65
+ ## API
75
66
 
76
- - **Cached images are detected on first render.** If the browser already has the image cached, `load`/`error` may not fire — the directive checks `<img>.complete` and `naturalWidth` after the first render and reports `loaded` / `error` accordingly. A cached image that is `complete` but has zero intrinsic width (e.g. an SVG without explicit dimensions) is ambiguous, so the directive stays `loading` and confirms validity with `img.decode()` rather than pessimistically flagging `error`.
77
- - **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.
78
- - **`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.
79
- - **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.
67
+ ### `ForAvatar`
80
68
 
81
- ## Styling
69
+ | Property | Type | Description |
70
+ | -------------------- | ------------------------- | ------------------------------------------------------------------------------------------------ |
71
+ | `fallbackDelayMs` | `input<number>` | ms to wait before `shouldShowFallback()` flips to `true` while idle/loading.<br>**Default:** `0` |
72
+ | `status` | `Signal<ForAvatarStatus>` | Read-only current status.<br>**Default:** — |
73
+ | `shouldShowFallback` | `Signal<boolean>` | `true` when the consumer should render the fallback. Drives `@if`.<br>**Default:** — |
74
+
75
+ | Data attribute | Values |
76
+ | -------------- | ------------------------------------------ |
77
+ | `data-status` | `idle` \| `loading` \| `loaded` \| `error` |
78
+
79
+ ### `ForAvatarImage`
80
+
81
+ | Property | Type | Description |
82
+ | --------------------- | ------------------------- | ------------------------------------------------------------------- |
83
+ | `(loadStatusChanged)` | `output<ForAvatarStatus>` | Output. Emits whenever the lifecycle transitions.<br>**Default:** — |
82
84
 
83
- 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 below.
85
+ | Data attribute | Values |
86
+ | -------------- | ------------------------------------------ |
87
+ | `data-status` | `idle` \| `loading` \| `loaded` \| `error` |
84
88
 
85
- ### Data attributes
89
+ ### `ForAvatarFallback`
86
90
 
87
- | Piece | Attribute | Values |
88
- | --------------------- | ------------- | ------------------------------------------ |
89
- | `[forAvatar]` | `data-status` | `idle` \| `loading` \| `loaded` \| `error` |
90
- | `img[forAvatarImage]` | `data-status` | `idle` \| `loading` \| `loaded` \| `error` |
91
- | `[forAvatarFallback]` | `data-status` | `idle` \| `loading` \| `loaded` \| `error` |
91
+ | Data attribute | Values |
92
+ | -------------- | ------------------------------------------ |
93
+ | `data-status` | `idle` \| `loading` \| `loaded` \| `error` |
94
+
95
+ ## Accessibility
96
+
97
+ The directive does not impose a `role`. Pair the avatar with visible name text or `aria-label` on the surrounding element when identity matters. Set `alt=""` on the `<img>` for purely decorative avatars next to a name, or provide a meaningful `alt` description if the avatar stands alone.
98
+
99
+ ## Styling
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.
92
102
 
93
103
  ```css
94
104
  .avatar-image:not([data-status='loaded']) {
@@ -98,3 +108,10 @@ forty-cdk ships no styles. Add your own class to each piece — the `for*` selec
98
108
  color: #b00020;
99
109
  }
100
110
  ```
111
+
112
+ ## Behavior notes
113
+
114
+ - **Cached images are detected on first render.** If the browser already has the image cached, `load`/`error` may not fire — the directive checks `<img>.complete` and `naturalWidth` after the first render and reports `loaded` / `error` accordingly. A cached image that is `complete` but has zero intrinsic width (e.g. an SVG without explicit dimensions) is ambiguous, so the directive stays `loading` and confirms validity with `img.decode()` rather than pessimistically flagging `error`.
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
+ - **`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
+ - **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.
@@ -1,16 +1,20 @@
1
1
  # Breadcrumbs
2
2
 
3
- Headless breadcrumb trail implementing the [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/): a labelled `navigation` landmark wrapping a set of links, with [`aria-current="page"`](https://www.w3.org/TR/wai-aria-1.2/#aria-current) on the current page and decorative separators hidden from assistive technology. Ships no styles — apply your own.
3
+ A labelled navigation landmark for a breadcrumb trail: links with aria-current='page' on the current page and decorative separators hidden from assistive technology.
4
4
 
5
- ## Pieces
5
+ ## Anatomy
6
6
 
7
- | Class | Selector | Role |
8
- | ------------------------ | -------------------------- | ------------------------------------------------------------------------- |
9
- | `ForBreadcrumbs` | `[forBreadcrumbs]` | Root. `role="navigation"`, labelled `aria-label="Breadcrumb"` by default. |
10
- | `ForBreadcrumbItem` | `[forBreadcrumbItem]` | A link in the trail. Reflects `aria-current="page"` when `current`. |
11
- | `ForBreadcrumbSeparator` | `[forBreadcrumbSeparator]` | Decorative divider between items. Reflects `aria-hidden="true"`. |
7
+ ```html
8
+ <nav forBreadcrumbs>
9
+ <ol>
10
+ <li><a forBreadcrumbItem href="/">Home</a></li>
11
+ <li forBreadcrumbSeparator>/</li>
12
+ <li><a forBreadcrumbItem href="/data" current>Data</a></li>
13
+ </ol>
14
+ </nav>
15
+ ```
12
16
 
13
- ## Usage
17
+ ## Examples
14
18
 
15
19
  ```ts
16
20
  import { Component } from '@angular/core';
@@ -36,6 +40,28 @@ export class DemoBreadcrumbs {}
36
40
 
37
41
  The root defaults its label to `Breadcrumb`. Override it with `ariaLabel="…"` (or point a native `aria-labelledby` at a visible heading) when a page hosts more than one breadcrumb trail.
38
42
 
43
+ ## API
44
+
45
+ ### `ForBreadcrumbs`
46
+
47
+ | Property | Type | Description |
48
+ | ----------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
49
+ | `ariaLabel` | `input<string>` | Accessible label for the `navigation` landmark. Override when a page hosts more than one breadcrumb trail.<br>**Default:** `'Breadcrumb'` |
50
+
51
+ ### `ForBreadcrumbItem`
52
+
53
+ | Property | Type | Description |
54
+ | --------- | ---------------- | ------------------------------------------------------------------------ |
55
+ | `current` | `input<boolean>` | When true, reflects `aria-current="page"` on the link.<br>**Default:** — |
56
+
57
+ ## Accessibility
58
+
59
+ Implements the [WAI-ARIA Breadcrumb pattern](https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/).
60
+
61
+ - **Navigation landmark.** `[forBreadcrumbs]` applies `role="navigation"` and labels it `aria-label="Breadcrumb"` by default, creating a named landmark that screen-reader users can jump to directly.
62
+ - **Current page.** Set `current` on `[forBreadcrumbItem]` for the active page; the directive reflects `aria-current="page"` so assistive technology announces the user's location in the trail.
63
+ - **Decorative separators.** `[forBreadcrumbSeparator]` reflects `aria-hidden="true"` so the visual divider (e.g. `/`) is skipped by screen readers.
64
+
39
65
  ## Styling
40
66
 
41
67
  forty-cdk ships no styles. Style the current item via `[aria-current="page"]`.
@@ -1,8 +1,8 @@
1
1
  # Breakpoints
2
2
 
3
- A signal-first, zoneless, SSR-safe viewport breakpoint observer. Configure the breakpoint map **once** via a provider; read it anywhere with `injectBreakpoints()` — no need to repeat the breakpoint set at every call site.
3
+ A signal-first, zoneless, SSR-safe viewport breakpoint observer (injectBreakpoints). Configure the breakpoint map once via provideForBreakpoints — or use the Tailwind scale by default — then read up / down / between / only / active or any arbitrary media query, each as a live Signal&lt;boolean&gt;.
4
4
 
5
- It is a headless reactive utility, not a UI primitive: no DOM, no ARIA, no template. Each query method returns a `Signal<boolean>`.
5
+ It is a headless reactive utility, not a UI primitive: no DOM, no ARIA, no template. Configure the breakpoint map **once** via a provider; read it anywhere with `injectBreakpoints()` — no need to repeat the breakpoint set at every call site.
6
6
 
7
7
  ## Setup
8
8
 
@@ -18,7 +18,7 @@ export const appConfig: ApplicationConfig = {
18
18
 
19
19
  Providing it again on a component injector replaces the map for that subtree only (nearest scope wins; the map is replaced wholesale, never merged key-by-key).
20
20
 
21
- ## Usage
21
+ ## Examples
22
22
 
23
23
  ```ts
24
24
  import { Component, inject } from '@angular/core';
@@ -41,15 +41,6 @@ export class Layout {
41
41
  }
42
42
  ```
43
43
 
44
- | Method | Matches |
45
- | ---------------- | ------------------------------------------------------------------------------ |
46
- | `up(name)` | the breakpoint and wider — `(min-width: N px)` |
47
- | `down(name)` | narrower than the breakpoint — `(max-width: (N − 0.02) px)` |
48
- | `between(a, b)` | from `a` (inclusive) up to but not including `b` |
49
- | `only(name)` | the breakpoint's own band, up to but not including the next-larger one |
50
- | `active` | the largest breakpoint whose `min-width` matches, or `null` below the smallest |
51
- | `matches(query)` | escape hatch for an arbitrary media query (orientation, `prefers-*`, …) |
52
-
53
44
  The returned handle captures its injection context, so the query methods can be called lazily from a `computed()` or a template, not only during construction:
54
45
 
55
46
  ```ts
@@ -76,6 +67,19 @@ declare module 'forty-cdk' {
76
67
 
77
68
  Now `injectBreakpoints()` autocompletes `'mobile' | 'tablet' | 'laptop' | 'desktop'` across the whole app.
78
69
 
70
+ ## API
71
+
72
+ ### `injectBreakpoints`
73
+
74
+ | Method | Matches |
75
+ | ---------------- | ------------------------------------------------------------------------------ |
76
+ | `up(name)` | the breakpoint and wider — `(min-width: N px)` |
77
+ | `down(name)` | narrower than the breakpoint — `(max-width: (N − 0.02) px)` |
78
+ | `between(a, b)` | from `a` (inclusive) up to but not including `b` |
79
+ | `only(name)` | the breakpoint's own band, up to but not including the next-larger one |
80
+ | `active` | the largest breakpoint whose `min-width` matches, or `null` below the smallest |
81
+ | `matches(query)` | escape hatch for an arbitrary media query (orientation, `prefers-*`, …) |
82
+
79
83
  ## SSR
80
84
 
81
85
  On the server (or where `matchMedia` is unavailable) every query signal reads `false` and `active` reads `null`. No `matchMedia` access happens server-side, so the helper is safe under Angular Universal.
package/button/README.md CHANGED
@@ -1,10 +1,22 @@
1
1
  # ForButton
2
2
 
3
- Headless implementation of the [WAI-ARIA Button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/).
3
+ Turns any element — a native <button> or a custom host like <div> / <span> — into an accessible button with keyboard activation. Disabled stays focusable (aria-disabled, never the native attribute) and pressed / hovered / focus-visible are reflected as data-\* hooks.
4
4
 
5
- A single `[forButton]` directive turns any element into an accessible, interactive button. It works on a native `<button>` host and on any non-button host (e.g. `<div>`, `<span>`). Install from `forty-cdk`.
5
+ A single `[forButton]` directive does all of this. On a native `<button>` host the platform owns Enter/Space activation and `type` handling; on any non-button host the directive adds `role="button"`, `tabindex="0"`, and keyboard activation so the contract matches.
6
6
 
7
- ## Basic usage
7
+ ## Anatomy
8
+
9
+ ```html
10
+ <!-- Native button — platform owns Enter/Space and type handling -->
11
+ <button forButton [disabled]="saving()" (activate)="save()">Save</button>
12
+
13
+ <!-- Non-button host — role="button", tabindex="0", keyboard activation added -->
14
+ <div forButton (activate)="save()">Save</div>
15
+ ```
16
+
17
+ ## Examples
18
+
19
+ ### Basic usage
8
20
 
9
21
  ```html
10
22
  <!-- Native button — platform handles Enter/Space → click synthesis -->
@@ -14,7 +26,7 @@ A single `[forButton]` directive turns any element into an accessible, interacti
14
26
  <div forButton (activate)="save()">Save</div>
15
27
  ```
16
28
 
17
- ## Disabled
29
+ ### Disabled
18
30
 
19
31
  Disabled buttons stay focusable so assistive technology can announce them. The native `disabled` attribute is never set; instead `aria-disabled="true"` is reflected.
20
32
 
@@ -22,7 +34,7 @@ Disabled buttons stay focusable so assistive technology can announce them. The n
22
34
  <button forButton [disabled]="isSaving()" (activate)="save()">Save</button>
23
35
  ```
24
36
 
25
- ## Preserve consumer `type`
37
+ ### Preserve consumer `type`
26
38
 
27
39
  A native `<button>` without an explicit `type` attribute defaults to `type="button"`. A consumer-set `type="submit"` is preserved:
28
40
 
@@ -30,20 +42,45 @@ A native `<button>` without an explicit `type` attribute defaults to `type="butt
30
42
  <button type="submit" forButton>Submit form</button>
31
43
  ```
32
44
 
33
- ## Data attributes
45
+ ## API
34
46
 
35
- The directive reflects the following boolean `data-*` attributes (present with an empty-string value when true, absent when false). There is no `data-state` — this primitive has no open/closed or checked/unchecked logical state.
47
+ ### `ForButton`
36
48
 
37
- | Attribute | When present |
38
- | -------------------- | ----------------------------------------------- |
39
- | `data-disabled` | `[disabled]="true"` |
40
- | `data-pressed` | Primary pointer held down, or Enter/Space held |
41
- | `data-hovered` | Mouse/pen pointer is over the element |
42
- | `data-focus-visible` | Focused via keyboard (keyboard modality active) |
49
+ | Property | Type | Description |
50
+ | ---------- | ---------------- | -------------------------------------------------------------------------------------------------- |
51
+ | `disabled` | `input<boolean>` | Suppresses activation and reflects `aria-disabled` + `data-disabled`.<br>**Default:** `false` |
52
+ | `activate` | `output<void>` | Fires once per user activation (click, Enter, Space). Never fires when disabled.<br>**Default:** — |
43
53
 
44
- ## API
54
+ The directive reflects boolean `data-*` attributes (present with an empty-string value when true, absent when false). There is no `data-state` — this primitive has no open/closed or checked/unchecked logical state.
45
55
 
46
- | Member | Type | Default | Description |
47
- | ---------- | ---------------- | ------- | -------------------------------------------------------------------------------- |
48
- | `disabled` | `input<boolean>` | `false` | Suppresses activation and reflects `aria-disabled` + `data-disabled`. |
49
- | `activate` | `output<void>` | — | Fires once per user activation (click, Enter, Space). Never fires when disabled. |
56
+ | Data attribute | Values |
57
+ | -------------------- | ----------------- |
58
+ | `data-disabled` | present \| absent |
59
+ | `data-pressed` | present \| absent |
60
+ | `data-hovered` | present \| absent |
61
+ | `data-focus-visible` | present \| absent |
62
+
63
+ `data-pressed` is present while the primary pointer is held down or Enter/Space is held. `data-hovered` is present while a mouse/pen pointer is over the element. `data-focus-visible` is present when focused via keyboard (keyboard modality active).
64
+
65
+ ## Accessibility
66
+
67
+ Implements the [WAI-ARIA Button pattern](https://www.w3.org/WAI/ARIA/apg/patterns/button/).
68
+
69
+ - **Native `<button>` semantics are preserved.** On a native host, no extra ARIA is added; the browser's built-in button role, Enter/Space activation, and `type` handling all apply.
70
+ - **Non-button hosts get `role="button"` and `tabindex="0"`** plus keyboard activation (Enter/Space), matching the native button contract.
71
+ - **Disabled buttons stay focusable.** `aria-disabled="true"` is used instead of the native `disabled` attribute so assistive technology can still announce the control's purpose.
72
+
73
+ ## Styling
74
+
75
+ 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.
76
+
77
+ ```css
78
+ [forButton][data-disabled] {
79
+ opacity: 0.4;
80
+ pointer-events: none;
81
+ }
82
+
83
+ [forButton][data-pressed] {
84
+ transform: scale(0.97);
85
+ }
86
+ ```