actual-css 0.9.1 → 0.10.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 (107) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/README.md +67 -45
  3. package/components.json +33 -13
  4. package/dist/actual.full.css +607 -502
  5. package/dist/actual.full.js +1 -1
  6. package/dist/actual.full.min.css +1 -1
  7. package/docs/design-notes/app-ui.md +1 -1
  8. package/docs/design-notes/column-layout.md +103 -520
  9. package/docs/design-notes/forced-colors.md +12 -1
  10. package/docs/design-notes/naming.md +1 -1
  11. package/docs/design-notes/surface-reparenting.md +8 -9
  12. package/docs/navigation.json +2 -0
  13. package/docs/pages/components/accordion.md +55 -1
  14. package/docs/pages/components/alert.md +9 -15
  15. package/docs/pages/components/badge.md +6 -7
  16. package/docs/pages/components/bleed.md +42 -0
  17. package/docs/pages/components/button.md +30 -4
  18. package/docs/pages/components/card.md +21 -86
  19. package/docs/pages/components/close.md +48 -0
  20. package/docs/pages/components/data-list.md +26 -1
  21. package/docs/pages/components/dialog.md +16 -16
  22. package/docs/pages/components/drawer.md +9 -9
  23. package/docs/pages/components/navbar.md +5 -5
  24. package/docs/pages/components/spinner.md +3 -3
  25. package/docs/pages/components/table.md +3 -2
  26. package/docs/pages/enhancements/overview.md +5 -0
  27. package/docs/pages/enhancements/tooltip.md +3 -1
  28. package/docs/pages/examples/overview.md +3 -1
  29. package/docs/pages/forms/choice-cards.md +39 -0
  30. package/docs/pages/forms/combobox.md +23 -11
  31. package/docs/pages/forms/input-policy.md +59 -0
  32. package/docs/pages/forms/joined-controls.md +2 -5
  33. package/docs/pages/forms/overview.md +6 -0
  34. package/docs/pages/forms/validation.md +37 -1
  35. package/docs/pages/foundations/theming.md +64 -18
  36. package/docs/pages/foundations/tokens.md +50 -13
  37. package/docs/pages/foundations/typography.md +10 -0
  38. package/docs/pages/guides/introduction.md +17 -24
  39. package/docs/pages/guides/modular-import.md +53 -6
  40. package/docs/pages/guides/progressive-enhancement.md +9 -5
  41. package/docs/pages/guides/tailwind.md +17 -5
  42. package/docs/pages/layout/column-layout.md +92 -54
  43. package/docs/pages/layout/media.md +1 -1
  44. package/docs/pages/layout/overview.md +1 -1
  45. package/docs/pages/utilities/overview.md +1 -1
  46. package/docs/pages/utilities/sizing-wrapping.md +5 -3
  47. package/llms.txt +24 -4
  48. package/package.json +7 -6
  49. package/reserved-classes.json +5 -30
  50. package/skills/actual-css/PROJECT.md.example +65 -0
  51. package/skills/actual-css/README.md +64 -0
  52. package/skills/actual-css/SKILL.md +234 -0
  53. package/skills/actual-css/references/accessibility.md +44 -0
  54. package/skills/actual-css/references/api-and-vocabulary.md +94 -0
  55. package/skills/actual-css/references/enhancements.md +59 -0
  56. package/skills/actual-css/references/forms.md +49 -0
  57. package/skills/actual-css/references/layout-and-theming.md +82 -0
  58. package/skills/actual-css/references/review-checklist.md +51 -0
  59. package/skills/actual-css/scripts/inspect-actual-css.mjs +171 -0
  60. package/src/css/components/accordion.css +44 -7
  61. package/src/css/components/alert.css +23 -43
  62. package/src/css/components/app-nav.css +9 -0
  63. package/src/css/components/badge.css +11 -39
  64. package/src/css/components/bleed.css +79 -0
  65. package/src/css/components/button.css +6 -17
  66. package/src/css/components/card.css +7 -40
  67. package/src/css/components/close.css +91 -0
  68. package/src/css/components/data-list.css +18 -0
  69. package/src/css/components/drawer.css +15 -56
  70. package/src/css/components/fab.css +10 -0
  71. package/src/css/components/flyout.css +1 -0
  72. package/src/css/components/index.css +2 -0
  73. package/src/css/components/join.css +36 -20
  74. package/src/css/components/menu.css +12 -0
  75. package/src/css/components/modal.css +10 -57
  76. package/src/css/components/navbar.css +27 -0
  77. package/src/css/core/base.css +2 -0
  78. package/src/css/core/focus.css +9 -3
  79. package/src/css/core/theme.css +25 -43
  80. package/src/css/core/tokens.css +3 -1
  81. package/src/css/core/variants.css +15 -36
  82. package/src/css/forms/choice-card.css +74 -4
  83. package/src/css/forms/choice.css +2 -3
  84. package/src/css/forms/control.css +15 -8
  85. package/src/css/forms/custom-select.css +4 -4
  86. package/src/css/forms/form.css +6 -0
  87. package/src/css/forms/native.css +4 -4
  88. package/src/css/forms/range.css +23 -8
  89. package/src/css/forms/switch.css +2 -3
  90. package/src/css/layout/app-layout.css +1 -1
  91. package/src/css/layout/column-layout.css +26 -111
  92. package/src/css/utilities/extra.css +7 -6
  93. package/src/js/dialog.js +95 -24
  94. package/src/js/enhance.js +7 -3
  95. package/src/js/escape.js +1 -0
  96. package/src/js/flyout.js +10 -1
  97. package/src/js/focus-group.js +2 -1
  98. package/src/js/keys.js +14 -0
  99. package/src/js/menu.js +2 -1
  100. package/src/js/status.js +19 -0
  101. package/src/js/surface.js +12 -8
  102. package/src/js/tab.js +2 -1
  103. package/src/js/tooltip.js +18 -1
  104. package/src/js/validation.js +36 -6
  105. package/dist/actual.css +0 -863
  106. package/dist/actual.js +0 -1
  107. package/dist/actual.min.css +0 -1
