actual-css 0.3.1 → 0.4.1

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 (223) hide show
  1. package/CHANGELOG.md +109 -3
  2. package/README.md +35 -30
  3. package/dist/actual.css +66 -4958
  4. package/dist/actual.full.css +7548 -0
  5. package/dist/actual.full.js +1 -0
  6. package/dist/actual.full.min.css +1 -0
  7. package/dist/actual.js +1 -1
  8. package/dist/actual.min.css +1 -1
  9. package/docs/design-notes/app-ui.md +33 -0
  10. package/docs/design-notes/cascade-layer.md +34 -13
  11. package/docs/design-notes/column-layout.md +677 -0
  12. package/docs/design-notes/enhancement-contract.md +25 -0
  13. package/docs/design-notes/forced-colors.md +3 -0
  14. package/docs/design-notes/grid.md +785 -0
  15. package/docs/design-notes/naming.md +15 -0
  16. package/docs/design-notes/references.md +1 -1
  17. package/docs/design-notes/widget-primitives.md +1 -0
  18. package/docs/navigation.json +12 -2
  19. package/docs/pages/components/alert.md +21 -14
  20. package/docs/pages/components/app-navigation.md +34 -0
  21. package/docs/pages/components/aura.md +1 -1
  22. package/docs/pages/components/avatar.md +16 -6
  23. package/docs/pages/components/badge.md +9 -7
  24. package/docs/pages/components/button.md +46 -16
  25. package/docs/pages/components/card.md +17 -9
  26. package/docs/pages/components/chat.md +8 -8
  27. package/docs/pages/components/dialog.md +7 -7
  28. package/docs/pages/components/drawer.md +3 -3
  29. package/docs/pages/components/fab.md +9 -7
  30. package/docs/pages/components/indicator.md +52 -0
  31. package/docs/pages/components/list.md +62 -0
  32. package/docs/pages/components/navbar.md +15 -8
  33. package/docs/pages/components/pagination.md +4 -3
  34. package/docs/pages/components/rating.md +68 -0
  35. package/docs/pages/components/skeleton.md +4 -4
  36. package/docs/pages/components/spinner.md +5 -5
  37. package/docs/pages/components/steps.md +57 -0
  38. package/docs/pages/components/table.md +30 -5
  39. package/docs/pages/enhancements/flyout.md +15 -15
  40. package/docs/pages/enhancements/overview.md +1 -1
  41. package/docs/pages/enhancements/scrollspy.md +6 -6
  42. package/docs/pages/enhancements/status-bar.md +5 -1
  43. package/docs/pages/enhancements/tabs.md +7 -8
  44. package/docs/pages/enhancements/tooltip.md +1 -1
  45. package/docs/pages/examples/overview.md +47 -22
  46. package/docs/pages/forms/choice-cards.md +11 -6
  47. package/docs/pages/forms/controls.md +1 -1
  48. package/docs/pages/forms/detached-actions.md +1 -1
  49. package/docs/pages/forms/floating-field.md +10 -5
  50. package/docs/pages/forms/form-actions.md +1 -1
  51. package/docs/pages/forms/input-policy.md +1 -1
  52. package/docs/pages/forms/joined-controls.md +4 -4
  53. package/docs/pages/forms/labels.md +6 -6
  54. package/docs/pages/forms/otp.md +33 -1
  55. package/docs/pages/forms/overview.md +19 -19
  56. package/docs/pages/forms/password.md +1 -1
  57. package/docs/pages/forms/range.md +1 -1
  58. package/docs/pages/forms/states-and-sizes.md +44 -48
  59. package/docs/pages/forms/sticky-actions.md +3 -3
  60. package/docs/pages/forms/switch.md +110 -2
  61. package/docs/pages/forms/validation.md +9 -9
  62. package/docs/pages/foundations/philosophy.md +1 -1
  63. package/docs/pages/foundations/theming.md +17 -2
  64. package/docs/pages/foundations/tokens.md +22 -10
  65. package/docs/pages/foundations/typography.md +50 -20
  66. package/docs/pages/guides/bootstrap.md +11 -11
  67. package/docs/pages/guides/integrating-actual-css.md +192 -0
  68. package/docs/pages/guides/introduction.md +23 -26
  69. package/docs/pages/guides/modular-import.md +173 -0
  70. package/docs/pages/guides/progressive-enhancement.md +81 -78
  71. package/docs/pages/guides/tailwind.md +6 -8
  72. package/docs/pages/layout/app-layout.md +57 -0
  73. package/docs/pages/layout/app-shell.md +28 -7
  74. package/docs/pages/layout/center.md +12 -1
  75. package/docs/pages/layout/choosing.md +54 -0
  76. package/docs/pages/layout/column-layout.md +206 -0
  77. package/docs/pages/layout/frame.md +6 -1
  78. package/docs/pages/layout/grid.md +133 -102
  79. package/docs/pages/layout/media.md +1 -1
  80. package/docs/pages/layout/overview.md +15 -2
  81. package/docs/pages/layout/scroll-snap.md +2 -2
  82. package/docs/pages/layout/scroll-target.md +45 -0
  83. package/docs/pages/layout/sidebar-layout.md +38 -18
  84. package/docs/pages/layout/stack.md +70 -1
  85. package/docs/pages/layout/switcher.md +10 -26
  86. package/docs/pages/layout/topbar.md +2 -2
  87. package/docs/pages/patterns/actions.md +1 -1
  88. package/docs/pages/patterns/code-block.md +1 -1
  89. package/docs/pages/patterns/exclusive-accordion.md +1 -1
  90. package/docs/pages/patterns/hierarchical-nav.md +1 -1
  91. package/docs/pages/patterns/icon-input.md +1 -1
  92. package/docs/pages/patterns/icon-slot.md +61 -0
  93. package/docs/pages/patterns/intent-nesting.md +1 -1
  94. package/docs/pages/patterns/navigation-list.md +1 -1
  95. package/docs/pages/patterns/overview.md +1 -1
  96. package/docs/pages/patterns/structured-lists.md +38 -40
  97. package/docs/pages/utilities/accessibility.md +3 -3
  98. package/docs/pages/utilities/flex.md +15 -15
  99. package/docs/pages/utilities/links.md +4 -4
  100. package/docs/pages/utilities/list.md +8 -8
  101. package/docs/pages/utilities/overflow.md +8 -8
  102. package/docs/pages/utilities/overview.md +1 -1
  103. package/docs/pages/utilities/shape.md +3 -3
  104. package/docs/pages/utilities/sizing-wrapping.md +23 -16
  105. package/docs/pages/utilities/spacing.md +20 -20
  106. package/docs/pages/utilities/text.md +10 -10
  107. package/llms.txt +46 -4
  108. package/package.json +43 -13
  109. package/scripts/reserved-classes.json +278 -0
  110. package/src/cli/actual-css.js +133 -0
  111. package/src/css/actual.css +1 -31
  112. package/src/css/actual.full.css +11 -2
  113. package/src/css/components/alert.css +17 -4
  114. package/src/css/components/app-nav.css +76 -0
  115. package/src/css/components/avatar.css +4 -0
  116. package/src/css/components/badge.css +19 -19
  117. package/src/css/components/breadcrumb.css +1 -1
  118. package/src/css/components/button.css +20 -32
  119. package/src/css/components/card.css +34 -9
  120. package/src/css/{optional → components}/chat.css +0 -6
  121. package/src/css/components/drawer.css +24 -5
  122. package/src/css/{optional → components}/fab.css +1 -7
  123. package/src/css/components/flyout.css +5 -0
  124. package/src/css/components/index.css +37 -27
  125. package/src/css/components/indicator.css +47 -0
  126. package/src/css/components/join.css +8 -1
  127. package/src/css/components/key.css +2 -0
  128. package/src/css/components/list.css +83 -0
  129. package/src/css/components/menu.css +11 -8
  130. package/src/css/components/meter.css +4 -1
  131. package/src/css/components/modal.css +5 -17
  132. package/src/css/components/navbar.css +6 -15
  133. package/src/css/components/pagination.css +11 -0
  134. package/src/css/components/rating.css +101 -0
  135. package/src/css/components/spinner.css +5 -0
  136. package/src/css/components/status-bar.css +5 -0
  137. package/src/css/components/steps.css +122 -0
  138. package/src/css/components/surface.css +5 -0
  139. package/src/css/components/tab.css +20 -22
  140. package/src/css/components/table.css +6 -0
  141. package/src/css/components/tooltip.css +3 -7
  142. package/src/css/{focus.css → core/focus.css} +0 -8
  143. package/src/css/core/index.css +9 -0
  144. package/src/css/core/print.css +18 -0
  145. package/src/css/{theme.css → core/theme.css} +19 -19
  146. package/src/css/{tokens.css → core/tokens.css} +16 -4
  147. package/src/css/{variants.css → core/variants.css} +3 -2
  148. package/src/css/{optional → effects}/aura.css +1 -10
  149. package/src/css/effects/index.css +2 -0
  150. package/src/css/forms/base.css +8 -0
  151. package/src/css/forms/choice-card.css +7 -15
  152. package/src/css/forms/choice.css +49 -37
  153. package/src/css/forms/control.css +4 -1
  154. package/src/css/forms/custom-select.css +2 -0
  155. package/src/css/{optional → forms}/floating-field.css +25 -7
  156. package/src/css/forms/index.css +10 -13
  157. package/src/css/forms/native.css +15 -2
  158. package/src/css/{optional → forms}/otp.css +25 -16
  159. package/src/css/forms/range.css +14 -4
  160. package/src/css/forms/switch.css +31 -29
  161. package/src/css/layout/app-layout.css +102 -0
  162. package/src/css/layout/app-shell.css +6 -0
  163. package/src/css/layout/center.css +14 -0
  164. package/src/css/layout/cluster.css +21 -0
  165. package/src/css/layout/column-layout.css +144 -0
  166. package/src/css/layout/container-query.css +15 -0
  167. package/src/css/layout/frame.css +19 -0
  168. package/src/css/layout/grid.css +155 -0
  169. package/src/css/layout/index.css +44 -0
  170. package/src/css/layout/measure.css +9 -0
  171. package/src/css/layout/media.css +10 -0
  172. package/src/css/layout/scroll-target.css +15 -0
  173. package/src/css/layout/scroller.css +41 -0
  174. package/src/css/layout/sidebar-layout.css +52 -0
  175. package/src/css/layout/stack.css +21 -0
  176. package/src/css/layout/switcher.css +23 -0
  177. package/src/css/{optional/layout-extra.css → layout/topbar.css} +0 -20
  178. package/src/css/typography/index.css +5 -0
  179. package/src/css/typography/lead.css +7 -0
  180. package/src/css/{components → typography}/overline.css +2 -2
  181. package/src/css/{prose.css → typography/prose.css} +57 -38
  182. package/src/css/{utilities.css → utilities/base.css} +2 -0
  183. package/src/css/utilities/index.css +3 -0
  184. package/src/js/escape.js +36 -0
  185. package/src/js/flyout.js +22 -12
  186. package/src/js/full.js +26 -0
  187. package/src/js/index.js +5 -21
  188. package/src/js/surface.js +1 -31
  189. package/src/js/tooltip.js +48 -19
  190. package/src/js/validation.js +1 -1
  191. package/src/tooling/css-bundle.js +308 -0
  192. package/dist/actual-themes.min.css +0 -1
  193. package/dist/optional.css +0 -1048
  194. package/dist/optional.min.css +0 -1
  195. package/docs/pages/guides/optional-components.md +0 -33
  196. package/docs/pages/layout/list.md +0 -41
  197. package/src/css/grid.css +0 -109
  198. package/src/css/layout.css +0 -123
  199. package/src/css/optional/index.css +0 -10
  200. package/src/css/optional/scroller.css +0 -63
  201. package/src/css/print.css +0 -47
  202. package/src/css/themes/corporate.css +0 -60
  203. package/src/css/themes/cyberpunk.css +0 -60
  204. package/src/css/themes/dim.css +0 -33
  205. package/src/css/themes/edge.css +0 -117
  206. package/src/css/themes/forest.css +0 -65
  207. package/src/css/themes/gradient.css +0 -97
  208. package/src/css/themes/index.css +0 -47
  209. package/src/css/themes/indigo.css +0 -33
  210. package/src/css/themes/lavender.css +0 -66
  211. package/src/css/themes/mono.css +0 -60
  212. package/src/css/themes/neon.css +0 -66
  213. package/src/css/themes/ocean.css +0 -60
  214. package/src/css/themes/petrol.css +0 -64
  215. package/src/css/themes/spruce.css +0 -64
  216. package/src/css/themes/square.css +0 -65
  217. package/src/css/themes/sunset.css +0 -66
  218. /package/src/css/{base.css → core/base.css} +0 -0
  219. /package/src/css/{intents.css → core/intents.css} +0 -0
  220. /package/src/css/{reset.css → core/reset.css} +0 -0
  221. /package/src/css/{optional → layout}/scroll-snap.css +0 -0
  222. /package/src/css/{optional/typography-fluid.css → typography/fluid.css} +0 -0
  223. /package/src/css/{optional/utilities-extra.css → utilities/extra.css} +0 -0
