actual-css 0.2.0 → 0.3.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 (214) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/README.md +24 -9
  3. package/dist/actual-themes.min.css +1 -0
  4. package/dist/actual.css +1244 -915
  5. package/dist/actual.js +1 -3
  6. package/dist/actual.min.css +1 -1
  7. package/dist/optional.css +1048 -0
  8. package/dist/optional.min.css +1 -0
  9. package/docs/design-notes/browser-support.md +94 -14
  10. package/docs/design-notes/cascade-layer.md +1 -1
  11. package/docs/design-notes/enhancement-contract.md +11 -14
  12. package/docs/design-notes/forced-colors.md +105 -0
  13. package/docs/design-notes/naming.md +2 -1
  14. package/docs/design-notes/platform-alignment.md +492 -0
  15. package/docs/design-notes/references.md +8 -0
  16. package/docs/design-notes/widget-primitives.md +37 -1
  17. package/docs/navigation.json +135 -0
  18. package/docs/pages/components/accordion.md +80 -0
  19. package/docs/pages/components/alert.md +203 -0
  20. package/docs/pages/components/aura.md +35 -0
  21. package/docs/pages/components/avatar.md +95 -0
  22. package/docs/pages/components/badge.md +102 -0
  23. package/docs/pages/components/breadcrumb.md +19 -0
  24. package/docs/pages/components/button.md +205 -0
  25. package/docs/pages/components/card.md +183 -0
  26. package/docs/pages/components/chat.md +50 -0
  27. package/docs/pages/components/dialog.md +367 -0
  28. package/docs/pages/components/drawer.md +113 -0
  29. package/docs/pages/components/fab.md +118 -0
  30. package/docs/pages/components/key.md +33 -0
  31. package/docs/pages/components/meter.md +23 -0
  32. package/docs/pages/components/navbar.md +92 -0
  33. package/docs/pages/components/pagination.md +55 -0
  34. package/docs/pages/components/progress.md +29 -0
  35. package/docs/pages/components/skeleton.md +38 -0
  36. package/docs/pages/components/spinner.md +93 -0
  37. package/docs/pages/components/table.md +102 -0
  38. package/docs/pages/enhancements/flyout.md +391 -0
  39. package/docs/pages/enhancements/overview.md +36 -0
  40. package/docs/pages/enhancements/scrollspy.md +71 -0
  41. package/docs/pages/enhancements/status-bar.md +72 -0
  42. package/docs/pages/enhancements/tabs.md +80 -0
  43. package/docs/pages/enhancements/tooltip.md +130 -0
  44. package/docs/pages/examples/overview.md +41 -0
  45. package/docs/pages/forms/choice-cards.md +102 -0
  46. package/docs/pages/forms/controls.md +32 -0
  47. package/docs/pages/forms/detached-actions.md +59 -0
  48. package/docs/pages/forms/floating-field.md +88 -0
  49. package/docs/pages/forms/form-actions.md +56 -0
  50. package/docs/pages/forms/input-policy.md +91 -0
  51. package/docs/pages/forms/joined-controls.md +37 -0
  52. package/docs/pages/forms/labels.md +42 -0
  53. package/docs/pages/forms/otp.md +50 -0
  54. package/docs/pages/forms/overview.md +188 -0
  55. package/docs/pages/forms/password.md +40 -0
  56. package/docs/pages/forms/range.md +17 -0
  57. package/docs/pages/forms/states-and-sizes.md +321 -0
  58. package/docs/pages/forms/sticky-actions.md +187 -0
  59. package/docs/pages/forms/switch.md +31 -0
  60. package/docs/pages/forms/validation.md +241 -0
  61. package/docs/{mental-model.md → pages/foundations/philosophy.md} +33 -18
  62. package/docs/pages/foundations/theming.md +92 -0
  63. package/docs/pages/foundations/tokens.md +421 -0
  64. package/docs/{typography.md → pages/foundations/typography.md} +27 -9
  65. package/docs/pages/guides/bootstrap.md +438 -0
  66. package/docs/pages/guides/introduction.md +157 -0
  67. package/docs/pages/guides/optional-components.md +33 -0
  68. package/docs/pages/guides/progressive-enhancement.md +686 -0
  69. package/docs/pages/guides/tailwind.md +455 -0
  70. package/docs/pages/layout/app-shell.md +25 -0
  71. package/docs/pages/layout/center.md +29 -0
  72. package/docs/pages/layout/cluster.md +57 -0
  73. package/docs/pages/layout/frame.md +30 -0
  74. package/docs/pages/layout/grid.md +142 -0
  75. package/docs/pages/layout/list.md +41 -0
  76. package/docs/pages/layout/media.md +21 -0
  77. package/docs/pages/layout/overview.md +37 -0
  78. package/docs/pages/layout/scroll-snap.md +54 -0
  79. package/docs/pages/layout/sidebar-layout.md +27 -0
  80. package/docs/pages/layout/stack.md +18 -0
  81. package/docs/pages/layout/switcher.md +36 -0
  82. package/docs/pages/layout/topbar.md +23 -0
  83. package/docs/pages/patterns/actions.md +52 -0
  84. package/docs/pages/patterns/code-block.md +36 -0
  85. package/docs/pages/patterns/exclusive-accordion.md +16 -0
  86. package/docs/pages/patterns/hierarchical-nav.md +26 -0
  87. package/docs/pages/patterns/icon-input.md +25 -0
  88. package/docs/pages/patterns/intent-nesting.md +3 -0
  89. package/docs/pages/patterns/navigation-list.md +235 -0
  90. package/docs/pages/patterns/overview.md +58 -0
  91. package/docs/pages/patterns/structured-lists.md +47 -0
  92. package/docs/pages/utilities/accessibility.md +34 -0
  93. package/docs/pages/utilities/flex.md +105 -0
  94. package/docs/pages/utilities/links.md +18 -0
  95. package/docs/pages/utilities/list.md +31 -0
  96. package/docs/pages/utilities/overflow.md +43 -0
  97. package/docs/pages/utilities/overview.md +18 -0
  98. package/docs/pages/utilities/shape.md +25 -0
  99. package/docs/pages/utilities/sizing-wrapping.md +115 -0
  100. package/docs/pages/utilities/spacing.md +79 -0
  101. package/docs/pages/utilities/text.md +43 -0
  102. package/llms.txt +4 -4
  103. package/package.json +20 -12
  104. package/src/css/actual.css +12 -1
  105. package/src/css/base.css +3 -5
  106. package/src/css/components/accordion.css +18 -30
  107. package/src/css/components/actions.css +4 -1
  108. package/src/css/components/alert.css +126 -99
  109. package/src/css/components/avatar.css +16 -10
  110. package/src/css/components/badge.css +55 -35
  111. package/src/css/components/breadcrumb.css +7 -15
  112. package/src/css/components/busy.css +6 -0
  113. package/src/css/components/button.css +78 -32
  114. package/src/css/components/card.css +17 -37
  115. package/src/css/components/drawer.css +30 -13
  116. package/src/css/components/flyout.css +39 -109
  117. package/src/css/components/index.css +1 -0
  118. package/src/css/components/join.css +14 -2
  119. package/src/css/components/key.css +2 -0
  120. package/src/css/components/menu.css +125 -0
  121. package/src/css/components/meter.css +0 -23
  122. package/src/css/components/modal.css +162 -7
  123. package/src/css/components/navbar.css +17 -18
  124. package/src/css/components/overline.css +0 -7
  125. package/src/css/components/pagination.css +1 -1
  126. package/src/css/components/progress.css +8 -26
  127. package/src/css/components/skeleton.css +7 -0
  128. package/src/css/components/spinner.css +6 -7
  129. package/src/css/components/status-bar.css +15 -24
  130. package/src/css/components/surface.css +2 -8
  131. package/src/css/components/tab.css +19 -48
  132. package/src/css/components/table.css +7 -5
  133. package/src/css/components/tooltip.css +14 -7
  134. package/src/css/focus.css +14 -38
  135. package/src/css/forms/choice-card.css +13 -14
  136. package/src/css/forms/choice.css +21 -27
  137. package/src/css/forms/control.css +16 -17
  138. package/src/css/forms/custom-select.css +9 -11
  139. package/src/css/forms/form-actions.css +14 -3
  140. package/src/css/forms/form.css +7 -4
  141. package/src/css/forms/index.css +4 -4
  142. package/src/css/forms/input-icon.css +30 -12
  143. package/src/css/forms/native.css +50 -11
  144. package/src/css/forms/range.css +17 -9
  145. package/src/css/forms/select.css +8 -8
  146. package/src/css/forms/switch.css +14 -12
  147. package/src/css/forms/validation.css +32 -5
  148. package/src/css/grid.css +19 -18
  149. package/src/css/intents.css +2 -2
  150. package/src/css/layout.css +29 -1
  151. package/src/css/optional/aura.css +93 -0
  152. package/src/css/optional/chat.css +105 -0
  153. package/src/css/optional/fab.css +104 -0
  154. package/src/css/optional/floating-field.css +87 -0
  155. package/src/css/optional/index.css +5 -0
  156. package/src/css/optional/layout-extra.css +7 -4
  157. package/src/css/optional/otp.css +115 -0
  158. package/src/css/optional/scroll-snap.css +6 -0
  159. package/src/css/optional/scroller.css +13 -7
  160. package/src/css/optional/typography-fluid.css +5 -0
  161. package/src/css/optional/utilities-extra.css +17 -2
  162. package/src/css/prose.css +18 -8
  163. package/src/css/reset.css +0 -10
  164. package/src/css/theme.css +21 -5
  165. package/src/css/themes/cyberpunk.css +1 -1
  166. package/src/css/themes/dim.css +3 -3
  167. package/src/css/themes/edge.css +8 -10
  168. package/src/css/themes/index.css +3 -3
  169. package/src/css/themes/{dark.css → indigo.css} +5 -4
  170. package/src/css/themes/neon.css +5 -5
  171. package/src/css/themes/square.css +0 -3
  172. package/src/css/tokens.css +74 -20
  173. package/src/css/utilities.css +8 -10
  174. package/src/css/variants.css +37 -65
  175. package/src/js/command.js +1 -3
  176. package/src/js/context-menu.js +67 -35
  177. package/src/js/dialog.js +68 -79
  178. package/src/js/dismiss.js +15 -0
  179. package/src/js/enhance.js +54 -23
  180. package/src/js/enhancement-loader.js +301 -0
  181. package/src/js/events.js +3 -0
  182. package/src/js/filter.js +3 -3
  183. package/src/js/floating.js +101 -131
  184. package/src/js/flyout.js +104 -87
  185. package/src/js/focus-group.js +116 -0
  186. package/src/js/focus.js +6 -1
  187. package/src/js/index.js +4 -2
  188. package/src/js/input.js +1 -1
  189. package/src/js/keys.js +27 -0
  190. package/src/js/mask.js +7 -6
  191. package/src/js/menu.js +87 -34
  192. package/src/js/parse-config.js +47 -0
  193. package/src/js/password.js +9 -1
  194. package/src/js/scrollspy.js +12 -4
  195. package/src/js/selectors.js +0 -7
  196. package/src/js/status.js +3 -1
  197. package/src/js/surface.js +185 -136
  198. package/src/js/tab.js +36 -53
  199. package/src/js/tooltip.js +53 -31
  200. package/src/js/validation.js +34 -8
  201. package/dist/actual.js.map +0 -31
  202. package/docs/README.md +0 -28
  203. package/docs/components.md +0 -860
  204. package/docs/design-notes/previous-work.md +0 -5
  205. package/docs/forms.md +0 -1265
  206. package/docs/javascript.md +0 -496
  207. package/docs/layout.md +0 -480
  208. package/docs/patterns.md +0 -504
  209. package/docs/reserved.md +0 -7
  210. package/docs/tokens.md +0 -455
  211. package/docs/ui.md +0 -929
  212. package/docs/utilities.md +0 -384
  213. package/src/css/dialog-fallback.css +0 -56
  214. package/src/js/dialog-fallback.js +0 -163
