actual-css 0.3.1 → 0.4.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 (203) hide show
  1. package/CHANGELOG.md +67 -3
  2. package/README.md +35 -30
  3. package/dist/actual.css +73 -4946
  4. package/dist/actual.full.css +7090 -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/cascade-layer.md +34 -13
  10. package/docs/design-notes/column-layout.md +677 -0
  11. package/docs/design-notes/forced-colors.md +3 -0
  12. package/docs/design-notes/grid.md +612 -0
  13. package/docs/design-notes/naming.md +15 -0
  14. package/docs/design-notes/widget-primitives.md +1 -0
  15. package/docs/navigation.json +5 -1
  16. package/docs/pages/components/alert.md +16 -14
  17. package/docs/pages/components/aura.md +1 -1
  18. package/docs/pages/components/avatar.md +6 -6
  19. package/docs/pages/components/badge.md +7 -7
  20. package/docs/pages/components/button.md +16 -16
  21. package/docs/pages/components/card.md +13 -9
  22. package/docs/pages/components/chat.md +8 -8
  23. package/docs/pages/components/dialog.md +7 -7
  24. package/docs/pages/components/drawer.md +3 -3
  25. package/docs/pages/components/fab.md +7 -7
  26. package/docs/pages/components/navbar.md +9 -8
  27. package/docs/pages/components/pagination.md +4 -3
  28. package/docs/pages/components/skeleton.md +4 -4
  29. package/docs/pages/components/spinner.md +5 -5
  30. package/docs/pages/components/table.md +30 -5
  31. package/docs/pages/enhancements/flyout.md +13 -13
  32. package/docs/pages/enhancements/overview.md +1 -1
  33. package/docs/pages/enhancements/scrollspy.md +6 -6
  34. package/docs/pages/enhancements/status-bar.md +5 -1
  35. package/docs/pages/enhancements/tabs.md +6 -6
  36. package/docs/pages/enhancements/tooltip.md +1 -1
  37. package/docs/pages/examples/overview.md +39 -22
  38. package/docs/pages/forms/choice-cards.md +11 -6
  39. package/docs/pages/forms/controls.md +1 -1
  40. package/docs/pages/forms/detached-actions.md +1 -1
  41. package/docs/pages/forms/floating-field.md +10 -5
  42. package/docs/pages/forms/form-actions.md +1 -1
  43. package/docs/pages/forms/input-policy.md +1 -1
  44. package/docs/pages/forms/joined-controls.md +4 -4
  45. package/docs/pages/forms/labels.md +6 -6
  46. package/docs/pages/forms/otp.md +1 -1
  47. package/docs/pages/forms/overview.md +19 -19
  48. package/docs/pages/forms/password.md +1 -1
  49. package/docs/pages/forms/range.md +1 -1
  50. package/docs/pages/forms/states-and-sizes.md +41 -45
  51. package/docs/pages/forms/sticky-actions.md +3 -3
  52. package/docs/pages/forms/switch.md +110 -2
  53. package/docs/pages/forms/validation.md +9 -9
  54. package/docs/pages/foundations/philosophy.md +1 -1
  55. package/docs/pages/foundations/theming.md +17 -2
  56. package/docs/pages/foundations/tokens.md +22 -10
  57. package/docs/pages/foundations/typography.md +50 -20
  58. package/docs/pages/guides/bootstrap.md +11 -11
  59. package/docs/pages/guides/integrating-actual-css.md +186 -0
  60. package/docs/pages/guides/introduction.md +23 -26
  61. package/docs/pages/guides/modular-import.md +173 -0
  62. package/docs/pages/guides/progressive-enhancement.md +81 -78
  63. package/docs/pages/guides/tailwind.md +6 -8
  64. package/docs/pages/layout/app-shell.md +1 -1
  65. package/docs/pages/layout/center.md +1 -1
  66. package/docs/pages/layout/choosing.md +39 -0
  67. package/docs/pages/layout/column-layout.md +206 -0
  68. package/docs/pages/layout/frame.md +6 -1
  69. package/docs/pages/layout/grid.md +100 -102
  70. package/docs/pages/layout/list.md +1 -1
  71. package/docs/pages/layout/media.md +1 -1
  72. package/docs/pages/layout/overview.md +15 -2
  73. package/docs/pages/layout/scroll-snap.md +2 -2
  74. package/docs/pages/layout/scroll-target.md +45 -0
  75. package/docs/pages/layout/sidebar-layout.md +20 -19
  76. package/docs/pages/layout/stack.md +53 -1
  77. package/docs/pages/layout/switcher.md +10 -26
  78. package/docs/pages/layout/topbar.md +2 -2
  79. package/docs/pages/patterns/actions.md +1 -1
  80. package/docs/pages/patterns/code-block.md +1 -1
  81. package/docs/pages/patterns/exclusive-accordion.md +1 -1
  82. package/docs/pages/patterns/hierarchical-nav.md +1 -1
  83. package/docs/pages/patterns/icon-input.md +1 -1
  84. package/docs/pages/patterns/intent-nesting.md +1 -1
  85. package/docs/pages/patterns/navigation-list.md +1 -1
  86. package/docs/pages/patterns/overview.md +1 -1
  87. package/docs/pages/patterns/structured-lists.md +1 -1
  88. package/docs/pages/utilities/accessibility.md +3 -3
  89. package/docs/pages/utilities/flex.md +15 -15
  90. package/docs/pages/utilities/links.md +4 -4
  91. package/docs/pages/utilities/list.md +8 -8
  92. package/docs/pages/utilities/overflow.md +8 -8
  93. package/docs/pages/utilities/overview.md +1 -1
  94. package/docs/pages/utilities/shape.md +3 -3
  95. package/docs/pages/utilities/sizing-wrapping.md +23 -16
  96. package/docs/pages/utilities/spacing.md +20 -20
  97. package/docs/pages/utilities/text.md +10 -10
  98. package/llms.txt +46 -4
  99. package/package.json +42 -13
  100. package/scripts/reserved-classes.json +261 -0
  101. package/src/cli/actual-css.js +133 -0
  102. package/src/css/actual.css +1 -31
  103. package/src/css/actual.full.css +11 -2
  104. package/src/css/components/alert.css +17 -4
  105. package/src/css/components/badge.css +18 -6
  106. package/src/css/components/breadcrumb.css +1 -1
  107. package/src/css/components/button.css +19 -1
  108. package/src/css/components/card.css +27 -2
  109. package/src/css/components/drawer.css +27 -2
  110. package/src/css/components/flyout.css +5 -0
  111. package/src/css/components/index.css +32 -27
  112. package/src/css/components/join.css +8 -1
  113. package/src/css/components/key.css +2 -0
  114. package/src/css/components/menu.css +11 -1
  115. package/src/css/components/meter.css +4 -1
  116. package/src/css/components/modal.css +5 -0
  117. package/src/css/components/pagination.css +11 -0
  118. package/src/css/components/spinner.css +5 -0
  119. package/src/css/components/status-bar.css +5 -0
  120. package/src/css/components/surface.css +5 -0
  121. package/src/css/components/table.css +6 -0
  122. package/src/css/components/tooltip.css +5 -0
  123. package/src/css/core/index.css +9 -0
  124. package/src/css/core/print.css +18 -0
  125. package/src/css/{theme.css → core/theme.css} +20 -10
  126. package/src/css/{tokens.css → core/tokens.css} +15 -2
  127. package/src/css/{variants.css → core/variants.css} +3 -2
  128. package/src/css/effects/index.css +2 -0
  129. package/src/css/forms/base.css +8 -0
  130. package/src/css/forms/choice-card.css +5 -4
  131. package/src/css/forms/choice.css +49 -37
  132. package/src/css/forms/control.css +4 -1
  133. package/src/css/forms/custom-select.css +2 -0
  134. package/src/css/{optional → forms}/floating-field.css +25 -7
  135. package/src/css/forms/index.css +10 -13
  136. package/src/css/forms/native.css +15 -2
  137. package/src/css/{optional → forms}/otp.css +20 -16
  138. package/src/css/forms/switch.css +31 -29
  139. package/src/css/layout/app-shell.css +6 -0
  140. package/src/css/layout/center.css +13 -0
  141. package/src/css/layout/cluster.css +21 -0
  142. package/src/css/layout/column-layout.css +144 -0
  143. package/src/css/layout/container-query.css +13 -0
  144. package/src/css/layout/frame.css +19 -0
  145. package/src/css/layout/grid.css +141 -0
  146. package/src/css/layout/index.css +42 -0
  147. package/src/css/layout/measure.css +9 -0
  148. package/src/css/layout/media.css +10 -0
  149. package/src/css/layout/scroll-target.css +15 -0
  150. package/src/css/layout/scroller.css +41 -0
  151. package/src/css/layout/sidebar-layout.css +31 -0
  152. package/src/css/layout/stack.css +21 -0
  153. package/src/css/layout/switcher.css +23 -0
  154. package/src/css/{optional/layout-extra.css → layout/topbar.css} +0 -20
  155. package/src/css/typography/index.css +5 -0
  156. package/src/css/typography/lead.css +7 -0
  157. package/src/css/{components → typography}/overline.css +2 -2
  158. package/src/css/{prose.css → typography/prose.css} +53 -38
  159. package/src/css/{utilities.css → utilities/base.css} +2 -0
  160. package/src/css/utilities/index.css +3 -0
  161. package/src/js/escape.js +36 -0
  162. package/src/js/flyout.js +22 -12
  163. package/src/js/full.js +26 -0
  164. package/src/js/index.js +5 -21
  165. package/src/js/surface.js +1 -31
  166. package/src/js/tooltip.js +46 -18
  167. package/src/js/validation.js +1 -1
  168. package/src/tooling/css-bundle.js +308 -0
  169. package/dist/actual-themes.min.css +0 -1
  170. package/dist/optional.css +0 -1048
  171. package/dist/optional.min.css +0 -1
  172. package/docs/pages/guides/optional-components.md +0 -33
  173. package/src/css/grid.css +0 -109
  174. package/src/css/layout.css +0 -123
  175. package/src/css/optional/index.css +0 -10
  176. package/src/css/optional/scroller.css +0 -63
  177. package/src/css/print.css +0 -47
  178. package/src/css/themes/corporate.css +0 -60
  179. package/src/css/themes/cyberpunk.css +0 -60
  180. package/src/css/themes/dim.css +0 -33
  181. package/src/css/themes/edge.css +0 -117
  182. package/src/css/themes/forest.css +0 -65
  183. package/src/css/themes/gradient.css +0 -97
  184. package/src/css/themes/index.css +0 -47
  185. package/src/css/themes/indigo.css +0 -33
  186. package/src/css/themes/lavender.css +0 -66
  187. package/src/css/themes/mono.css +0 -60
  188. package/src/css/themes/neon.css +0 -66
  189. package/src/css/themes/ocean.css +0 -60
  190. package/src/css/themes/petrol.css +0 -64
  191. package/src/css/themes/spruce.css +0 -64
  192. package/src/css/themes/square.css +0 -65
  193. package/src/css/themes/sunset.css +0 -66
  194. /package/src/css/{optional → components}/chat.css +0 -0
  195. /package/src/css/{optional → components}/fab.css +0 -0
  196. /package/src/css/{base.css → core/base.css} +0 -0
  197. /package/src/css/{focus.css → core/focus.css} +0 -0
  198. /package/src/css/{intents.css → core/intents.css} +0 -0
  199. /package/src/css/{reset.css → core/reset.css} +0 -0
  200. /package/src/css/{optional → effects}/aura.css +0 -0
  201. /package/src/css/{optional → layout}/scroll-snap.css +0 -0
  202. /package/src/css/{optional/typography-fluid.css → typography/fluid.css} +0 -0
  203. /package/src/css/{optional/utilities-extra.css → utilities/extra.css} +0 -0