@@ -4,11 +4,11 @@
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- |---|---|---|
9
- | `.field` | Composition | Field wrapper; canonical on `<label>` or `<div>`. |
10
- | `.field-label` | Composition | Label text, element-agnostic — works on `<span>`, a detached `<label for>`, or a `<legend>`. |
11
- | `.required-mark` | Composition | Visual required marker (`aria-hidden`) added inside a label. |
7
+ | Class | Kind | Description |
8
+ |------------------|-------------|----------------------------------------------------------------------------------------------|
9
+ | `.field` | Composition | Field wrapper; canonical on `<label>` or `<div>`. |
10
+ | `.field-label` | Composition | Label text, element-agnostic — works on `<span>`, a detached `<label for>`, or a `<legend>`. |
11
+ | `.required-mark` | Composition | Visual required marker (`aria-hidden`) added inside a label. |
12
12
 
13
13
  Use a **wrapped label** for simple hand-authored forms. The whole label area becomes the click target, there is no `id` noise, and the markup is harder to break.
14
14
 
@@ -39,4 +39,4 @@ Use **`for`/`id`** when the layout needs the label and control in different grid
39
39
  </div>
40
40
  ```
41
41
 
42
- The mixed shape `<label for="x">…<input id="x"></label>` is allowed by the spec but rarely useful. Prefer either pure wrapped or pure `for`/`id`.
42
+ The mixed shape `<label for="x">…<input id="x"></label>` is allowed by the spec but rarely useful. Prefer either pure wrapped or pure `for`/`id`.
@@ -1,6 +1,6 @@
1
1
  # One-time code input
2
2
 
3
- > **Optional** — import `actual-css/css/optional/otp` or `actual-css/css/optional/index`.
3
+ > **Module** — import `actual-css/css/forms/otp` or `actual-css/css/forms/all`.
4
4
 
5
5
  OTP presents one native input as separate visual cells. Keeping a single input
6
6
  preserves paste, selection, autofill, password-manager, and
@@ -40,6 +40,38 @@ The component follows shared `.sm` and `.lg` density through `--control-size`.
40
40
  Use `aria-invalid="true"` for explicit invalid state, or place it in the
41
41
  standard `.needs-validation.was-validated` form flow.
42
42
 
43
+ The `<input>` must be the **first** direct child, with one `<span>` per
44
+ character after it — the state rules (focus, invalid, disabled) select the
45
+ cells as following siblings, so that order is part of the contract.
46
+
47
+ ## Disabled
48
+
49
+ Set `disabled` on the input. The cells rest on `--surface-subtle` with a muted
50
+ `--state-disabled` border at `--disabled-opacity`, the code glyph turns
51
+ `--state-disabled`, and the cursor is `not-allowed` — a clearly distinct,
52
+ non-interactive look that follows the shared disabled contract. A disabled
53
+ `<fieldset>` disables it the same way.
54
+
55
+ ```html demo
56
+ <div class="otp" aria-label="Disabled verification code">
57
+ <input
58
+ class="input"
59
+ type="text"
60
+ autocomplete="one-time-code"
61
+ inputmode="numeric"
62
+ maxlength="6"
63
+ disabled
64
+ aria-label="Disabled verification code"
65
+ />
66
+ <span aria-hidden="true"></span>
67
+ <span aria-hidden="true"></span>
68
+ <span aria-hidden="true"></span>
69
+ <span aria-hidden="true"></span>
70
+ <span aria-hidden="true"></span>
71
+ <span aria-hidden="true"></span>
72
+ </div>
73
+ ```
74
+
43
75
  ## CSS hooks
44
76
 
45
77
  - `--otp-cell-size` — width and height of every cell; follows `--control-size`.
@@ -12,23 +12,23 @@
12
12
 
13
13
  ## Class reference
14
14
 
15
- | Class | Kind | Description |
16
- |---|---|---|
17
- | `.input` | Component | Text input; shares the text-control recipe with `.textarea` and `.select`. |
18
- | `.textarea` | Component | Multi-line text control. |
19
- | `.select` | Component | Dropdown — native baseline with a CSS chevron and an opt-in enhanced picker. |
20
- | `.check` | Component | Styled checkbox. |
21
- | `.radio` | Component | Styled radio button. |
22
- | `.switch` | Component | Toggle switch on a checkbox input with `role="switch"`. |
23
- | `.range` | Component | Native range slider with a theme-aware accent color. |
24
- | `.file` | Component | Native file input with a themed picker button. |
25
- | `.color` | Component | Native color picker in a control-sized box. |
26
- | `.field` | Composition | Field wrapper that works on `<label>` and `<div>`. |
27
- | `.field-label` | Composition | Label text; element-agnostic — `<span>`, detached `<label>`, or `<legend>`. |
28
- | `.field-help` | Composition | Supporting helper text under a control. |
29
- | `.field-error` | Composition | Inline error message shown while the field is invalid. |
30
- | `.field-group` | Composition | Grouped `<fieldset>` container with a tunable legend gutter. |
31
- | `.choice` | Composition | Choice-label layout wrapping a `.check`, `.radio`, or `.switch`. |
15
+ | Class | Kind | Description |
16
+ |-----------------|-------------|-------------------------------------------------------------------------------|
17
+ | `.input` | Component | Text input; shares the text-control recipe with `.textarea` and `.select`. |
18
+ | `.textarea` | Component | Multi-line text control. |
19
+ | `.select` | Component | Dropdown — native baseline with a CSS chevron and an opt-in enhanced picker. |
20
+ | `.check` | Component | Styled checkbox. |
21
+ | `.radio` | Component | Styled radio button. |
22
+ | `.switch` | Component | Toggle switch on a checkbox input with `role="switch"`. |
23
+ | `.range` | Component | Native range slider with a theme-aware accent color. |
24
+ | `.file` | Component | Native file input with a themed picker button. |
25
+ | `.color` | Component | Native color picker in a control-sized box. |
26
+ | `.field` | Composition | Field wrapper that works on `<label>` and `<div>`. |
27
+ | `.field-label` | Composition | Label text; element-agnostic — `<span>`, detached `<label>`, or `<legend>`. |
28
+ | `.field-help` | Composition | Supporting helper text under a control. |
29
+ | `.field-error` | Composition | Inline error message shown while the field is invalid. |
30
+ | `.field-group` | Composition | Grouped `<fieldset>` container with a tunable legend gutter. |
31
+ | `.choice` | Composition | Choice-label layout wrapping a `.check`, `.radio`, or `.switch`. |
32
32
  | `.form-actions` | Composition | Class-only flex row that closes a form; works inside or outside the `<form>`. |
33
33
 
34
34
  ## Example form
@@ -91,7 +91,7 @@ A full form using the control classes, field wrappers, and form actions:
91
91
 
92
92
  <fieldset class="field-group">
93
93
  <legend class="field-label">Availability</legend>
94
- <div class="stack">
94
+ <div class="stack sm">
95
95
  <label class="choice">
96
96
  <input class="radio" type="radio" name="availability" value="available" checked />
97
97
  <span>Available for work</span>
@@ -185,4 +185,4 @@ form[data-required-marks="auto"]
185
185
  }
186
186
  ```
