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.
- package/CHANGELOG.md +67 -3
- package/README.md +35 -30
- package/dist/actual.css +73 -4946
- package/dist/actual.full.css +7090 -0
- package/dist/actual.full.js +1 -0
- package/dist/actual.full.min.css +1 -0
- package/dist/actual.js +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/cascade-layer.md +34 -13
- package/docs/design-notes/column-layout.md +677 -0
- package/docs/design-notes/forced-colors.md +3 -0
- package/docs/design-notes/grid.md +612 -0
- package/docs/design-notes/naming.md +15 -0
- package/docs/design-notes/widget-primitives.md +1 -0
- package/docs/navigation.json +5 -1
- package/docs/pages/components/alert.md +16 -14
- package/docs/pages/components/aura.md +1 -1
- package/docs/pages/components/avatar.md +6 -6
- package/docs/pages/components/badge.md +7 -7
- package/docs/pages/components/button.md +16 -16
- package/docs/pages/components/card.md +13 -9
- package/docs/pages/components/chat.md +8 -8
- package/docs/pages/components/dialog.md +7 -7
- package/docs/pages/components/drawer.md +3 -3
- package/docs/pages/components/fab.md +7 -7
- package/docs/pages/components/navbar.md +9 -8
- package/docs/pages/components/pagination.md +4 -3
- package/docs/pages/components/skeleton.md +4 -4
- package/docs/pages/components/spinner.md +5 -5
- package/docs/pages/components/table.md +30 -5
- package/docs/pages/enhancements/flyout.md +13 -13
- package/docs/pages/enhancements/overview.md +1 -1
- package/docs/pages/enhancements/scrollspy.md +6 -6
- package/docs/pages/enhancements/status-bar.md +5 -1
- package/docs/pages/enhancements/tabs.md +6 -6
- package/docs/pages/enhancements/tooltip.md +1 -1
- package/docs/pages/examples/overview.md +39 -22
- package/docs/pages/forms/choice-cards.md +11 -6
- package/docs/pages/forms/controls.md +1 -1
- package/docs/pages/forms/detached-actions.md +1 -1
- package/docs/pages/forms/floating-field.md +10 -5
- package/docs/pages/forms/form-actions.md +1 -1
- package/docs/pages/forms/input-policy.md +1 -1
- package/docs/pages/forms/joined-controls.md +4 -4
- package/docs/pages/forms/labels.md +6 -6
- package/docs/pages/forms/otp.md +1 -1
- package/docs/pages/forms/overview.md +19 -19
- package/docs/pages/forms/password.md +1 -1
- package/docs/pages/forms/range.md +1 -1
- package/docs/pages/forms/states-and-sizes.md +41 -45
- package/docs/pages/forms/sticky-actions.md +3 -3
- package/docs/pages/forms/switch.md +110 -2
- package/docs/pages/forms/validation.md +9 -9
- package/docs/pages/foundations/philosophy.md +1 -1
- package/docs/pages/foundations/theming.md +17 -2
- package/docs/pages/foundations/tokens.md +22 -10
- package/docs/pages/foundations/typography.md +50 -20
- package/docs/pages/guides/bootstrap.md +11 -11
- package/docs/pages/guides/integrating-actual-css.md +186 -0
- package/docs/pages/guides/introduction.md +23 -26
- package/docs/pages/guides/modular-import.md +173 -0
- package/docs/pages/guides/progressive-enhancement.md +81 -78
- package/docs/pages/guides/tailwind.md +6 -8
- package/docs/pages/layout/app-shell.md +1 -1
- package/docs/pages/layout/center.md +1 -1
- package/docs/pages/layout/choosing.md +39 -0
- package/docs/pages/layout/column-layout.md +206 -0
- package/docs/pages/layout/frame.md +6 -1
- package/docs/pages/layout/grid.md +100 -102
- package/docs/pages/layout/list.md +1 -1
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +15 -2
- package/docs/pages/layout/scroll-snap.md +2 -2
- package/docs/pages/layout/scroll-target.md +45 -0
- package/docs/pages/layout/sidebar-layout.md +20 -19
- package/docs/pages/layout/stack.md +53 -1
- package/docs/pages/layout/switcher.md +10 -26
- package/docs/pages/layout/topbar.md +2 -2
- package/docs/pages/patterns/actions.md +1 -1
- package/docs/pages/patterns/code-block.md +1 -1
- package/docs/pages/patterns/exclusive-accordion.md +1 -1
- package/docs/pages/patterns/hierarchical-nav.md +1 -1
- package/docs/pages/patterns/icon-input.md +1 -1
- package/docs/pages/patterns/intent-nesting.md +1 -1
- package/docs/pages/patterns/navigation-list.md +1 -1
- package/docs/pages/patterns/overview.md +1 -1
- package/docs/pages/patterns/structured-lists.md +1 -1
- package/docs/pages/utilities/accessibility.md +3 -3
- package/docs/pages/utilities/flex.md +15 -15
- package/docs/pages/utilities/links.md +4 -4
- package/docs/pages/utilities/list.md +8 -8
- package/docs/pages/utilities/overflow.md +8 -8
- package/docs/pages/utilities/overview.md +1 -1
- package/docs/pages/utilities/shape.md +3 -3
- package/docs/pages/utilities/sizing-wrapping.md +23 -16
- package/docs/pages/utilities/spacing.md +20 -20
- package/docs/pages/utilities/text.md +10 -10
- package/llms.txt +46 -4
- package/package.json +42 -13
- package/scripts/reserved-classes.json +261 -0
- package/src/cli/actual-css.js +133 -0
- package/src/css/actual.css +1 -31
- package/src/css/actual.full.css +11 -2
- package/src/css/components/alert.css +17 -4
- package/src/css/components/badge.css +18 -6
- package/src/css/components/breadcrumb.css +1 -1
- package/src/css/components/button.css +19 -1
- package/src/css/components/card.css +27 -2
- package/src/css/components/drawer.css +27 -2
- package/src/css/components/flyout.css +5 -0
- package/src/css/components/index.css +32 -27
- package/src/css/components/join.css +8 -1
- package/src/css/components/key.css +2 -0
- package/src/css/components/menu.css +11 -1
- package/src/css/components/meter.css +4 -1
- package/src/css/components/modal.css +5 -0
- package/src/css/components/pagination.css +11 -0
- package/src/css/components/spinner.css +5 -0
- package/src/css/components/status-bar.css +5 -0
- package/src/css/components/surface.css +5 -0
- package/src/css/components/table.css +6 -0
- package/src/css/components/tooltip.css +5 -0
- package/src/css/core/index.css +9 -0
- package/src/css/core/print.css +18 -0
- package/src/css/{theme.css → core/theme.css} +20 -10
- package/src/css/{tokens.css → core/tokens.css} +15 -2
- package/src/css/{variants.css → core/variants.css} +3 -2
- package/src/css/effects/index.css +2 -0
- package/src/css/forms/base.css +8 -0
- package/src/css/forms/choice-card.css +5 -4
- package/src/css/forms/choice.css +49 -37
- package/src/css/forms/control.css +4 -1
- package/src/css/forms/custom-select.css +2 -0
- package/src/css/{optional → forms}/floating-field.css +25 -7
- package/src/css/forms/index.css +10 -13
- package/src/css/forms/native.css +15 -2
- package/src/css/{optional → forms}/otp.css +20 -16
- package/src/css/forms/switch.css +31 -29
- package/src/css/layout/app-shell.css +6 -0
- package/src/css/layout/center.css +13 -0
- package/src/css/layout/cluster.css +21 -0
- package/src/css/layout/column-layout.css +144 -0
- package/src/css/layout/container-query.css +13 -0
- package/src/css/layout/frame.css +19 -0
- package/src/css/layout/grid.css +141 -0
- package/src/css/layout/index.css +42 -0
- package/src/css/layout/measure.css +9 -0
- package/src/css/layout/media.css +10 -0
- package/src/css/layout/scroll-target.css +15 -0
- package/src/css/layout/scroller.css +41 -0
- package/src/css/layout/sidebar-layout.css +31 -0
- package/src/css/layout/stack.css +21 -0
- package/src/css/layout/switcher.css +23 -0
- package/src/css/{optional/layout-extra.css → layout/topbar.css} +0 -20
- package/src/css/typography/index.css +5 -0
- package/src/css/typography/lead.css +7 -0
- package/src/css/{components → typography}/overline.css +2 -2
- package/src/css/{prose.css → typography/prose.css} +53 -38
- package/src/css/{utilities.css → utilities/base.css} +2 -0
- package/src/css/utilities/index.css +3 -0
- package/src/js/escape.js +36 -0
- package/src/js/flyout.js +22 -12
- package/src/js/full.js +26 -0
- package/src/js/index.js +5 -21
- package/src/js/surface.js +1 -31
- package/src/js/tooltip.js +46 -18
- package/src/js/validation.js +1 -1
- package/src/tooling/css-bundle.js +308 -0
- package/dist/actual-themes.min.css +0 -1
- package/dist/optional.css +0 -1048
- package/dist/optional.min.css +0 -1
- package/docs/pages/guides/optional-components.md +0 -33
- package/src/css/grid.css +0 -109
- package/src/css/layout.css +0 -123
- package/src/css/optional/index.css +0 -10
- package/src/css/optional/scroller.css +0 -63
- package/src/css/print.css +0 -47
- package/src/css/themes/corporate.css +0 -60
- package/src/css/themes/cyberpunk.css +0 -60
- package/src/css/themes/dim.css +0 -33
- package/src/css/themes/edge.css +0 -117
- package/src/css/themes/forest.css +0 -65
- package/src/css/themes/gradient.css +0 -97
- package/src/css/themes/index.css +0 -47
- package/src/css/themes/indigo.css +0 -33
- package/src/css/themes/lavender.css +0 -66
- package/src/css/themes/mono.css +0 -60
- package/src/css/themes/neon.css +0 -66
- package/src/css/themes/ocean.css +0 -60
- package/src/css/themes/petrol.css +0 -64
- package/src/css/themes/spruce.css +0 -64
- package/src/css/themes/square.css +0 -65
- package/src/css/themes/sunset.css +0 -66
- /package/src/css/{optional → components}/chat.css +0 -0
- /package/src/css/{optional → components}/fab.css +0 -0
- /package/src/css/{base.css → core/base.css} +0 -0
- /package/src/css/{focus.css → core/focus.css} +0 -0
- /package/src/css/{intents.css → core/intents.css} +0 -0
- /package/src/css/{reset.css → core/reset.css} +0 -0
- /package/src/css/{optional → effects}/aura.css +0 -0
- /package/src/css/{optional → layout}/scroll-snap.css +0 -0
- /package/src/css/{optional/typography-fluid.css → typography/fluid.css} +0 -0
- /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
|
|
265
|
-
|
|
266
|
-
| `--z-sticky`
|
|
267
|
-
| `--z-menu`
|
|
268
|
-
| `--z-tooltip` | 50
|
|
269
|
-
| `--z-status`
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
###
|
|
55
|
+
### Fluid module
|
|
56
56
|
|
|
57
|
-
`src/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
|
|
62
|
-
|
|
63
|
-
| `--fluid-display` | `.text-display` | Hero / display sizes.
|
|
64
|
-
| `--fluid-title`
|
|
65
|
-
| `--fluid-lead`
|
|
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
|
|
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
|
-
|
|
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
|
|
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`,
|
|
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
|
|
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
|
-
|
|
186
|
+
```css
|
|
187
|
+
@import "actual-css/css";
|
|
188
|
+
@import "actual-css/css/typography/fluid";
|
|
189
|
+
```
|
|
160
190
|
|
|
161
|
-
|
|
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
|
|
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
|
|
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
|
-
@
|
|
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
|
-
|
|
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="
|
|
135
|
-
<div
|
|
136
|
-
|
|
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="
|
|
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
|
-
|
|
|
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
|
|
8
|
-
cascade layers, import order, or
|
|
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
|
-
```
|
|
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
|
|
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
|
-
`
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
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
|
|
146
|
-
|
|
147
|
-
| Degraded
|
|
148
|
-
| **Minimal**
|
|
149
|
-
| Intermediate | 121+
|
|
150
|
-
| Recommended
|
|
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.
|