@@ -87,9 +87,20 @@ Soft surfaces (`.btn.soft`, `.badge.soft`, and the default `.alert`) are generat
87
87
  --soft-bg-mix: 88%;
88
88
  --soft-border-mix: 65%;
89
89
  --soft-hover-alpha: 12%;
90
+ --soft-fg-mix: 100%;
90
91
  }
91
92
  ```
92
93
 
94
+ `--soft-fg-mix` is the share of raw intent in soft *text*. At its `100%` default soft ink is the intent color itself, which is what a palette of dark, muted intents wants. A vivid or light palette cannot afford that: a soft badge then paints intent-tinted ink on an intent-tinted surface, and the two converge. Lowering the mix rebates the ink toward `--text`, which is the right direction in both schemes because `--text` is dark on a light theme and light on a dark one. Around `45%` a fully saturated palette recovers AA while the ink still reads as its intent.
95
+
96
+ **When an intent is tinted against a theme-controlled surface or border, the mix interpolates in `oklab`.** That covers the soft recipe, the checked `.choice-card` tint, and the `.overline.pill` border.
97
+
98
+ Not because rectangular interpolation is more faithful to the intent hue — across the shipped presets it is a few degrees *less* faithful. A browser treats hue as powerless below a small chroma epsilon, so for the low-chroma surfaces and borders the presets actually use, a polar mix snaps cleanly onto the intent and wins.
99
+
100
+ The reason is continuity. A theme owns `--surface` and `--border`, and may put them anywhere from achromatic to vivid. Under polar interpolation that range contains a cliff: below the epsilon the mix tracks the intent, above it the surface hue starts winning, and crossing it moves the result into a different color family — 116 degrees, a soft `.secondary` landing in the greens. Cartesian interpolation responds smoothly across the whole range a public token allows. A recipe that is only correct while an adopter's surface stays under an unstated, implementation-defined threshold is not a contract, so the framework trades a small continuous error for a predictable one. The present cost is imperceptible: the largest difference the rule introduces in any preset is 3/255 on one channel.
101
+
102
+ Mixing against `black`, `white`, `transparent`, or `currentColor` is exempt and stays in `oklch`: those have no hue to interpolate, so both spaces give byte-identical results. Polar interpolation also stays legitimate where hue rotation is the point, such as sweeping a gradient through hues. `bun run check:color-space` enforces the rule for contextual tints and takes an `intentional-oklch` comment as an escape hatch.
103
+
93
104
  ### Shape
94
105
 
95
106
  Shape is intentionally small. Components can derive local radii from these.
@@ -261,12 +272,12 @@ Under `forced-colors: active`, `--backdrop-fill` maps to `Canvas`.
261
272
 
262
273
  A small z-index scale orders non-dialog overlays. Native `<dialog>` elements (modal, drawer) render on the platform top-layer, which has no numeric z-index and always paints above the page — they never compete with this scale.
263
274
 
264
- | Token | Value | Used by |
265
- |---|---|---|
266
- | `--z-sticky` | 10 | Sticky page chrome (`form-actions.sticky`, `topbar`). |
267
- | `--z-menu` | 20 | Floating menus (`flyout`); `.surface-backdrop` sits at `calc(var(--z-menu) - 1)`. |
268
- | `--z-tooltip` | 50 | Tooltips. |
269
- | `--z-status` | 60 | `status-bar`. |
275
+ | Token | Value | Used by |
276
+ |---------------|-------|-----------------------------------------------------------------------------------|
277
+ | `--z-sticky` | 10 | Sticky page chrome (`form-actions.sticky`, `topbar`). |
278
+ | `--z-menu` | 20 | Floating menus (`flyout`); `.surface-backdrop` sits at `calc(var(--z-menu) - 1)`. |
279
+ | `--z-tooltip` | 50 | Tooltips. |
280
+ | `--z-status` | 60 | `status-bar`. |
270
281
 
271
282
  Stacking-context traps: a flyout or tooltip that is a DOM descendant of a dialog, or of a container with `transform`, `filter`, `contain`, `isolation`, or `will-change`, is confined to that ancestor's stacking context. Its z-index token only orders it against siblings within that context. When a dialog's top-layer clips a descendant flyout, move the flyout markup outside the dialog or use a portal/popover to escape the stacking context.
272
283
 
@@ -330,9 +341,9 @@ Rules:
330
341
 
331
342
  ## Theme contract
332
343
 
333
- Themes override tokens, not selectors. The themes in `src/css/themes/` are demo examples, not included in the default stylesheet; they exist to show valid ways to use this contract. They ship in the package as **reference sources for copying** — the `./css/themes` exports are intentionally `null`, so they are not supported import entrypoints.
344
+ Themes override tokens, not selectors. The themes in `src/css/themes/` are repository-only demo examples, not included in the default stylesheet or the npm package; they exist to show valid ways to use this contract, such as `ocean`, `square`, `cyberpunk`, and `brutalist`.
334
345
 
335
- A minimal recolor theme overrides the intent pairs, surfaces, text colors, border, focus, focus ring, and hover overlay.
346
+ A minimal recolor theme overrides the intent pairs, surfaces, text colors, border, focus, and hover overlay. In browsers with `color-mix()` support, the core derives `--focus-ring` from the island's `--focus`; override the ring only for a deliberate visual treatment or when a matching pre-`color-mix()` fallback is required.
336
347
 
337
348
  Shape, shadow, motion, typography, and soft-variant mix tokens are optional knobs. Override them only when the theme actually changes that part of the system.
338
349
 
@@ -372,7 +383,6 @@ A minimal recolor theme (illustrative, not a shipped theme):
372
383
  --text-subtle: hsl(260 8% 56%);
373
384
  --border: hsl(260 24% 84%);
374
385
  --focus: var(--primary);
375
- --focus-ring: hsl(260 70% 54% / 0.22);
376
386
  --hover-overlay: hsl(260 30% 10% / 0.04);
377
387
  --soft-bg-mix: 84%;
378
388
  --soft-border-mix: 58%;
@@ -389,6 +399,8 @@ Without `data-theme`, the default theme advertises `color-scheme: light dark` an
389
399
 
390
400
  Because the native select chevron is an SVG background image and cannot inherit `currentColor`, the select hands the arrow back to the browser under `prefers-contrast: more` (same treatment as `forced-colors: active`) instead of shipping a recolored chevron per theme — one native fallback covers every theme without a high-contrast icon variant to maintain.
391
401
 
402
+ Under `prefers-contrast: more`, the user's contrast preference wins over a theme's palette by contract: the core collapses `--text-muted`/`--text-subtle` to `--text` and thickens borders and focus so no theme can defeat a legibility requirement. This is a deliberate decision, not a specificity quirk — the repeated `[data-theme][data-theme]` attribute only outranks a preset theme stylesheet loaded after the core.
403
+
392
404
  Under `forced-colors: active`, the browser automatically remaps `color`, `background`, and `border` properties to system colors. The default theme provides the few necessary system-color overrides for cases the browser cannot handle — `--focus-outline-color`, `--state-selected`/`--state-selected-fg`, `--state-disabled`, `--backdrop-fill`, and decorative shadow removal. Because that rule targets `:root` (which always matches regardless of `data-theme`) and the `[data-theme]` attributes, it reaches every theme. Components should inherit that baseline; keep local forced-color rules only for custom control geometry, native bar parts, disabled states, or selected states that would otherwise be communicated only by background color.
393
405
 
394
406
  ## Component mapping
@@ -418,4 +430,4 @@ Components map global tokens to local aliases at the top of the component, then
418
430
  - Do not mirror Pico-style component-specific variables unless there is a clear need.
419
431
  - Do not add Tailwind-like color scales as public API.
420
432
  - Do not rely on Sass, PostCSS, or generated token files.
421
- - Do not make dark mode required for old browsers. Light mode is the fallback.
433
+ - Do not make dark mode required for old browsers. Light mode is the fallback.
@@ -10,7 +10,7 @@ Typography has three layers:
10
10
 
11
11
  Components must not depend on `.prose`. App screens, forms, cards, tables, navigation, dashboards, and component examples should use their own component or layout rules.
12
12
 
13
- Use `.prose` directly on a semantic content container, usually an `article`.
13
+ Use `.prose` directly on a semantic content container, usually an `article`. It is not a page-level wrapper: component demos, navigation, forms, and other application UI belong outside it. Compose prose sections with those siblings using a layout primitive such as `.stack`.
14
14
 
15
15
  ## Why this shape
16
16
 
@@ -46,31 +46,47 @@ Global links inherit color. The visual affordance of a link is the responsibilit
46
46
 
47
47
  `.prose` is the opt-in rich-text scope. It owns the visual hierarchy of authored content.
48
48
 
49
- It owns: readable measure, vertical rhythm between common text elements, heading hierarchy, link affordance, list spacing, code and keyboard treatment, blockquote treatment, table treatment, image / figure / figcaption treatment, mark, and `text-wrap` balance / pretty for prose elements.
49
+ It owns: readable measure, vertical rhythm, heading hierarchy, link affordance, list spacing, description-list treatment, code and keyboard treatment, blockquote treatment, table treatment, image / figure / figcaption treatment, mark, and `text-wrap` balance / pretty for authored or generated rich-text descendants.
50
50
 
51
51
  It does not own: page layout, app cards, alerts, forms, app tables, navigation, component variants, or any automatic styling outside the `.prose` subtree.
52
52
 
53
- The reason for that boundary: a dialog title, a card heading, or a sidebar section heading should not become a "prose h1". Components own those. If a card or a dialog wants a richer heading, it can use a component-level rule, not a global selector.
53
+ The reason for that boundary: a dialog title, a card heading, or a sidebar section heading should not become a "prose h1". Components own those. Put the component next to the prose section in a layout container; do not ask `.prose` to infer where an embedded component begins.
54
54
 
55
- ### Optional fluid module
55
+ ### Fluid module
56
56
 
57
- `src/css/optional/typography-fluid.css` is a small file that is not imported by `actual.css`. Projects that need display, title, and lead sizes that scale with the viewport (landing pages, marketing pages, hero sections, documentation homepages) import it manually after `actual.css`.
57
+ `src/css/typography/fluid.css` is a small module that is not imported by
58
+ `actual.css`. Projects that need display, title, and lead sizes that scale
59
+ with the viewport (landing pages, marketing pages, hero sections,
60
+ documentation homepages) import it manually:
61
+
62
+ ```css
63
+ @import "actual-css/css/typography/fluid";
64
+ ```
58
65
 
59
66
  The module exposes three size tokens and three composed classes. It is deliberately not the foundation: body text, app UI, and the default prose scale stay static.
60
67
 
61
- | Token | Class | Purpose |
62
- |---|---|---|
63
- | `--fluid-display` | `.text-display` | Hero / display sizes. |
64
- | `--fluid-title` | `.text-title` | Section titles. |
65
- | `--fluid-lead` | `.text-lead` | Intro paragraphs on display surfaces. |
68
+ | Token | Class | Purpose |
69
+ |-------------------|-----------------|---------------------------------------|
70
+ | `--fluid-display` | `.text-display` | Hero / display sizes. |
71
+ | `--fluid-title` | `.text-title` | Section titles. |
72
+ | `--fluid-lead` | `.text-lead` | Intro paragraphs on display surfaces. |
66
73
 
67
- There is also a non-fluid `.lead` utility in the core, for a simple readable intro paragraph. The optional module does not redefine `.lead`; it adds a separate `.text-lead` for display contexts. This avoids an optional import silently changing how existing markup renders.
74
+ There is also a non-fluid `.lead` utility in the core, for a simple readable intro paragraph. The fluid module does not redefine `.lead`; it adds a separate `.text-lead` for display contexts. This avoids a fluid import silently changing how existing markup renders.
68
75
 
69
76
  ## Link treatment
70
77
 
71
78
  Global links are intentionally neutral. Only `.prose` styles them as prose links, and only components style their own link-like elements (nav links, button-as-link, tabs, breadcrumbs, etc.). The reason is that underlining every `a` globally fights against button-like links, nav links, clickable cards, menu items, and tab triggers. Components that need link semantics should opt in with their own rule.
72
79
 
73
- There are no `--link-decoration-thickness`, `--link-decoration-thickness-hover`, or `--link-underline-offset` tokens; no global rule consumes them. Prose and components set their own underline metrics inline.
80
+ Prose links take `--primary`. That breaks down for a theme whose primary is a light or fully saturated accent: it can carry a filled button, where it is the background under its own `--primary-fg`, but not inline body text, where it has to hold contrast against `--surface` on its own. `--link` is the optional override for exactly that case — a theme sets it and `.prose` link ink follows, hover included, with no rule to restate. Unset, it falls back to `--primary`, so existing themes are unaffected.
81
+
82
+ ```css
83
+ [data-theme="vivid"] {
84
+ --primary: hsl(48 100% 50%); /* fine as a button fill */
85
+ --link: hsl(215 100% 30%); /* 1.4:1 as body ink, so lend a darker hue */
86
+ }
87
+ ```
88
+
89
+ It is a color token only. There are still no `--link-decoration-thickness`, `--link-decoration-thickness-hover`, or `--link-underline-offset` tokens; no global rule consumes them. Prose and components set their own underline metrics inline.
74
90
 
75
91
  ## Type scale
76
92
 
@@ -113,10 +129,11 @@ There is no `--line-height-normal`. It would be redundant with `--line-height`.
113
129
 
114
130
  ### Prose hooks
115
131
 
116
- - `--prose-measure` — readable line length cap, shared with `.measure`.
132
+ - `--prose-measure` — readable line length cap for `.prose` (the standalone `.measure` helper reads its own `--measure`).
117
133
  - `--prose-line-height` — body line height inside `.prose`.
118
134
  - `--prose-heading-line-height` — heading line height inside `.prose`.
119
135
  - `--prose-flow` — vertical rhythm between sibling blocks inside `.prose`.
136
+ - `--link` — prose link ink; falls back to `--primary` when a theme leaves it unset.
120
137
 
121
138
  ## text-wrap
122
139
 
@@ -134,7 +151,7 @@ Headings are split between the global baseline and `.prose`.
134
151
 
135
152
  Globally, headings get a tight line-height, a color from `--heading` (or inherited), and progressive `text-wrap: balance`. They do not get a `font-size`, a `font-weight`, or margins. The reason is that a card heading, a dialog title, or a sidebar section should not be auto-magically enlarged by selector.
136
153
 
137
- Inside `.prose`, headings get a `font-weight` from `--font-weight-strong`, a tighter line-height from `--prose-heading-line-height`, a margin-block rhythm, and keep the global `text-wrap: balance`. The `h1` to `h4` font sizes are also scoped to `.prose` and are not exposed as a public scale.
154
+ Inside `.prose`, headings get a `font-weight` from `--font-weight-strong`, a tighter line-height from `--prose-heading-line-height`, margin rhythm, and keep the global `text-wrap: balance`. Their color follows the contextual `--heading` alias, so prose also composes correctly with a contextual surface. The `h1` to `h4` font sizes are scoped to `.prose`, remain low-specificity, and are not exposed as a public scale.
138
155
 
139
156
  The `hgroup` element gets only structural styling: a grid layout, a gap, and a reset of internal margins. No font-size, no color, no weight. Its visual identity is the responsibility of the headings and paragraphs it contains.
140
157
 
@@ -154,13 +171,26 @@ Three logical alignment utilities exist (`.text-start`, `.text-center`, `.text-e
154
171
 