@@ -30,6 +30,12 @@ an essential shape, state, or affordance.
30
30
 
31
31
  ## Pattern: custom property + single override
32
32
 
33
+ The hook-only form below is fine until an application overrides the hook: the
34
+ override sits at the same specificity, loads after the framework, and wins the
35
+ variable — leaving a non-system fill the browser then remaps to `Canvas`. For
36
+ a masked marker, write the forced value on the property itself so no hook
37
+ override can take it away (accordion) — same for range track/thumb fills.
38
+
33
39
  ```css
34
40
  .component {
35
41
  --cmp-marker-color: var(--text-muted);
@@ -48,6 +54,11 @@ an essential shape, state, or affordance.
48
54
  .component {
49
55
  --cmp-marker-color: CanvasText;
50
56
  }
57
+
58
+ /* Masked markers: pin the property, not just the hook. */
59
+ .component::marker {
60
+ background-color: CanvasText;
61
+ }
51
62
  }
52
63
  ```
53
64
 
@@ -104,7 +115,7 @@ remapping is perfect:
104
115
 
105
116
  - `.card` — `background: Canvas`, `color: CanvasText` are already the defaults
106
117
  - `.flyout` / `.menu` — `Canvas`/`CanvasText` for content, `ButtonText` for borders
107
- - `.accordion` / `.breadcrumb` — pure remapping gets everything right
118
+ - `.breadcrumb` — pure remapping gets everything right
108
119
  - `.status-bar` — `CanvasText` on `Canvas` background
109
120
 
110
121
  The only forced-colors rules that should remain after a full audit are the ones
@@ -47,7 +47,7 @@ never about opting in.
47
47
  - **Base** (`utilities/base.css`) is a curated set of common ergonomic
48
48
  shortcuts (`.overflow-auto`, `.gap-none`, `.text-nowrap`, `.px`, `.py`).
49
49
  - **Extra** (`utilities/extra.css`) uses explicit property/value names
50
- (`.overflow-hidden`, `.padding-context`, `.white-space-normal`).
50
+ (`.overflow-hidden`, `.padding-context`, `.flex-nowrap`).
51
51
 
52
52
  Call them base and extra, after the files. "Optional" is the wrong word for
53
53
  either: it reads as a layer an author has to opt into, which is true of every
@@ -20,8 +20,8 @@ every scope that reached it *by inheritance*. The panel was styled by one set of
20
20
  ancestors while closed, and by a different set from the moment it became
21
21
  visible.
22
22
 
23
- Actual leans on inheritance deliberately: theme islands, density, `.inverted`,
24
- and the custom properties an application scopes to its own containers all
23
+ Actual leans on inheritance deliberately: theme islands, density, and the
24
+ custom properties an application scopes to its own containers all
25
25
  travel that way. So this was not one bug about themes. It was the same bug in as
