actual-css 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/CHANGELOG.md +90 -1
  2. package/README.md +25 -8
  3. package/dist/actual-themes.min.css +1 -0
  4. package/dist/actual.css +1492 -931
  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 +95 -6
  10. package/docs/design-notes/cascade-layer.md +1 -1
  11. package/docs/design-notes/enhancement-contract.md +101 -0
  12. package/docs/design-notes/forced-colors.md +105 -0
  13. package/docs/design-notes/naming.md +3 -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 +119 -0
  17. package/docs/navigation.json +135 -0
  18. package/docs/pages/components/accordion.md +80 -0
  19. package/docs/pages/components/alert.md +203 -0
  20. package/docs/pages/components/aura.md +35 -0
  21. package/docs/pages/components/avatar.md +95 -0
  22. package/docs/pages/components/badge.md +102 -0
  23. package/docs/pages/components/breadcrumb.md +19 -0
  24. package/docs/pages/components/button.md +205 -0
  25. package/docs/pages/components/card.md +183 -0
  26. package/docs/pages/components/chat.md +50 -0
  27. package/docs/pages/components/dialog.md +367 -0
  28. package/docs/pages/components/drawer.md +113 -0
  29. package/docs/pages/components/fab.md +118 -0
  30. package/docs/pages/components/key.md +33 -0
  31. package/docs/pages/components/meter.md +23 -0
  32. package/docs/pages/components/navbar.md +92 -0
  33. package/docs/pages/components/pagination.md +55 -0
  34. package/docs/pages/components/progress.md +29 -0
  35. package/docs/pages/components/skeleton.md +38 -0
  36. package/docs/pages/components/spinner.md +93 -0
  37. package/docs/pages/components/table.md +102 -0
  38. package/docs/pages/enhancements/flyout.md +391 -0
  39. package/docs/pages/enhancements/overview.md +36 -0
  40. package/docs/pages/enhancements/scrollspy.md +71 -0
  41. package/docs/pages/enhancements/status-bar.md +72 -0
  42. package/docs/pages/enhancements/tabs.md +80 -0
  43. package/docs/pages/enhancements/tooltip.md +130 -0
  44. package/docs/pages/examples/overview.md +41 -0
  45. package/docs/pages/forms/choice-cards.md +102 -0
  46. package/docs/pages/forms/controls.md +32 -0
  47. package/docs/pages/forms/detached-actions.md +59 -0
  48. package/docs/pages/forms/floating-field.md +88 -0
  49. package/docs/pages/forms/form-actions.md +56 -0
  50. package/docs/pages/forms/input-policy.md +91 -0
  51. package/docs/pages/forms/joined-controls.md +37 -0
  52. package/docs/pages/forms/labels.md +42 -0
  53. package/docs/pages/forms/otp.md +50 -0
  54. package/docs/pages/forms/overview.md +188 -0
  55. package/docs/pages/forms/password.md +40 -0
  56. package/docs/pages/forms/range.md +17 -0
  57. package/docs/pages/forms/states-and-sizes.md +321 -0
  58. package/docs/pages/forms/sticky-actions.md +187 -0
  59. package/docs/pages/forms/switch.md +31 -0
  60. package/docs/pages/forms/validation.md +241 -0
  61. package/docs/{mental-model.md → pages/foundations/philosophy.md} +33 -18
  62. package/docs/pages/foundations/theming.md +92 -0
  63. package/docs/pages/foundations/tokens.md +421 -0
  64. package/docs/{typography.md → pages/foundations/typography.md} +27 -9
  65. package/docs/pages/guides/bootstrap.md +438 -0
  66. package/docs/pages/guides/introduction.md +157 -0
  67. package/docs/pages/guides/optional-components.md +33 -0
  68. package/docs/pages/guides/progressive-enhancement.md +686 -0
  69. package/docs/pages/guides/tailwind.md +455 -0
  70. package/docs/pages/layout/app-shell.md +25 -0
  71. package/docs/pages/layout/center.md +29 -0
  72. package/docs/pages/layout/cluster.md +57 -0
  73. package/docs/pages/layout/frame.md +30 -0
  74. package/docs/pages/layout/grid.md +142 -0
  75. package/docs/pages/layout/list.md +41 -0
  76. package/docs/pages/layout/media.md +21 -0
  77. package/docs/pages/layout/overview.md +37 -0
  78. package/docs/pages/layout/scroll-snap.md +54 -0
  79. package/docs/pages/layout/sidebar-layout.md +27 -0
  80. package/docs/pages/layout/stack.md +18 -0
  81. package/docs/pages/layout/switcher.md +36 -0
  82. package/docs/pages/layout/topbar.md +23 -0
  83. package/docs/pages/patterns/actions.md +52 -0
  84. package/docs/pages/patterns/code-block.md +36 -0
  85. package/docs/pages/patterns/exclusive-accordion.md +16 -0
  86. package/docs/pages/patterns/hierarchical-nav.md +26 -0
  87. package/docs/pages/patterns/icon-input.md +25 -0
  88. package/docs/pages/patterns/intent-nesting.md +3 -0
  89. package/docs/pages/patterns/navigation-list.md +235 -0
  90. package/docs/pages/patterns/overview.md +58 -0
  91. package/docs/pages/patterns/structured-lists.md +47 -0
  92. package/docs/pages/utilities/accessibility.md +34 -0
  93. package/docs/pages/utilities/flex.md +105 -0
  94. package/docs/pages/utilities/links.md +18 -0
  95. package/docs/pages/utilities/list.md +31 -0
  96. package/docs/pages/utilities/overflow.md +43 -0
  97. package/docs/pages/utilities/overview.md +18 -0
  98. package/docs/pages/utilities/shape.md +25 -0
  99. package/docs/pages/utilities/sizing-wrapping.md +115 -0
  100. package/docs/pages/utilities/spacing.md +79 -0
  101. package/docs/pages/utilities/text.md +43 -0
  102. package/llms.txt +10 -4
  103. package/package.json +30 -16
  104. package/src/css/actual.css +12 -1
  105. package/src/css/base.css +3 -9
  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 +162 -49
  109. package/src/css/components/avatar.css +16 -10
  110. package/src/css/components/badge.css +82 -39
  111. package/src/css/components/breadcrumb.css +7 -15
  112. package/src/css/components/busy.css +7 -0
  113. package/src/css/components/button.css +82 -38
  114. package/src/css/components/card.css +17 -37
  115. package/src/css/components/drawer.css +30 -13
  116. package/src/css/components/flyout.css +39 -110
  117. package/src/css/components/index.css +1 -0
  118. package/src/css/components/join.css +14 -3
  119. package/src/css/components/key.css +2 -0
  120. package/src/css/components/menu.css +125 -0
  121. package/src/css/components/meter.css +0 -23
  122. package/src/css/components/modal.css +162 -7
  123. package/src/css/components/navbar.css +17 -18
  124. package/src/css/components/overline.css +0 -7
  125. package/src/css/components/pagination.css +1 -1
  126. package/src/css/components/progress.css +8 -26
  127. package/src/css/components/scrollspy.css +2 -2
  128. package/src/css/components/skeleton.css +7 -0
  129. package/src/css/components/spinner.css +10 -11
  130. package/src/css/components/status-bar.css +18 -28
  131. package/src/css/components/surface.css +2 -8
  132. package/src/css/components/tab.css +20 -48
  133. package/src/css/components/table.css +7 -5
  134. package/src/css/components/tooltip.css +14 -7
  135. package/src/css/focus.css +16 -3
  136. package/src/css/forms/choice-card.css +13 -15
  137. package/src/css/forms/choice.css +21 -28
  138. package/src/css/forms/control.css +16 -26
  139. package/src/css/forms/custom-select.css +9 -11
  140. package/src/css/forms/form-actions.css +14 -3
  141. package/src/css/forms/form.css +7 -4
  142. package/src/css/forms/index.css +5 -4
  143. package/src/css/forms/input-icon.css +30 -12
  144. package/src/css/forms/native.css +48 -33
  145. package/src/css/forms/range.css +103 -0
  146. package/src/css/forms/select.css +8 -8
  147. package/src/css/forms/switch.css +14 -13
  148. package/src/css/forms/validation.css +32 -5
  149. package/src/css/grid.css +19 -18
  150. package/src/css/intents.css +2 -2
  151. package/src/css/layout.css +29 -1
  152. package/src/css/optional/aura.css +93 -0
  153. package/src/css/optional/chat.css +105 -0
  154. package/src/css/optional/fab.css +104 -0
  155. package/src/css/optional/floating-field.css +87 -0
  156. package/src/css/optional/index.css +5 -0
  157. package/src/css/optional/layout-extra.css +7 -4
  158. package/src/css/optional/otp.css +115 -0
  159. package/src/css/optional/scroll-snap.css +6 -0
  160. package/src/css/optional/scroller.css +19 -7
  161. package/src/css/optional/typography-fluid.css +5 -0
  162. package/src/css/optional/utilities-extra.css +17 -2
  163. package/src/css/prose.css +18 -8
  164. package/src/css/reset.css +0 -10
  165. package/src/css/theme.css +29 -13
  166. package/src/css/themes/cyberpunk.css +1 -1
  167. package/src/css/themes/dim.css +3 -3
  168. package/src/css/themes/edge.css +11 -37
  169. package/src/css/themes/index.css +3 -3
  170. package/src/css/themes/{dark.css → indigo.css} +5 -4
  171. package/src/css/themes/neon.css +5 -5
  172. package/src/css/themes/square.css +0 -3
  173. package/src/css/tokens.css +81 -21
  174. package/src/css/utilities.css +8 -10
  175. package/src/css/variants.css +37 -65
  176. package/src/js/command.js +1 -3
  177. package/src/js/context-menu.js +72 -53
  178. package/src/js/dialog.js +68 -79
  179. package/src/js/dismiss.js +15 -0
  180. package/src/js/enhance.js +76 -19
  181. package/src/js/enhancement-loader.js +301 -0
  182. package/src/js/events.js +3 -0
  183. package/src/js/filter.js +3 -3
  184. package/src/js/floating.js +101 -131
  185. package/src/js/flyout.js +106 -96
  186. package/src/js/focus-group.js +116 -0
  187. package/src/js/focus.js +6 -1
  188. package/src/js/index.js +4 -2
  189. package/src/js/input.js +1 -1
  190. package/src/js/keys.js +27 -0
  191. package/src/js/mask.js +7 -6
  192. package/src/js/menu.js +89 -35
  193. package/src/js/parse-config.js +47 -0
  194. package/src/js/password.js +9 -1
  195. package/src/js/scrollspy.js +133 -62
  196. package/src/js/selectors.js +14 -31
  197. package/src/js/status.js +16 -14
  198. package/src/js/surface.js +219 -125
  199. package/src/js/tab.js +46 -66
  200. package/src/js/tooltip.js +53 -31
  201. package/src/js/validation.js +45 -51
  202. package/dist/actual.js.map +0 -31
  203. package/docs/README.md +0 -26
  204. package/docs/components.md +0 -860
  205. package/docs/design-notes/previous-work.md +0 -5
  206. package/docs/forms.md +0 -1265
  207. package/docs/javascript.md +0 -426
  208. package/docs/layout.md +0 -480
  209. package/docs/patterns.md +0 -415
  210. package/docs/reserved.md +0 -7
  211. package/docs/tokens.md +0 -455
  212. package/docs/ui.md +0 -937
  213. package/docs/utilities.md +0 -384
  214. package/src/css/dialog-fallback.css +0 -56
  215. package/src/js/dialog-fallback.js +0 -163