155
172
  ## Tables
156
173
 
157
- Prose tables are a different concern from app data tables. Long-form content can contain a small table, but a sortable, dense, row-actionable app table belongs to the table component. The `.prose table` rule is intentionally minimal: it does not define sticky headers, selection states, row actions, or density variants.
174
+ Prose tables are a different concern from app data tables. Long-form content can contain a small table, but a sortable, dense, row-actionable app table belongs to the table component outside the prose boundary. The prose table recipe is intentionally minimal: it does not define sticky headers, selection states, row actions, or density variants.
175
+
176
+ ## Description lists
177
+
178
+ `.prose` renders `dl` / `dt` / `dd` in document flow: terms take the strong weight, definitions keep a modest indent, and a term that follows a definition gets breathing room.
179
+
180
+ There is no grid-based key/value layout by default. A valid `<dl>` may pair several terms with one definition or the reverse, and a two-column grid silently assumes strict pairs. For true key/value metadata, compose a grid layout around the list yourself.
181
+
182
+ ## Fluid module — how to use it
183
+
184
+ To enable fluid display type on a marketing or documentation surface, import the module after `actual.css` in the project's stylesheet entry point:
158
185
 
159
- ## Optional module — how to use it
186
+ ```css
187
+ @import "actual-css/css";
188
+ @import "actual-css/css/typography/fluid";
189
+ ```
160
190
 