26
26
  many forms as there are inherited contexts.
27
27
 
@@ -30,13 +30,12 @@ many forms as there are inherited contexts.
30
30
  Chrome, one `.flyout` per scope, values read off the panel before and after the
31
31
  click that opens it:
32
32
 
33
- | Scope | Closed | Open |
34
- | ------------------------- | ---------------------------------------- | -------------------- |
35
- | `[data-theme="dark"]` | `background: rgb(30, 26, 35)` | `rgb(255, 255, 255)` |
36
- | `.sm` | `--control-size: 2rem` | `2.375rem` |
37
- | `.inverted` | `--ui-fg: light-dark(hsl(0 0% 100%), …)` | *(unset)* |
38
- | app-scoped `--app-accent` | `rgb(1, 2, 3)` | *(unset)* |
39
- | `.sm` inside a `<dialog>` | `--control-size: 2rem` | `2.375rem` |
33
+ | Scope | Closed | Open |
34
+ | ------------------------- | ----------------------------- | -------------------- |
35
+ | `[data-theme="dark"]` | `background: rgb(30, 26, 35)` | `rgb(255, 255, 255)` |
36
+ | `.sm` | `--control-size: 2rem` | `2.375rem` |
37
+ | app-scoped `--app-accent` | `rgb(1, 2, 3)` | *(unset)* |
38
+ | `.sm` inside a `<dialog>` | `--control-size: 2rem` | `2.375rem` |
40
39
 
41
40
  Every context broke. Three consequences were worth stating plainly:
42
41
 
@@ -49,10 +49,12 @@
49
49
  "aura",
50
50
  "avatar",
51
51
  "badge",
52
+ "bleed",
52
53
  "breadcrumb",
53
54
  "button",
54
55
  "card",
55
56
  "chat",
57
+ "close",
56
58
  "dialog",
57
59
  "drawer",
58
60
  "data-list",
@@ -116,4 +116,58 @@ The end marker is a shared token-based chevron; swapping it for a local icon is
116
116
  ## CSS hooks
117
117
 
118
118
  - `--accordion-radius` — outer corner radius.
