actual-css 0.2.0 → 0.3.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 (214) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/README.md +24 -9
  3. package/dist/actual-themes.min.css +1 -0
  4. package/dist/actual.css +1532 -1114
  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 +7 -6
  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 +210 -0
  25. package/docs/pages/components/card.md +195 -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 +43 -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 +99 -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 +59 -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 +28 -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 +55 -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 +32 -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 +21 -12
  104. package/src/css/actual.css +16 -2
  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 +139 -102
  109. package/src/css/components/avatar.css +16 -10
  110. package/src/css/components/badge.css +69 -42
  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 +84 -34
  114. package/src/css/components/card.css +26 -38
  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 +30 -23
  124. package/src/css/components/overline.css +8 -9
  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 +18 -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 +32 -18
  149. package/src/css/intents.css +2 -2
  150. package/src/css/layout.css +45 -2
  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 +8 -4
  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 +51 -66
  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 +187 -136
  198. package/src/js/tab.js +36 -53
  199. package/src/js/tooltip.js +70 -35
  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
@@ -0,0 +1,42 @@
1
+ # Labels
2
+
3
+ > The field wrapper is `.field`. Choose between a wrapped `<label>` and a detached `for`/`id` pair based on the layout.
4
+
5
+ ## Class reference
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. |
12
+
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
+
15
+ ```html demo
16
+ <label class="field">
17
+ <span class="field-label">Full name</span>
18
+ <input class="input" type="text" name="name" autocomplete="name" />
19
+ </label>
20
+ ```
21
+
22
+ Use **`for`/`id`** when the layout needs the label and control in different grid areas, when the markup is generated by a backend or form library, or when a single label is paired with multiple controls. `.field-label` is element-agnostic, so it works on a detached `<label>`.
23
+
24
+ ```html demo
25
+ <div class="field">
26
+ <label class="field-label" for="profile-email">
27
+ Email address <span class="required-mark" aria-hidden="true">*</span>
28
+ </label>
29
+ <input class="input"
30
+ id="profile-email"
31
+ name="email"
32
+ type="email"
33
+ autocomplete="email"
34
+ required
35
+ aria-describedby="profile-email-help" />
36
+ <span class="field-help" id="profile-email-help">
37
+ We only use this for account notifications.
38
+ </span>
39
+ </div>
40
+ ```
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`.
@@ -0,0 +1,50 @@
1
+ # One-time code input
2
+
3
+ > **Optional** — import `actual-css/css/optional/otp` or `actual-css/css/optional/index`.
4
+
5
+ OTP presents one native input as separate visual cells. Keeping a single input
6
+ preserves paste, selection, autofill, password-manager, and
7
+ `autocomplete="one-time-code"` behavior without JavaScript.
8
+
9
+ ```html demo
10
+ <label class="field">
11
+ <span class="field-label">Verification code</span>
12
+ <span class="otp">
13
+ <input
14
+ class="input"
15
+ type="text"
16
+ autocomplete="one-time-code"
17
+ inputmode="numeric"
18
+ maxlength="6"
19
+ pattern="[0-9]{6}"
20
+ required
21
+ aria-describedby="otp-help"
22
+ />
23
+ <span aria-hidden="true"></span>
24
+ <span aria-hidden="true"></span>
25
+ <span aria-hidden="true"></span>
26
+ <span aria-hidden="true"></span>
27
+ <span aria-hidden="true"></span>
28
+ <span aria-hidden="true"></span>
29
+ </span>
30
+ <span class="field-help" id="otp-help">Enter the six-digit code.</span>
31
+ </label>
32
+ ```
33
+
34
+ Add exactly one visual `span` per expected character. Their count must match
35
+ `maxlength` and the input's validation pattern. `inputmode="numeric"` requests
36
+ a numeric keyboard but does not enforce numeric input; validate the submitted
37
+ value as usual.
38
+
39
+ The component follows shared `.sm` and `.lg` density through `--control-size`.
40
+ Use `aria-invalid="true"` for explicit invalid state, or place it in the
41
+ standard `.needs-validation.was-validated` form flow.
42
+
43
+ ## CSS hooks
44
+
45
+ - `--otp-cell-size` — width and height of every cell; follows `--control-size`.
46
+ - `--otp-gap` — space between cells.
47
+ - `--otp-font-size` — code glyph size; density does not change typography.
48
+
49
+ Surface, border, focus, disabled, and danger colors come from the shared form
50
+ and theme tokens.
@@ -0,0 +1,188 @@
1
+ # Forms
2
+
3
+ > Cohesive set of form elements that share focus styles, helpers, and validation patterns.
4
+
5
+ - Form controls are styled with explicit classes, not by parent scope.
6
+ - Control classes: `.input`, `.textarea`, `.select`, `.check`, `.radio`, `.switch`, `.range`, `.file`, `.color`.
7
+ - Reusable layout classes: `.field`, `.field-label`, `.field-help`, `.field-error`, `.field-group`, `.choice`, `.form-actions`, `.form-actions.sticky`.
8
+ - Floating labels available as optional CSS via `.floating-field` — standard `.field` labels remain the recommended default.
9
+ - Proper focus style that preserves keyboard navigation.
10
+ - `.field-group` and `.choice` provide layout without depending on parent scope.
11
+ - Customizable select is progressive enhancement only — the native select remains the baseline.
12
+
13
+ ## Class reference
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`. |
32
+ | `.form-actions` | Composition | Class-only flex row that closes a form; works inside or outside the `<form>`. |
33
+
34
+ ## Example form
35
+
36
+ A full form using the control classes, field wrappers, and form actions:
37
+
38
+ ```html demo
39
+ <form novalidate>
40
+ <div class="stack">
41
+
42
+ <label class="field">
43
+ <span class="field-label">Full name <span class="required-mark" aria-hidden="true">*</span></span>
44
+ <input class="input" type="text" autocomplete="name" required placeholder="Jane Doe" />
45
+ </label>
46
+
47
+ <label class="field">
48
+ <span class="field-label">Email address <span class="required-mark" aria-hidden="true">*</span></span>
49
+ <input class="input" type="email"
50
+ autocomplete="email"
51
+ required
52
+ aria-invalid="true"
53
+ aria-describedby="email-error"
54
+ value="jane@" />
55
+ <span class="field-error" id="email-error" role="alert">
56
+ Enter a valid email address
57
+ </span>
58
+ </label>
59
+
60
+ <label class="field">
61
+ <span class="field-label">Website</span>
62
+ <input class="input" type="url"
63
+ autocomplete="url"
64
+ aria-describedby="website-help"
65
+ placeholder="https://example.com" />
66
+ <span class="field-help" id="website-help">Your public portfolio or personal site</span>
67
+ </label>
68
+
69
+ <label class="field">
70
+ <span class="field-label">Bio</span>
71
+ <textarea class="textarea" aria-describedby="bio-help"
72
+ maxlength="200"
73
+ placeholder="Tell us a bit about yourself…"></textarea>
74
+ <span class="field-help" id="bio-help">Max 200 characters. Shown on your public profile.</span>
75
+ </label>
76
+
77
+ <label class="field">
78
+ <span class="field-label">Language</span>
79
+ <select class="select" autocomplete="language">
80
+ <option value="">Choose a language</option>
81
+ <optgroup label="Available languages">
82
+ <option value="en" selected>English</option>
83
+ <option value="fr">Français</option>
84
+ <option value="nl">Nederlands</option>
85
+ </optgroup>
86
+ <optgroup label="Not yet available">
87
+ <option value="de" disabled>Deutsch</option>
88
+ </optgroup>
89
+ </select>
90
+ </label>
91
+
92
+ <fieldset class="field-group">
93
+ <legend class="field-label">Availability</legend>
94
+ <div class="stack">
95
+ <label class="choice">
96
+ <input class="radio" type="radio" name="availability" value="available" checked />
97
+ <span>Available for work</span>
98
+ </label>
99
+ <label class="choice">
100
+ <input class="radio" type="radio" name="availability" value="open" />
101
+ <span>Open to opportunities</span>
102
+ </label>
103
+ <label class="choice">
104
+ <input class="radio" type="radio" name="availability" value="unavailable" />
105
+ <span>Not available</span>
106
+ </label>
107
+ </div>
108
+ </fieldset>
109
+
110
+ <label class="field">
111
+ <span class="field-label">Experience level</span>
112
+ <input class="range" type="range"
113
+ min="0"
114
+ max="10"
115
+ value="5"
116
+ aria-valuetext="5 years"
117
+ aria-describedby="range-help" />
118
+ <span class="field-help" id="range-help">Years of professional experience</span>
119
+ </label>
120
+
121
+ <label class="choice">
122
+ <input class="switch" type="checkbox" role="switch" checked />
123
+ <span>
124
+ <span class="field-label">Email notifications</span>
125
+ <span class="field-help">Receive updates about your account activity</span>
126
+ </span>
127
+ </label>
128
+
129
+ <label class="choice">
130
+ <input class="check" type="checkbox" />
131
+ <span>
132
+ I agree to the <a href="#">terms of service</a>
133
+ <span class="field-help">Required to create an account</span>
134
+ </span>
135
+ </label>
136
+
137
+ <label class="field">
138
+ <span class="field-label">Profile picture</span>
139
+ <input class="file" type="file" accept="image/*" aria-describedby="file-help" />
140
+ <span class="field-help" id="file-help">JPG, PNG or WebP — max 2 MB</span>
141
+ </label>
142
+
143
+ <label class="field">
144
+ <span class="field-label">Username</span>
145
+ <input class="input" type="text"
146
+ value="janedoe"
147
+ disabled
148
+ aria-describedby="username-help" />
149
+ <span class="field-help" id="username-help">Contact support to change your username</span>
150
+ </label>
151
+
152
+ </div>
153
+
154
+ <div class="form-actions">
155
+ <button type="button" class="btn outline">Cancel</button>
156
+ <button type="submit" class="btn primary">Save changes</button>
157
+ </div>
158
+ </form>
159
+ ```
160
+
161
+ ## Notes
162
+
163
+ - `required` is enough on native inputs. `aria-required="true"` is redundant and not used in these examples.
164
+ - `required` is the semantic source of truth. Add `<span class="required-mark" aria-hidden="true">*</span>` inside a label only when a visual required marker is wanted; the framework does not add one automatically.
165
+ - `.field` is the canonical field wrapper. It works on `<label>` and `<div>`.
166
+ - `.field-label` is element-agnostic. Use it on `<span>` inside a wrapped label, on `<label for="…">` in a detached layout, or on `<legend>` inside a fieldset.
167
+ - `.field-group` styles a `<fieldset class="field-group">` as a grouped field container. Its legend gutter is tunable with `--fieldset-legend-padding-inline` (`var(--space-10)`).
168
+ - Use native `<optgroup>` and `disabled` options in `.select` controls when applicable; the customizable picker preserves both as a progressive enhancement.
169
+ - `.choice` is the choice-label API. Use `.check` or `.radio` on the nested control. The control goes first, the label group second, so multi-line labels align the control with the first line.
170
+ - For a switch, use `class="switch"` and `role="switch"`.
171
+ - Core range controls stay native and are enhanced by `accent-color`. An optional richer range skin may come later if real projects need it.
172
+ - `.form-actions` carries a default top margin. Override it with `--form-actions-margin-block-start`, `--form-actions-align`, or `--form-actions-justify`. It is class-only and may live inside or outside `<form>`. See Detached Actions below.
173
+ - `.join` visually joins adjacent controls into a single unit. Use `.join-addon` for static prefix/suffix content. The container still carries `role="group"` for accessibility.
174
+
175
+ ## Optional automatic required marks
176
+
177
+ If a form consistently marks every required field, opt into automatic visual markers in application CSS. Scope the rule to that form so other forms stay explicit, and retain a per-field opt-out:
178
+
179
+ ```css
180
+ form[data-required-marks="auto"]
181
+ .field:not([data-required-mark="none"]):has(:required)
182
+ > .field-label::after {
183
+ content: " *";
184
+ color: var(--danger);
185
+ }
186
+ ```
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.
@@ -0,0 +1,40 @@
1
+ # Password Toggle
2
+
3
+ > A password field reveals its value through a linked button. The markup is a
4
+ > plain command invoker; the behavior ships with the JS runtime
5
+ > (`actual-css/js` or `actual-css/js/password`).
6
+
7
+ Point a `button` at the input with `commandfor` and
8
+ `command="--password-toggle"`. Use `.join` when the button should sit attached
9
+ to the field.
10
+
11
+ ```html demo
12
+ <div class="field">
13
+ <label class="field-label" for="pw">Password</label>
14
+ <div class="join">
15
+ <input class="input" type="password" id="pw" name="password"
16
+ autocomplete="current-password" required />
17
+ <button class="btn outline" type="button" commandfor="pw"
18
+ command="--password-toggle" aria-controls="pw"
19
+ aria-label="Show password" aria-pressed="false"><i class="ti ti-eye" aria-hidden="true"></i></button>
20
+ </div>
21
+ </div>
22
+ ```
23
+
24
+ Start with `aria-pressed="false"`; this makes the toggle semantics available
25
+ before JavaScript runs. The runtime flips the input between `type="password"`
26
+ and `type="text"` and mirrors the state on every linked trigger, which
27
+ `.btn[aria-pressed="true"]` already styles. Keep the trigger label stable
28
+ ("Show password") — `aria-pressed` announces the state, so a label that flips
29
+ to "Hide" reads as a double negation to screen readers. Style an icon swap off
30
+ the attribute instead:
31
+
32
+ ```css
33
+ [command="--password-toggle"][aria-pressed="true"] .icon-show { display: none; }
34
+ [command="--password-toggle"]:not([aria-pressed="true"]) .icon-hide { display: none; }
35
+ ```
36
+
37
+ A revealed value does not survive navigation: a non-canceled form submit and
38
+ `pagehide` both revert the input to hidden, so back navigation cannot land on
39
+ a readable password. A canceled submit (failed validation, ajax) keeps the
40
+ value revealed.
@@ -0,0 +1,17 @@
1
+ # Range
2
+
3
+ > Native range inputs with theme-aware accent color.
4
+
5
+ Core: native range, enhanced by `accent-color`. Optional richer skin may come later.
6
+
7
+ ```html demo
8
+ <label class="field">
9
+ <span class="field-label">Volume</span>
10
+ <input class="range" type="range" min="0" max="100" value="50" />
11
+ </label>
12
+ ```
13
+
14
+ ## CSS hooks
15
+
16
+ - `--range-thumb-size` — thumb diameter.
17
+ - `--range-track-height` — track thickness.
@@ -0,0 +1,321 @@
1
+ # States And Sizes
2
+
3
+ > Read-only, disabled, and shared size modifiers for form controls.
4
+
5
+ ## Class reference
6
+
7
+ | Class | Kind | Description |
8
+ |---|---|---|
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`. |
11
+
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
+
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.
15
+
16
+ ```html demo
17
+ <form novalidate>
18
+ <fieldset class="field-group">
19
+ <legend class="field-label">Text control states</legend>
20
+ <div class="stack container-query">
21
+ <div class="grid-3 items-start">
22
+ <label class="field">
23
+ <span class="field-label">Editable</span>
24
+ <input class="input" type="text" value="Ocean Beach Clinic" />
25
+ </label>
26
+
27
+ <label class="field">
28
+ <span class="field-label">Read-only</span>
29
+ <input class="input" type="text" value="INV-2048" readonly />
30
+ <span class="field-help">Focusable and submitted with the form.</span>
31
+ </label>
32
+
33
+ <label class="field">
34
+ <span class="field-label">Disabled</span>
35
+ <input class="input" type="text" value="Locked by billing" disabled />
36
+ <span class="field-help">Unavailable and not submitted.</span>
37
+ </label>
38
+ </div>
39
+
40
+ <div class="grid-3 items-start">
41
+ <label class="field">
42
+ <span class="field-label">Editable notes</span>
43
+ <textarea class="textarea">Patient asked for an invoice copy.</textarea>
44
+ </label>
45
+
46
+ <label class="field">
47
+ <span class="field-label">Read-only notes</span>
48
+ <textarea class="textarea" readonly>Insurance details imported from the patient record.</textarea>
49
+ </label>
50
+
51
+ <label class="field">
52
+ <span class="field-label">Disabled notes</span>
53
+ <textarea class="textarea" disabled>Notes are disabled until the transaction is selected.</textarea>
54
+ </label>
55
+ </div>
56
+ </div>
57
+ </fieldset>
58
+
59
+ <fieldset class="field-group">
60
+ <legend class="field-label">Choice and select states</legend>
61
+ <div class="stack container-query">
62
+ <div class="grid-3 items-start">
63
+ <label class="field">
64
+ <span class="field-label">Editable select</span>
65
+ <select class="select">
66
+ <option>Card</option>
67
+ <option>Bank transfer</option>
68
+ <option>Cash</option>
69
+ </select>
70
+ </label>
71
+
72
+ <label class="field">
73
+ <span class="field-label">Invalid select</span>
74
+ <select class="select" aria-invalid="true">
75
+ <option>Choose a method</option>
76
+ <option>Card</option>
77
+ <option>Bank transfer</option>
78
+ </select>
79
+ <span class="field-error">Choose a payment method.</span>
80
+ </label>
81
+
82
+ <label class="field">
83
+ <span class="field-label">Disabled select</span>
84
+ <select class="select" disabled>
85
+ <option>Card</option>
86
+ <option>Bank transfer</option>
87
+ <option>Cash</option>
88
+ </select>
89
+ </label>
90
+ </div>
91
+
92
+ <div class="grid-5 items-start">
93
+ <label class="choice">
94
+ <input class="check" type="checkbox" />
95
+ <span>
96
+ <span class="field-label">Unchecked checkbox</span>
97
+ <span class="field-help">Available and not selected.</span>
98
+ </span>
99
+ </label>
100
+
101
+ <label class="choice">
102
+ <input class="check" type="checkbox" checked />
103
+ <span>
104
+ <span class="field-label">Checked checkbox</span>
105
+ <span class="field-help">Available and selected.</span>
106
+ </span>
107
+ </label>
108
+
109
+ <label class="choice">
110
+ <input class="check" type="checkbox" checked disabled />
111
+ <span>
112
+ <span class="field-label">Disabled checked</span>
113
+ <span class="field-help">Selected but unavailable.</span>
114
+ </span>
115
+ </label>
116
+
117
+ <label class="choice">
118
+ <input class="check" type="checkbox" disabled />
119
+ <span>
120
+ <span class="field-label">Disabled unchecked</span>
121
+ <span class="field-help">Unavailable and not selected.</span>
122
+ </span>
123
+ </label>
124
+
125
+ <label class="choice">
126
+ <input class="check" type="checkbox" id="indet-demo" />
127
+ <span>
128
+ <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>
130
+ </span>
131
+ </label>
132
+ </div>
133
+
134
+ <div class="grid-4 items-start">
135
+ <label class="choice">
136
+ <input class="radio" type="radio" name="demo-radio-state" />
137
+ <span>
138
+ <span class="field-label">Unselected radio</span>
139
+ <span class="field-help">Available and not selected.</span>
140
+ </span>
141
+ </label>
142
+
143
+ <label class="choice">
144
+ <input class="radio" type="radio" name="demo-radio-state" checked />
145
+ <span>
146
+ <span class="field-label">Selected radio</span>
147
+ <span class="field-help">Available and selected.</span>
148
+ </span>
149
+ </label>
150
+
151
+ <label class="choice">
152
+ <input class="radio" type="radio" name="demo-radio-disabled" checked disabled />
153
+ <span>
154
+ <span class="field-label">Disabled selected</span>
155
+ <span class="field-help">Selected but unavailable.</span>
156
+ </span>
157
+ </label>
158
+
159
+ <label class="choice">
160
+ <input class="radio" type="radio" name="demo-radio-disabled" disabled />
161
+ <span>
162
+ <span class="field-label">Disabled unselected</span>
163
+ <span class="field-help">Unavailable and not selected.</span>
164
+ </span>
165
+ </label>
166
+ </div>
167
+
168
+ <div class="grid-4 items-start">
169
+ <label class="choice">
170
+ <input class="switch" type="checkbox" role="switch" />
171
+ <span>
172
+ <span class="field-label">Switch off</span>
173
+ <span class="field-help">Available and inactive.</span>
174
+ </span>
175
+ </label>
176
+
177
+ <label class="choice">
178
+ <input class="switch" type="checkbox" role="switch" checked />
179
+ <span>
180
+ <span class="field-label">Switch on</span>
181
+ <span class="field-help">Available and enabled.</span>
182
+ </span>
183
+ </label>
184
+
185
+ <label class="choice">
186
+ <input class="switch" type="checkbox" role="switch" checked disabled />
187
+ <span>
188
+ <span class="field-label">Disabled on</span>
189
+ <span class="field-help">On but unavailable.</span>
190
+ </span>
191
+ </label>
192
+
193
+ <label class="choice">
194
+ <input class="switch" type="checkbox" role="switch" disabled />
195
+ <span>
196
+ <span class="field-label">Disabled off</span>
197
+ <span class="field-help">Off and unavailable.</span>
198
+ </span>
199
+ </label>
200
+ </div>
201
+ </div>
202
+ </fieldset>
203
+
204
+ <fieldset class="field-group">
205
+ <legend class="field-label">Control sizes</legend>
206
+ <div class="stack container-query">
207
+ <div class="grid-3 items-start">
208
+ <label class="field sm">
209
+ <span class="field-label">Small field</span>
210
+ <input class="input" type="text" value="sm" />
211
+ </label>
212
+
213
+ <label class="field">
214
+ <span class="field-label">Default field</span>
215
+ <input class="input" type="text" value="default" />
216
+ </label>
217
+
218
+ <label class="field lg">
219
+ <span class="field-label">Large field</span>
220
+ <input class="input" type="text" value="lg" />
221
+ </label>
222
+ </div>
223
+
224
+ <div class="grid-3 items-start">
225
+ <label class="field sm">
226
+ <span class="field-label">Small select</span>
227
+ <select class="select">
228
+ <option>Compact</option>
229
+ </select>
230
+ </label>
231
+
232
+ <label class="field">
233
+ <span class="field-label">Default select</span>
234
+ <select class="select">
235
+ <option>Default</option>
236
+ </select>
237
+ </label>
238
+
239
+ <label class="field lg">
240
+ <span class="field-label">Large select</span>
241
+ <select class="select">
242
+ <option>Comfortable</option>
243
+ </select>
244
+ </label>
245
+ </div>
246
+
247
+ <div class="grid-3 items-start">
248
+ <label class="field sm">
249
+ <span class="field-label">Small textarea</span>
250
+ <textarea class="textarea" rows="2">Short note</textarea>
251
+ </label>
252
+
253
+ <label class="field">
254
+ <span class="field-label">Default textarea</span>
255
+ <textarea class="textarea" rows="2">Default note</textarea>
256
+ </label>
257
+
258
+ <label class="field lg">
259
+ <span class="field-label">Large textarea</span>
260
+ <textarea class="textarea" rows="2">Comfortable note</textarea>
261
+ </label>
262
+ </div>
263
+
264
+ <div class="grid-3 items-start">
265
+ <button class="btn outline sm" type="button">Small</button>
266
+ <button class="btn outline" type="button">Default</button>
267
+ <button class="btn outline lg" type="button">Large</button>
268
+ </div>
269
+
270
+ <div class="grid-4 items-start">
271
+ <label class="choice sm">
272
+ <input class="check" type="checkbox" checked />
273
+ <span>Small checkbox</span>
274
+ </label>
275
+ <label class="choice">
276
+ <input class="check" type="checkbox" checked />
277
+ <span>Default checkbox</span>
278
+ </label>
279
+ <label class="choice lg">
280
+ <input class="check" type="checkbox" checked />
281
+ <span>Large checkbox</span>
282
+ </label>
283
+ <label class="choice">
284
+ <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>
286
+ </label>
287
+ </div>
288
+
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>
294
+ <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>
308
+ </label>
309
+ <label class="choice">
310
+ <input class="switch" type="checkbox" role="switch" checked />
311
+ <span>Default switch</span>
312
+ </label>
313
+ <label class="choice lg">
314
+ <input class="switch" type="checkbox" role="switch" checked />
315
+ <span>Large switch</span>
316
+ </label>
317
+ </div>
318
+ </div>
319
+ </fieldset>
320
+ </form>
321
+ ```