161
- To enable fluid display type on a marketing or documentation surface, import the optional module after `actual.css` in the project's stylesheet entry point. The module does not change anything in the core: it adds three size tokens and three classes. Body text, app UI, and the default prose scale are not affected.
191
+ The module does not change anything in the core: it adds three size tokens and three classes. Body text, app UI, and the default prose scale are not affected.
162
192
 
163
- Projects that do not need fluid display type do nothing — the optional module is not part of the default bundle.
193
+ Projects that do not need fluid display type do nothing — the module is not part of the default bundle.
164
194
 
165
195
  ## What not to reintroduce
166
196
 
@@ -172,6 +202,6 @@ These are decisions that the framework has already made. Re-introducing them sil
172
202
  - Do not add `transition: all`. Transitions target specific properties.
173
203
  - Do not add a `--font-weight-light`. It is rarely a good default and depends on the typeface.
174
204
  - Do not add a `text-align: center` shortcut to `.text-balance`. Balance is about wrapping, not alignment.
175
- - Do not add RFS-style fluid type to the foundation. Fluid sizing is for display text in the optional module.
205
+ - Do not add RFS-style fluid type to the foundation. Fluid sizing is for display text in the fluid module.
176
206
  - Do not add heading-level utility classes (`h1`, `h2`, …) or a public `fs-*` scale until theme work and real components prove they are needed.