@@ -7,42 +7,48 @@
7
7
  * --alert-border-inline-start-color/-width are unset extension points, mirroring
8
8
  * --btn-border-block-end-* (button.css), for themes that give alerts a
9
9
  * callout-style colored flag on the leading edge instead of a uniform border.
10
+ *
11
+ * Public hooks:
12
+ * --alert-pad-block
13
+ * --alert-pad-inline
14
+ * --alert-font-size
15
+ * --alert-radius
16
+ * --alert-icon-size
17
+ * --alert-dismiss-size
18
+ *
19
+ * Internal:
20
+ * --alert-bg
21
+ * --alert-fg
22
+ * --alert-border
23
+ * --alert-dismiss-icon-size
10
24
  */
11
25
  /* Intent boundary — inherited intents do not tint this component.
12
26
  Local intent classes still win because :where() has zero specificity. */
13
27
  :where(.alert) {
14
- /* @sync intent-boundary */
15
28
  --intent: initial;
16
29
  --intent-fg: initial;
17
30
  --ui-bg: initial;
18
31
  --ui-fg: initial;
19
32
  --ui-border: initial;
20
- --ui-hover-bg: initial;
21
- /* @sync intent-boundary */
22
33
  }
23
34
 
24
35
  .alert {
25
- --alert-pad-block: var(--variant-pad-block, var(--space-3));
26
- --alert-pad-inline: var(--space-4);
27
- --alert-font-size: var(--variant-font-size, var(--font-size-md));
28
- --alert-icon-size: var(--variant-icon-size, 1.25rem);
29
- --alert-bg: var(--surface-subtle);
30
- --alert-border: var(--border);
31
- --ui-bg: var(--alert-bg);
32
- --ui-border: var(--alert-border);
33
- --ui-fg: var(--intent, var(--text));
36
+ --alert-pad-block: var(--space-30);
37
+ --alert-pad-inline: var(--space-40);
38
+ --alert-font-size: var(--font-size-md);
39
+ --alert-icon-size: 1.25rem;
40
+ --alert-bg: var(--ui-bg, var(--surface-subtle));
41
+ --alert-fg: var(--ui-fg, var(--intent, var(--text)));
42
+ --alert-border: var(--ui-border, var(--border));
34
43
 
35
44
  padding-block: var(--alert-pad-block);
36
45
  padding-inline: var(--alert-pad-inline);
37
- background: var(--ui-bg, transparent);
38
- color: var(--ui-fg, var(--text));
39
- border: var(--border-width) solid var(--ui-border, var(--border));
40
- border-inline-start-color: var(
41
- --alert-border-inline-start-color,
42
- var(--ui-border, var(--border))
43
- );
46
+ background: var(--alert-bg);
47
+ color: var(--alert-fg);
48
+ border: var(--border-width) solid var(--alert-border);
49
+ border-inline-start-color: var(--alert-border-inline-start-color, var(--alert-border));
44
50
  border-inline-start-width: var(--alert-border-inline-start-width, var(--border-width));
45
- border-radius: var(--radius-lg);
51
+ border-radius: var(--alert-radius, var(--radius-lg));
46
52
  font-size: var(--alert-font-size);
47
53
  }