119
- - `--accordion-marker-color` — color of the end marker; it reinforces to the summary text color on hover, and forced-colors mode overrides it to `CanvasText`.
119
+ - `--accordion-pad` — inset of the summary row and the panel. The panel relays it as `--surface-pad`, so a direct `.bleed` child of the panel reaches the item's sides and bottom corners (never the summary); `.flush` drops the inline half and the relay with it.
120
+ - `--accordion-marker-color` — color of the end marker; it reinforces to the summary text color on hover. In forced-colors mode the marker is pinned to `CanvasText` on the property itself, so an application override of the hook cannot leave a `Canvas`-remapped fill.
121
+ - `--accordion-marker-size` — edge length of the end marker.
122
+
123
+ ### Replacing the marker
124
+
125
+ The marker is a masked box: `--accordion-marker-color` fills it and the chevron
126
+ mask clips it to the glyph. A background, ring, or shadow set on it is clipped
127
+ to the same silhouette, so a decorated marker — a chevron inside a pill — has
128
+ to be a real element: hide the built-in one and put a
129
+ `<span class="marker" aria-hidden="true"></span>` at the end of the summary. The
130
+ pill is the span; the chevron is its pseudo-element, which reads the same color
131
+ hook, so the hover reinforcement carries over. The forced-colors pin does not
132
+ carry over through the hook — an application hook override at the same
133
+ specificity loads after the framework and would win the variable — so the
134
+ recipe repeats it on its own property:
135
+
136
+ ```css
137
+ .accordion.custom-marker summary::after {
138
+ display: none;
139
+ }
140
+
141
+ .accordion.custom-marker .marker {
142
+ display: grid;
143
+ place-items: center;
144
+ margin-inline-start: auto;
145
+ padding: var(--space-10);
146
+ border-radius: 50%;
147
+ background: var(--surface-subtle);
148
+ transition: transform var(--duration);
149
+ }
150
+
151
+ .accordion.custom-marker .marker::before {
152
+ content: "";
153
+ inline-size: var(--accordion-marker-size);
154
+ block-size: var(--accordion-marker-size);
155
+ background-color: var(--accordion-marker-color);
156
+ -webkit-mask: var(--icon-chevron) center / contain no-repeat;
157
+ mask: var(--icon-chevron) center / contain no-repeat;
158
+ }
159
+
160
+ .accordion.custom-marker summary:hover .marker {
161
+ --accordion-marker-color: currentColor;
162
+ }
163
+
164
+ .accordion.custom-marker details[open] .marker {
165
+ transform: rotate(180deg);
166
+ }
167
+
168
+ @media (forced-colors: active) {
169
+ .accordion.custom-marker .marker::before {
170
+ background-color: CanvasText;
171
+ }
172
+ }
173
+ ```
@@ -5,7 +5,7 @@
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 `.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.
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.
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.
@@ -13,7 +13,7 @@
13
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.
14
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.
15
15
  - Use `.compact` or `.spacious` for density changes. The inline padding stays stable.
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
+ - Use a [`.close`](close.md) button to make the alert dismissible. It is a direct trailing child in standard alerts and lives inside `.alert-title` in admonitions; the alert makes it compact.
17
17
 
18
18
  Because alerts are soft by default, adding `.soft` to an intent (`.alert.soft.primary`,
19
19
  `.alert.soft.danger`, …) is a no-op: it already resolves to a soft version of that
@@ -26,14 +26,13 @@ itself needs to change, not `.soft`.
26
26
  | ---------------- | ----------- | ----------------------------------------------- |
27
27
  | `.alert` | Component | Inline status surface; soft by default. |
28
28
  | `.alert-icon` | Composition | Decorative leading icon, in the first column. |
29
- | `.alert-dismiss` | Modifier | Compact dismiss button. |
29
+ | `.close` | Composition | Dismiss button, in the last column. |
30
30
  | `.alert-title` | Composition | Admonition title bar; hosts the dismiss button. |
31
31
  | `.alert-body` | Composition | Admonition body on the page surface. |
32
32
  | `.callout` | Variant | Leading-edge flag; owns the border geometry. |
33
33
  | `.admonition` | Variant | Tinted title bar, body on the page surface. |
34
34
  | Shared intents | Intent | `.primary`, `.secondary`, `.success`, … |
35
35
  | Shared variants | Variant | `.solid`, `.outline`, `.surface`. |
36
- | `.inverted` | Surface | Inverse surface, any intent. |
37
36
  | Density contexts | Density | Descendant rhythm; inline padding stays stable. |
38
37
 
39
38
  ## Basic usage
@@ -101,7 +100,7 @@ intent treatment when the color itself carries the meaning.
101
100
 
102
101
  ## Trailing action
103
102
 
104
- The grid pins `.alert-icon` and `.alert-dismiss` to the outer columns. A
103
+ The grid pins `.alert-icon` and `.close` to the outer columns. A
105
104
  trailing link or button is content, not anatomy, so it lives in the text column:
106
105
  wrap the row in a `.cluster` and push the two ends apart. Same recipe as any
107
106
  split / spread row — there is no `.alert-action` class.
@@ -129,10 +128,6 @@ center.
129
128
  I'm a surface alert — theme chrome, intent in the ink
130
129
  </div>
131
130
 
132
- <div class="alert inverted" role="alert">
133
- I'm an inverted alert <a href="#">with a link</a>
134
- </div>
135
-
136
131
  <div class="alert danger compact" role="alert">
137
132
  I'm an error in a compact context
138
133
  </div>
@@ -153,7 +148,7 @@ center.
153
148
 
154
149
  ## Dismiss
155
150
 
156
- Use `.alert-dismiss` to let a user remove an inline alert. The button is transparent at rest with a muted glyph; it works with any intent because it uses `currentColor` and reduced opacity. It relies on the runtime `--dismiss` command — no custom script.
151
+ Add a `.close` button to let a user remove an inline alert. Inside an alert it is compact and at reduced opacity, and it follows the alert's ink with any intent. The runtime `--dismiss` command removes the alert — no custom script.
157
152
 
158
153
  ```html demo
159
154
  <div class="alert warning" id="warning-alert">
@@ -162,7 +157,7 @@ Use `.alert-dismiss` to let a user remove an inline alert. The button is transpa
162
157
  <p>Something needs your attention.</p>
163
158
 
164
159
  <button
165
- class="alert-dismiss"
160
+ class="close"
166
161
  type="button"
167
162
  commandfor="warning-alert"
168
163
  command="--dismiss"