@@ -0,0 +1,130 @@
1
+ # Tooltip
2
+
3
+ > Supplemental label for a trigger, shown on hover and focus, hidden on dismissal.
4
+
5
+ Tooltips are supplemental: `data-tooltip` on the trigger wires a `.tooltip`
6
+ element (`role="tooltip"`) that appears near the trigger. Position, placement,
7
+ and arrow position are written by JavaScript; the element is shown and hidden
8
+ with the `hidden` attribute.
9
+
10
+ - Use `data-tooltip` on the trigger. With text (`data-tooltip="Help"`), the tooltip element is generated. Empty (`data-tooltip`) marks an explicit tooltip connected via `aria-describedby`.
11
+ - Tooltips are supplemental. Do not put required information or interactive controls inside them.
12
+ - Show on hover and focus. Hide on Escape, blur, pointer leave, or scroll when appropriate.
13
+ - JavaScript can generate tooltip elements from `data-tooltip`.
14
+ - Add `data-tooltip-click` to toggle it on click instead of hover/focus.
15
+ - Add `data-tooltip-visible` to show it immediately and keep it visible while its trigger is in view.
16
+ - Use `data-tooltip-placement` to set the preferred placement (default `top`).
17
+ - The arrow inherits the tooltip background — custom gradients carry through automatically.
18
+ - Avoid relying on the native `title` attribute as the primary implementation.
19
+
20
+ ```html demo
21
+ <button class="btn ghost"
22
+ type="button"
23
+ data-tooltip
24
+ aria-describedby="tooltip-save"
25
+ aria-label="Save">
26
+ <i class="ti ti-device-floppy" aria-hidden="true"></i>
27
+ </button>
28
+
29
+ <div class="tooltip" role="tooltip" id="tooltip-save" hidden>
30
+ Save changes
31
+ </div>
32
+ ```
33
+
34
+ ```html demo
35
+ <button class="btn" type="button" data-tooltip="Save changes">
36
+ Save
37
+ </button>
38
+ ```
39
+
40
+ ## Display modes
41
+
42
+ Click tooltips stay open when the pointer leaves and close on a second click or
43
+ Escape. They remain supplemental, non-interactive content; use a flyout when the
44
+ floating content contains controls.
45
+
46
+ ```html demo
47
+ <p class="cluster">
48
+ <button class="btn outline" type="button"
49
+ data-tooltip="Click again or press Escape to close"
50
+ data-tooltip-click>
51
+ Toggle on click
52
+ </button>
53
+
54
+ <button class="btn outline" type="button"
55
+ data-tooltip="Always visible while this trigger is in view"
56
+ data-tooltip-visible
57
+ data-tooltip-placement="bottom">
58
+ Always visible
59
+ </button>
60
+ </p>
61
+ ```
62
+
63
+ ## HTML and long content
64
+
65
+ The `data-tooltip="…"` shorthand is intentionally plain text. For trusted HTML,
66
+ use an explicit tooltip referenced by `aria-describedby`. Keep its content
67
+ non-interactive and concise; long content wraps up to the tooltip's
68
+ viewport-aware maximum width.
69
+
70
+ ```html demo
71
+ <button class="btn" type="button"
72
+ data-tooltip
73
+ aria-describedby="tip-rich-long"
74
+ data-tooltip-placement="bottom">
75
+ Long HTML tooltip
76
+ </button>
77
+
78
+ <div class="tooltip" role="tooltip" id="tip-rich-long" hidden>
79
+ <strong>Keyboard shortcut:</strong> press <kbd>Ctrl</kbd> + <kbd>K</kbd> to open search.
80
+ This longer explanation demonstrates wrapping when a supplemental label needs a little more context.
81
+ </div>
82
+ ```
83
+
84
+ ## Positioning
85
+
86
+ Set `data-tooltip-placement` to control where the tooltip appears relative to
87
+ its trigger. The arrow follows the placement automatically.
88
+
89
+ ```html demo
90
+ <p>
91
+ <button class="btn outline" type="button" data-tooltip="Above the button" data-tooltip-placement="top">Top</button>
92
+ <button class="btn outline" type="button" data-tooltip="To the right" data-tooltip-placement="right">Right</button>
93
+ <button class="btn outline" type="button" data-tooltip="Below the button" data-tooltip-placement="bottom">Bottom</button>
94
+ <button class="btn outline" type="button" data-tooltip="To the left" data-tooltip-placement="left">Left</button>
95
+ </p>
96
+ ```
97
+
98
+ ## Custom styling
99
+
100
+ Override `--tooltip-bg` and `--tooltip-fg` on the tooltip element to change the
101
+ background and text color. The arrow picks up `background: inherit` so gradients
102
+ and solid colors both work.
103
+
104
+ ```html demo
105
+ <button class="btn primary" type="button"
106
+ data-tooltip
107
+ aria-describedby="tip-grad"
108
+ data-tooltip-placement="right">
109
+ Hover for gradient
110
+ </button>
111
+
112
+ <div class="tooltip" role="tooltip" id="tip-grad" hidden
113
+ style="background-color: #5b2d9e;
114
+ --tooltip-bg: linear-gradient(135deg, oklch(0.45 0.22 280), oklch(0.5 0.2 10));
115
+ --tooltip-fg: white;
116
+ padding: 0.4em 0.8em;
117
+ font-weight: var(--font-weight-medium);">
118
+ <strong>Custom gradient tooltip:</strong> the arrow matches the background, and this longer
119
+ sentence demonstrates how the tooltip wraps and flips when the preferred side lacks space.
120
+ </div>
121
+ ```
122
+
123
+ ## CSS hooks
124
+
125
+ - `--tooltip-bg` — bubble background (defaults to `var(--surface-solid)`); the arrow follows it.
126
+ - `--tooltip-fg` — text color.
127
+ - `--tooltip-arrow-size` — arrow size.
128
+
129
+ `--arrow-x` and `--arrow-y` are written by the positioner at runtime, not set by
130
+ the author. See the JavaScript runtime documentation.
@@ -0,0 +1,41 @@
1
+ # Examples
2
+
3
+ > Real pages built with Actual CSS - an admin application and a set of standalone templates - to see the primitives, components, and patterns composed the way they are meant to be used.
4
+
5
+ The examples live in the repository under `demo/` and load the shipped bundles
6
+ from `dist/` (`actual.css`, `optional.css`, `actual-themes.min.css`,
7
+ `actual.js`), referenced with stable URLs.
8
+
9
+ ## Admin application
10
+
11
+ `demo/admini/` is a complete admin interface: application shell, navigation,
12
+ tables, forms, dialogs, and feedback states.
13
+
14
+ - [Dashboard](../../demo/admini/index.html) - app shell, navbar, stat cards, charts, recent activity
15
+ - [Login](../../demo/admini/login.html) - centered auth card with social actions
16
+ - [Forms](../../demo/admini/forms.html) - inputs, choice cards, validation, joined controls, sticky actions
17
+ - [Tables](../../demo/admini/tables.html) - data tables, status badges, bulk actions
18
+ - [Kanban](../../demo/admini/kanban.html) - drag-free column layout with cards
19
+ - [Chat](../../demo/admini/chat.html) - conversation list, message thread, composer
20
+ - [Calendar](../../demo/admini/calendar.html) - month grid with event cards
21
+ - [Invoice](../../demo/admini/invoice.html) - structured document layout
22
+ - [Profile](../../demo/admini/profile.html) - avatar, tabs, settings rows
23
+ - [Settings](../../demo/admini/settings.html) - grouped settings with switches
24
+ - [User grid](../../demo/admini/user-grid.html) - avatar grid, filters, pagination
25
+ - [404](../../demo/admini/404.html) - empty state
26
+
27
+ ## Templates
28
+
29
+ `demo/templates/` are standalone pages covering common page shapes.
30
+
31
+ - [Kitchen sink](../../demo/templates/kitchen-sink.html) - every component and variant on one page
32
+ - [Keyboard controls](../../demo/templates/keyboard-controls.html) - hands-on keyboard support matrix and focus test bench
33
+ - [Dashboard](../../demo/templates/dashboard.html) - data-heavy overview page
34
+ - [App](../../demo/templates/app.html) - application shell with sidebar
35
+ - [Marketing](../../demo/templates/marketing.html) - landing page with typography and fluid type
36
+ - [Blog](../../demo/templates/blog.html) - article page with prose
37
+ - [Blocks](../../demo/templates/blocks.html) - layout building blocks
38
+ - [Density](../../demo/templates/density.html) - compact control density
39
+
40
+ > Note: the examples reference the repository's source files, so they open from
41
+ > the checkout, not from a standalone copy of `site`.
@@ -0,0 +1,102 @@
1
+ # Choice Cards
2
+
3
+ > Card-style radio and checkbox labels where the whole card acts as a clickable target.
4
+
5
+ ## Class reference
6
+
7
+ | Class | Kind | Description |
8
+ |---|---|---|
9
+ | `.choice-card` | Component | Card-style `<label>` wrapping a native radio or checkbox; the whole card is the click target. |
10
+ | `.primary` / `.secondary` | Intent | Intent accent for the selected state (supported on the card). |
11
+
12
+ - Use `.choice-card` on a `<label>` wrapping a native `<input type="radio">` or `<input type="checkbox">`. The input is visually hidden; in browsers without `:has()` it stays visible so selection state never disappears.
13
+ - Selected items show a primary border, subtle background tint, and a checkmark badge at the top-right corner — round for radio, rounded square for checkbox.
14
+ - Pair with grid or flex utilities for group layout. The badge overhangs the top-right corner, so avoid `overflow: hidden` on the direct container.
15
+ - Supports disabled, checked, focus-visible, and invalid states.
16
+
17
+ ```html demo
18
+ <div class="grid-3">
19
+ <label class="choice-card">
20
+ <input type="radio" name="cpu" value="1" checked />
21
+ <strong>8-core CPU</strong>
22
+ <span class="muted">32 GB RAM</span>
23
+ </label>
24
+ <label class="choice-card">
25
+ <input type="radio" name="cpu" value="2" />
26
+ <strong>6-core CPU</strong>
27
+ <span class="muted">24 GB RAM</span>
28
+ </label>
29
+ <label class="choice-card">
30
+ <input type="radio" name="cpu" value="3" />
31
+ <strong>4-core CPU</strong>
32
+ <span class="muted">16 GB RAM</span>
33
+ </label>
34
+ </div>
35
+ ```
36
+
37
+ ```html demo
38
+ <fieldset class="field-group">
39
+ <legend class="field-label">Features</legend>
40
+ <div class="grid-3">
41
+ <label class="choice-card">
42
+ <input type="checkbox" name="features" value="wifi" checked />
43
+ <strong>WiFi</strong>
44
+ <span class="muted">Dual-band 2.4/5 GHz</span>
45
+ </label>
46
+ <label class="choice-card">
47
+ <input type="checkbox" name="features" value="bluetooth" />
48
+ <strong>Bluetooth</strong>
49
+ <span class="muted">5.3, low energy</span>
50
+ </label>
51
+ <label class="choice-card">
52
+ <input type="checkbox" name="features" value="nfc" disabled />
53
+ <strong>NFC</strong>
54
+ <span class="muted">Coming soon</span>
55
+ </label>
56
+ </div>
57
+ </fieldset>
58
+ ```
59
+
60
+ Validation follows the shared pattern: `aria-invalid="true"` on the input (set manually or by the `.needs-validation` enhancer) turns the card border to danger, and a `.field-error` inside a `.field` wrapper is shown.
61
+
62
+ ```html demo
63
+ <fieldset class="field field-group">
64
+ <legend class="field-label">Plan</legend>
65
+ <div class="grid-3">
66
+ <label class="choice-card">
67
+ <input type="radio" name="plan" value="starter"
68
+ aria-invalid="true" aria-describedby="plan-error" />
69
+ <strong>Starter</strong>
70
+ <span class="muted">1 project</span>
71
+ </label>
72
+ <label class="choice-card">
73
+ <input type="radio" name="plan" value="pro"
74
+ aria-invalid="true" aria-describedby="plan-error" />
75
+ <strong>Pro</strong>
76
+ <span class="muted">Unlimited projects</span>
77
+ </label>
78
+ <label class="choice-card">
79
+ <input type="radio" name="plan" value="team"
80
+ aria-invalid="true" aria-describedby="plan-error" />
81
+ <strong>Team</strong>
82
+ <span class="muted">Unlimited + SSO</span>
83
+ </label>
84
+ </div>
85
+ <span class="field-error" id="plan-error" role="alert">
86
+ Choose a plan to continue.
87
+ </span>
88
+ </fieldset>
89
+ ```
90
+
91
+ Sizes follow the shared `.sm` and `.lg` modifiers. Intent classes (`.primary`, `.secondary`) are supported on the `.choice-card` element.
92
+
93
+ ## CSS hooks
94
+
95
+ - `--choice-card-pad` — inner padding.
96
+ - `--choice-card-radius` — corner radius.
97
+ - `--choice-card-check-size` — size of the check indicator.
98
+
99
+ Plain `.check` / `.radio` controls expose one hook:
100
+
101
+ - `--choice-control-offset` — top offset that aligns the control with the first
102
+ line of a multi-line label.
@@ -0,0 +1,32 @@
1
+ # Controls
2
+
3
+ Native-only control chrome: the file input and color picker mostly defeat custom styling, so the framework stays out of the way and keeps them readable and theme-aware.
4
+
5
+ ## Color
6
+
7
+ > Small, theme-aware chrome for the native color picker.
8
+
9
+ Use `.color` on an `input[type="color"]` to give the native picker a control-sized
10
+ box with the framework's border, radius, surface, disabled, and focus styles.
11
+ The color dialog and selected-color swatch remain browser-native.
12
+
13
+ ```html demo
14
+ <label class="field">
15
+ <span class="field-label">Accent color</span>
16
+ <input class="color" type="color" value="#6d5dfc" />
17
+ </label>
18
+ ```
19
+
20
+ ## File
21
+
22
+ Use `.file` on an `input[type="file"]` to keep the field native while theming the
23
+ picker button with the framework's surface, border, radius, and hover states.
24
+ The filename area stays muted.
25
+
26
+ ```html demo
27
+ <label class="field">
28
+ <span class="field-label">Profile picture</span>
29
+ <input class="file" type="file" accept="image/*" aria-describedby="file-help" />
30
+ <span class="field-help" id="file-help">JPG, PNG or WebP — max 2 MB</span>
31
+ </label>
32
+ ```
@@ -0,0 +1,59 @@
1
+ # Detached Actions
2
+
3
+ > `.form-actions` is class-only. It may live inside or outside the `<form>`. Use the native `form` attribute to connect a detached submit button to its form.
4
+
5
+ The simplest case stays inside the form.
6
+
7
+ ```html
8
+ <form novalidate>
9
+ <div class="stack">
10
+ <!-- fields -->
11
+ </div>
12
+
13
+ <div class="form-actions">
14
+ <button type="button" class="btn outline">Cancel</button>
15
+ <button type="submit" class="btn primary">Save changes</button>
16
+ </div>
17
+ </form>
18
+ ```
19
+
20
+ For sticky page footers, dialog footers, card footers, and split layouts, detach the actions and connect the submit button with `form="<id>"`.
21
+
22
+ ```html
23
+ <form id="profile-form" novalidate>
24
+ <div class="stack">
25
+ <!-- long form fields -->
26
+ </div>
27
+ </form>
28
+
29
+ <footer class="form-actions">
30
+ <button type="button" class="btn outline">Cancel</button>
31
+ <button type="submit" class="btn primary" form="profile-form">
32
+ Save changes
33
+ </button>
34
+ </footer>
35
+ ```
36
+
37
+ For multiple submit intents, pair the `form` attribute with `formaction`. The form has one action; each submit button can override it.
38
+
39
+ ```html
40
+ <form id="article-form"
41
+ action="/articles/publish"
42
+ method="post">
43
+ <!-- fields -->
44
+ </form>
45
+
46
+ <div class="form-actions">
47
+ <button type="submit"
48
+ class="btn outline"
49
+ form="article-form"
50
+ formaction="/articles/draft">
51
+ Save draft
52
+ </button>
53
+ <button type="submit"
54
+ class="btn primary"
55
+ form="article-form">
56
+ Publish
57
+ </button>
58
+ </div>
59
+ ```
@@ -0,0 +1,88 @@
1
+ # Floating labels
2
+
3
+ > **Optional** — import `actual-css/css/optional/floating-field` or `actual-css/css/optional`.
4
+
5
+ `.floating-field` turns a control and its label into a floating-label cell:
6
+ the label rests inside the control and floats to the top when the control is
7
+ focused or filled. It composes the existing `.field` layout and the text
8
+ controls (`.input`, `.textarea`, `.select`) with zero JavaScript.
9
+
10
+ The cell holds the control and its label only. Help and error messages stay in
11
+ the surrounding `.field`, otherwise the resting label centers on the whole
12
+ field instead of the control.
13
+
14
+ ```html demo
15
+ <div class="field">
16
+ <label class="floating-field">
17
+ <input class="input" type="email" placeholder=" " aria-describedby="email-help" />
18
+ <span class="field-label">Email</span>
19
+ </label>
20
+ <span class="field-help" id="email-help">We'll never share your email.</span>
21
+ </div>
22
+ ```
23
+
24
+ Every `.input` and `.textarea` needs `placeholder=" "` so `:placeholder-shown`
25
+ can detect emptiness — the space keeps the placeholder invisible while making
26
+ the state detectable. `:autofill` is covered too, so a password manager that
27
+ prefills a field still lifts the label.
28
+
29
+ ## Textarea
30
+
31
+ A textarea's resting label aligns with the first text line instead of centering
32
+ in the tall control.
33
+
34
+ ```html demo
35
+ <div class="field">
36
+ <label class="floating-field">
37
+ <textarea class="textarea" rows="4" placeholder=" "></textarea>
38
+ <span class="field-label">Message</span>
39
+ </label>
40
+ </div>
41
+ ```
42
+
43
+ ## Select and date/time inputs
44
+
45
+ Selects have no `:placeholder-shown` signal, and date/time inputs render their
46
+ placeholder inconsistently (Firefox shows none). These controls stay floated in
47
+ every state, so their label always sits at the top.
48
+
49
+ ```html demo
50
+ <div class="stack">
51
+ <label class="floating-field">
52
+ <select class="select">
53
+ <option value="">Choose a country</option>
54
+ <option>Belgium</option>
55
+ <option>France</option>
56
+ <option>Netherlands</option>
57
+ </select>
58
+ <span class="field-label">Country</span>
59
+ </label>
60
+
61
+ <label class="floating-field">
62
+ <input class="input" type="date" />
63
+ <span class="field-label">Start date</span>
64
+ </label>
65
+ </div>
66
+ ```
67
+
68
+ ## Density
69
+
70
+ The reserved headroom responds to control density through `--control-size`:
71
+ `.sm` and `.lg` on the cell or an ancestor resize the control geometry and the
72
+ label spacing with it. Typography is untouched, as everywhere in the framework.
73
+ The floating label does add height — the headroom reserves a second vertical
74
+ line on top of the control's minimum size.
75
+
76
+ ## CSS hooks
77
+
78
+ - `--floating-pad-block-start` — headroom reserved above the control for the
79
+ floated label; defaults to half the control height plus one spacing step.
80
+
81
+ Validation and focus come from the existing form contracts: the control's
82
+ border and focus ring flip through `--form-invalid-border` and the shared
83
+ control focus rules. Coloring the label itself on focus or invalid state is
84
+ left to the surrounding recipe — the joined floating form in the
85
+ [blocks demo](../../demo/templates/blocks.html) shows one such recipe.
86
+
87
+ > Not currently compatible with `.input-icon`; use standard labels when an
88
+ > embedded leading icon is required.
@@ -0,0 +1,56 @@
1
+ # Form Actions
2
+
3
+ > `.form-actions` is a class-only flex row that closes a form. It may live
4
+ > inside or outside the `<form>` (see Detached Actions) and has an opt-in
5
+ > sticky variant (see Sticky Actions).
6
+
7
+ The default layout is a wrapping flex row: items are centered on the cross
8
+ axis and start-aligned on the main axis, with a default top margin that
9
+ separates the actions from the fields above.
10
+
11
+ ```html
12
+ <form novalidate>
13
+ <div class="stack">
14
+ <!-- fields -->
15
+ </div>
16
+
17
+ <div class="form-actions">
18
+ <button type="button" class="btn outline">Cancel</button>
19
+ <button type="submit" class="btn primary">Save changes</button>
20
+ </div>
21
+ </form>
22
+ ```
23
+
24
+ ## CSS hooks
25
+
26
+ The three layout hooks are unset by default and fall back to the values below.
27
+
28
+ - `--form-actions-align` — `center` — cross-axis alignment (`align-items`).
29
+ - `--form-actions-justify` — `flex-start` — main-axis distribution (`justify-content`).
30
+ - `--form-actions-margin-block-start` — `var(--space-50)` — top margin separating actions from the form.
31
+
32
+ ### Common arrangements
33
+
34
+ Right-align the submit on wide rows:
35
+
36
+ ```css
37
+ .form-actions {
38
+ --form-actions-justify: flex-end;
39
+ }
40
+ ```
41
+
42
+ Stretch each action to fill the full row width:
43
+
44
+ ```css
45
+ .form-actions {
46
+ --form-actions-align: stretch;
47
+ }
48
+ ```
49
+
50
+ Because the hooks are inherited custom properties, set them on a page or
51
+ container to apply one arrangement to every `.form-actions` inside it.
52
+
53
+ > The sticky variant adds `--form-actions-sticky-block-offset`,
54
+ > `--form-actions-sticky-padding`, and `--form-actions-sticky-inline-offset` —
55
+ > see Sticky Actions. `.form-actions` is class-only and works inside or
56
+ > outside the form — see Detached Actions.
@@ -0,0 +1,91 @@
1
+ # Input Policy
2
+
3
+ > Native input intent first. Actual's runtime filters values only when explicitly asked, and fixed-shape masks stay opt-in through `data-mask`.
4
+
5
+ `inputmode` changes the virtual keyboard but does not constrain the value. Actual
6
+ does not filter plain `inputmode` fields by default because numeric entry often
7
+ needs temporary, signed, grouped, or locale-specific values while the user types.
8
+
9
+ When the JavaScript runtime is loaded, Actual enhances `input[data-filter]`.
10
+ Textareas are left alone.
11
+
12
+ Add an explicit `data-filter` value when the field should enforce one of
13
+ Actual's small built-in filters. Unsupported filter names are ignored. An
14
+ empty `data-filter` never filters — `inputmode` is not read as a fallback,
15
+ even when it is `numeric` or `decimal`. `data-filter="numeric"` /
16
+ `data-filter="decimal"` are intentionally destructive (they rewrite the
17
+ value as the user types), unlike `inputmode`, which only hints the virtual
18
+ keyboard.
19
+
20
+ ```html demo
21
+ <label class="field">
22
+ <span class="field-label">Quantity</span>
23
+ <input class="input" inputmode="numeric" data-filter="numeric" placeholder="42" />
24
+ </label>
25
+ ```
26
+
27
+ ```html demo
28
+ <label class="field">
29
+ <span class="field-label">Amount</span>
30
+ <input class="input" inputmode="decimal" data-filter="decimal" placeholder="12.34" />
31
+ </label>
32
+ ```
33
+
34
+ ### Built-in filters
35
+
36
+ The built-in filters are intentionally narrow. They are input helpers, not
37
+ domain validation.
38
+
39
+ - `numeric` — Keeps ASCII digits only: `0` through `9`.
40
+ - `decimal` — Converts `,` to `.`, keeps ASCII digits, and keeps the first `.` separator.
41
+ - `lower` — Converts the value with `toLocaleLowerCase()`.
42
+ - `upper` — Converts the value with `toLocaleUpperCase()`.
43
+ - `letters` — Keeps Unicode letters only. Digits, punctuation, symbols, and spaces are removed.
44
+ - `slug` — Normalizes accents, lowercases text, turns runs of non-letter and non-digit characters into `-`, collapses repeated `-`, and trims edge separators.
45
+
46
+ During direct typing, `slug` may keep a trailing `-` until the next character so
47
+ words do not merge while the user is still entering text.
48
+
49
+ Filters can be piped with `|` when a field needs more than one transform. They
50
+ run from left to right.
51
+
52
+ ```html demo
53
+ <label class="field">
54
+ <span class="field-label">Slug</span>
55
+ <input class="input" data-filter="lower|slug" autocomplete="off" placeholder="release-notes" />
56
+ </label>
57
+ ```
58
+
59
+ ```html demo
60
+ <label class="field">
61
+ <span class="field-label">Code</span>
62
+ <input class="input" data-filter="upper|letters" autocomplete="off" placeholder="ABC" />
63
+ </label>
64
+ ```
65
+
66
+ For signed numbers, locale formatting, currency rules, time ranges, or
67
+ app-specific character policies, use application code. Those rules are domain
68
+ policy, not a framework default. See the JavaScript docs for custom filter
69
+ patterns built on `enhance()`.
70
+
71
+ ### Pattern mask
72
+
73
+ Use `data-mask` when the input has a fixed shape. Tokens are `9` for a digit,
74
+ `a` for a letter, and `*` for any character.
75
+
76
+ ```html demo
77
+ <label class="field">
78
+ <span class="field-label">Reference</span>
79
+ <input class="input" data-mask="aaa-999" autocomplete="off" placeholder="abc-123" />
80
+ </label>
81
+ ```
82
+
83
+ ```html demo
84
+ <label class="field">
85
+ <span class="field-label">Date</span>
86
+ <input class="input" data-mask="9999-99-99" inputmode="numeric" autocomplete="off" placeholder="yyyy-mm-dd" />
87
+ </label>
88
+ ```
89
+
90
+ Masks enforce shape, not domain validity. Use application validation when a date
91
+ must reject impossible values such as `2026-13-40`.
@@ -0,0 +1,37 @@
1
+ # Joined Controls
2
+
3
+ > Use `.join` when controls need to be visually joined into a single unit.
4
+
5
+ ## Class reference
6
+
7
+ | Class | Kind | Description |
8
+ |---|---|---|
9
+ | `.join` | Composition | Visually joins adjacent controls into a single unit. |
10
+ | `.join-addon` | Composition | Static prefix/suffix content such as currency symbols, units, or protocol text. |
11
+
12
+ Add `.join` to the wrapper that groups the controls. Use `.join-addon` for static prefix/suffix content such as currency symbols, units, or protocol text. Action buttons inside the group should use `.btn`. The wrapper should still carry `role="group"` with a label for accessibility.
13
+
14
+ When composing source files manually, import `components/join.css` after the controls it groups. `.join` writes child border-radius longhands directly so joined corners win at equal specificity.
15
+
16
+ ```html demo
17
+ <div class="field">
18
+ <label class="field-label" id="amount-label" for="amount">Amount</label>
19
+ <div class="join" role="group" aria-labelledby="amount-label">
20
+ <span class="join-addon">$</span>
21
+ <input class="input" id="amount" name="amount" inputmode="decimal" />
22
+ <button class="btn outline" type="button">Clear</button>
23
+ </div>
24
+ <span class="field-help">Enter the invoice total before tax.</span>
25
+ </div>
26
+ ```
27
+
28
+ `.join` handles border-radius and border collapsing between adjacent children. It works with any direct child — `.input`, `.btn`, `.select`, or `.join-addon`.
29
+ When an `.input`, `.textarea`, or `.select` receives keyboard-relevant focus,
30
+ the focus ring surrounds the complete joined field. Attached buttons keep their
31
+ own focus indicator when reached directly, so the actionable segment remains
32
+ identifiable.
33
+
34
+ ## CSS hooks
35
+
36
+ - `--join-radius` — outer corner radius of the joined group. The per-corner
37
+ values of each child are derived from it.