actual-css 0.11.1 → 0.13.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 +96 -0
- package/README.md +25 -6
- package/components.json +12 -18
- package/dist/actual.full.css +780 -479
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/docs/brand/favicon.svg +5 -0
- package/docs/brand/lockup.svg +12 -0
- package/docs/brand/logo-dark.svg +9 -0
- package/docs/brand/logo-light.svg +9 -0
- package/docs/brand/logo.svg +11 -0
- package/docs/brand/mark.svg +11 -0
- package/docs/design-notes/architecture.md +67 -0
- package/docs/design-notes/steps.md +45 -28
- package/docs/navigation.json +3 -2
- package/docs/pages/components/alert.md +2 -10
- package/docs/pages/components/avatar.md +17 -11
- package/docs/pages/components/badge.md +3 -9
- package/docs/pages/components/breadcrumb.md +16 -2
- package/docs/pages/components/card.md +31 -2
- package/docs/pages/components/dialog.md +121 -33
- package/docs/pages/components/drawer.md +2 -2
- package/docs/pages/components/list.md +9 -13
- package/docs/pages/components/navbar.md +49 -30
- package/docs/pages/components/pagination.md +19 -0
- package/docs/pages/components/steps.md +24 -20
- package/docs/pages/components/table.md +3 -3
- package/docs/pages/enhancements/flyout.md +2 -2
- package/docs/pages/enhancements/scrollspy.md +7 -7
- package/docs/pages/enhancements/status-bar.md +2 -2
- package/docs/pages/enhancements/tabs.md +74 -3
- package/docs/pages/examples/overview.md +2 -1
- package/docs/pages/forms/date-picker.md +90 -0
- package/docs/pages/forms/detached-actions.md +37 -30
- package/docs/pages/forms/floating-field.md +21 -7
- package/docs/pages/forms/form-actions.md +58 -28
- package/docs/pages/forms/input-policy.md +1 -1
- package/docs/pages/forms/overview.md +6 -5
- package/docs/pages/forms/states-and-sizes.md +15 -15
- package/docs/pages/forms/sticky-actions.md +85 -83
- package/docs/pages/forms/validation.md +5 -5
- package/docs/pages/foundations/philosophy.md +102 -4
- package/docs/pages/foundations/theming.md +15 -4
- package/docs/pages/foundations/tokens.md +89 -36
- package/docs/pages/foundations/typography.md +4 -7
- package/docs/pages/guides/bootstrap.md +2 -2
- package/docs/pages/guides/design-tools.md +2 -2
- package/docs/pages/guides/integrating-actual-css.md +79 -9
- package/docs/pages/guides/introduction.md +5 -5
- package/docs/pages/guides/modular-import.md +3 -0
- package/docs/pages/guides/progressive-enhancement.md +27 -9
- package/docs/pages/guides/tailwind.md +70 -0
- package/docs/pages/guides/upgrading.md +151 -0
- package/docs/pages/layout/center.md +2 -1
- package/docs/pages/layout/cluster.md +37 -4
- package/docs/pages/layout/grid.md +2 -0
- package/docs/pages/layout/overview.md +1 -1
- package/docs/pages/layout/sidebar-layout.md +3 -0
- package/docs/pages/layout/stack.md +21 -16
- package/docs/pages/layout/switcher.md +2 -0
- package/docs/pages/layout/topbar.md +7 -5
- package/docs/pages/patterns/icon-slot.md +17 -1
- package/docs/pages/patterns/navigation-list.md +16 -46
- package/docs/pages/patterns/overview.md +3 -4
- package/docs/pages/utilities/flex.md +30 -20
- package/docs/pages/utilities/sizing-wrapping.md +52 -7
- package/docs/pages/utilities/spacing.md +4 -3
- package/package.json +6 -6
- package/reserved-classes.json +3 -3
- package/skills/actual-css/references/enhancements.md +10 -7
- package/src/cli/actual-css.js +39 -13
- package/src/css/components/alert.css +34 -34
- package/src/css/components/avatar.css +8 -17
- package/src/css/components/badge.css +1 -6
- package/src/css/components/breadcrumb.css +74 -8
- package/src/css/components/button.css +3 -9
- package/src/css/components/card.css +18 -10
- package/src/css/components/chat.css +4 -5
- package/src/css/components/drawer.css +42 -45
- package/src/css/components/flyout.css +3 -1
- package/src/css/components/index.css +0 -1
- package/src/css/components/join.css +2 -2
- package/src/css/components/list.css +10 -10
- package/src/css/components/modal.css +113 -134
- package/src/css/components/navbar.css +22 -31
- package/src/css/components/pagination.css +5 -0
- package/src/css/components/scrollspy.css +1 -1
- package/src/css/components/steps.css +66 -12
- package/src/css/components/tab.css +134 -4
- package/src/css/core/base.css +1 -1
- package/src/css/core/focus.css +8 -6
- package/src/css/core/reset.css +2 -1
- package/src/css/core/theme.css +10 -1
- package/src/css/core/tokens.css +32 -12
- package/src/css/core/variants.css +41 -14
- package/src/css/forms/choice-card.css +1 -1
- package/src/css/forms/choice.css +1 -1
- package/src/css/forms/control.css +1 -1
- package/src/css/forms/floating-field.css +20 -18
- package/src/css/forms/form-actions.css +14 -12
- package/src/css/forms/native.css +2 -2
- package/src/css/forms/otp.css +4 -5
- package/src/css/forms/switch.css +1 -1
- package/src/css/forms/validation.css +23 -30
- package/src/css/layout/center.css +2 -0
- package/src/css/layout/cluster.css +25 -0
- package/src/css/layout/grid.css +7 -0
- package/src/css/layout/scroller.css +19 -3
- package/src/css/layout/sidebar-layout.css +3 -0
- package/src/css/layout/switcher.css +3 -0
- package/src/css/layout/topbar.css +12 -6
- package/src/css/utilities/base.css +5 -0
- package/src/css/utilities/extra.css +3 -7
- package/src/js/full.js +4 -0
- package/src/js/inline-visible.js +54 -0
- package/src/js/parsed.js +21 -0
- package/src/js/tab.js +33 -7
- package/src/tooling/color.js +65 -0
- package/src/tooling/design-export.js +1 -1
- package/src/tooling/design-tokens.js +53 -34
- package/src/tooling/theme-contrast.js +217 -0
- package/docs/pages/patterns/actions.md +0 -52
- package/src/css/components/actions.css +0 -27
|
@@ -115,13 +115,10 @@ baseline stays sans so app UI inherits predictably.
|
|
|
115
115
|
|
|
116
116
|
### Font width
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
typeface has no useful width axis sets `--font-width-dense: 100%`. Width is a
|
|
123
|
-
density token, not a responsive escape hatch — never condense text to fix an
|
|
124
|
-
overflowing label.
|
|
118
|
+
`--font-width` sets the document's `font-stretch`: a theme whose typeface has a
|
|
119
|
+
`wdth` axis or several width faces picks its width there. Density does not
|
|
120
|
+
change it. To condense one dense table or panel, set `font-stretch` on that
|
|
121
|
+
element; never condense text to fix an overflowing label.
|
|
125
122
|
|
|
126
123
|
## Font weights
|
|
127
124
|
|
|
@@ -361,12 +361,12 @@ Actual's navbar is intentionally a simpler navigation pattern.
|
|
|
361
361
|
Keep the desktop navigation as a navbar and compose the mobile experience from a
|
|
362
362
|
drawer or flyout rather than looking for a `.navbar-expand-lg` equivalent:
|
|
363
363
|
|
|
364
|
-
* desktop → `.navbar` with `.
|
|
364
|
+
* desktop → `.navbar` with a `.cluster` link list;
|
|
365
365
|
* mobile → `.drawer` with `.nav-list`;
|
|
366
366
|
* trigger → a `command="show-modal"` / `commandfor` button.
|
|
367
367
|
|
|
368
368
|
The [Navbar component page](../components/navbar.md) shows this composition
|
|
369
|
-
as *the Actual way*: a horizontal `.
|
|
369
|
+
as *the Actual way*: a horizontal `.cluster` on the desktop bar and a vertical
|
|
370
370
|
`.nav-list` inside the mobile drawer, opened with the native dialog command
|
|
371
371
|
pattern. There is no collapse/toggler state to keep in sync.
|
|
372
372
|
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
`actual-css design` exports a theme to design tools: its tokens for Figma and Penpot, and a Penpot plugin that builds Button, Input, Badge, Alert and Card bound to those tokens. Use it to hand designers the same palette, scale and components the code ships — a design kit, a UI kit, a Figma or Penpot library generated from your theme.
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
bunx actual-css design --theme src/theme.css --out design/
|
|
6
|
+
bunx --bun actual-css design --theme src/theme.css --out design/
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
`--theme` is a file declaring one `[data-theme="…"]` block (see [Theming](../foundations/theming.md)); without it the default theme is exported. The command needs Bun: it reads the theme in headless Chrome, so every `light-dark()`, `color-mix()` and `var()` chain resolves exactly as the browser paints it.
|
|
9
|
+
`--theme` is a file declaring one theme, on `:root` or in a `[data-theme="…"]` block (see [Theming](../foundations/theming.md)); without it the default theme is exported. The command needs Bun, and `--bun` makes `bunx` run it under Bun instead of the CLI's Node shebang: it reads the theme in headless Chrome, so every `light-dark()`, `color-mix()` and `var()` chain resolves exactly as the browser paints it.
|
|
10
10
|
|
|
11
11
|
## What is exported
|
|
12
12
|
|
|
@@ -8,7 +8,8 @@ In short: **use Actual for generic structure and behavior; add product-specific
|
|
|
8
8
|
classes for brand and compositions rather than forcing a generic primitive.** A
|
|
9
9
|
local rule is not a failure of Actual — overfitting the framework to a
|
|
10
10
|
product-specific look is. Reach for an Actual primitive first, and write
|
|
11
|
-
application CSS when the need is genuinely product-specific (see §
|
|
11
|
+
application CSS when the need is genuinely product-specific (see §9–10). The
|
|
12
|
+
principles behind this order are in [Philosophy](../foundations/philosophy.md).
|
|
12
13
|
|
|
13
14
|
## 1. Start with the theme
|
|
14
15
|
|
|
@@ -60,7 +61,29 @@ For example, three panels that must all stack together are a `.switcher`, not au
|
|
|
60
61
|
|
|
61
62
|
If local CSS mainly makes one layout primitive behave like another, reconsider the primitive first.
|
|
62
63
|
|
|
63
|
-
## 3.
|
|
64
|
+
## 3. Keep outer spacing in the layout
|
|
65
|
+
|
|
66
|
+
Components own their internal rhythm; layout primitives own the space between
|
|
67
|
+
components.
|
|
68
|
+
|
|
69
|
+
```html
|
|
70
|
+
<div class="stack">
|
|
71
|
+
<article class="card">…</article>
|
|
72
|
+
<article class="card">…</article>
|
|
73
|
+
</div>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
```css
|
|
77
|
+
/* Avoid */
|
|
78
|
+
.card + .card {
|
|
79
|
+
margin-block-start: var(--space-50);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
A card then moves between a stack, a grid or a sidebar without its outer
|
|
84
|
+
spacing to undo.
|
|
85
|
+
|
|
86
|
+
## 4. Tune before replacing
|
|
64
87
|
|
|
65
88
|
Actual primitives expose public hooks for common adjustments.
|
|
66
89
|
|
|
@@ -76,11 +99,15 @@ Prefer:
|
|
|
76
99
|
--card-pad: var(--space-50);
|
|
77
100
|
}
|
|
78
101
|
|
|
79
|
-
.actions {
|
|
102
|
+
.product-actions {
|
|
80
103
|
--cluster-justify: space-between;
|
|
81
104
|
}
|
|
82
105
|
```
|
|
83
106
|
|
|
107
|
+
`.product-actions` is an application class composed onto the primitive:
|
|
108
|
+
`<div class="cluster product-actions">`. The hook is read by `.cluster`, so the
|
|
109
|
+
CSS stays a local adjustment, not a reimplementation.
|
|
110
|
+
|
|
84
111
|
over reimplementing their layout:
|
|
85
112
|
|
|
86
113
|
```css
|
|
@@ -93,7 +120,12 @@ over reimplementing their layout:
|
|
|
93
120
|
|
|
94
121
|
**Rule:** tune the recipe before replacing the recipe.
|
|
95
122
|
|
|
96
|
-
|
|
123
|
+
`--gap` is inherited: set on a region, it re-spaces every nested stack, cluster
|
|
124
|
+
and grid too. Set it on the layout that reads it, as `.feature-grid` does above.
|
|
125
|
+
For a region's own rhythm, set the `gap` property instead; see
|
|
126
|
+
[Region rhythm](../layout/stack.md#region-rhythm-goes-on-gap-not-on-gap).
|
|
127
|
+
|
|
128
|
+
## 5. Reuse the existing vocabulary
|
|
97
129
|
|
|
98
130
|
Before introducing a generic application class, check whether Actual already expresses the idea.
|
|
99
131
|
|
|
@@ -109,7 +141,7 @@ Small alignment change → existing utility
|
|
|
109
141
|
|
|
110
142
|
Application classes should primarily describe product-specific concepts, not recreate generic framework utilities.
|
|
111
143
|
|
|
112
|
-
##
|
|
144
|
+
## 6. Use structural component markup
|
|
113
145
|
|
|
114
146
|
When a component exposes meaningful structure, use it.
|
|
115
147
|
|
|
@@ -136,7 +168,44 @@ Connection restored.
|
|
|
136
168
|
|
|
137
169
|
Persistent state such as metrics, availability, queue size or account information belongs in normal page content.
|
|
138
170
|
|
|
139
|
-
##
|
|
171
|
+
## 7. Style state through its attribute
|
|
172
|
+
|
|
173
|
+
Use the native attribute or the ARIA state the markup already needs, then
|
|
174
|
+
`data-*` for state with no equivalent. Classes stay for presentation.
|
|
175
|
+
|
|
176
|
+
```html
|
|
177
|
+
<a class="nav-link" aria-current="page">Settings</a>
|
|
178
|
+
<button class="btn primary" disabled>Save</button>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
```css
|
|
182
|
+
.checkout-form[aria-busy="true"] { … }
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Not `.nav-link.active` or `.is-disabled`: a state class duplicates the attribute
|
|
186
|
+
and drifts from it. Two runtime markers are off limits too:
|
|
187
|
+
|
|
188
|
+
- `data-enhance` turns behavior on; it is never a styling hook.
|
|
189
|
+
- `.is-*` classes (`.is-open`, `.is-static`) are runtime internals: neither set
|
|
190
|
+
nor select them.
|
|
191
|
+
|
|
192
|
+
## 8. Bring third-party widgets onto the tokens
|
|
193
|
+
|
|
194
|
+
A date picker, rich text editor or data grid can come from a dedicated package.
|
|
195
|
+
Map its public visual hooks to Actual tokens instead of restyling it:
|
|
196
|
+
|
|
197
|
+
```css
|
|
198
|
+
.third-party-picker {
|
|
199
|
+
--picker-bg: var(--surface-raised);
|
|
200
|
+
--picker-border: var(--border);
|
|
201
|
+
--picker-text: var(--text);
|
|
202
|
+
--picker-focus: var(--focus);
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The package keeps its behavior; the theme reaches it through the tokens.
|
|
207
|
+
|
|
208
|
+
## 9. Keep application CSS for application identity
|
|
140
209
|
|
|
141
210
|
Custom CSS is expected for things such as:
|
|
142
211
|
|
|
@@ -162,7 +231,7 @@ A better target is:
|
|
|
162
231
|
|
|
163
232
|
> No duplicated framework behavior and no palette literals outside the token layer.
|
|
164
233
|
|
|
165
|
-
##
|
|
234
|
+
## 10. Before adding a CSS rule
|
|
166
235
|
|
|
167
236
|
Ask, in this order:
|
|
168
237
|
|
|
@@ -171,9 +240,10 @@ Ask, in this order:
|
|
|
171
240
|
3. Can a public hook configure it?
|
|
172
241
|
4. Does an existing utility express the adjustment?
|
|
173
242
|
5. Can the color be derived from a theme token?
|
|
174
|
-
6.
|
|
243
|
+
6. Does an attribute already name the state?
|
|
244
|
+
7. Is this genuinely application-specific CSS?
|
|
175
245
|
|
|
176
|
-
If the answer reaches step
|
|
246
|
+
If the answer reaches step 7, write the CSS.
|
|
177
247
|
|
|
178
248
|
That is the intended integration model:
|
|
179
249
|
|
|
@@ -22,7 +22,8 @@ cascade layers, import order, or a project-owned build-time prefix transform.
|
|
|
22
22
|
The distinction is structural, not stylistic: presentation lives in classes,
|
|
23
23
|
JavaScript behavior opts in through `data-enhance` tokens, semantics come from
|
|
24
24
|
ARIA and native HTML, and per-widget configuration uses self-describing
|
|
25
|
-
`data-*` attributes.
|
|
25
|
+
`data-*` attributes. [Philosophy](../foundations/philosophy.md) lays out the
|
|
26
|
+
layers and the order to follow before adding CSS.
|
|
26
27
|
|
|
27
28
|
## Install
|
|
28
29
|
|
|
@@ -52,7 +53,7 @@ optimizing — the bare `actual-css` entrypoint and `actual-css/full` both resol
|
|
|
52
53
|
to the complete compiled framework:
|
|
53
54
|
|
|
54
55
|
```html
|
|
55
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/actual-css@0.
|
|
56
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/actual-css@0.13/dist/actual.full.min.css">
|
|
56
57
|
```
|
|
57
58
|
|
|
58
59
|
The package does not maintain separate partial bundles. Modular entrypoints map
|
|
@@ -72,9 +73,8 @@ shared by buttons, badges, alerts, and cards; a subtree that needs another
|
|
|
72
73
|
palette is a `data-theme` island, not a class. `.ghost` and `.link` are
|
|
73
74
|
button-only. Size variants `.sm` and `.lg` scale typography and
|
|
74
75
|
participating component geometry consistently. Density contexts `.compact` and
|
|
75
|
-
`.spacious` change geometry and spacing without changing font size
|
|
76
|
-
|
|
77
|
-
Undocumented `is-*` classes are runtime internals.
|
|
76
|
+
`.spacious` change geometry and spacing without changing font size, font width
|
|
77
|
+
or icons. Undocumented `is-*` classes are runtime internals.
|
|
78
78
|
|
|
79
79
|
```html
|
|
80
80
|
<button class="btn">Click me</button>
|
|
@@ -98,6 +98,9 @@ cells, so they read as a list:
|
|
|
98
98
|
- **Utilities** — `actual-css/css/utilities`
|
|
99
99
|
base and extra utilities
|
|
100
100
|
|
|
101
|
+
`.form-actions` composes with `.cluster`: a build that takes forms without the
|
|
102
|
+
layout family also imports `actual-css/css/layout/cluster`, in any order.
|
|
103
|
+
|
|
101
104
|
Individual modules can be imported through their domain path:
|
|
102
105
|
|
|
103
106
|
```css
|
|
@@ -63,11 +63,30 @@ handle, and cleanup happens per element when an element leaves the DOM.
|
|
|
63
63
|
|
|
64
64
|
## The full runtime
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
Import `actual-css/js/full` to enable the full runtime:
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
import "actual-css/js/full";
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The same entrypoint exposes the public enhancement API from the same runtime
|
|
73
|
+
instance, for adopters that extend the runtime they already run:
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
import { applyEnhancement, enhance, registerEnhancement } from "actual-css/js/full";
|
|
77
|
+
|
|
78
|
+
registerEnhancement("autosubmit", (form) => {
|
|
79
|
+
// ...
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
For anything beyond that extension API, keep using `actual-css/js/*`.
|
|
84
|
+
|
|
85
|
+
The same entrypoint produces the `dist/actual.full.js` standalone for a
|
|
86
|
+
classic `<script>`, where no global API is exposed. Static HTML that needs to
|
|
87
|
+
trigger something from its own inline `<script>` dispatches the module's
|
|
88
|
+
public event instead of calling into a global — `actual:status` for the
|
|
89
|
+
status bar, for example:
|
|
71
90
|
|
|
72
91
|
```html
|
|
73
92
|
<script src="actual.full.js"></script>
|
|
@@ -110,8 +129,7 @@ needed.
|
|
|
110
129
|
The JS is designed to run on its own, without Actual's stylesheet.
|
|
111
130
|
|
|
112
131
|
- **`data-enhance` tokens** register named behaviors. The runtime discovers
|
|
113
|
-
components through `data-enhance="tabs"`, `data-enhance="flyout"`,
|
|
114
|
-
`data-enhance="range"`, `data-enhance="scrollspy"`, and
|
|
132
|
+
components through `data-enhance="tabs"`, `data-enhance="flyout"`, `data-enhance="range"`, `data-enhance="scrollspy"`, and
|
|
115
133
|
`data-enhance="validation"` — no Actual presentation class needed.
|
|
116
134
|
Self-describing attributes (`data-mask`, `data-tooltip`,
|
|
117
135
|
`data-context-menu`) stay framework-neutral by construction.
|
|
@@ -760,12 +778,12 @@ field, and dispatches a bubbling `actual:invalid` event with
|
|
|
760
778
|
`{ form, firstInvalid, message }`. Valid fields are not marked automatically.
|
|
761
779
|
|
|
762
780
|
```html demo
|
|
763
|
-
<form class="needs-validation" data-enhance="validation"
|
|
781
|
+
<form class="stack needs-validation" data-enhance="validation"
|
|
764
782
|
data-validation-message="Please check the fields.">
|
|
765
783
|
<input class="input" name="email" type="email" required
|
|
766
784
|
aria-describedby="email-error" />
|
|
767
785
|
<span class="field-error" id="email-error">Enter a valid email.</span>
|
|
768
|
-
<div class="form-actions">
|
|
786
|
+
<div class="cluster form-actions">
|
|
769
787
|
<button class="btn primary" type="submit">Submit</button>
|
|
770
788
|
</div>
|
|
771
789
|
</form>
|
|
@@ -380,6 +380,76 @@ Actual theme tokens should handle shared theme behavior. Local CSS remains appro
|
|
|
380
380
|
|
|
381
381
|
This is particularly important when migrating a Tailwind application with a large custom color palette.
|
|
382
382
|
|
|
383
|
+
### Map the Tailwind palette to theme roles
|
|
384
|
+
|
|
385
|
+
Tailwind's theme is a set of ramps: `--color-zinc-50` … `--color-zinc-950`,
|
|
386
|
+
one per hue. Actual's theme contract is a set of roles: `--surface`,
|
|
387
|
+
`--border`, `--text-muted`, `--primary`, `--focus`. Each element picks a
|
|
388
|
+
step in Tailwind; Actual picks once per role, in the theme, and every
|
|
389
|
+
component reads the role. Map one neutral family and one brand hue, then
|
|
390
|
+
stop: the rest of the ramp has no consumer.
|
|
391
|
+
|
|
392
|
+
A starting point for a zinc and blue application:
|
|
393
|
+
|
|
394
|
+
| Role | Light | Dark |
|
|
395
|
+
| ------------------ | ---------- | ---------- |
|
|
396
|
+
| `--surface` | `white` | `zinc-950` |
|
|
397
|
+
| `--surface-raised` | `white` | `zinc-900` |
|
|
398
|
+
| `--surface-subtle` | `zinc-100` | `zinc-800` |
|
|
399
|
+
| `--surface-solid` | `zinc-900` | `zinc-100` |
|
|
400
|
+
| `--border` | `zinc-200` | `zinc-700` |
|
|
401
|
+
| `--text` | `zinc-950` | `zinc-50` |
|
|
402
|
+
| `--text-muted` | `zinc-600` | `zinc-400` |
|
|
403
|
+
| `--text-subtle` | `zinc-500` | `zinc-500` |
|
|
404
|
+
| `--neutral` | `zinc-600` | `zinc-400` |
|
|
405
|
+
| `--primary` | `blue-600` | `blue-400` |
|
|
406
|
+
| `--focus` | `blue-500` | `blue-500` |
|
|
407
|
+
|
|
408
|
+
Each scheme is one `light-dark()` pair on the role, instead of a `dark:`
|
|
409
|
+
variant on every element:
|
|
410
|
+
|
|
411
|
+
```css
|
|
412
|
+
[data-theme="app"] {
|
|
413
|
+
color-scheme: light dark;
|
|
414
|
+
|
|
415
|
+
--surface: light-dark(var(--color-white), var(--color-zinc-950));
|
|
416
|
+
--border: light-dark(var(--color-zinc-200), var(--color-zinc-700));
|
|
417
|
+
--primary: light-dark(var(--color-blue-600), var(--color-blue-400));
|
|
418
|
+
--primary-fg: light-dark(var(--color-white), var(--color-zinc-950));
|
|
419
|
+
--focus: var(--color-blue-500);
|
|
420
|
+
/* … the other roles of the table */
|
|
421
|
+
}
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
Tailwind v4 emits only the theme variables your sources use; declare the
|
|
425
|
+
palette with `@theme static` while the theme reads them, and replace them with
|
|
426
|
+
their literal values before Tailwind leaves the build.
|
|
427
|
+
|
|
428
|
+
What the ramp does not decide for you:
|
|
429
|
+
|
|
430
|
+
- **Foreground pairs.** Tailwind has no `-fg` step. White on `blue-600` holds
|
|
431
|
+
4.5:1, white on `blue-500` does not, so a dark-scheme primary lightened to
|
|
432
|
+
`blue-400` needs a dark `--primary-fg`. Every intent needs its pair:
|
|
433
|
+
`--warning-fg` on an amber is dark ink.
|
|
434
|
+
- **Focus.** `--focus` must hold 3:1 on `--surface` and `--surface-solid` in
|
|
435
|
+
both schemes, so it is a mid-tone, not the primary step. `blue-500` holds
|
|
436
|
+
on `white`, `zinc-900`, `zinc-950` and `zinc-100`: one value covers both
|
|
437
|
+
schemes.
|
|
438
|
+
- **Neutral temperature.** `slate`, `gray`, `zinc`, `neutral` and `stone` are
|
|
439
|
+
the same ramp at different temperatures; `neutral` is pure grey. Pick the
|
|
440
|
+
family that leans toward your primary, and take every neutral role from it.
|
|
441
|
+
- **No subtle intent steps.** Do not map `blue-50` / `blue-100` to tinted
|
|
442
|
+
surfaces: `.soft` mixes the intent into the local surface, so there is no
|
|
443
|
+
`--primary-subtle` to fill.
|
|
444
|
+
- **Shadow and hover ink.** Tailwind shadows are black; Actual derives
|
|
445
|
+
`--shadow` and `--shadow-popout` from one `--shadow-color`. Take it from the
|
|
446
|
+
neutral family's darkest step, and `--hover-overlay` as a few percent of
|
|
447
|
+
`--text`.
|
|
448
|
+
|
|
449
|
+
`bunx --bun actual-css contrast --theme FILE` measures the focus, soft and inverse
|
|
450
|
+
pairs in both schemes. Check the text tiers on `--surface-subtle` by hand:
|
|
451
|
+
`zinc-500` holds 4.5:1 on `white` but not on `zinc-100`.
|
|
452
|
+
|
|
383
453
|
## Forms
|
|
384
454
|
|
|
385
455
|
Tailwind applications often construct form controls from utility combinations or use an additional forms plugin.
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# Upgrading
|
|
2
|
+
|
|
3
|
+
Each step below is a search for a removed class or hook and its replacement.
|
|
4
|
+
The [changelog](https://github.com/lekoala/actual-css/blob/master/CHANGELOG.md)
|
|
5
|
+
lists every change; this page keeps the ones that need an edit. Apply the
|
|
6
|
+
sections in order when skipping a release.
|
|
7
|
+
|
|
8
|
+
**Related terms:** migration, breaking changes, update, version.
|
|
9
|
+
|
|
10
|
+
## 0.12 to 0.13
|
|
11
|
+
|
|
12
|
+
### Layout hooks stop at each layout
|
|
13
|
+
|
|
14
|
+
`--cluster-justify`, `--cluster-align` and `--cluster-wrap` are reset on every
|
|
15
|
+
`.cluster`, and `--grid-columns` and `--grid-min` on every `.grid`. A value set
|
|
16
|
+
on an ancestor, including an outer cluster or grid, no longer reaches the
|
|
17
|
+
layout inside it. Search application CSS and inline styles for these hooks on
|
|
18
|
+
anything but the layout itself, then set them on the row or grid, or select it:
|
|
19
|
+
|
|
20
|
+
```css
|
|
21
|
+
.toolbar > .cluster {
|
|
22
|
+
--cluster-justify: space-between;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`--gap` still inherits, so a density context keeps reaching every nested
|
|
27
|
+
layout.
|
|
28
|
+
|
|
29
|
+
### Avatar stacks
|
|
30
|
+
|
|
31
|
+
`.avatar-stack` no longer takes its size from a child `.avatar.sm` or
|
|
32
|
+
`.avatar.lg`. Move the size class to the stack and drop it from its avatars:
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<div class="avatar-stack sm" role="group" aria-label="Team members">
|
|
36
|
+
<div class="avatar" role="img" aria-label="John Doe">…</div>
|
|
37
|
+
</div>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Focus outline
|
|
41
|
+
|
|
42
|
+
`--focus-outline` has no default value any more. Search application CSS for
|
|
43
|
+
`var(--focus-outline)` and write the generic line instead:
|
|
44
|
+
|
|
45
|
+
```css
|
|
46
|
+
outline: var(--focus-ring-width) solid var(--focus-outline-color);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Setting `--focus-outline` still replaces the generic line.
|
|
50
|
+
|
|
51
|
+
### List rows
|
|
52
|
+
|
|
53
|
+
`.list-item` regions center on the row. For rows with long supporting text,
|
|
54
|
+
add `.items-start` to the row or to the `.list` to keep them top-aligned.
|
|
55
|
+
|
|
56
|
+
### Step rows
|
|
57
|
+
|
|
58
|
+
`.steps-horizontal` budgets each step from its marker instead of `7rem`, so a
|
|
59
|
+
row fits narrower widths before it scrolls. To keep the previous budget on a
|
|
60
|
+
row, set `--step-min: 7rem` on it.
|
|
61
|
+
|
|
62
|
+
### Current links and rows
|
|
63
|
+
|
|
64
|
+
`.nav-link` shows its current state only for `aria-current="page"` or
|
|
65
|
+
`aria-current="location"`, and `a.list-item` only for `aria-current="page"` or
|
|
66
|
+
`aria-current="true"`. Search markup for other values on these classes and
|
|
67
|
+
use the matching one.
|
|
68
|
+
|
|
69
|
+
### Tabs
|
|
70
|
+
|
|
71
|
+
`data-enhance="tabs"` only wires an element that also carries
|
|
72
|
+
`role="tablist"`. Search markup for the token and add the role where it is
|
|
73
|
+
missing; the console names each list it skipped.
|
|
74
|
+
|
|
75
|
+
### Rendering changes without an edit
|
|
76
|
+
|
|
77
|
+
- A horizontal `.tabs` strip never wraps: a strip that does not fit scrolls
|
|
78
|
+
horizontally, with no scrollbar and faded edges.
|
|
79
|
+
- `.breadcrumb` never wraps either: a trail that does not fit truncates its
|
|
80
|
+
labels with an ellipsis, ancestors first. Ancestor labels need an `<a>` or
|
|
81
|
+
`<span>`.
|
|
82
|
+
- The focus reserve of a scrolling dialog or drawer body no longer grows with
|
|
83
|
+
`--border-width`: 1px narrower under `prefers-contrast: more`.
|
|
84
|
+
|
|
85
|
+
## 0.11 to 0.12
|
|
86
|
+
|
|
87
|
+
### Action rows
|
|
88
|
+
|
|
89
|
+
`.actions`, `.navbar-nav` and the layout of `.form-actions` are gone. A row of
|
|
90
|
+
actions is a `.cluster`; a vertical list is `stack list-reset`. Replace
|
|
91
|
+
`class="actions"` and `class="navbar-nav"` with `class="cluster"`, and
|
|
92
|
+
`class="form-actions"` with `class="cluster form-actions"`:
|
|
93
|
+
|
|
94
|
+
```html
|
|
95
|
+
<menu class="cluster">…</menu>
|
|
96
|
+
<ul class="cluster">…</ul>
|
|
97
|
+
<div class="cluster form-actions">…</div>
|
|
98
|
+
<ul class="stack list-reset">…</ul>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- The row takes the framework `--gap` (0.75rem). The former `.actions` used
|
|
102
|
+
0.5rem: add `.tight` to keep it.
|
|
103
|
+
- The former `.navbar-nav` used `--space-10` (0.25rem), which no utility
|
|
104
|
+
reproduces. If the tighter navbar rhythm matters, set
|
|
105
|
+
`gap: var(--space-10)` on that row in application CSS.
|
|
106
|
+
- `.form-actions` has no top margin and no `--form-actions-margin-block-start`:
|
|
107
|
+
put `.stack` on the form. `--form-actions-justify` and `--form-actions-align`
|
|
108
|
+
still apply. In a modular build, also import `layout/cluster`.
|
|
109
|
+
- `.justify-content-*` aligns only its own element and no longer reaches a
|
|
110
|
+
nested `.cluster`. Put the utility on the row it should align.
|
|
111
|
+
|
|
112
|
+
### Dialogs
|
|
113
|
+
|
|
114
|
+
- `.dialog-confirmation` is removed. Compose `.media` with `.dialog-icon`, then
|
|
115
|
+
a `<footer class="bleed background-subtle cluster justify-content-space-between">`;
|
|
116
|
+
the recipe is in [Dialog](../components/dialog.md).
|
|
117
|
+
- A `dialog.modal` body `<footer>` has no layout of its own: write
|
|
118
|
+
`<footer class="cluster justify-content-end">`.
|
|
119
|
+
- `--modal-header-bg` is removed: put a `.background-*` utility on the
|
|
120
|
+
`dialog.modal > header` band. Only that default fill has zero specificity;
|
|
121
|
+
the header's layout keeps its structural rule.
|
|
122
|
+
- The confirmation recipe uses the regular 1rem modal inset where the former
|
|
123
|
+
`.dialog-confirmation` used 1.5rem. Set `--modal-pad: var(--space-50)` on
|
|
124
|
+
the dialog to keep the roomier treatment.
|
|
125
|
+
|
|
126
|
+
### Cards
|
|
127
|
+
|
|
128
|
+
The layout of a `.card` header or footer row is a zero-specificity default: any
|
|
129
|
+
author rule that targets the slot replaces it, not only a `.media`, `.cluster`,
|
|
130
|
+
`.form-actions` or `.justify-content-*` class. Check application CSS for bare
|
|
131
|
+
element rules such as `header { … }` or `footer { … }`, which now win over the
|
|
132
|
+
card's slot row.
|
|
133
|
+
|
|
134
|
+
### Themes and density
|
|
135
|
+
|
|
136
|
+
- A theme sets `--control-pad-x-sm`, `--control-pad-x-md` and
|
|
137
|
+
`--control-pad-x-lg` instead of `--control-pad-x`. Used as a nested island,
|
|
138
|
+
it re-selects them in its own block; see
|
|
139
|
+
[Tokens](../foundations/tokens.md).
|
|
140
|
+
- `--font-width-dense` is removed, and `.compact` no longer condenses type: set
|
|
141
|
+
`font-stretch` on the element that should render condensed.
|
|
142
|
+
|
|
143
|
+
### Rendering changes without an edit
|
|
144
|
+
|
|
145
|
+
These need no markup change; compare the affected screens.
|
|
146
|
+
|
|
147
|
+
- `.sm` controls have 13px of inline padding instead of 14px.
|
|
148
|
+
- `.compact` and `.spacious` controls get narrower or wider, not only shorter
|
|
149
|
+
or taller.
|
|
150
|
+
- `.nav-list` rows sit `--space-10` apart instead of `--space-20`.
|
|
151
|
+
- `.card.compact` lowers the gap between its children to `--space-20`.
|
|
@@ -15,7 +15,8 @@ Constrain readable content and center it in the viewport, with width and padding
|
|
|
15
15
|
</main>
|
|
16
16
|
```
|
|
17
17
|
|
|
18
|
-
Tune width and side padding with
|
|
18
|
+
Tune width and side padding with `--center-size` and `--center-pad`. Both
|
|
19
|
+
inherit: set them on `:root` for the site's content width, or on one region.
|
|
19
20
|
|
|
20
21
|
```css
|
|
21
22
|
.docs-page {
|
|
@@ -17,6 +17,30 @@ between items always comes from `--gap`. Unlike `.stack`, which resets only the
|
|
|
17
17
|
block axis, cluster resets margins on both axes — a wrapping cluster's `gap`
|
|
18
18
|
controls inline spacing between items and block spacing between wrapped rows.
|
|
19
19
|
|
|
20
|
+
## Action rows
|
|
21
|
+
|
|
22
|
+
Actions are content; the cluster owns the geometry. A semantic action list is a
|
|
23
|
+
`<menu>` (or `<ul>`) with `.cluster`:
|
|
24
|
+
|
|
25
|
+
```html demo
|
|
26
|
+
<menu class="cluster">
|
|
27
|
+
<li><button class="btn primary" type="button">Save</button></li>
|
|
28
|
+
<li><button class="btn outline" type="button">Cancel</button></li>
|
|
29
|
+
</menu>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
There is no `.actions` class. A dense row is `cluster tight`; a vertical action
|
|
33
|
+
list is `stack list-reset`.
|
|
34
|
+
|
|
35
|
+
## List chrome
|
|
36
|
+
|
|
37
|
+
`.cluster` neutralizes the native chrome of a `<ul>`, `<ol>` or `<menu>` it is
|
|
38
|
+
applied to. The element stays a list in the DOM and for assistive technology,
|
|
39
|
+
but a horizontal, wrapping group cannot carry markers and indentation
|
|
40
|
+
meaningfully, so they are removed. `.stack` makes no such change — an
|
|
41
|
+
`<ol class="stack">` keeps its numbers. Use `.list-reset` alone for a plain list
|
|
42
|
+
with no layout.
|
|
43
|
+
|
|
20
44
|
## CSS hooks
|
|
21
45
|
|
|
22
46
|
- `--cluster-justify` — main-axis distribution (`justify-content`).
|
|
@@ -37,19 +61,28 @@ separate `.split` or `.spread` primitive; this is the same relationship.
|
|
|
37
61
|
</div>
|
|
38
62
|
```
|
|
39
63
|
|
|
64
|
+
Once the row wraps, `space-between` puts an item alone on its line at the
|
|
65
|
+
start. To keep the end item on the end edge when it wraps, add
|
|
66
|
+
`.margin-inline-start-auto` to it.
|
|
67
|
+
|
|
40
68
|
To align a row to its top edge instead of centering it — useful when one item
|
|
41
|
-
wraps to several lines — set `--cluster-align
|
|
69
|
+
wraps to several lines — set `--cluster-align` on the cluster itself.
|
|
42
70
|
|
|
43
71
|
```css
|
|
44
|
-
.filters {
|
|
72
|
+
.cluster.filters {
|
|
45
73
|
--cluster-align: start;
|
|
46
74
|
--cluster-justify: space-between;
|
|
47
75
|
}
|
|
48
76
|
```
|
|
49
77
|
|
|
50
78
|
The optional utility layer ships `.justify-content-start`, `.justify-content-center`,
|
|
51
|
-
`.justify-content-end` and `.justify-content-space-between
|
|
52
|
-
|
|
79
|
+
`.justify-content-end` and `.justify-content-space-between`. They set
|
|
80
|
+
`justify-content` on that one row only; a nested `.cluster` keeps its own
|
|
81
|
+
alignment. The `--cluster-*` hooks behave the same way: each cluster resets
|
|
82
|
+
them, so a value applies to the cluster it is set on and never to a nested
|
|
83
|
+
one. To align nested rows, select them (`.toolbar > .cluster`). `--gap` keeps
|
|
84
|
+
inheriting, so a density context still reaches every nested cluster. See the
|
|
85
|
+
utilities page.
|
|
53
86
|
|
|
54
87
|
## Controls inside a wrapping cluster
|
|
55
88
|
|
|
@@ -172,3 +172,5 @@ stacked, prefer `.switcher`.
|
|
|
172
172
|
- `--grid-min` tunes the minimum item width of the intrinsic `.grid` recipe.
|
|
173
173
|
It does not apply to `.grid-N`.
|
|
174
174
|
- `--grid-columns` replaces that recipe with an author-owned template.
|
|
175
|
+
|
|
176
|
+
Both stop at a nested `.grid`: set them on the grid itself.
|
|
@@ -57,4 +57,4 @@ Prefer a parent `gap` for relationships between peers instead of encoding siblin
|
|
|
57
57
|
|
|
58
58
|
Before adding a divider or other separating chrome, consider whether additional spacing already expresses the boundary clearly.
|
|
59
59
|
|
|
60
|
-
Density changes the amount of space available, not the relationships that space communicates. When a composition contains local, group, and section rhythms, preserve their relative hierarchy with nested or local `--gap` values rather than relying on one inherited gap at every level. See [Choosing a
|
|
60
|
+
Density changes the amount of space available, not the relationships that space communicates. When a composition contains local, group, and section rhythms, preserve their relative hierarchy with nested or local `--gap` values rather than relying on one inherited gap at every level. See [Choosing a rhythm](./stack.md#choosing-a-rhythm) for the nesting mechanics, and `.tight` / `.loose` for a gap-only change.
|
|
@@ -47,3 +47,6 @@ When the layout stacks (narrow space), the aside stays on top.
|
|
|
47
47
|
|
|
48
48
|
- `--sidebar-layout-size` sets the aside's preferred width.
|
|
49
49
|
- `--sidebar-content-min` sets the main region's minimum viable width.
|
|
50
|
+
|
|
51
|
+
The regions read both, so they also reach the regions of a nested
|
|
52
|
+
`.sidebar-layout`; set them on the nested layout to give it its own sizes.
|