@@ -171,14 +166,14 @@ Use `.alert-dismiss` to let a user remove an inline alert. The button is transpa
171
166
  </div>
172
167
  ```
173
168
 
174
- In an admonition, place `.alert-dismiss` inside `.alert-title`; it rides the inline end of the title bar:
169
+ In an admonition, place `.close` inside `.alert-title`; it rides the inline end of the title bar:
175
170
 
176
171
  ```html
177
172
  <div class="alert admonition warning" id="notice">
178
173
  <div class="alert-title">
179
174
  <span>Warning</span>
180
175
  <button
181
- class="alert-dismiss"
176
+ class="close"
182
177
  type="button"
183
178
  commandfor="notice"
184
179
  command="--dismiss"
@@ -261,8 +256,7 @@ An admonition is a structured box with a tinted title bar, an optional icon, and
261
256
  - `--alert-pad-block` — block padding.
262
257
  - `--alert-font-size` — base font size.
263
258
  - `--alert-icon-size` — size of a leading `.alert-icon`.
264
- - `--alert-dismiss-size` — inline and block size of the `.alert-dismiss` button.
265
- - `--alert-dismiss-icon-size` — size of the dismiss glyph.
259
+ - `--close-size` / `--close-icon-size` — the alert declares compact values for its `.close`; set them on the alert to change them.
266
260
  - `--alert-radius` — corner radius, when the default `--radius-lg` does not fit.
267
261
  - `--alert-border-inline-start-color` / `--alert-border-inline-start-width` — extension points for a callout-style colored flag on the leading edge instead of a uniform border.
268
262
 
@@ -177,21 +177,20 @@ Override the existing hooks when the three-step scale does not fit the content.
177
177
 
178
178
  ## Removable tag pattern
179
179
 
180
- Use `.badge soft` for tag visuals. Add a direct dismiss button only when the tag can actually be removed. There is no separate chip component.
180
+ Use `.badge soft` for tag visuals. Add a direct [`.close`](close.md) button only when the tag can actually be removed. There is no separate chip component.
181
181
  For a compact action or filter, use `.btn.sm` instead; badges describe content
182
182
  and must not be turned into toggle controls merely to obtain a compact shape.
183
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.
184
+ Inside a badge the close follows the pill's size, keeps a 24px pointer target,
185
+ and tucks into the padding.
187
186
 
188
187
  ```html demo
189
188
  <span class="badge primary soft">
190
189
  Design
191
- <button type="button" aria-label="Remove Design"></button>
190
+ <button class="close" type="button" aria-label="Remove Design"></button>
192
191
  </span>
193
192
  <span class="badge primary soft">
194
- <button type="button" aria-label="Remove Design"></button>
193
+ <button class="close" type="button" aria-label="Remove Design"></button>
195
194
  Design
196
195
  </span>
197
196
  ```
@@ -204,4 +203,4 @@ used instead.
204
203
  - `--badge-font-size` — label font size.
205
204
  - `--badge-icon-size` — decorative child size; `1em` follows the label by default.