187
187
 
188
- `required` remains the accessible source of truth; this recipe adds only a visual cue. Use `data-required-mark="none"` on a `.field` when a required control should not receive the generated marker.
188
+ `required` remains the accessible source of truth; this recipe adds only a visual cue. Use `data-required-mark="none"` on a `.field` when a required control should not receive the generated marker.
@@ -37,4 +37,4 @@ the attribute instead:
37
37
  A revealed value does not survive navigation: a non-canceled form submit and
38
38
  `pagehide` both revert the input to hidden, so back navigation cannot land on
39
39
  a readable password. A canceled submit (failed validation, ajax) keeps the
40
- value revealed.
40
+ value revealed.
@@ -14,4 +14,4 @@ Core: native range, enhanced by `accent-color`. Optional richer skin may come la
14
14
  ## CSS hooks
15
15
 
16
16
  - `--range-thumb-size` — thumb diameter.
17
- - `--range-track-height` — track thickness.
17
+ - `--range-track-height` — track thickness.
@@ -4,21 +4,28 @@
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- |---|---|---|
7
+ | Class | Kind | Description |
8
+ |-------|----------|------------------------------------------------------------------------------------------|
9
9
  | `.sm` | Modifier | Compact control height and spacing rhythm; sets `--control-size` to `--control-size-sm`. |
