actual-css 0.7.0 → 0.9.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 (144) hide show
  1. package/CHANGELOG.md +217 -0
  2. package/README.md +13 -11
  3. package/components.json +668 -0
  4. package/dist/actual.css +166 -37
  5. package/dist/actual.full.css +1087 -643
  6. package/dist/actual.full.js +1 -1
  7. package/dist/actual.full.min.css +1 -1
  8. package/dist/actual.js +1 -1
  9. package/dist/actual.min.css +1 -1
  10. package/docs/design-notes/accessibility-decisions.md +33 -0
  11. package/docs/design-notes/browser-support.md +62 -30
  12. package/docs/design-notes/column-layout.md +3 -3
  13. package/docs/design-notes/css-anchor.md +75 -21
  14. package/docs/design-notes/enhancement-contract.md +14 -6
  15. package/docs/design-notes/grid.md +4 -4
  16. package/docs/design-notes/naming.md +6 -1
  17. package/docs/design-notes/platform-alignment.md +53 -7
  18. package/docs/design-notes/popover-manual-poc.md +105 -25
  19. package/docs/design-notes/surface-reparenting.md +56 -47
  20. package/docs/design-notes/tooltip-coordinate-space.md +132 -0
  21. package/docs/design-notes/widget-primitives.md +7 -4
  22. package/docs/pages/components/accordion.md +41 -2
  23. package/docs/pages/components/alert.md +85 -30
  24. package/docs/pages/components/app-navigation.md +6 -0
  25. package/docs/pages/components/avatar.md +6 -6
  26. package/docs/pages/components/badge.md +101 -16
  27. package/docs/pages/components/button.md +16 -8
  28. package/docs/pages/components/card.md +10 -10
  29. package/docs/pages/components/dialog.md +21 -6
  30. package/docs/pages/components/drawer.md +13 -0
  31. package/docs/pages/components/fab.md +4 -3
  32. package/docs/pages/components/list.md +42 -4
  33. package/docs/pages/components/navbar.md +17 -1
  34. package/docs/pages/components/pagination.md +2 -2
  35. package/docs/pages/components/skeleton.md +3 -1
  36. package/docs/pages/components/steps.md +19 -0
  37. package/docs/pages/components/table.md +3 -2
  38. package/docs/pages/enhancements/flyout.md +82 -38
  39. package/docs/pages/enhancements/overview.md +25 -0
  40. package/docs/pages/enhancements/status-bar.md +1 -1
  41. package/docs/pages/enhancements/tabs.md +45 -37
  42. package/docs/pages/enhancements/tooltip.md +36 -10
  43. package/docs/pages/examples/overview.md +38 -5
  44. package/docs/pages/forms/choice-cards.md +11 -6
  45. package/docs/pages/forms/combobox.md +1 -1
  46. package/docs/pages/forms/floating-field.md +4 -3
  47. package/docs/pages/forms/joined-controls.md +22 -0
  48. package/docs/pages/forms/otp.md +2 -1
  49. package/docs/pages/forms/overview.md +1 -1
  50. package/docs/pages/forms/states-and-sizes.md +16 -18
  51. package/docs/pages/forms/sticky-actions.md +2 -2
  52. package/docs/pages/forms/switch.md +1 -1
  53. package/docs/pages/foundations/philosophy.md +57 -26
  54. package/docs/pages/foundations/theming.md +2 -2
  55. package/docs/pages/foundations/tokens.md +118 -21
  56. package/docs/pages/foundations/typography.md +35 -14
  57. package/docs/pages/guides/introduction.md +11 -9
  58. package/docs/pages/guides/progressive-enhancement.md +115 -30
  59. package/docs/pages/guides/tailwind.md +3 -2
  60. package/docs/pages/layout/media.md +1 -1
  61. package/docs/pages/layout/overview.md +11 -1
  62. package/docs/pages/layout/scroll-snap.md +3 -3
  63. package/docs/pages/layout/stack.md +17 -9
  64. package/docs/pages/patterns/icon-input.md +4 -2
  65. package/docs/pages/patterns/icon-slot.md +2 -0
  66. package/docs/pages/patterns/intent-nesting.md +1 -1
  67. package/docs/pages/utilities/overview.md +23 -2
  68. package/docs/pages/utilities/sizing-wrapping.md +1 -1
  69. package/docs/pages/utilities/spacing.md +10 -8
  70. package/llms.txt +2 -1
  71. package/package.json +159 -149
  72. package/{scripts/reserved-classes.json → reserved-classes.json} +4 -6
  73. package/src/css/components/accordion.css +42 -3
  74. package/src/css/components/alert.css +25 -13
  75. package/src/css/components/app-nav.css +18 -8
  76. package/src/css/components/avatar.css +13 -1
  77. package/src/css/components/badge.css +78 -12
  78. package/src/css/components/breadcrumb.css +2 -3
  79. package/src/css/components/button.css +9 -16
  80. package/src/css/components/card.css +9 -2
  81. package/src/css/components/chat.css +1 -0
  82. package/src/css/components/data-list.css +1 -1
  83. package/src/css/components/drawer.css +101 -32
  84. package/src/css/components/fab.css +0 -6
  85. package/src/css/components/flyout.css +35 -98
  86. package/src/css/components/index.css +0 -1
  87. package/src/css/components/indicator.css +21 -0
  88. package/src/css/components/key.css +10 -0
  89. package/src/css/components/list.css +40 -4
  90. package/src/css/components/menu.css +2 -1
  91. package/src/css/components/modal.css +48 -33
  92. package/src/css/components/navbar.css +37 -4
  93. package/src/css/components/pagination.css +1 -0
  94. package/src/css/components/skeleton.css +15 -4
  95. package/src/css/components/spinner.css +6 -6
  96. package/src/css/components/status-bar.css +0 -5
  97. package/src/css/components/steps.css +45 -59
  98. package/src/css/components/tab.css +29 -9
  99. package/src/css/components/table.css +13 -5
  100. package/src/css/components/tooltip.css +36 -40
  101. package/src/css/core/base.css +23 -1
  102. package/src/css/core/intents.css +17 -6
  103. package/src/css/core/theme.css +27 -0
  104. package/src/css/core/tokens.css +35 -15
  105. package/src/css/core/variants.css +64 -15
  106. package/src/css/forms/choice-card.css +4 -4
  107. package/src/css/forms/choice.css +76 -29
  108. package/src/css/forms/control.css +2 -1
  109. package/src/css/forms/floating-field.css +3 -3
  110. package/src/css/forms/form.css +4 -3
  111. package/src/css/forms/input-icon.css +20 -14
  112. package/src/css/forms/native.css +6 -1
  113. package/src/css/forms/otp.css +9 -1
  114. package/src/css/forms/range.css +2 -0
  115. package/src/css/forms/select.css +29 -9
  116. package/src/css/forms/switch.css +28 -8
  117. package/src/css/layout/app-layout.css +1 -1
  118. package/src/css/layout/grid.css +50 -76
  119. package/src/css/layout/scroll-snap.css +11 -7
  120. package/src/css/layout/scroller.css +3 -4
  121. package/src/css/layout/stack.css +2 -2
  122. package/src/css/typography/fluid.css +4 -3
  123. package/src/css/typography/prose.css +6 -5
  124. package/src/css/utilities/base.css +2 -1
  125. package/src/css/utilities/extra.css +20 -17
  126. package/src/js/context-menu.js +27 -26
  127. package/src/js/dialog.js +2 -1
  128. package/src/js/dismiss.js +5 -0
  129. package/src/js/enhance.js +34 -2
  130. package/src/js/enhancement-loader.js +36 -43
  131. package/src/js/events.js +0 -3
  132. package/src/js/filter.js +0 -1
  133. package/src/js/floating-space.js +42 -0
  134. package/src/js/flyout.js +9 -44
  135. package/src/js/focus-group.js +7 -0
  136. package/src/js/focus.js +6 -0
  137. package/src/js/menu.js +10 -1
  138. package/src/js/selectors.js +5 -9
  139. package/src/js/status.js +6 -5
  140. package/src/js/surface.js +97 -139
  141. package/src/js/tab.js +6 -1
  142. package/src/js/tooltip.js +150 -51
  143. package/src/js/validation.js +1 -1
  144. package/src/css/components/surface.css +0 -49