206
205
  - `--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.
206
+ - `--close-size` / `--close-icon-size` — the badge declares them for its `.close`; set them on the badge to change them.
@@ -0,0 +1,42 @@
1
+ # Bleed
2
+
3
+ `.bleed` lets a direct child of a padded surface reach its edges. Use it for a
4
+ full-width image, a colored header or footer, or a notice band.
5
+
6
+ It works inside `.card`, `.drawer`, and an accordion panel. The accordion panel
7
+ owns the padding, so place `.bleed` directly inside the panel, after the summary.
8
+ A deeper descendant stays within its wrapper. A modular build imports
9
+ `actual-css/css/components/bleed` alongside the surface component.
10
+
11
+ ```html demo
12
+ <article class="card stack" style="--card-max-inline-size: 24rem">
13
+ <img class="bleed" src="https://picsum.photos/seed/actual-css-card/600/300" alt="" />
14
+ <header><h3>Full-width image</h3></header>
15
+ <p>The image reaches the card edges.</p>
16
+ <footer class="bleed" style="background: var(--surface-subtle)">
17
+ <button type="button" class="btn outline">View</button>
18
+ </footer>
19
+ </article>
20
+ ```
21
+
22
+ An `<img>`, `<picture>`, `<video>`, `<canvas>`, or `<svg>` with `.bleed` expands
23
+ to the surface width. For a responsive image, put `.bleed` on `<picture>`; its
24
+ image fills the picture and is clipped by it.
25
+
26
+ Structural `header`, `footer`, and `figure` bands keep the surface's inner
27
+ padding while their background reaches the edge. Other bands can read the
28
+ relayed inset with `--bleed-pad`:
29
+
30
+ ```css
31
+ .status-row {
32
+ padding: var(--bleed-pad);
33
+ }
34
+ ```
35
+
36
+ The participating surface relays its inset to direct children only. Read
37
+ `--bleed-pad` on one of those children when a custom band needs matching
38
+ padding. An author-defined surface needs its own direct-child relay to join
39
+ this contract.
40
+
41
+ See [Card](card.md), [Drawer](drawer.md), and [Accordion](accordion.md) for
42
+ surface examples.
@@ -61,10 +61,37 @@ Reach for `.surface` when a secondary action must sit on an opaque, neutral
61
61
  chip: `.outline` lets whatever is behind it show through, and `.soft` is the
62
62
  intent-tinted treatment (its baseline fallback uses `--surface-subtle`).
63
63
 
64
+ None of the three gives an opaque fill *under* an intent-colored border —
65
+ `.surface` keeps the neutral `--border`, and `.outline` has no fill. Set
66
+ `--ui-bg` on the element for that combination; it is the variant relay every
67
+ component with shared variants reads, so the same declaration works on a badge:
68
+
69
+ ```css
70
+ .chip-on-tint {
71
+ --ui-bg: var(--surface);
72
+ }
73
+ ```
74
+
75
+ The rule ties with the variant's own `--ui-bg` on specificity, so load it after
76
+ Actual CSS or import `actual.layer.css`.
77
+
64
78
  Transparent treatments (`.outline`, `.ghost`, `.link`) follow the surrounding
65
- foreground by default, including on a contrasting `.inverted` surface. A local
66
- intent class still wins: `.danger.outline` uses the danger color in either
67
- context. Filled buttons own their surface and keep their normal intent recipe.
79
+ foreground by default, including on a band your application paints. A local
80
+ intent class still wins: `.danger.outline` uses the danger color in any
81
+ context, and nothing tunes that color for a surface you paint yourself. For
82
+ intent ink on a dark band, use a `data-theme` island, whose palette is tuned
83
+ for its surface. Filled buttons own their surface and keep their normal intent
84
+ recipe.
85
+
86
+ A dark button (inverse, contrast) is a custom intent on the button itself, so
87
+ every variant keeps working — `.outline` and `.ghost` take the solid ink:
88
+
89
+ ```css
90
+ .btn.app-dark {
91
+ --intent: var(--surface-solid);
92
+ --intent-fg: var(--surface);
93
+ }
94
+ ```
68
95
 
69
96
  `.btn.link` is a link-style action: it keeps button behavior and states, but
70
97
  remains intrinsically sized like text instead of stretching with its layout
@@ -251,5 +278,4 @@ For exclusive groups, manage a single pressed button:
251
278
  ## CSS hooks
252
279
 
253
280
  - `--btn-radius` — corner radius.
254
- - `--btn-focus-color` — focus ring base color; the ring itself is derived from it.
255
281
  - `--btn-gap` — space between an icon and the label text (default `0.375em`).
@@ -1,24 +1,20 @@
1
1
  # Card
2
2
 
3
- Flexible content container with optional header, body, and footer regions, plus full-bleed children.
3
+ Flexible content container with optional header, body, and footer regions.
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` | Density | Tighter padding and compact descendant context. |
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
+ | `.compact` | Density | Tighter padding and compact descendant context. |
15
14
 
16
15
  `.surface` is a shared variant: it paints the page surface instead of
17
- `--surface-raised` and leaves the intent to the text.
18
-
19
- `.inverted` paints `--surface-solid` and swaps the foreground to `--surface`,
20
- so it inverts in dark themes instead of always reading as dark. Write it as
21
- `class="card inverted"`, or on any block.
16
+ `--surface-raised` and leaves the intent to the text. For a dark card on a
17
+ light page (or the reverse), put `data-theme="dark"` on the card.
22
18
 
23
19
  ## Basic usage
24
20
 
@@ -59,52 +55,8 @@ gap.
59
55
 
60
56
  ## Bleed
61
57
 