10
- | `.lg` | Modifier | Large control height and spacing rhythm; sets `--control-size` to `--control-size-lg`. |
10
+ | `.lg` | Modifier | Large control height and spacing rhythm; sets `--control-size` to `--control-size-lg`. |
11
11
 
12
12
  Use `readonly` for text-like values that can still be focused, selected, and submitted. Use `disabled` for unavailable controls that should not receive focus or submit a value.
13
13
 
14
- The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, selects, textareas, buttons, and switches. Put the modifier on the control itself, or on a `.field` wrapper when the whole field should share that size.
14
+ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, selects, textareas, and buttons. Put the modifier on the control itself, or on a `.field` wrapper when the whole field should share that size.
15
+
16
+ Inline choices — `.check`, `.radio`, `.switch` — are the exception. They sit on
17
+ a line of text rather than standing as a field, so they are sized off the
18
+ control typography (`--choice-control-size`, an em value) and `.sm` / `.lg`
19
+ leave them alone: density changes field heights, not type. Resizing the control
20
+ without its label would only break the alignment. Scale them by scaling their
21
+ type instead.
15
22
 
16
23
  ```html demo
17
24
  <form novalidate>
18
25
  <fieldset class="field-group">
19
26
  <legend class="field-label">Text control states</legend>
20
- <div class="stack container-query">
21
- <div class="grid-3 items-start">
27
+ <div class="stack" style="--grid-min: 14rem;">
28
+ <div class="grid items-start">
22
29
  <label class="field">
23
30
  <span class="field-label">Editable</span>
24
31
  <input class="input" type="text" value="Ocean Beach Clinic" />
@@ -37,7 +44,7 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
37
44
  </label>
38
45
  </div>
39
46
 
40
- <div class="grid-3 items-start">
47
+ <div class="grid items-start">
41
48
  <label class="field">
42
49
  <span class="field-label">Editable notes</span>
43
50
  <textarea class="textarea">Patient asked for an invoice copy.</textarea>
@@ -58,8 +65,8 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
58
65
 
59
66
  <fieldset class="field-group">
60
67
  <legend class="field-label">Choice and select states</legend>
61
- <div class="stack container-query">
62
- <div class="grid-3 items-start">
68
+ <div class="stack" style="--grid-min: 14rem;">
69
+ <div class="grid items-start">
63
70
  <label class="field">
64
71
  <span class="field-label">Editable select</span>
65
72
  <select class="select">
@@ -89,7 +96,7 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
89
96
  </label>
90
97
  </div>
91
98
 
92
- <div class="grid-5 items-start">
99
+ <div class="grid items-start">
93
100
  <label class="choice">
94
101
  <input class="check" type="checkbox" />
95
102
  <span>
@@ -126,12 +133,12 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
126
133
  <input class="check" type="checkbox" id="indet-demo" />
127
134
  <span>
128
135
  <span class="field-label">Indeterminate checkbox</span>
129
- <span class="field-help">Partially selected — set via <code>element.indeterminate = true</code>. Try it: <button class="btn text sm" type="button" onclick="document.getElementById('indet-demo').indeterminate = !document.getElementById('indet-demo').indeterminate">toggle</button></span>
136
+ <span class="field-help">Partially selected — set via <code>element.indeterminate = true</code>. Try it: <button class="btn link sm" type="button" onclick="document.getElementById('indet-demo').indeterminate = !document.getElementById('indet-demo').indeterminate">toggle</button></span>
130
137
  </span>
131
138
  </label>
132
139
  </div>
133
140
 
134
- <div class="grid-4 items-start">
141
+ <div class="grid items-start">
135
142
  <label class="choice">
136
143
  <input class="radio" type="radio" name="demo-radio-state" />
137
144
  <span>
@@ -165,7 +172,7 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
165
172
  </label>
166
173
  </div>
167
174
 
168
- <div class="grid-4 items-start">
175
+ <div class="grid items-start">
169
176
  <label class="choice">
170
177
  <input class="switch" type="checkbox" role="switch" />
171
178
  <span>
@@ -203,8 +210,8 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
203
210
 
204
211
  <fieldset class="field-group">
205
212
  <legend class="field-label">Control sizes</legend>
206
- <div class="stack container-query">
207
- <div class="grid-3 items-start">
213
+ <div class="stack" style="--grid-min: 14rem;">
214
+ <div class="grid items-start">
208
215
  <label class="field sm">
209
216
  <span class="field-label">Small field</span>
210
217
  <input class="input" type="text" value="sm" />
@@ -221,7 +228,7 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
221
228
  </label>
222
229
  </div>
223
230
 
224
- <div class="grid-3 items-start">
231
+ <div class="grid items-start">
225
232
  <label class="field sm">
226
233
  <span class="field-label">Small select</span>
227
234
  <select class="select">
@@ -244,7 +251,7 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
244
251
  </label>
245
252
  </div>
246
253
 
247
- <div class="grid-3 items-start">
254
+ <div class="grid items-start">
248
255
  <label class="field sm">
249
256
  <span class="field-label">Small textarea</span>
250
257
  <textarea class="textarea" rows="2">Short note</textarea>
@@ -261,61 +268,50 @@ The `.sm` and `.lg` modifiers set the shared control tokens used by inputs, sele
261
268
  </label>
262
269
  </div>
263
270
 
264
- <div class="grid-3 items-start">
271
+ <div class="grid items-start">
265
272
  <button class="btn outline sm" type="button">Small</button>
266
273
  <button class="btn outline" type="button">Default</button>
267
274
  <button class="btn outline lg" type="button">Large</button>
268
275
  </div>
269
276
 
270
- <div class="grid-4 items-start">
277
+ <!-- Density is a deliberate no-op on inline choices: these three are
278
+ identical even though the modifiers differ. -->
279
+ <div class="grid items-start">
271
280
  <label class="choice sm">
272
281
  <input class="check" type="checkbox" checked />
273
- <span>Small checkbox</span>
282
+ <span>.sm checkbox</span>
274
283
  </label>
275
284
  <label class="choice">
276
- <input class="check" type="checkbox" checked />
277
- <span>Default checkbox</span>
285
+ <input class="radio" type="radio" name="demo-radio-size" checked />
286
+ <span>Default radio</span>
278
287
  </label>
279
288
  <label class="choice lg">
280
- <input class="check" type="checkbox" checked />
281
- <span>Large checkbox</span>
289
+ <input class="switch" type="checkbox" role="switch" checked />
290
+ <span>.lg switch</span>
282
291
  </label>
283
292
  <label class="choice">
284
293
  <input class="check" type="checkbox" id="indet-size" />
285
- <span>Indeterminate <button class="btn text sm" type="button" onclick="document.getElementById('indet-size').indeterminate = !document.getElementById('indet-size').indeterminate">toggle</button></span>
294
+ <span>Indeterminate <button class="btn link sm" type="button" onclick="document.getElementById('indet-size').indeterminate = !document.getElementById('indet-size').indeterminate">toggle</button></span>
286
295
  </label>
287
296
  </div>
288
297
 
289
- <div class="grid-3 items-start">
290
- <label class="choice sm">
291
- <input class="radio" type="radio" name="demo-radio-size" checked />
292
- <span>Small radio</span>
293
- </label>
298
+ <!-- Typography is the knob that does resize them — control and label
299
+ together, so the alignment survives. -->
300
+ <div class="grid items-start" style="--control-font-size: var(--font-size-sm);">
294
301
  <label class="choice">
295
- <input class="radio" type="radio" name="demo-radio-size" />
296
- <span>Default radio</span>
297
- </label>
298
- <label class="choice lg">
299
- <input class="radio" type="radio" name="demo-radio-size" />
300
- <span>Large radio</span>
301
- </label>
302
- </div>
303
-
304
- <div class="grid-3 items-start">
305
- <label class="choice sm">
306
- <input class="switch" type="checkbox" role="switch" checked />
307
- <span>Small switch</span>
302
+ <input class="check" type="checkbox" checked />
303
+ <span>Smaller type</span>
308
304
  </label>
309
305
  <label class="choice">
310
- <input class="switch" type="checkbox" role="switch" checked />
311
- <span>Default switch</span>
306
+ <input class="radio" type="radio" name="demo-radio-type" checked />
307
+ <span>Smaller type</span>
312
308
  </label>
313
- <label class="choice lg">
309
+ <label class="choice">
314
310
  <input class="switch" type="checkbox" role="switch" checked />
315
- <span>Large switch</span>
311
+ <span>Smaller type</span>
316
312
  </label>
317
313
  </div>
318
314
  </div>
319
315
  </fieldset>
320
316
  </form>
321
- ```
317
+ ```
@@ -57,7 +57,7 @@ Scroll the page to see the actions stay reachable.
57
57
 
58
58
  <fieldset class="field-group">
59
59
  <legend class="field-label">Theme</legend>
60
- <div class="stack">
60
+ <div class="stack sm">
61
61
  <label class="choice">
62
62
  <input class="radio" type="radio" name="theme" value="light" checked />
63
63
  <span>Light</span>
@@ -141,7 +141,7 @@ Scroll the page to see the detached footer stay reachable.
141
141
 
142
142
  <fieldset class="field-group">
143
143
  <legend class="field-label">Theme</legend>
144
- <div class="stack">
144
+ <div class="stack sm">
145
145
  <label class="choice">
146
146
  <input class="radio" type="radio" name="theme-detached" value="light" checked />
147
147
  <span>Light</span>
@@ -184,4 +184,4 @@ Scroll the page to see the detached footer stay reachable.
184
184
  }
185
185
  ```