@@ -5,13 +5,14 @@
5
5
  - Supports intent colors.
6
6
  - Supports longer text and lists.
7
7
  - Links inherit alert color by default.
8
- - Alerts are soft by default. Use `.solid` or `.outline` when the message needs stronger or quieter emphasis.
8
+ - Alerts are soft by default. Use `.surface` for neutral chrome with the intent only in the ink, `.solid` or `.outline` when the message needs stronger or quieter emphasis, and `.inverted` for an inverse surface.
9
9
  - Use `<menu class="actions cluster">` for alert action lists.
10
10
  - Use `role="alert"` only when the alert is injected dynamically and should be announced.
11
11
  - Not a toast.
12
12
  - Could have simple or complex html content.
13
+ - A title and a description are plain siblings: the alert owns the small space between its direct content blocks, so neither needs a wrapper. Nested content keeps its own rhythm — wrap several paragraphs in `.prose`, or a differently spaced group in `.stack`, when the message needs a rhythm of its own.
13
14
  - Alerts may include a decorative leading icon. Use `.alert-icon` on the icon element; the rest of the content flows into the remaining text column.
14
- - Use `.sm` or `.lg` for density changes. The inline padding stays stable.
15
+ - Use `.compact` or `.spacious` for density changes. The inline padding stays stable.
15
16
  - Use `.alert-dismiss` for a compact dismiss button. It is a direct trailing child in standard alerts and lives inside `.alert-title` in admonitions. It uses the shared `--icon-close` mask and the `--dismiss` runtime command — no icon font or custom JS.
16
17
 