62
- Use `.bleed` on a direct child to escape the card padding — full-width images,
63
- colored headers, or footers.
64
-
65
- ```html demo
66
- <div style="max-inline-size: 32rem">
67
- <article class="card stack">
68
- <img class="bleed" src="https://picsum.photos/seed/actual-css-card/600/300" alt="Coastal cliffs at dusk" />
69
- <header>
70
- <h3>Coastal cliffs at dusk</h3>
71
- </header>
72
- <section class="stack" aria-label="Summary">
73
- <p>A short caption that wraps across a few lines. The image bleeds to the card edges.</p>
74
- </section>
75
- <footer>
76
- <span class="badge primary soft">Photo</span>
77
- <button type="button" class="btn outline">View</button>
78
- </footer>
79
- </article>
80
- </div>
81
- ```
82
-
83
- ```html demo
84
- <article class="card stack" style="--card-max-inline-size: 24rem">
85
- <header class="bleed stack items-center text-center" style="background: var(--surface-subtle)">
86
- <hgroup>
87
- <h3>Team</h3>
88
- <p class="muted">For growing products</p>
89
- </hgroup>
90
- <p>
91
- <span style="font-size: 2rem; font-weight: var(--font-weight-semibold); line-height: 1">$24</span>
92
- <span class="muted">/ user / month</span>
93
- </p>
94
- </header>
95
-
96
- <ul class="stack items-center" style="list-style: none; padding: 0">
97
- <li>Unlimited projects</li>
98
- <li>Up to 25 seats</li>
99
- <li>Shared workspaces</li>
100
- <li>Priority support</li>
101
- </ul>
102
-
103
- <footer class="bleed" style="background: var(--surface-subtle); justify-content: center">
104
- <a class="btn primary" href="#">Upgrade</a>
105
- </footer>
106
- </article>
107
- ```
58
+ Use `.bleed` on a direct child to bring an image or band to the card edge. See
59
+ [Bleed](bleed.md) for the shared surface contract and its modular import.
108
60
 
109
61
  ## In grids
110
62
 
@@ -148,27 +100,10 @@ all footers in the row line up regardless of body length.
148
100
 
149
101
  ## Surfaces
150
102
 
151
- `.inverted` creates a contrasting surface context, not a complete theme island.
152
- On a bare block (`<header class="inverted">`, `<section class="inverted">`) it
153
- paints the surface directly; contextual content and transparent treatments such
154
- as `.btn.outline`, `.btn.ghost`, and `.btn.link` follow its foreground.
155
-
156
- Apply it directly to a component that consumes the shared `--ui-*` surface
157
- contract (`.card`, `.navbar`, `.alert`, `.badge`) to treat that component as the
158
- contrasting surface. A surface-owning component nested inside an inverted block
159
- keeps its own surface and establishes a new context for its descendants. Use a
160
- `data-theme` island when every component and form control in a subtree must use
161
- another theme.
162
-
163
- More specific component states or explicit surface variants still take
164
- precedence: `.card.inverted.subtle` keeps the `.subtle` background while the
165
- foreground stays inverted.
166
-
167
- `.inverted` and `.subtle` are competing surface treatments — do not combine
168
- them. `.subtle` paints the card's background directly while `.inverted` swaps
169
- the surface tokens, so `.card.subtle.inverted` renders a subtle background with
170
- inverted foreground. Treat them as mutually exclusive, like two competing
171
- variants.
103
+ A card is a surface-owning component: inside a painted band it keeps its own
104
+ surface and resets the context for its content. A card with
105
+ `data-theme="dark"` is a complete dark island — its fields, intents and states
106
+ all follow.
172
107
 
173
108
  `.surface` is the shared variant on a card: it drops the card to the page
174
109
  surface instead of `--surface-raised`, keeps the theme border, and lets an
@@ -198,12 +133,12 @@ treatments on either side of it.
198
133
  </hgroup>
199
134
  </article>
200
135
 
201
- <article class="card inverted stack">
136
+ <article class="card stack" data-theme="dark">
202
137
  <hgroup>
203
- <h3>Inverted</h3>
204
- <p>Inverse surface for emphasis. Text inherits the contrasting surface color.</p>
138
+ <h3>Dark island</h3>
139
+ <p class="muted">A complete dark theme on one card.</p>
205
140
  </hgroup>
206
- <button type="button" class="btn">Action</button>
141
+ <button type="button" class="btn primary outline">Action</button>
207
142
  </article>
208
143
 
209
144
  <article class="card compact">
@@ -219,5 +154,5 @@ treatments on either side of it.
219
154
 
220
155
  - `--card-radius` — corner radius.