186
186
 
187
- A sticky footer can cover the last field on short pages. That is a page-layout concern, not a framework concern. Add bottom padding to the page or scroll container if needed.
187
+ A sticky footer can cover the last field on short pages. That is a page-layout concern, not a framework concern. Add bottom padding to the page or scroll container if needed.
@@ -21,11 +21,119 @@
21
21
  onchange="this.setAttribute('aria-checked', this.checked)" />
22
22
  ```
23
23
 
24
+ Wrap a switch in `.choice` to pair it with a label. The switch stays
25
+ top-aligned with the first line of text, so labels that wrap onto two lines —
26
+ naturally or via an explicit line break — still sit cleanly.
27
+
28
+ ```html demo
29
+ <label class="choice">
30
+ <input class="switch" type="checkbox" role="switch" checked />
31
+ <span>
32
+ Release alerts
33
+ <span class="field-help">Send status changes to the release channel.</span>
34
+ </span>
35
+ </label>
36
+ ```
37
+
38
+ ```html demo
39
+ <!-- Multi-line labels keep the switch aligned with the first line. -->
40
+ <label class="choice">
41
+ <input class="switch" type="checkbox" role="switch" />
42
+ <span>
43
+ Send a summary of account activity for the past month, including any
44
+ unusual sign-ins, to the address on file.
45
+ <span class="field-help">A long label that wraps onto a second line.</span>
46
+ </span>
47
+ </label>
48
+ ```
49
+
50
+ ```html demo
51
+ <!-- Explicit line break, top-aligned with the first line. -->
52
+ <label class="choice">
53
+ <input class="switch" type="checkbox" role="switch" />
54
+ <span>
55
+ Weekly summary with a<br />manual line break
56
+ <span class="field-help">A forced two-line label.</span>
57
+ </span>
58
+ </label>
59
+ ```
60
+
61
+ ## Sizing and alignment
62
+
63
+ A switch is sized off the control typography, not field density. Its track
64
+ comes from `--choice-control-size` — the same token a checkbox and a radio use
65
+ — so the three sit on a line of text at matching weight. `.sm` and `.lg` change
66
+ `--control-size`, which is field height, and deliberately leave inline choices
67
+ alone.
68
+
69
+ The offset that centers each control on the first line of its label is derived
70
+ from that control's own height, so a checkbox and a taller switch land on the
71
+ same optical line. Change the type or the control size and they stay level:
72
+
73
+ ```html demo
74
+ <!-- Same row, three heights: 18px checkbox, 18px radio, 20px switch track. -->
75
+ <div class="cluster">
76
+ <label class="choice">
77
+ <input class="check" type="checkbox" checked />
78
+ <span>Checkbox</span>
79
+ </label>
80
+ <label class="choice">
81
+ <input class="radio" type="radio" name="align-default" checked />
82
+ <span>Radio</span>
83
+ </label>
84
+ <label class="choice">
85
+ <input class="switch" type="checkbox" role="switch" checked />
86
+ <span>Switch</span>
87
+ </label>
88
+ </div>
89
+ ```
90
+
91
+ ```html demo
92
+ <!-- A looser line-height re-centers every control by the same rule. -->
93
+ <div class="cluster" style="line-height: 2.2;">
94
+ <label class="choice">
95
+ <input class="check" type="checkbox" checked />
96
+ <span>Checkbox</span>
97
+ </label>
98
+ <label class="choice">
99
+ <input class="radio" type="radio" name="align-tall" checked />
100
+ <span>Radio</span>
101
+ </label>
102
+ <label class="choice">
103
+ <input class="switch" type="checkbox" role="switch" checked />
104
+ <span>Switch</span>
105
+ </label>
106
+ </div>
107
+ ```
108
+
109
+ ```html demo
110
+ <!-- Scaling --choice-control-size grows the checkbox and the switch
111
+ track together, and the row stays aligned. -->
112
+ <div class="cluster" style="--choice-control-size: 1.5em;">
113
+ <label class="choice">
114
+ <input class="check" type="checkbox" checked />
115
+ <span>Checkbox</span>
116
+ </label>
117
+ <label class="choice">
118
+ <input class="radio" type="radio" name="align-big" checked />
119
+ <span>Radio</span>
120
+ </label>
121
+ <label class="choice">
122
+ <input class="switch" type="checkbox" role="switch" checked />
123
+ <span>Switch</span>
124
+ </label>
125
+ </div>
126
+ ```
127
+
24
128
  ## CSS hooks
25
129
 
130
+ - `--choice-control-size` — shared size for inline choices; the switch track is
131
+ this plus `0.125em`, so scaling it scales the checkbox and the switch together.
26
132
  - `--switch-width` — track width.
27
133
  - `--switch-block-size` — track height.
28
134
  - `--switch-knob-margin` — inset between knob and track.
29
135
 
30
- Knob size and travel distance are derived from these three; set the three rather
31
- than the derived values. Prefer `.sm` / `.lg`, which change `--control-size`.
136
+ Knob size and travel distance are derived from the three `--switch-*` hooks; set
137
+ those rather than the derived values. The knob is concentric in its track: the
138
+ inset is measured inside the border on both axes, so it clears the rails by the
139
+ same distance it clears the ends.
@@ -4,14 +4,14 @@
4
4
 
5
5
  ## Class reference
6
6
 
7
- | Class | Kind | Description |
8
- |---|---|---|
9
- | `.field-error` | Composition | Canonical inline error message; shown while the field is invalid. |
10
- | `.field-help` | Composition | Canonical helper text; carries supporting copy before and after validation. |
11
- | `.field.danger` | Modifier | Manual invalid marking on the field wrapper. |
12
- | `.needs-validation` | Modifier | Opt-in class for the validation enhancer and `:user-invalid` styling. |
13
- | `.was-validated` | Modifier | Added to the form by the enhancer after the first submit attempt. |
14
- | `.status-bar` | Component | Optional status element that shows `data-validation-message` on invalid submit. |
7
+ | Class | Kind | Description |
8
+ |---------------------|-------------|---------------------------------------------------------------------------------|
9
+ | `.field-error` | Composition | Canonical inline error message; shown while the field is invalid. |
10
+ | `.field-help` | Composition | Canonical helper text; carries supporting copy before and after validation. |
11
+ | `.field.danger` | Modifier | Manual invalid marking on the field wrapper. |
12
+ | `.needs-validation` | Modifier | Opt-in class for the validation enhancer and `:user-invalid` styling. |
13
+ | `.was-validated` | Modifier | Added to the form by the enhancer after the first submit attempt. |
14
+ | `.status-bar` | Component | Optional status element that shows `data-validation-message` on invalid submit. |
15
15
 
16
16
  - `.field-error` is the canonical error message. It is wired to `aria-invalid="true"` via the input, so the visual border and the assistive-tech announcement move together.
17
17
  - `.field-help` is the canonical helper text. It carries supporting copy before and after validation.
@@ -238,4 +238,4 @@ Invalid visuals converge across the three ways a field can be flagged:
238
238
  `:user-invalid`. Each sets the danger border, and a checked or indeterminate
239
239
  `.check`, `.radio`, or `.switch` also flips its fill to danger so border and
240
240
  fill signal together. Choice cards surface the same invalid state on the card
241
- border. The exact selector coverage is asserted by `tests/validation.test.js`.
241
+ border. The exact selector coverage is asserted by `tests/validation.test.js`.
@@ -85,4 +85,4 @@ instead of consuming the shared tokens.
85
85
 
86
86
  When using a component, child elements follow the semantic pattern of that component. For example, `.avatar` accepts `<img>`, `<picture>`, text content, an empty `.badge` for status dots, and can be a `<button>` or `<a>`.
87
87
 
88
- No child classes like `.avatar-img` are needed.
88
+ No child classes like `.avatar-img` are needed.
@@ -20,7 +20,7 @@ Set `--cluster-align`.
20
20
 
21
21
  ### Change spacing without changing control size
22
22
 
23
- Set `--gap` locally on the layout primitive. The optional `utilities-extra.css` layer ships `.gap-sm` / `.gap-md` / `.gap-lg` for row/column containers.
23
+ Set `--gap` locally on the layout primitive. The extra utilities (`src/css/utilities/extra.css`) ship `.gap-sm` / `.gap-md` / `.gap-lg` for row/column containers.
24
24
 
25
25
  → Layout
26
26
 
@@ -56,7 +56,7 @@ Set `--form-actions-justify` (and `--form-actions-align`).
56
56
 
57
57
  ### Customize a switch's size
58
58
 
59
- Change `--switch-width`, `--switch-block-size`, and `--switch-knob-margin`; knob size and travel are derived from them. Prefer `.sm` / `.lg` first.
59
+ Change `--switch-width`, `--switch-block-size`, and `--switch-knob-margin`; knob size and travel are derived from them. To rescale the whole row of inline choices at once, set `--choice-control-size` instead — the checkbox, the radio, and the switch track all follow it. `.sm` / `.lg` are field density and leave inline choices alone.
60
60
 
61
61
  → Forms · Switch
62
62
 
@@ -64,6 +64,12 @@ Change `--switch-width`, `--switch-block-size`, and `--switch-knob-margin`; knob
64
64
 
65
65
  Set `--disabled-opacity`. All disabled-prone components — buttons, tabs, menu items, badges, avatars, choice cards, native inputs, and range sliders — read it for their dimmed state.
66
66
 
67
+ ### Keep bare native controls on-theme / accent-color
68
+
69
+ Native controls that no component class restyles — a bare checkbox, radio, range, or `<progress>` — follow the theme through `accent-color: var(--primary)`, declared on `:root` and every `[data-theme]` island so each island recomputes it from its own palette. Component classes (`.check`, `.radio`, `.range`) paint their own rendering and do not read it.
70
+
71
+ → Forms · Overview · Foundations · Tokens (theme contract)
72
+
67
73
  ### Loading / busy overlay on a container
68
74
 
69
75
  Use `aria-busy="true"` with a direct last-child `.spinner`; tint the overlay with `--busy-overlay-bg`.
@@ -97,3 +103,12 @@ surfaces — must use another palette. More specific component states or explici
97
103
  surface variants (`.card.subtle`) still take precedence.
98
104
 
99
105
  → Components · Card · Foundations · Tokens (theme contract)
106
+
107
+ ### Use or adapt a preset palette
108
+
109
+ `src/css/themes/` holds example palettes (`ocean`, `spruce`, `neon`, `brutalist`, …) as
110
+ reference material to copy into your own `[data-theme]` island — they are demo
111
+ assets, not package entrypoints. Each sets the intent/surface/text tokens and
112
+ lets the core derive everything else. In browsers with `color-mix()` support,
113
+ `--focus-ring` follows each island's `--focus` automatically; re-declare it only
114
+ for a deliberate visual override or a matching legacy fallback.