177
- - Do not extend `.prose` to style cards, alerts, app tables, or navigation. Those belong to their components.
207
+ - Do not extend `.prose` to style cards, alerts, app tables, or navigation. Those belong to their components.
@@ -24,14 +24,14 @@ The layered import is the cleanest way to keep both stylesheets during the
24
24
  migration:
25
25
 
26
26
  ```css
27
- /* Migration setup — Actual rules sit in @layer actual */
28
27
  @import "bootstrap.css";
29
- @import "actual-css/css/layer";
28
+ @layer actual;
29
+ @import "actual-css/full" layer(actual);
30
30
  /* Unlayered project overrides stay on top */
31
31
  @import "app.css";
32
32
  ```
33
33
 
34
- `actual.layer.css` wraps every Actual rule in a single `@layer actual`, so
34
+ The full import places every Actual rule in a single `actual` layer, so
35
35
  unlayered Bootstrap and project CSS keep precedence while the migration runs.
36
36
  See [Cascade layer strategy](https://github.com/lekoala/actual-css/blob/master/docs/design-notes/cascade-layer.md) for the
37
37
  limitations of the approach.
@@ -131,11 +131,9 @@ A Bootstrap layout such as:
131
131
  usually becomes an Actual grid:
132
132
 
133
133
  ```html
134
- <div class="container-query">
135
- <div class="grid-2">
136
- <div>...</div>
137
- <div>...</div>
138
- </div>
134
+ <div class="grid" style="--grid-min: 18rem">
135
+ <div>...</div>
136
+ <div>...</div>
139
137
  </div>
140
138
  ```
141
139
 
@@ -150,8 +148,10 @@ Or use the intrinsic grid when the real requirement is simply “fit as many use
150
148
  For asymmetric layouts, express the layout directly:
151
149
 
152
150
  ```html
153
- <div class="dashboard-grid grid">
154
- ...
151
+ <div class="container-query">
152
+ <div class="dashboard-grid grid">
153
+ ...
154
+ </div>
155
155
  </div>
156
156
  ```
157
157
 
@@ -216,7 +216,7 @@ Typical choices are:
216
216
  |--------------------------------------------|--------------------------------------------|
217
217
  | flex row that may wrap | `.cluster` |
218
218
  | vertical flex stack | `.stack` |
219
- | equal columns | `.grid-2`, `.grid-3`, `.grid-4`, `.grid-6` |
219
+ | exact structural columns | `.grid-2`, `.grid-3`, `.grid-4`, `.grid-6` |
220
220
  | intrinsically responsive cards | `.grid` |
221
221
  | row that becomes a column when constrained | `.switcher` |
222
222
  | sidebar/content layout | `.sidebar-layout` |
@@ -0,0 +1,186 @@
1
+ # Building with Actual CSS
2
+
3
+ Use Actual CSS as a vocabulary before writing application CSS.
4
+
5
+ The goal is not to eliminate custom CSS. The goal is to keep framework concerns in the framework and application identity in the application.
6
+
7
+ ## 1. Start with the theme
8
+
9
+ Define palette values once, then derive decorative colors from them.
10
+
11
+ ```css
12
+ [data-theme="brand"] {
13
+ --primary: hsl(326 100% 60%);
14
+ --secondary: hsl(187 100% 52%);
15
+
16
+ --brand-glow:
17
+ color-mix(in oklch, var(--primary) 40%, transparent);
18
+ --brand-line:
19
+ color-mix(in oklch, var(--secondary) 25%, transparent);
20
+ }
21
+ ```
22
+
23
+ Avoid repeating literal palette colors elsewhere:
24
+
25
+ ```css
26
+ /* Avoid */
27
+ .hero {
28
+ box-shadow: 0 0 2rem hsl(326 100% 60% / 0.4);
29
+ }
30
+
31
+ /* Prefer */
32
+ .hero {
33
+ box-shadow: 0 0 2rem var(--brand-glow);
34
+ }
35
+ ```
36
+
37
+ **Rule:** keep palette literals inside the theme/token layer. Application styles should consume or derive tokens.
38
+
39
+ ## 2. Choose layout by relationship
40
+
41
+ Do not choose a primitive because of the number of visible columns.
42
+
43
+ ```text
44
+ Repeated items, intrinsic reflow → .grid
45
+ Known equal peer density → .grid-N
46
+ Peers that switch together → .switcher
47
+ Main content + secondary region → .sidebar-layout
48
+ Media + flexible content → .media
49
+ Explicit 12-column placement → .column-layout
50
+ Custom exact track template → --grid-columns
51
+ ```
52
+
53
+ For example, three panels that must all stack together are a `.switcher`, not automatically a `.grid-3`.
54
+
55
+ If local CSS mainly makes one layout primitive behave like another, reconsider the primitive first.
56
+
57
+ ## 3. Tune before replacing
58
+
59
+ Actual primitives expose public hooks for common adjustments.
60
+
61
+ Prefer:
62
+
63
+ ```css
64
+ .feature-grid {
65
+ --grid-min: 18rem;
66
+ --gap: var(--space-50);
67
+ }
68
+
69
+ .product-card {
70
+ --card-pad: var(--space-50);
71
+ }
72
+
73
+ .actions {
74
+ --cluster-justify: space-between;
75
+ }
76
+ ```
77
+
78
+ over reimplementing their layout:
79
+
80
+ ```css
81
+ /* Avoid when an Actual primitive already owns this behavior */
82
+ .feature-grid {
83
+ display: grid;
84
+ grid-template-columns: repeat(...);
85
+ }
86
+ ```
87
+
88
+ **Rule:** tune the recipe before replacing the recipe.
89
+
90
+ ## 4. Reuse the existing vocabulary
91
+
92
+ Before introducing a generic application class, check whether Actual already expresses the idea.
93
+
94
+ Examples:
95
+
96
+ ```text
97
+ Small uppercase label → .overline
98
+ Unstyled list → .list-reset
99
+ Readable line length → .measure
100
+ Simple separator/dot → .dot
101
+ Small alignment change → existing utility
102
+ ```
103
+
104
+ Application classes should primarily describe product-specific concepts, not recreate generic framework utilities.
105
+
106
+ ## 5. Use structural component markup
107
+
108
+ When a component exposes meaningful structure, use it.
109
+
110
+ For cards, prefer semantic regions such as direct `header` and `footer` elements instead of reproducing their behavior inside arbitrary wrappers.
111
+
112
+ For transient application feedback, use `.status-bar`.
113
+
114
+ ```html
115
+ <div
116
+ class="status-bar"
117
+ data-status
118
+ role="status"
119
+ aria-live="polite"
120
+ aria-atomic="true"></div>
121
+ ```
122
+
123
+ A status bar is for messages such as:
124
+
125
+ ```text
126
+ Saved.
127
+ Item added.
128
+ Connection restored.
129
+ ```
130
+
131
+ Persistent state such as metrics, availability, queue size or account information belongs in normal page content.
132
+
133
+ ## 6. Keep application CSS for application identity
134
+
135
+ Custom CSS is expected for things such as:
136
+
137
+ * brand marks and decorative treatments;
138
+ * unusual geometry;
139
+ * illustrations and visual effects;
140
+ * bespoke animation;
141
+ * product-specific compositions;
142
+ * product-specific responsive decisions.
143
+
144
+ These are healthy application styles:
145
+
146
+ ```css
147
+ .brand-mark { ... }
148
+ .neon-glow { ... }
149
+ .ticket-cut { ... }
150
+ .signal-animation { ... }
151
+ ```
152
+
153
+ The target is **not zero custom CSS**.
154
+
155
+ A better target is:
156
+
157
+ > No duplicated framework behavior and no palette literals outside the token layer.
158
+
159
+ ## 7. Before adding a CSS rule
160
+
161
+ Ask, in this order:
162
+
163
+ 1. Is there already a suitable layout primitive?
164
+ 2. Is there already a component for this structure?
165
+ 3. Can a public hook configure it?
166
+ 4. Does an existing utility express the adjustment?
167
+ 5. Can the color be derived from a theme token?
168
+ 6. Is this genuinely application-specific CSS?
169
+
170
+ If the answer reaches step 6, write the CSS.
171
+
172
+ That is the intended integration model:
173
+
174
+ ```text
175
+ Theme
176
+ ↓
177
+ Choose the relationship
178
+ ↓
179
+ Use the component
180
+ ↓
181
+ Tune its hooks
182
+ ↓
183
+ Use small utilities
184
+ ↓
185
+ Add application identity
186
+ ```
@@ -4,8 +4,8 @@
4
4
 
5
5
  Actual CSS claims a small set of global class names and keeps everything opt-in:
6
6
  components define structure, variants change the style, and intents set the
7
- color. It is designed for new projects and works as-is in existing ones through
8
- cascade layers, import order, or an optional prefix transform.
7
+ color. It is designed for new projects and can coexist in existing ones through
8
+ cascade layers, import order, or a project-owned build-time prefix transform.
9
9
 
10
10
  ## CSS-first philosophy
11
11
 
@@ -26,44 +26,41 @@ ARIA and native HTML, and per-widget configuration uses self-describing
26
26
 
27
27
  ## Install
28
28
 
29
- ```text
29
+ ```sh
30
30
  npm install actual-css
31
31
  ```
32
32
 
33
33
  Import the full framework in your CSS:
34
34
 
35
35
  ```css
36
- @import "actual-css";
36
+ @import "actual-css/full";
37
37
  ```
38
38
 
39
+ `actual-css/full` ships every functional family. The bare `actual-css` entrypoint
40
+ is the minimal core (reset, tokens, theme, base, intents, variants, focus,
41
+ print).
42
+
39
43
  Or compose only the pieces you use:
40
44
 
41
45
  ```css
42
- @import "actual-css/css/reset";
43
- @import "actual-css/css/base";
44
- @import "actual-css/css/tokens";
45
- @import "actual-css/css/theme";
46
- @import "actual-css/css/intents";
47
- @import "actual-css/css/variants";
46
+ @import "actual-css/css";
48
47
  @import "actual-css/css/layout";
49
- @import "actual-css/css/grid";
50
- @import "actual-css/css/forms";
51
48
  @import "actual-css/css/components/button";
52
49
  @import "actual-css/css/components/card";
50
+ @import "actual-css/css/forms";
53
51
  @import "actual-css/css/components/flyout";
54
52
  @import "actual-css/css/utilities";
55
- /* Keep the focus baseline after components and controls. */
56
- @import "actual-css/css/focus";
57
53
  ```
58
54
 
59
- `focus` is the final interactive baseline in modular builds: older browsers
60
- retain a visible `:focus` outline, while browsers with `:focus-visible` avoid
61
- showing it for pointer focus.
55
+ `actual-css/css` is the minimal core. Family manifests (`css/layout`,
56
+ `css/forms`, `css/components`, `css/typography`, `css/effects`,
57
+ `css/utilities`) and their modules map one-to-one to `src/css/`. See the
58
+ [modular import guide](modular-import.md) for the full map.
62
59
 
63
- You can also use the compiled file directly:
60
+ You can also use the compiled full bundle directly:
64
61
 
65
62
  ```html
66
- <link rel="stylesheet" href="actual.min.css">
63
+ <link rel="stylesheet" href="actual.full.min.css">
67
64
  ```
68
65
 
69
66
  The package does not maintain separate partial bundles. Modular entrypoints map
@@ -99,7 +96,7 @@ filtering, and input masks. Presentation and behavior are separate layers —
99
96
  import the full runtime or any primitive independently to build custom widgets.
100
97
 
101
98
  ```html
102
- <script src="actual.js" type="module"></script>
99
+ <script src="actual.full.js" type="module"></script>
103
100
  ```
104
101
 
105
102
  ```js
@@ -142,12 +139,12 @@ internal design documents for contributors.
142
139
 
143
140
  Actual CSS is built around progressive enhancement.
144
141
 
145
- | Tier | Firefox | Safari | Chromium |
146
- |---|---|---|---|
147
- | Degraded | 78+ | 14+ | 88+ |
148
- | **Minimal** | **98+** | **15.4+** | **99+** |
149
- | Intermediate | 121+ | 16+ | 106+ |
150
- | Recommended | 129+ | 17.5+ | 123+ |
142
+ | Tier | Firefox | Safari | Chromium |
143
+ |--------------|---------|-----------|----------|
144
+ | Degraded | 78+ | 14+ | 88+ |
145
+ | **Minimal** | **98+** | **15.4+** | **99+** |
146
+ | Intermediate | 121+ | 16+ | 106+ |
147
+ | Recommended | 129+ | 17.5+ | 123+ |
151
148
 
152
149
  **Degraded** — semantic HTML and core CSS remain usable. JavaScript
153
150
  enhancements are outside the supported contract.