48
54
 
@@ -58,19 +64,8 @@
58
64
  margin-block-end: 0;
59
65
  }
60
66
 
61
- /* Alerts are soft by default. Use .solid or .outline for stronger emphasis. */
62
- .alert.solid {
63
- --ui-bg: var(--intent, var(--neutral));
64
- --ui-fg: var(--intent-fg, var(--neutral-fg));
65
- --ui-border: transparent;
66
- }
67
-
68
- .alert.outline {
69
- --ui-bg: transparent;
70
- --ui-fg: var(--intent, var(--text));
71
- --ui-border: var(--intent, var(--border));
72
- }
73
-
67
+ /* Alerts are soft by default. .solid and .outline need no local rules — the
68
+ shared variant plumbing in variants.css sets --ui-* that alert consumes. */
74
69
  @supports (color: color-mix(in oklch, red, white)) {
75
70
  .alert:where(:not(.solid, .outline, .callout)) {
76
71
  /* @sync soft-recipe */
@@ -84,8 +79,8 @@
84
79
  }
85
80
  }
86
81
 
87
- /* Decorative leading icon */
88
- .alert > :first-child[aria-hidden="true"] {
82
+ /* Decorative leading icon — use .alert-icon on the icon element. */
83
+ .alert > .alert-icon {
89
84
  display: inline-block;
90
85
  flex: none;
91
86
  inline-size: var(--alert-icon-size);
@@ -95,33 +90,84 @@
95
90
  vertical-align: -0.125em;
96
91
  }
97
92
 
93
+ /* Dismiss — compact close button for inline alerts. Transparent at rest with
94
+ a reduced opacity glyph so it stays secondary inside an already-colored
95
+ alert; hover reinforces it via the shared overlay. Uses the shared
96
+ --icon-close mask, so no icon font or text glyph is required in the markup.
97
+ Standard alerts place it as a direct trailing child; admonitions place it
98
+ inside .alert-title (see below). */
99
+ .alert-dismiss {
100
+ --alert-dismiss-size: 1.75rem;
101
+ --alert-dismiss-icon-size: 0.875rem;
102
+
103
+ display: inline-grid;
104
+ place-items: center;
105
+ inline-size: var(--alert-dismiss-size);
106
+ block-size: var(--alert-dismiss-size);
107
+ flex: none;
108
+ padding: 0;
109
+ border: 0;
110
+ border-radius: var(--radius);
111
+ background: transparent;
112
+ color: currentColor;
113
+ opacity: 0.65;
114
+ cursor: pointer;
115
+ }
116
+
117
+ .alert-dismiss::before {
118
+ content: "";
119
+ inline-size: var(--alert-dismiss-icon-size);
120
+ block-size: var(--alert-dismiss-icon-size);
121
+ background: currentColor;
122
+ -webkit-mask: var(--icon-close) center / contain no-repeat;
123
+ mask: var(--icon-close) center / contain no-repeat;
124
+ }
125
+
126
+ @media (hover: hover) {
127
+ .alert-dismiss:hover {
128
+ background: var(--hover-overlay);
129
+ opacity: 1;
130
+ }
131
+ }
132
+
133
+ /* Grid anatomy — activated by a leading icon, a trailing dismiss button, or
134
+ both. Icon and dismiss are pinned to the outer columns; anything else (the
135
+ message content) flows into the remaining text column. */
98
136
  @supports selector(.alert:has(> *)) {
99
- .alert:has(> :first-child[aria-hidden="true"]) {
137
+ .alert:has(> .alert-icon, > .alert-dismiss) {
100
138
  display: grid;
101
- grid-template-columns: auto minmax(0, 1fr);
139
+ grid-template-columns: minmax(0, 1fr);
102
140
  align-items: var(--items-align, start);
103
- column-gap: var(--space-3);
141
+ column-gap: var(--space-30);
142
+ }
143
+
144
+ .alert:has(> .alert-icon) {
145
+ grid-template-columns: auto minmax(0, 1fr);
104
146
  }
105
147
 
106
- .alert:has(> :first-child[aria-hidden="true"]) > :first-child {
148
+ .alert:has(> .alert-icon) > .alert-icon {
107
149
  grid-column: 1;
108
150
  grid-row: 1 / -1;
109
151
  display: inline-grid;
110
152
  place-items: center;
111
- inline-size: var(--alert-icon-size);
112
- block-size: var(--alert-icon-size);
113
153
  margin-block-start: 0.125em;
114
- font-size: var(--alert-icon-size);
115
- line-height: 1;
116
154
  }
117
155
 
118
- .alert:has(> :first-child[aria-hidden="true"]) > :first-child:is(svg, i) {
119
- inline-size: var(--alert-icon-size);
120
- block-size: var(--alert-icon-size);
156
+ .alert:has(> .alert-icon) > :not(.alert-icon, .alert-dismiss) {
157
+ grid-column: 2;
121
158
  }
122
159
 
123
- .alert:has(> :first-child[aria-hidden="true"]) > :not(:first-child) {
124
- grid-column: 2;
160
+ .alert:has(> .alert-dismiss) {
161
+ grid-template-columns: minmax(0, 1fr) auto;
162
+ }
163
+
164
+ .alert:has(> .alert-icon):has(> .alert-dismiss) {
165
+ grid-template-columns: auto minmax(0, 1fr) auto;
166
+ }
167
+
168
+ .alert:has(> .alert-dismiss) > .alert-dismiss {
169
+ grid-column: -2 / -1;
170
+ grid-row: 1;
125
171
  }
126
172
  }
127
173
 
@@ -136,88 +182,69 @@
136
182
  Canvas backplate (unreadable) — so .solid intentionally falls back to the
137
183
  default look. */
138
184
 
139
- /* Callout — neutral surface with an inset accent band on the leading edge.
140
- The ::before pseudo is absolutely positioned and inset to respect the
141
- outer border-radius, so it never needs its own radius. */
185
+ /* Callout — neutral surface with a thick accent border on the leading edge.
186
+ The thickness follows the shared --alert-border-inline-start-width hook. */
142
187
  .alert.callout {
143
188
  --alert-bg: var(--surface-subtle);
144
- --ui-bg: var(--alert-bg);
145
- --ui-fg: var(--text);
146
- --ui-border: var(--border);
147
- position: relative;
148
- padding-inline-start: calc(var(--alert-pad-inline) + var(--space-3));
149
- }
150
-
151
- .alert.callout::before {
152
- content: "";
153
- position: absolute;
154
- inset-block: var(--space-2);
155
- inset-inline-start: var(--space-2);
156
- inline-size: 0.25rem;
157
- border-radius: var(--radius-full);
158
- background: var(--intent, var(--neutral));
189
+ --alert-fg: var(--text);
190
+ border: 0;
191
+ border-inline-start: var(--alert-border-inline-start-width, 4px) solid
192
+ var(--alert-border-inline-start-color, var(--intent, var(--neutral)));
159
193
  }
160
194
 
161
- /* Admonition — structured title/body anatomy with a tinted title band.
162
- The root is layout-neutralized: no padding, no gap, no icon grid. */
195
+ /* Admonition — structured static box with a tinted title bar and a body
196
+ that rests on the page surface. Not collapsible — use .accordion inside
197
+ the body if the content needs to be expandable. */
163
198
  .alert.admonition {
164
199
  padding: 0;
165
- gap: 0;
166
200
  display: block;
167
201
  overflow: clip;
202
+ border-color: var(--intent, var(--border));
168
203
  }
169
204
 
170
- .alert-title {
205
+ .alert.admonition > .alert-title {
206
+ display: flex;
207
+ align-items: center;
208
+ gap: var(--space-20);
171
209
  padding-block: var(--alert-pad-block);
172
210
  padding-inline: var(--alert-pad-inline);
211
+ border-start-start-radius: calc(var(--alert-radius, var(--radius-lg)) - var(--border-width));
212
+ border-start-end-radius: calc(var(--alert-radius, var(--radius-lg)) - var(--border-width));
213
+ }
173
214
 
174
- @supports (color: color-mix(in oklch, red, white)) {
215
+ /* Dismiss inside an admonition title bar rides the inline end. */
216
+ .alert.admonition > .alert-title > .alert-dismiss {
217
+ margin-inline-start: auto;
218
+ }
219
+
220
+ @supports (color: color-mix(in oklch, red, white)) {
221
+ .alert.admonition > .alert-title {
175
222
  background: color-mix(
176
223
  in oklch,
177
224
  var(--surface) var(--soft-bg-mix),
178
225
  var(--intent, var(--neutral))
179
226
  );
180
227
  }
228
+ }
181
229
 
182
- @supports not (color: color-mix(in oklch, red, white)) {
230
+ @supports not (color: color-mix(in oklch, red, white)) {
231
+ .alert.admonition > .alert-title {
183
232
  background: var(--intent, var(--neutral));
233
+ color: var(--intent-fg, var(--neutral-fg));
184
234
  }
185
-
186
- border-start-start-radius: calc(var(--radius-lg) - var(--border-width));
187
- border-start-end-radius: calc(var(--radius-lg) - var(--border-width));
188
- }
189
-
190
- .alert-title:where(summary) {
191
- cursor: pointer;
192
- user-select: none;
193
- list-style: none;
194
- }
195
-
196
- .alert-title:where(summary)::-webkit-details-marker {
197
- display: none;
198
- }
199
-
200
- .alert-title[aria-hidden] {
201
- display: inline-flex;
202
- gap: var(--space-2);
203
- align-items: center;
204
235
  }
205
236
 
206
- .alert-body {
237
+ .alert.admonition > .alert-body {
207
238
  padding-block: var(--alert-pad-block);
208
239
  padding-inline: var(--alert-pad-inline);
240
+ background: var(--surface);
209
241
  color: var(--text);
210
242
  }
211
243
 
212
- .alert-body > :first-child {
244
+ .alert.admonition > .alert-body > :first-child {
213
245
  margin-block-start: 0;
214
246
  }
215
247
 
216
- .alert-body > :last-child {
248
+ .alert.admonition > .alert-body > :last-child {
217
249
  margin-block-end: 0;
218
250
  }
219
-
220
- details.alert.admonition[open] .alert-title {
221
- border-end-start-radius: 0;
222
- border-end-end-radius: 0;
223
- }
@@ -4,18 +4,26 @@
4
4
  * The visual box stays the same across semantic elements. Grouping and status
5
5
  * dots (an empty .badge child) compose around the avatar without changing its
6
6
  * meaning.
7
+ *
8
+ * Public hooks:
9
+ * --avatar-size
10
+ * --avatar-radius
11
+ * --avatar-font-size
12
+ * --avatar-stack-size
13
+ * --avatar-stack-font-size
14
+ * --avatar-stack-overlap
15
+ * --avatar-stack-ring
16
+ *
17
+ * Internal:
18
+ * --avatar-bg
19
+ * --avatar-fg
20
+ * --avatar-status-translate-x
7
21
  */
8
22
  /* Intent boundary — inherited intents do not tint this component.
9
23
  Local intent classes still win because :where() has zero specificity. */
10
24
  :where(.avatar) {
11
- /* @sync intent-boundary */
12
25
  --intent: initial;
13
26
  --intent-fg: initial;
14
- --ui-bg: initial;
15
- --ui-fg: initial;
16
- --ui-border: initial;
17
- --ui-hover-bg: initial;
18
- /* @sync intent-boundary */
19
27
  }
20
28
 
21
29
  .avatar {
@@ -107,10 +115,8 @@ button.avatar {
107
115
  transition: opacity var(--duration);
108
116
  }
109
117
 
110
- @media (hover: hover) {
111
- :where(a.avatar, button.avatar):not(:where(:disabled, [aria-disabled="true"])):hover::after {
112
- opacity: 1;
113
- }
118
+ :where(a.avatar, button.avatar):not(:where(:disabled, [aria-disabled="true"])):hover::after {
119
+ opacity: 1;
114
120
  }
115
121
 
116
122
  :where(a.avatar, button.avatar):not(:where(:disabled, [aria-disabled="true"])):active {
@@ -4,34 +4,46 @@
4
4
  * Soft by default. .solid for counters and notification badges. The dot
5
5
  * (.badge:empty) stays solid regardless — a soft dot is pointless.
6
6
  * Shared .outline variant stays available.
7
+ *
8
+ * Public hooks:
9
+ * --badge-size
10
+ * --badge-pad-x
11
+ * --badge-font-size
12
+ * --badge-dot-size
13
+ * --badge-radius
14
+ *
15
+ * Internal:
16
+ * --badge-bg
17
+ * --badge-fg
18
+ * --badge-border
7
19
  */
8
20
  /* Intent boundary — inherited intents do not tint this component.
9
21
  Local intent classes still win because :where() has zero specificity. */
10
22
  :where(.badge) {
11
- /* @sync intent-boundary */
12
23
  --intent: initial;
13
24
  --intent-fg: initial;
14
25
  --ui-bg: initial;
15
26
  --ui-fg: initial;
16
27
  --ui-border: initial;
17
28
  --ui-hover-bg: initial;
18
- /* @sync intent-boundary */
19
29
  }
20
30
 
21
31
  .badge {
22
- --badge-size: var(--variant-compact-size, 1.5rem);
32
+ --badge-size: var(--density-compact-size);
23
33
  --badge-pad-x: 0.55em;
24
- --badge-font-size: var(--variant-compact-font-size, 0.8125rem);
34
+ --badge-font-size: 0.8125rem;
25
35
  --badge-dot-size: 0.5rem;
26
- --badge-bg: var(--ui-bg, transparent);
36
+ --badge-bg: var(--ui-bg, var(--surface-subtle));
27
37
  --badge-fg: var(--ui-fg, var(--intent, var(--text)));
28
- --badge-border: var(--ui-border, var(--intent, var(--border)));
29
- --badge-radius: 999px;
38
+ --badge-border: var(--ui-border, var(--border));
39
+ --badge-radius: var(--radius-full);
30
40
 
31
41
  display: inline-flex;
32
42
  align-items: center;
33
43
  justify-content: center;
34
44
  gap: 0.35em;
45
+ inline-size: fit-content;
46
+ max-inline-size: 100%;
35
47
  min-block-size: var(--badge-size);
36
48
  padding-inline: var(--badge-pad-x);
37
49
  border: var(--border-width) solid var(--badge-border);
@@ -100,27 +112,44 @@
100
112
  cursor: pointer;
101
113
  }
102
114
 
103
- @media (hover: hover) {
104
- .badge > button:not(:disabled):hover {
105
- background: var(--hover-overlay);
106
- }
115
+ .badge > button:not(:disabled):hover {
116
+ background: var(--hover-overlay);
117
+ }
118
+
119
+ /* Hover recipes — sets --ui-hover-bg consumed by the hover rule below.
120
+ Declared outside :hover like button.css for the same reason. */
121
+ :is(a.badge, button.badge).outline {
122
+ --ui-hover-bg: var(--surface-subtle);
107
123
  }
108
124
 
109
- /* .solid/.soft/.outline are shared via variants.css, including the
110
- .soft/.outline hover recipe (--ui-hover-bg). */
111
- @media (hover: hover) {
112
- :where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"])):hover {
113
- background: var(--ui-hover-bg, var(--badge-bg));
125
+ @supports (color: color-mix(in oklch, red, white)) {
126
+ :is(a.badge, button.badge):is(.solid, :empty) {
127
+ --ui-hover-bg: color-mix(in oklch, var(--intent, var(--neutral)) 88%, black);
128
+ }
129
+
130
+ :is(a.badge, button.badge):where(:not(.solid, .outline, :empty)) {
131
+ --ui-hover-bg: color-mix(
132
+ in oklch,
133
+ var(--surface) calc(var(--soft-bg-mix) - var(--soft-hover-alpha)),
134
+ var(--intent, var(--neutral))
135
+ );
114
136
  }
115
137
 
116
- :where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"], .outline)):hover {
117
- box-shadow: inset 0 0 0 999px var(--hover-overlay);
138
+ :is(a.badge, button.badge).outline {
139
+ --ui-hover-bg: color-mix(in oklch, var(--surface-subtle) 92%, var(--intent, var(--neutral)));
118
140
  }
119
141
  }
120
142
 
143
+ :where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"])):hover {
144
+ background: var(--ui-hover-bg, var(--badge-bg));
145
+ }
146
+
147
+ :where(a.badge, button.badge):not(:where(:disabled, [aria-disabled="true"], .outline)):hover {
148
+ box-shadow: inset 0 0 0 999px var(--hover-overlay);
149
+ }
150
+
121
151
  button.badge {
122
152
  cursor: pointer;
123
- font: inherit;
124
153
  }
125
154
 
126
155
  button.badge:disabled,
@@ -134,27 +163,18 @@ a.badge[aria-disabled="true"] {
134
163
  }
135
164
 
136
165
  @media (forced-colors: active) {
137
- .badge {
138
- border-color: ButtonText;
139
- background: Canvas;
140
- color: CanvasText;
141
- box-shadow: none;
142
- }
143
-
144
166
  .badge:empty {
145
167
  background: ButtonText;
146
168
  }
147
169
 
148
- @media (hover: hover) {
149
- :where(a.badge, button.badge):hover:not(:where(:disabled, [aria-disabled="true"])) {
150
- outline: var(--border-width) solid ButtonText;
151
- outline-offset: 2px;
152
- }
170
+ :where(a.badge, button.badge):hover:not(:where(:disabled, [aria-disabled="true"])) {
171
+ outline: var(--border-width) solid ButtonText;
172
+ outline-offset: 2px;
173
+ }
153
174
 
154
- .badge > button:not(:disabled):hover {
155
- outline: var(--border-width) solid currentColor;
156
- outline-offset: 1px;
157
- }
175
+ .badge > button:not(:disabled):hover {
176
+ outline: var(--border-width) solid currentColor;
177
+ outline-offset: 1px;
158
178
  }
159
179
 
160
180
  button.badge:disabled,
@@ -8,7 +8,7 @@
8
8
  display: flex;
9
9
  flex-wrap: wrap;
10
10
  align-items: center;
11
- gap: var(--space-1);
11
+ gap: var(--space-10);
12
12
  margin: 0;
13
13
  padding: 0;
14
14
  color: var(--text-subtle);
@@ -19,7 +19,7 @@
19
19
  .breadcrumb li {
20
20
  display: inline-flex;
21
21
  align-items: center;
22
- gap: var(--space-1);
22
+ gap: var(--space-10);
23
23
  }
24
24
 
25
25
  .breadcrumb li + li::before {
@@ -32,25 +32,17 @@
32
32
  text-decoration: none;
33
33
  }
34
34
 
35
- @media (hover: hover) {
36
- .breadcrumb
37
- li:not(:where([aria-current]:not([aria-current="false"])))
38
- > a:hover:not(:where([aria-current]:not([aria-current="false"]))) {
39
- color: var(--text);
40
- text-decoration: underline;
41
- }
35
+ .breadcrumb a:not([aria-current]):hover,
36
+ .breadcrumb a[aria-current="false"]:hover {
37
+ color: var(--text);
38
+ text-decoration: underline;
42
39
  }
43
40
 
44
41
  .breadcrumb a:focus-visible {
45
42
  border-radius: var(--radius-sm);
46
43
  }
47
44
 
48
- .breadcrumb a:where([aria-current]:not([aria-current="false"])) {
49
- color: var(--text);
50
- font-weight: var(--font-weight-medium);
51
- }
52
-
53
- .breadcrumb li:where([aria-current]:not([aria-current="false"])) > :where(a, span) {
45
+ .breadcrumb :where(a, span):where([aria-current]:not([aria-current="false"])) {
54
46
  color: var(--text);
55
47
  font-weight: var(--font-weight-medium);
56
48
  }
@@ -4,6 +4,12 @@
4
4
  * A non-button element with aria-busy="true" and a direct last-child .spinner
5
5
  * shows that spinner as a centered overlay over a faded surface. Buttons keep
6
6
  * their spinners inline (e.g. .btn[aria-busy] > .spinner stays inline-flex).
7
+ *
8
+ * Public hooks:
9
+ * --busy-overlay-bg
10
+ *
11
+ * Internal:
12
+ * --spinner-size (overlay copy of the shared spinner hook)
7
13
  */
8
14
  [aria-busy] > .spinner:last-child {
9
15
  display: none;