17
18
  Because alerts are soft by default, adding `.soft` to an intent (`.alert.soft.primary`,
@@ -21,26 +22,33 @@ itself needs to change, not `.soft`.
21
22
 
22
23
  ## Class reference
23
24
 
24
- | Class | Kind | Description |
25
- | ---------------- | ----------- | --------------------------------------------- |
26
- | `.alert` | Component | Inline status surface; soft by default. |
27
- | `.alert-icon` | Composition | Decorative leading icon, in the first column. |
28
- | `.alert-dismiss` | Modifier | Compact dismiss button. |
29
- | `.callout` | Variant | Neutral panel, thick accent leading edge. |
30
- | `.admonition` | Variant | Tinted title bar, body on the page surface. |
31
-
32
- `.alert-dismiss` draws itself with the `--icon-close` mask and acts through the
33
- `--dismiss` command.
34
- | `.alert-title` | Composition | Admonition title bar; hosts the dismiss button. |
35
- | `.alert-body` | Composition | Admonition body resting on the page surface. |
36
- | Shared intents | Intent | `.primary`, `.secondary`, `.success`, `.warning`, `.danger`. |
37
- | Shared variants | Variant | `.solid`, `.outline`, and `.surface` emphasis. |
38
- | `.sm` / `.lg` | Size | Density; inline padding stays stable. |
25
+ | Class | Kind | Description |
26
+ | ---------------- | ----------- | ----------------------------------------------- |
27
+ | `.alert` | Component | Inline status surface; soft by default. |
28
+ | `.alert-icon` | Composition | Decorative leading icon, in the first column. |
29
+ | `.alert-dismiss` | Modifier | Compact dismiss button. |
30
+ | `.alert-title` | Composition | Admonition title bar; hosts the dismiss button. |
31
+ | `.alert-body` | Composition | Admonition body on the page surface. |
32
+ | `.callout` | Variant | Leading-edge flag; owns the border geometry. |
33
+ | `.admonition` | Variant | Tinted title bar, body on the page surface. |
34
+ | Shared intents | Intent | `.primary`, `.secondary`, `.success`, … |
35
+ | Shared variants | Variant | `.solid`, `.outline`, `.surface`. |
36
+ | `.inverted` | Surface | Inverse surface, any intent. |
37
+ | Density contexts | Density | Descendant rhythm; inline padding stays stable. |
39
38
 
40
39
  ## Basic usage
41
40
 
41
+ `.surface` comes first on purpose: it keeps the theme's own chrome and puts the
42
+ intent in the ink alone, which is what most informational messages want. Add an
43
+ intent treatment when the color itself carries the meaning.
44
+
42
45
  ```html demo
43
46
  <div class="stack">
47
+ <div class="alert surface">
48
+ <i class="ti ti-info-circle alert-icon" aria-hidden="true"></i>
49
+ <div><strong>New message.</strong> 12 unread. <a href="#">Open inbox</a>.</div>
50
+ </div>
51
+
44
52
  <div class="alert success">
45
53
  <i class="ti ti-circle-check alert-icon" aria-hidden="true"></i>
46
54
  <div>Your changes have been saved. <a href="#">View activity</a>.</div>
@@ -91,16 +99,46 @@ itself needs to change, not `.soft`.
91
99
  </div>
92
100
  ```
93
101
 
102
+ ## Trailing action
103
+
104
+ The grid pins `.alert-icon` and `.alert-dismiss` to the outer columns. A
105
+ trailing link or button is content, not anatomy, so it lives in the text column:
106
+ wrap the row in a `.cluster` and push the two ends apart. Same recipe as any
107
+ split / spread row — there is no `.alert-action` class.
108
+
109
+ The alert aligns its columns to the block start, so a multi-line message keeps
110
+ its icon on the first line. A trailing control makes that row taller than a line
111
+ of text: add `.items-center` to put the icon, the text and the button on one
112
+ center.
113
+
114
+ ```html demo
115
+ <div class="alert surface items-center">
116
+ <i class="ti ti-info-circle alert-icon" aria-hidden="true"></i>
117
+ <div class="cluster justify-content-space-between">
118
+ <strong>New message!</strong>
119
+ <a class="btn sm outline" href="#">Open</a>
120
+ </div>
121
+ </div>
122
+ ```
123
+
94
124
  ## Variants
95
125
 
96
126
  ```html demo
97
127
  <div class="stack">
98
- <div class="alert danger sm" role="alert">
99
- I'm a small error
128
+ <div class="alert surface" role="alert">
129
+ I'm a surface alert — theme chrome, intent in the ink
130
+ </div>
131
+
132
+ <div class="alert inverted" role="alert">
133
+ I'm an inverted alert <a href="#">with a link</a>
134
+ </div>
135
+
136
+ <div class="alert danger compact" role="alert">
137
+ I'm an error in a compact context
100
138
  </div>
101
139
 
102
- <div class="alert danger lg" role="alert">
103
- I'm a large error
140
+ <div class="alert danger spacious" role="alert">
141
+ I'm an error in a spacious context
104
142
  </div>
105
143
 
106
144
  <div class="alert danger outline" role="alert">
@@ -120,11 +158,8 @@ Use `.alert-dismiss` to let a user remove an inline alert. The button is transpa
120
158
  ```html demo
121
159
  <div class="alert warning" id="warning-alert">
122
160
  <i class="ti ti-alert-triangle alert-icon" aria-hidden="true"></i>
123
-
124
- <div class="stack" style="--gap: var(--space-10)">
125
- <strong>Warning</strong>
126
- <p>Something needs your attention.</p>
127
- </div>
161
+ <strong>Warning</strong>
162
+ <p>Something needs your attention.</p>
128
163
 
129
164
  <button
130
165
  class="alert-dismiss"
@@ -156,22 +191,42 @@ In an admonition, place `.alert-dismiss` inside `.alert-title`; it rides the inl
156
191
 
157
192
  ## Callout
158
193
 
159
- Use `.callout` for a neutral panel with a thick accent border on the leading edge. Intent classes tint the border color.
194
+ `.callout` is a **leading-edge flag treatment**: it owns the border geometry and
195
+ nothing else, so the surface and the ink keep coming from the alert's own intent
196
+ and treatment. Bare is the soft panel, `.surface` the page surface, `.solid` a
197
+ filled one — and `.outline` has no fill and no other side, since the flag *is*
198
+ the border.
160
199
 
161
200
  ```html demo
162
201
  <div class="stack">
163
202
  <div class="alert callout">
164
203
  <strong>Note</strong>
165
- <p>This is a callout — a neutral panel with a thick accent border on the leading edge.</p>
204
+ <p>A neutral panel with a thick accent flag on the leading edge.</p>
166
205
  </div>
167
206
 
168
207
  <div class="alert warning callout" role="alert">
169
208
  <strong>Heads up</strong>
170
- <p>The border color follows the intent. No other borders are drawn.</p>
209
+ <p>The intent tints the panel and the flag together.</p>
210
+ </div>
211
+
212
+ <div class="alert success callout surface">
213
+ <strong>On the page surface</strong>
214
+ <p>The flag keeps the intent, the panel drops the tint.</p>
171
215
  </div>
172
216
  </div>
173
217
  ```
174
218
 
219
+ Tune the flag with `--alert-border-inline-start-width` and
220
+ `--alert-border-inline-start-color`; `--alert-radius: 0` turns it into a
221
+ full-bleed notice.
222
+
223
+ ```html demo
224
+ <div class="alert danger callout" style="--alert-radius: 0">
225
+ <i class="ti ti-user-x alert-icon" aria-hidden="true"></i>
226
+ <div>Your request to join the team is denied.</div>
227
+ </div>
228
+ ```
229
+
175
230
  ## Admonition
176
231
 
177
232
  An admonition is a structured box with a tinted title bar, an optional icon, and body content on the page surface — like the `!!! note` callouts in mkdocs. Use `.alert-title` for the header and `.alert-body` for the content. Intent classes tint the title bar background and the border.
@@ -202,7 +257,7 @@ An admonition is a structured box with a tinted title bar, an optional icon, and
202
257
 
203
258
  ## CSS hooks
204
259
 
205
- - `--alert-pad-inline` — inline padding. Stays stable across `.sm`/`.lg`.
260
+ - `--alert-pad-inline` — inline padding. Stays stable across density contexts.
206
261
  - `--alert-pad-block` — block padding.
207
262
  - `--alert-font-size` — base font size.
208
263
  - `--alert-icon-size` — size of a leading `.alert-icon`.
@@ -5,6 +5,12 @@
5
5
  Use one `.app-nav` landmark for the application's primary destinations. Links
6
6
  remain links, and `aria-current="page"` is the only current-page state.
7
7
 
8
+ Reach for `.app-nav` only as part of a persistent application shell: it exists
9
+ as a direct child of `.app-layout`, which turns the landmark from a bottom bar
10
+ into side navigation. A site or webapp header with normal scrolling stays
11
+ `.navbar` — `.app-nav` is not a stacked-icon variant of the horizontal bar. See
12
+ [Navbar](navbar.md).
13
+
8
14
  **Related terms:** tab bar, bottom navigation, bottom nav, navigation rail, side navigation.
9
15
 
10
16
  ```html demo
@@ -12,12 +12,12 @@
12
12
 
13
13
  ## Class reference
14
14
 
15
- | Class | Kind | Description |
16
- | --------------- | ----------- | --------------------------------------- |
17
- | `.avatar` | Component | Initials or image in a circular box. |
18
- | `.avatar-stack` | Composition | Overlapping avatar group. |
19
- | `.badge:empty` | Composition | Status dot, at the bottom-end corner. |
20
- | `.sm` / `.lg` | Size | Density; also rescales `.avatar-stack`. |
15
+ | Class | Kind | Description |
16
+ | --------------- | ----------- | -------------------------------------------- |
17
+ | `.avatar` | Component | Initials or image in a circular box. |
18
+ | `.avatar-stack` | Composition | Overlapping avatar group. |
19
+ | `.badge:empty` | Composition | Status dot, at the bottom-end corner. |
20
+ | `.sm` / `.lg` | Size | Optical size; also rescales `.avatar-stack`. |
21
21
 
22
22
  `.avatar` works on `div`, `a` and `button`. The status dot is an empty
23
23
  `.badge` inside an `.avatar`, and needs an `aria-label` of its own.
@@ -4,17 +4,18 @@
4
4
 
5
5
  - Supports intent colors.
6
6
  - Soft by default. Use `.solid` for counters and notification badges, or `.outline` for quieter emphasis.
7
- - Dots stay solid regardless of variant.
8
- - Use `.sm` or `.lg` for compact density changes.
7
+ - Empty badges are solid status dots: visual variants such as `.soft` and `.outline` do not change their fill.
8
+ - Use `.sm` or `.lg` to scale the badge locally.
9
+ - Use `.compact` or `.spacious` on a context to change geometry without changing the label or icon size.
9
10
  - Can be used inline in headings.
10
11
  - Can be used as a count badge.
11
12
  - Can be used as a stable dot badge when empty and given an accessible name.
12
13
  - Empty badges render as dots. The element must be truly empty: no text and no whitespace.
13
14
 
14
15
  A badge is **content-sized**: it never stretches to fill its container, even as a
15
- direct child of `.stack` (where flex children otherwise stretch). Use
16
- `inline-size: 100%` — or the optional `.inline-size-full` utility — when a
17
- full-width badge is intentional.
16
+ direct child of `.stack` (where flex children otherwise stretch), and a flex
17
+ parent never compresses it (`flex: none`). Use `inline-size: 100%` — or the
18
+ optional `.inline-size-full` utility — when a full-width badge is intentional.
18
19
 
19
20
  **Related terms:** chip, tag, pill, removable tag.
20
21
 
@@ -24,9 +25,10 @@ full-width badge is intentional.
24
25
  | --------------- | ----------- | ----------------------------------------- |
25
26
  | `.badge` | Component | Compact label; soft by default. |
26
27
  | `.badge:empty` | Composition | Truly-empty badge renders as a solid dot. |
28
+ | `.dot` | Composition | Status dot inside a badge. |
27
29
  | Shared intents | Intent | `.primary`, `.secondary`, `.success`, … |
28
30
  | Shared variants | Variant | `.solid`, `.outline`, `.surface`. |
29
- | `.sm` / `.lg` | Size | Compact density. |
31
+ | `.sm` / `.lg` | Size | Smaller or larger optical scale. |
30
32
 
31
33
  The shared intents are `.primary`, `.secondary`, `.success`, `.warning` and
32
34
  `.danger`. The variants set emphasis: `.solid` is filled, `.outline` bordered,
@@ -42,6 +44,9 @@ The shared intents are `.primary`, `.secondary`, `.success`, `.warning` and
42
44
  <span class="badge danger">Danger</span>
43
45
  <span class="badge success outline">Outline success</span>
44
46
  <span class="badge warning soft">Soft warning</span>
47
+ <span class="badge primary solid">Solid primary</span>
48
+ <span class="badge success solid">Solid success</span>
49
+ <span class="badge danger solid">Solid danger</span>
45
50
  ```
46
51
 
47
52
  ## In a title
@@ -73,6 +78,29 @@ The shared intents are `.primary`, `.secondary`, `.success`, `.warning` and
73
78
  An autonomous dot conveys state without visible text, so give it an accessible
74
79
  name.
75
80
 
81
+ For a dot beside a label, put the shared `.dot` utility inside the badge. It
82
+ paints with `currentColor`, so it follows the badge ink instead of needing the
83
+ intent class repeated on it.
84
+
85
+ ```html demo
86
+ <span class="badge warning">
87
+ <span class="dot" aria-hidden="true"></span>
88
+ In progress
89
+ </span>
90
+ <span class="badge danger">
91
+ <span class="dot" aria-hidden="true"></span>
92
+ Blocked
93
+ </span>
94
+ <span class="badge success">
95
+ <span class="dot" aria-hidden="true"></span>
96
+ Completed
97
+ </span>
98
+ ```
99
+
100
+ Custom status or category dots define a custom intent (`--intent` /
101
+ `--intent-fg`); `--ui-*` controls treatment, while empty dots intentionally
102
+ remain solid.
103
+
76
104
  ## Color marker
77
105
 
78
106
  Use an empty badge as a compact intent-colored marker in legends and status
@@ -87,9 +115,64 @@ Regular
87
115
  ## Size variants
88
116
 
89
117
  ```html demo
90
- <span class="badge success sm">Small Success</span>
91
- <span class="badge success">Regular Success</span>
92
- <span class="badge success lg">Large Success</span>
118
+ <span class="badge success sm">Small</span>
119
+ <span class="badge success">Default</span>
120
+ <span class="badge success lg">Large</span>
121
+ ```
122
+
123
+ ## Density
124
+
125
+ Density changes the pill geometry without scaling its label or icon.
126
+
127
+ ```html demo
128
+ <span class="compact"><span class="badge success">Compact</span></span>
129
+ <span><span class="badge success">Default</span></span>
130
+ <span class="spacious"><span class="badge success">Spacious</span></span>
131
+ ```
132
+
133
+ ## With an icon
134
+
135
+ Decorative SVG, image, or `[aria-hidden="true"]` children follow
136
+ `--badge-icon-size`.
137
+
138
+ ```html demo
139
+ <span class="badge success">
140
+ <i class="ti ti-rosette-discount-check" aria-hidden="true"></i>
141
+ Verified
142
+ </span>
143
+ <span class="badge warning">
144
+ <i class="ti ti-clock" aria-hidden="true"></i>
145
+ Pending
146
+ </span>
147
+ <span class="badge danger solid">
148
+ <i class="ti ti-alert-circle" aria-hidden="true"></i>
149
+ Failed
150
+ </span>
151
+ ```
152
+
153
+ ## Loading status
154
+
155
+ A decorative spinner follows the badge color and optical size.
156
+
157
+ ```html demo
158
+ <span class="badge danger sm">
159
+ <span class="spinner" aria-hidden="true"></span>
160
+ Deleting
161
+ </span>
162
+ <span class="badge sm">
163
+ Generating
164
+ <span class="spinner" aria-hidden="true"></span>
165
+ </span>
166
+ ```
167
+
168
+ ## Custom sizing
169
+
170
+ Override the existing hooks when the three-step scale does not fit the content.
171
+
172
+ ```html demo
173
+ <span class="badge primary" style="--badge-size: 2rem; --badge-font-size: var(--font-size-md);">
174
+ Prominent
175
+ </span>
93
176
  ```
94
177
 
95
178
  ## Removable tag pattern
@@ -98,17 +181,17 @@ Use `.badge soft` for tag visuals. Add a direct dismiss button only when the tag
98
181
  For a compact action or filter, use `.btn.sm` instead; badges describe content
99
182
  and must not be turned into toggle controls merely to obtain a compact shape.
100
183
 
184
+ Leave the dismiss button empty: it paints its own X, so the pattern needs no
185
+ icon font and no text glyph. Put your own icon inside it and that content is
186
+ used instead.
187
+
101
188
  ```html demo
102
189
  <span class="badge primary soft">
103
190
  Design
104
- <button type="button" aria-label="Remove Design">
105
- <i class="ti ti-x" aria-hidden="true"></i>
106
- </button>
191
+ <button type="button" aria-label="Remove Design"></button>
107
192
  </span>
108
193
  <span class="badge primary soft">
109
- <button type="button" aria-label="Remove Design">
110
- <i class="ti ti-x" aria-hidden="true"></i>
111
- </button>
194
+ <button type="button" aria-label="Remove Design"></button>
112
195
  Design
113
196
  </span>
114
197
  ```
@@ -119,4 +202,6 @@ and must not be turned into toggle controls merely to obtain a compact shape.
119
202
  - `--badge-size` — minimum block size; also the square size of an `:empty` dot badge.
120
203
  - `--badge-dot-size` — size of an `:empty` dot badge.
121
204
  - `--badge-font-size` — label font size.
122
- - `--badge-pad-x` — inline padding.
205
+ - `--badge-icon-size` — decorative child size; `1em` follows the label by default.
206
+ - `--badge-pad-x` — inline padding. Derived from `--badge-size`, so it stays proportional to the pill; set it to opt out.
207
+ - `--badge-dismiss-icon-size` — size of the X painted by an empty dismiss button.
@@ -110,21 +110,29 @@ Always name it with an `aria-label` since there is no visible text.
110
110
 
111
111
  ### Button groups
112
112
 
113
- Join adjacent buttons with `.join`. The joined group is not a new component —
114
- the buttons keep their own variants.
113
+ Join adjacent buttons with `.join`. The joined group is not a new component:
114
+ each button keeps its own classes, and `.join` only reshapes the outer corners
115
+ and collapses the shared edges.
115
116
 
116
117
  ```html demo
117
- <div class="join" role="group" aria-label="Text alignment">
118
- <button class="btn" type="button">Left</button>
119
- <button class="btn outline" type="button">Center</button>
120
- <button class="btn soft" type="button">Right</button>
118
+ <div class="join" role="group" aria-label="Row actions">
119
+ <button class="btn outline" type="button">Edit</button>
120
+ <button class="btn outline" type="button">Duplicate</button>
121
+ <button class="btn outline" type="button">Archive</button>
121
122
  </div>
122
123
  ```
123
124
 
125
+ Keep one variant family inside a group. Because each button keeps its own
126
+ classes, nothing stops `.btn`, `.btn outline` and `.btn soft` from sitting side
127
+ by side — but three fills and three border treatments in one shape read as
128
+ unrelated controls that happen to touch, not as one unit. Change the variant
129
+ only to carry meaning: [Toggle](#toggle) marks the active segment with a solid
130
+ button among outlines, which is the pattern a segmented control wants.
131
+
124
132
  ## Sizes
125
133
 
126
- Prefer `.sm` and `.lg` over setting sizes directly. Density variants never
127
- change typography.
134
+ Prefer `.sm` and `.lg` over setting sizes directly. They scale control geometry
135
+ and typography together; `.compact` and `.spacious` only change density.
128
136
 
129
137
  ```html demo
130
138
  <div class="cluster">
@@ -4,14 +4,14 @@ Flexible content container with optional header, body, and footer regions, plus
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- | ----------- | ---------------- | ------------------------------------------- |
9
- | `.card` | Component | Neutral raised surface for grouped content. |
10
- | `.raised` | Variant | Elevated surface with a soft shadow. |
11
- | `.subtle` | Variant | Lower contrast against the page surface. |
12
- | `.surface` | Variant | The page surface; intent in the text only. |
13
- | `.inverted` | Surface modifier | Inverse surface treatment. |
14
- | `.compact` | Variant | Tighter padding for dense contexts. |
7
+ | Class | Kind | Description |
8
+ | ----------- | ---------------- | ----------------------------------------------- |
9
+ | `.card` | Component | Neutral raised surface for grouped content. |
10
+ | `.raised` | Variant | Elevated surface with a soft shadow. |
11
+ | `.subtle` | Variant | Lower contrast against the page surface. |
12
+ | `.surface` | Variant | The page surface; intent in the text only. |
13
+ | `.inverted` | Surface modifier | Inverse surface treatment. |
14
+ | `.compact` | Density | Tighter padding and compact descendant context. |
15
15
 
16
16
  `.surface` is a shared variant: it paints the page surface instead of
17
17
  `--surface-raised` and leaves the intent to the text.
@@ -88,7 +88,7 @@ colored headers, or footers.
88
88
  <p class="muted">For growing products</p>
89
89
  </hgroup>
90
90
  <p>
91
- <span style="font-size: 2rem; font-weight: var(--font-weight-strong); line-height: 1">$24</span>
91
+ <span style="font-size: 2rem; font-weight: var(--font-weight-semibold); line-height: 1">$24</span>
92
92
  <span class="muted">/ user / month</span>
93
93
  </p>
94
94
  </header>
@@ -220,4 +220,4 @@ treatments on either side of it.
220
220
  - `--card-radius` — corner radius.
221
221
  - `--card-max-inline-size` — maximum width.
222
222
  - `--card-pad` — inner padding; also drives the negative offsets that let a `.bleed` child reach the card edge. `.compact` lowers it.
223
- - `--card-gap` — space between direct children of a bare card. A composed layout primitive owns its own gap instead. `.compact` does not change this hook.
223
+ - `--card-gap` — space between direct children of a bare card. A composed layout primitive owns its own gap instead.
@@ -167,7 +167,13 @@ Use `modal scrollable` when the header and footer should stay visible while the
167
167
  dialog body scrolls. Modal dialogs lock page scroll while a modal is open: the
168
168
  runtime writes `html.has-modal-open`, and the stylesheet applies `overflow:
169
169
  hidden` (and reserves the scrollbar gutter with `scrollbar-gutter: stable` when
170
- one was present).
170
+ one was present). The height cap follows the small viewport, so the panel keeps
171
+ a stable size while mobile browser chrome expands or collapses.
172
+
173
+ The scrolling body is a scroll container, and browsers make one
174
+ keyboard-focusable so it can be scrolled without a pointer — it takes a focus
175
+ ring like any other focusable element, and it can claim initial focus when
176
+ nothing focusable precedes it. Mark the control you want focused `autofocus`.
171
177
 
172
178
  ```html demo
173
179
  <button class="btn"
@@ -213,8 +219,15 @@ one was present).
213
219
 
214
220
  ## Overlays inside modals
215
221
 
216
- Flyouts and tooltips opened from inside a modal dialog are mounted inside the
217
- dialog so they stay in the same top-layer context.
222
+ Flyouts and tooltips opened from inside a modal dialog stay exactly where they
223
+ are authored. The runtime promotes them to the top layer instead of moving
224
+ them, which is what keeps them inside every scope that reaches them by
225
+ inheritance.
226
+
227
+ Author them inside the dialog. Promotion paints an element above a modal, but
228
+ it does not lift it out of the modal's inertness — that is computed on the DOM,
229
+ not on paint order — so an overlay authored outside the dialog would be
230
+ promoted above it and still unclickable.
218
231
 
219
232
  ```html demo
220
233
  <button class="btn"
@@ -295,7 +308,7 @@ Available options:
295
308
  ## Browser support
296
309
 
297
310
  Actual relies on the native `<dialog>` API in its supported JavaScript range
298
- (Safari 15.4+, Firefox 98+, Chromium 99+). No dialog polyfill or fallback shim
311
+ (Safari 17+, Firefox 125+, Chromium 116+). No dialog polyfill or fallback shim
299
312
  is shipped.
300
313
 
301
314
  ## Animation
@@ -306,7 +319,7 @@ remain in the top layer while `display` and `overlay` transition out. Browsers
306
319
  without that support keep native close behavior.
307
320
 
308
321
  The open dialog root intentionally ends at `transform: none`; fixed flyouts and
309
- tooltips mounted inside a modal dialog rely on viewport coordinates.
322
+ tooltips authored inside a modal dialog rely on viewport coordinates.
310
323
 
311
324
  ### View transition
312
325
 
@@ -362,7 +375,9 @@ Keep the button a direct child of `dialog.modal` so the scrolling content
362
375
 
363
376
  ## CSS hooks
364
377
 
365
- - `--modal-size` — maximum dialog width.
378
+ - `--modal-size` — maximum dialog width. Fallback-only, so a class on the
379
+ dialog or an inherited value both reach it. A dialog is shrink-to-fit: a
380
+ composition that must fill this width sets `inline-size` as well.
366
381
  - `--dialog-viewport-gap` — distance kept between the dialog and the viewport edges.
367
382
  - `--dialog-icon-size` — diameter of the `.dialog-icon` circle.
368
383
  - `--dialog-icon-glyph-size` — size of the glyph centered inside `.dialog-icon`.
@@ -21,8 +21,21 @@
21
21
  - Use `closedby="none"` when the drawer must not be dismissed by the user at all
22
22
  (e.g. a form with unsaved changes): no backdrop close and no Escape close.
23
23
  - Use `[data-side="end"]` for a right-side drawer.
24
+ - Put the body between a `header` and a `footer`: the panel is a three-row grid
25
+ whose middle region scrolls, so the footer stays reachable however long the
26
+ content gets.
24
27
  - Permanent desktop sidebars belong in layout, not here.
25
28
 
29
+ The panel is `--viewport-block` tall, so it follows the space a mobile browser
30
+ actually leaves on screen as its URL bar collapses. A shell that fakes a
31
+ viewport (an embedded preview) overrides that token rather than the drawer.
32
+
33
+ The body region is a scroll container, and browsers make one keyboard-focusable
34
+ so it can be scrolled without a pointer — it takes a focus ring like any other
35
+ focusable element. `showModal()` focuses the first focusable thing in the
36
+ drawer, so put the close button in the `header` (as both examples do) or mark
37
+ your intended target `autofocus`; otherwise the body claims initial focus.
38
+
26
39
  ```html demo
27
40
  <button class="btn ghost"
28
41
  type="button"
@@ -96,7 +96,7 @@ does not close automatically after an action, on outside click, or on Escape;
96
96
  add application behavior only when the product requires those policies.
97
97
 
98
98
  Each `.fab-action` keeps its `.fab-label` and secondary button on one compact
99
- line. The example uses the button's existing `.lg` density for 44px secondary
99
+ line. The example uses the button's existing `.lg` size for 44px secondary
100
100
  actions and its `--btn-min-size` hook for a dominant 56px trigger. Size still
101
101
  belongs to Button rather than introducing FAB-specific size modifiers.
102
102
  Unlike the plain FAB, the speed dial trigger takes no icon: FAB owns it as a
@@ -120,5 +120,6 @@ two states stay visually identical in size and stroke.
120
120
  - `--fab-gap` — gap between actions and between the trigger and action list.
121
121
  - `--fab-icon-size` — size of the speed dial toggle's open and close markers.
122
122
 
123
- FAB is hidden in print and uses `--z-menu`, leaving tooltips and status UI above
124
- it. Dialogs remain in the browser's top layer.
123
+ FAB uses `--z-menu`, leaving `status-bar` above it on the scale. Open flyouts
124
+ and tooltips are promoted to the top layer and paint above it whatever their
125
+ number. Dialogs remain in the browser's top layer.
@@ -29,9 +29,15 @@ list markers when order or bullets carry meaning.
29
29
  ```
30
30
 
31
31
  Leading and trailing content are unconstrained: avatars, native choices,
32
- badges, text, switches, and buttons retain their own component sizing. The
33
- middle region has no minimum content floor, so supporting text and long tokens
34
- wrap instead of widening the page.
32
+ badges, text, switches, and buttons retain their own component sizing.
33
+ `.list-item-content` has no minimum content floor, so supporting text and long
34
+ tokens wrap instead of widening the page.
35
+
36
+ Both outer regions are optional, and an absent one costs neither space nor a
37
+ gap: `.list-item-content` always takes the row's free space and
38
+ `.list-item-trailing` always sits on the end edge, whether the row has a
39
+ leading region or not. A settings row — a label, a description, and one
40
+ control on the end edge — is the two-region form.
35
41
 
36
42
  Regions align to the row's first line: the leading check or avatar, the title,
37
43
  and the trailing metadata or control sit on one horizontal line while
@@ -42,6 +48,30 @@ A pure traversal affordance such as a trailing caret can opt out of the
42
48
  first-line rule on its own item with `align-self: center`, keeping metadata
43
49
  rows unaffected.
44
50
 
51
+ ```html demo
52
+ <ul class="list">
53
+ <li class="list-item">
54
+ <label class="list-item-content" for="row-memory">
55
+ <span class="list-item-title">Memory</span>
56
+ <span class="list-item-text">Remember details across conversations.</span>
57
+ </label>
58
+ <span class="list-item-trailing">
59
+ <input class="switch" type="checkbox" role="switch" id="row-memory" checked>
60
+ </span>
61
+ </li>
62
+ <li class="list-item">
63
+ <label class="list-item-content list-item-title" for="row-theme">Theme</label>
64
+ <span class="list-item-trailing">
65
+ <select class="select sm fit" id="row-theme">
66
+ <option>System</option>
67
+ <option>Light</option>
68
+ <option>Dark</option>
69
+ </select>
70
+ </span>
71
+ </li>
72
+ </ul>
73
+ ```
74
+
45
75
  For a navigable row, keep the list semantics and put `.list-item` on the link:
46
76
 
47
77
  ```html demo
@@ -62,10 +92,18 @@ For a navigable row, keep the list semantics and put `.list-item` on the link:
62
92
  Do not attach click JavaScript to the entire row. Use a link for navigation and
63
93
  a button or native control for actions.
64
94
 
95
+ In a master/detail navigation, mark the current row with `aria-current` on its
96
+ link, using the value that matches the semantics (`page`, `location`, …).
97
+ Presence is the contract: set the attribute on the current row and remove it
98
+ elsewhere, never `aria-current="false"`. The [Workspaces example](../examples/overview.md)
99
+ shows a current row with trailing actions and a flyout. Navigable rows usually
100
+ want a non-zero `--list-item-pad-inline`: without that gutter, the current
101
+ indicator touches the leading region and the trailing touches the row edge.
102
+
65
103
  ### Hooks
66
104
 
67
105
  - `--list-item-min-size` controls the row's minimum touch height.
68
106
  - `--list-item-pad-block` and `--list-item-pad-inline` control row padding.
69
- - `--list-item-gap` controls spacing between the three regions.
107
+ - `--list-item-gap` controls spacing between the regions a row actually has.
70
108
  - `--list-divider` controls the leading and inter-row dividers. The final row
71
109
  deliberately has no trailing divider.