221
156
  - `--card-max-inline-size` — maximum width.
222
- - `--card-pad` — inner padding; also drives the negative offsets that let a `.bleed` child reach the card edge. `.compact` lowers it.
157
+ - `--card-pad` — inner padding, relayed to direct children for `.bleed`. `.compact` lowers it.
223
158
  - `--card-gap` — space between direct children of a bare card. A composed layout primitive owns its own gap instead.
@@ -0,0 +1,48 @@
1
+ # Close
2
+
3
+ > Icon-only X button that closes a dialog, dismisses an alert, or removes a tag.
4
+
5
+ **Related terms:** close button, dismiss button, remove button, clear, X.
6
+
7
+ - Use an empty `<button class="close" aria-label="…">`: it paints its own X from `--icon-close`, so it needs no icon font and no text glyph.
8
+ - `.close` is always the X. A button with another pictogram is a `.btn.icon-only`.
9
+ - The class names the look, not the mechanism. Close with the container's own action: `command="request-close"` or `method="dialog"` in a dialog, `command="--dismiss"` on an alert, application logic on a tag.
10
+ - The container sets the size and place: the top inline-end corner of a `.modal` or a `.drawer`, the last column of an `.alert`, the end of a `.badge`.
11
+ - A modular build imports `actual-css/css/components/close` next to the component that hosts it.
12
+
13
+ ## Basic usage
14
+
15
+ ```html demo
16
+ <div class="cluster">
17
+ <button class="close" type="button" aria-label="Close"></button>
18
+
19
+ <span class="badge primary soft">
20
+ Design
21
+ <button class="close" type="button" aria-label="Remove Design"></button>
22
+ </span>
23
+ </div>
24
+ ```
25
+
26
+ ## In a dialog
27
+
28
+ In a `.modal` or a `.drawer`, the close sits out of the content flow at the
29
+ panel's top inline-end corner. Place it as a direct child of the dialog, of its
30
+ top-level `form` or `header`, or of a `form` inside that header; a `.close`
31
+ deeper in the content keeps its own place. See [Dialog](dialog.md) and
32
+ [Drawer](drawer.md).
33
+
34
+ ```html
35
+ <dialog class="modal" id="details">
36
+ <button class="close" type="button" commandfor="details" command="request-close"
37
+ aria-label="Close dialog"></button>
38
+ …
39
+ </dialog>
40
+ ```
41
+
42
+ ## CSS hooks
43
+
44
+ - `--close-size` — inline and block size of the button; `--control-size` unless the container sets it.
45
+ - `--close-icon-size` — size of the X.
46
+
47
+ Alerts and badges declare both hooks on themselves, so set them on the container
48
+ or on the button.
@@ -61,6 +61,24 @@ metadata.
61
61
  </article>
62
62
  ```
63
63
 
64
+ ## Stacked
65
+
66
+ `.stacked` puts each value under its term instead of beside it, for a narrow
67
+ column or for values too long to share a row.
68
+
69
+ ```html demo
70
+ <dl class="data-list stacked" style="max-inline-size: 16rem">
71
+ <dt>Billing contact</dt>
72
+ <dd>alice@example.com</dd>
73
+
74
+ <dt>Registered address</dt>
75
+ <dd>42 Rue des Fleurs, 1000 Brussels, Belgium</dd>
76
+
77
+ <dt>Renewal</dt>
78
+ <dd><time datetime="2026-09-14">14 Sep 2026</time></dd>
79
+ </dl>
80
+ ```
81
+
64
82
  ## Contract
65
83
 
66
84
  `.data-list` styles one `<dt>` followed by one `<dd>` per property. A valid
@@ -69,4 +87,11 @@ markup does not belong in `.data-list` — use a plain description list instead,
69
87
  where `.prose` already covers the editorial cases.
70
88
 
71
89
  Use `.data-list` for the properties of a single object. Use a `<table>` when
72
- comparing the same properties across multiple objects.
90
+ comparing the same properties across multiple objects.
91
+
92
+ A per-property icon goes inside the `<dt>`, next to the term. There is no
93
+ gutter slot spanning a term and its value: a `<dl>` admits only `<dt>` and
94
+ `<dd>` per pair — directly or inside a `<div>` wrapper — so no third element
95
+ can hold it. When the icon is the point and the term/value relation is not,
96
+ the content is not a description list: reach for `.media` with an `.overline`
97
+ label instead.