actual-css 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +90 -1
- package/README.md +25 -8
- package/dist/actual-themes.min.css +1 -0
- package/dist/actual.css +1492 -931
- package/dist/actual.js +1 -3
- package/dist/actual.min.css +1 -1
- package/dist/optional.css +1048 -0
- package/dist/optional.min.css +1 -0
- package/docs/design-notes/browser-support.md +95 -6
- package/docs/design-notes/cascade-layer.md +1 -1
- package/docs/design-notes/enhancement-contract.md +101 -0
- package/docs/design-notes/forced-colors.md +105 -0
- package/docs/design-notes/naming.md +3 -1
- package/docs/design-notes/platform-alignment.md +492 -0
- package/docs/design-notes/references.md +8 -0
- package/docs/design-notes/widget-primitives.md +119 -0
- package/docs/navigation.json +135 -0
- package/docs/pages/components/accordion.md +80 -0
- package/docs/pages/components/alert.md +203 -0
- package/docs/pages/components/aura.md +35 -0
- package/docs/pages/components/avatar.md +95 -0
- package/docs/pages/components/badge.md +102 -0
- package/docs/pages/components/breadcrumb.md +19 -0
- package/docs/pages/components/button.md +205 -0
- package/docs/pages/components/card.md +183 -0
- package/docs/pages/components/chat.md +50 -0
- package/docs/pages/components/dialog.md +367 -0
- package/docs/pages/components/drawer.md +113 -0
- package/docs/pages/components/fab.md +118 -0
- package/docs/pages/components/key.md +33 -0
- package/docs/pages/components/meter.md +23 -0
- package/docs/pages/components/navbar.md +92 -0
- package/docs/pages/components/pagination.md +55 -0
- package/docs/pages/components/progress.md +29 -0
- package/docs/pages/components/skeleton.md +38 -0
- package/docs/pages/components/spinner.md +93 -0
- package/docs/pages/components/table.md +102 -0
- package/docs/pages/enhancements/flyout.md +391 -0
- package/docs/pages/enhancements/overview.md +36 -0
- package/docs/pages/enhancements/scrollspy.md +71 -0
- package/docs/pages/enhancements/status-bar.md +72 -0
- package/docs/pages/enhancements/tabs.md +80 -0
- package/docs/pages/enhancements/tooltip.md +130 -0
- package/docs/pages/examples/overview.md +41 -0
- package/docs/pages/forms/choice-cards.md +102 -0
- package/docs/pages/forms/controls.md +32 -0
- package/docs/pages/forms/detached-actions.md +59 -0
- package/docs/pages/forms/floating-field.md +88 -0
- package/docs/pages/forms/form-actions.md +56 -0
- package/docs/pages/forms/input-policy.md +91 -0
- package/docs/pages/forms/joined-controls.md +37 -0
- package/docs/pages/forms/labels.md +42 -0
- package/docs/pages/forms/otp.md +50 -0
- package/docs/pages/forms/overview.md +188 -0
- package/docs/pages/forms/password.md +40 -0
- package/docs/pages/forms/range.md +17 -0
- package/docs/pages/forms/states-and-sizes.md +321 -0
- package/docs/pages/forms/sticky-actions.md +187 -0
- package/docs/pages/forms/switch.md +31 -0
- package/docs/pages/forms/validation.md +241 -0
- package/docs/{mental-model.md → pages/foundations/philosophy.md} +33 -18
- package/docs/pages/foundations/theming.md +92 -0
- package/docs/pages/foundations/tokens.md +421 -0
- package/docs/{typography.md → pages/foundations/typography.md} +27 -9
- package/docs/pages/guides/bootstrap.md +438 -0
- package/docs/pages/guides/introduction.md +157 -0
- package/docs/pages/guides/optional-components.md +33 -0
- package/docs/pages/guides/progressive-enhancement.md +686 -0
- package/docs/pages/guides/tailwind.md +455 -0
- package/docs/pages/layout/app-shell.md +25 -0
- package/docs/pages/layout/center.md +29 -0
- package/docs/pages/layout/cluster.md +57 -0
- package/docs/pages/layout/frame.md +30 -0
- package/docs/pages/layout/grid.md +142 -0
- package/docs/pages/layout/list.md +41 -0
- package/docs/pages/layout/media.md +21 -0
- package/docs/pages/layout/overview.md +37 -0
- package/docs/pages/layout/scroll-snap.md +54 -0
- package/docs/pages/layout/sidebar-layout.md +27 -0
- package/docs/pages/layout/stack.md +18 -0
- package/docs/pages/layout/switcher.md +36 -0
- package/docs/pages/layout/topbar.md +23 -0
- package/docs/pages/patterns/actions.md +52 -0
- package/docs/pages/patterns/code-block.md +36 -0
- package/docs/pages/patterns/exclusive-accordion.md +16 -0
- package/docs/pages/patterns/hierarchical-nav.md +26 -0
- package/docs/pages/patterns/icon-input.md +25 -0
- package/docs/pages/patterns/intent-nesting.md +3 -0
- package/docs/pages/patterns/navigation-list.md +235 -0
- package/docs/pages/patterns/overview.md +58 -0
- package/docs/pages/patterns/structured-lists.md +47 -0
- package/docs/pages/utilities/accessibility.md +34 -0
- package/docs/pages/utilities/flex.md +105 -0
- package/docs/pages/utilities/links.md +18 -0
- package/docs/pages/utilities/list.md +31 -0
- package/docs/pages/utilities/overflow.md +43 -0
- package/docs/pages/utilities/overview.md +18 -0
- package/docs/pages/utilities/shape.md +25 -0
- package/docs/pages/utilities/sizing-wrapping.md +115 -0
- package/docs/pages/utilities/spacing.md +79 -0
- package/docs/pages/utilities/text.md +43 -0
- package/llms.txt +10 -4
- package/package.json +30 -16
- package/src/css/actual.css +12 -1
- package/src/css/base.css +3 -9
- package/src/css/components/accordion.css +18 -30
- package/src/css/components/actions.css +4 -1
- package/src/css/components/alert.css +162 -49
- package/src/css/components/avatar.css +16 -10
- package/src/css/components/badge.css +82 -39
- package/src/css/components/breadcrumb.css +7 -15
- package/src/css/components/busy.css +7 -0
- package/src/css/components/button.css +82 -38
- package/src/css/components/card.css +17 -37
- package/src/css/components/drawer.css +30 -13
- package/src/css/components/flyout.css +39 -110
- package/src/css/components/index.css +1 -0
- package/src/css/components/join.css +14 -3
- package/src/css/components/key.css +2 -0
- package/src/css/components/menu.css +125 -0
- package/src/css/components/meter.css +0 -23
- package/src/css/components/modal.css +162 -7
- package/src/css/components/navbar.css +17 -18
- package/src/css/components/overline.css +0 -7
- package/src/css/components/pagination.css +1 -1
- package/src/css/components/progress.css +8 -26
- package/src/css/components/scrollspy.css +2 -2
- package/src/css/components/skeleton.css +7 -0
- package/src/css/components/spinner.css +10 -11
- package/src/css/components/status-bar.css +18 -28
- package/src/css/components/surface.css +2 -8
- package/src/css/components/tab.css +20 -48
- package/src/css/components/table.css +7 -5
- package/src/css/components/tooltip.css +14 -7
- package/src/css/focus.css +16 -3
- package/src/css/forms/choice-card.css +13 -15
- package/src/css/forms/choice.css +21 -28
- package/src/css/forms/control.css +16 -26
- package/src/css/forms/custom-select.css +9 -11
- package/src/css/forms/form-actions.css +14 -3
- package/src/css/forms/form.css +7 -4
- package/src/css/forms/index.css +5 -4
- package/src/css/forms/input-icon.css +30 -12
- package/src/css/forms/native.css +48 -33
- package/src/css/forms/range.css +103 -0
- package/src/css/forms/select.css +8 -8
- package/src/css/forms/switch.css +14 -13
- package/src/css/forms/validation.css +32 -5
- package/src/css/grid.css +19 -18
- package/src/css/intents.css +2 -2
- package/src/css/layout.css +29 -1
- package/src/css/optional/aura.css +93 -0
- package/src/css/optional/chat.css +105 -0
- package/src/css/optional/fab.css +104 -0
- package/src/css/optional/floating-field.css +87 -0
- package/src/css/optional/index.css +5 -0
- package/src/css/optional/layout-extra.css +7 -4
- package/src/css/optional/otp.css +115 -0
- package/src/css/optional/scroll-snap.css +6 -0
- package/src/css/optional/scroller.css +19 -7
- package/src/css/optional/typography-fluid.css +5 -0
- package/src/css/optional/utilities-extra.css +17 -2
- package/src/css/prose.css +18 -8
- package/src/css/reset.css +0 -10
- package/src/css/theme.css +29 -13
- package/src/css/themes/cyberpunk.css +1 -1
- package/src/css/themes/dim.css +3 -3
- package/src/css/themes/edge.css +11 -37
- package/src/css/themes/index.css +3 -3
- package/src/css/themes/{dark.css → indigo.css} +5 -4
- package/src/css/themes/neon.css +5 -5
- package/src/css/themes/square.css +0 -3
- package/src/css/tokens.css +81 -21
- package/src/css/utilities.css +8 -10
- package/src/css/variants.css +37 -65
- package/src/js/command.js +1 -3
- package/src/js/context-menu.js +72 -53
- package/src/js/dialog.js +68 -79
- package/src/js/dismiss.js +15 -0
- package/src/js/enhance.js +76 -19
- package/src/js/enhancement-loader.js +301 -0
- package/src/js/events.js +3 -0
- package/src/js/filter.js +3 -3
- package/src/js/floating.js +101 -131
- package/src/js/flyout.js +106 -96
- package/src/js/focus-group.js +116 -0
- package/src/js/focus.js +6 -1
- package/src/js/index.js +4 -2
- package/src/js/input.js +1 -1
- package/src/js/keys.js +27 -0
- package/src/js/mask.js +7 -6
- package/src/js/menu.js +89 -35
- package/src/js/parse-config.js +47 -0
- package/src/js/password.js +9 -1
- package/src/js/scrollspy.js +133 -62
- package/src/js/selectors.js +14 -31
- package/src/js/status.js +16 -14
- package/src/js/surface.js +219 -125
- package/src/js/tab.js +46 -66
- package/src/js/tooltip.js +53 -31
- package/src/js/validation.js +45 -51
- package/dist/actual.js.map +0 -31
- package/docs/README.md +0 -26
- package/docs/components.md +0 -860
- package/docs/design-notes/previous-work.md +0 -5
- package/docs/forms.md +0 -1265
- package/docs/javascript.md +0 -426
- package/docs/layout.md +0 -480
- package/docs/patterns.md +0 -415
- package/docs/reserved.md +0 -7
- package/docs/tokens.md +0 -455
- package/docs/ui.md +0 -937
- package/docs/utilities.md +0 -384
- package/src/css/dialog-fallback.css +0 -56
- package/src/js/dialog-fallback.js +0 -163
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
# Migrating from Tailwind CSS to Actual CSS
|
|
2
|
+
|
|
3
|
+
This guide primarily targets Tailwind CSS v4. The same migration principles apply to Tailwind v3 projects.
|
|
4
|
+
|
|
5
|
+
Tailwind and Actual CSS solve styling at different abstraction levels, so this should **not** be approached as a class-by-class conversion.
|
|
6
|
+
|
|
7
|
+
The useful migration model is:
|
|
8
|
+
|
|
9
|
+
**repeated utility clusters → Actual components**
|
|
10
|
+
**layout utility clusters → Actual layout primitives**
|
|
11
|
+
**design tokens → Actual theme tokens**
|
|
12
|
+
**one-off utility combinations → small project CSS**
|
|
13
|
+
|
|
14
|
+
If you finish the migration with a new `.mt-4`, `.flex`, `.bg-primary`, and `md:*` API built around Actual, you have recreated the framework you were trying to remove.
|
|
15
|
+
|
|
16
|
+
## Before you start: avoid class collisions
|
|
17
|
+
|
|
18
|
+
Tailwind and Actual can temporarily coexist, but some short class names overlap.
|
|
19
|
+
|
|
20
|
+
Examples include layout and flex-related names such as `.grid`, `.grow`, and `.items-center`.
|
|
21
|
+
|
|
22
|
+
For a staged migration, use cascade layers, controlled import order, or a project-side Actual prefix transform where necessary.
|
|
23
|
+
|
|
24
|
+
The layered import is the cleanest way to keep both stylesheets during the
|
|
25
|
+
migration:
|
|
26
|
+
|
|
27
|
+
```css
|
|
28
|
+
/* Migration setup — Actual rules sit in @layer actual */
|
|
29
|
+
@import "tailwind.css"; /* your compiled Tailwind output */
|
|
30
|
+
@import "actual-css/css/layer";
|
|
31
|
+
/* Unlayered project overrides stay on top */
|
|
32
|
+
@import "app.css";
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`actual.layer.css` wraps every Actual rule in a single `@layer actual`, so
|
|
36
|
+
unlayered Tailwind output and project CSS keep precedence while the migration
|
|
37
|
+
runs. See [Cascade layer strategy](https://github.com/lekoala/actual-css/blob/master/docs/design-notes/cascade-layer.md) for the
|
|
38
|
+
limitations of the approach.
|
|
39
|
+
|
|
40
|
+
Do not assume that loading both complete stylesheets is automatically safe.
|
|
41
|
+
|
|
42
|
+
## Start with components, not utilities
|
|
43
|
+
|
|
44
|
+
A Tailwind button may look like this:
|
|
45
|
+
|
|
46
|
+
```html
|
|
47
|
+
<button
|
|
48
|
+
class="
|
|
49
|
+
inline-flex items-center gap-2
|
|
50
|
+
rounded-md
|
|
51
|
+
bg-blue-600 px-4 py-2
|
|
52
|
+
font-medium text-white
|
|
53
|
+
hover:bg-blue-700
|
|
54
|
+
focus:outline-none focus:ring-2
|
|
55
|
+
"
|
|
56
|
+
>
|
|
57
|
+
Save
|
|
58
|
+
</button>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
With Actual:
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<button class="btn primary">
|
|
65
|
+
Save
|
|
66
|
+
</button>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The border, spacing, typography, hover state, focus treatment, disabled state, and theme behavior belong to the component.
|
|
70
|
+
|
|
71
|
+
The intent remains visible in the markup without re-describing its CSS implementation.
|
|
72
|
+
|
|
73
|
+
The same principle applies to badges, alerts, cards, form controls, pagination, tabs, dialogs, and other Actual components.
|
|
74
|
+
|
|
75
|
+
## Translate layout relationships
|
|
76
|
+
|
|
77
|
+
### Vertical spacing
|
|
78
|
+
|
|
79
|
+
Tailwind:
|
|
80
|
+
|
|
81
|
+
```html
|
|
82
|
+
<div class="flex flex-col gap-4">
|
|
83
|
+
...
|
|
84
|
+
</div>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Actual:
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<div class="stack">
|
|
91
|
+
...
|
|
92
|
+
</div>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Inline actions
|
|
96
|
+
|
|
97
|
+
Tailwind:
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<div class="flex flex-wrap items-center gap-3">
|
|
101
|
+
...
|
|
102
|
+
</div>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Actual:
|
|
106
|
+
|
|
107
|
+
```html
|
|
108
|
+
<div class="cluster">
|
|
109
|
+
...
|
|
110
|
+
</div>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Fixed column count
|
|
114
|
+
|
|
115
|
+
Tailwind:
|
|
116
|
+
|
|
117
|
+
```html
|
|
118
|
+
<div class="grid grid-cols-3 gap-6">
|
|
119
|
+
...
|
|
120
|
+
</div>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Actual:
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<div class="grid-3">
|
|
127
|
+
...
|
|
128
|
+
</div>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
If the grid should respond to its container:
|
|
132
|
+
|
|
133
|
+
```html
|
|
134
|
+
<div class="container-query">
|
|
135
|
+
<div class="grid-3">
|
|
136
|
+
...
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Intrinsic responsive grid
|
|
142
|
+
|
|
143
|
+
Instead of:
|
|
144
|
+
|
|
145
|
+
```html
|
|
146
|
+
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-4">
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
consider whether the real requirement is simply “create another column whenever there is enough room”:
|
|
150
|
+
|
|
151
|
+
```html
|
|
152
|
+
<div class="grid" style="--grid-min: 16rem">
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
This removes viewport breakpoint choreography entirely.
|
|
156
|
+
|
|
157
|
+
## Prefer intrinsic behavior over translating breakpoints
|
|
158
|
+
|
|
159
|
+
Tailwind allows almost every utility to be changed independently at a breakpoint:
|
|
160
|
+
|
|
161
|
+
```html
|
|
162
|
+
<div class="flex flex-col gap-4 md:flex-row md:items-center xl:gap-8">
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
There is intentionally no equivalent Actual syntax such as:
|
|
166
|
+
|
|
167
|
+
```text
|
|
168
|
+
md:cluster xl:gap-lg
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
First ask what the layout is supposed to do.
|
|
172
|
+
|
|
173
|
+
If the requirement is:
|
|
174
|
+
|
|
175
|
+
> Show these items horizontally when there is enough room, otherwise stack them.
|
|
176
|
+
|
|
177
|
+
use a layout primitive such as `.switcher`.
|
|
178
|
+
|
|
179
|
+
If the requirement truly is:
|
|
180
|
+
|
|
181
|
+
> At exactly this application breakpoint, change these particular properties.
|
|
182
|
+
|
|
183
|
+
write project CSS:
|
|
184
|
+
|
|
185
|
+
```html
|
|
186
|
+
<div class="account-header">
|
|
187
|
+
...
|
|
188
|
+
</div>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```css
|
|
192
|
+
.account-header {
|
|
193
|
+
display: grid;
|
|
194
|
+
gap: var(--space-40);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
@media (width >= 48rem) {
|
|
198
|
+
.account-header {
|
|
199
|
+
grid-template-columns: 1fr auto;
|
|
200
|
+
align-items: center;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Actual does not prohibit media queries. It simply does not encode every media query/property combination into the class API.
|
|
206
|
+
|
|
207
|
+
## Utility translation guide
|
|
208
|
+
|
|
209
|
+
| Tailwind usage | Actual approach |
|
|
210
|
+
|--------------------------------------|---------------------------------------------------|
|
|
211
|
+
| `flex flex-col gap-*` | `.stack` |
|
|
212
|
+
| `flex flex-wrap items-center gap-*` | `.cluster` |
|
|
213
|
+
| `grid grid-cols-*` | `.grid`, `.grid-2`, `.grid-3`, etc. |
|
|
214
|
+
| responsive row → column | `.switcher` when intrinsic behavior fits |
|
|
215
|
+
| `max-w-*` for readable text | `.measure` where appropriate |
|
|
216
|
+
| common visual component bundle | Actual component |
|
|
217
|
+
| component colors | intents such as `.primary`, `.danger`, `.success` |
|
|
218
|
+
| repeated layout spacing | `--gap` / layout hooks |
|
|
219
|
+
| small common corrections | Actual utilities |
|
|
220
|
+
| breakpoint-prefixed utilities | intrinsic layout, container query, or project CSS |
|
|
221
|
+
| arbitrary values | custom property or project CSS |
|
|
222
|
+
| arbitrary selectors | project CSS |
|
|
223
|
+
| `group-*` / `peer-*` variants | semantic state + project CSS |
|
|
224
|
+
| `data-*` / `aria-*` variants | component state or project CSS |
|
|
225
|
+
| decorative color utilities | theme token or project CSS |
|
|
226
|
+
| transforms/effects/complex animation | project CSS |
|
|
227
|
+
|
|
228
|
+
## Do not translate Tailwind's full spacing API
|
|
229
|
+
|
|
230
|
+
Tailwind makes it convenient to express:
|
|
231
|
+
|
|
232
|
+
```html
|
|
233
|
+
<div class="mt-3 mb-8 px-5 md:px-8">
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Actual deliberately has a much smaller spacing utility surface.
|
|
237
|
+
|
|
238
|
+
Use layout primitives to create rhythm between siblings:
|
|
239
|
+
|
|
240
|
+
```html
|
|
241
|
+
<section class="stack">
|
|
242
|
+
...
|
|
243
|
+
</section>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Use component padding when the space belongs to the component.
|
|
247
|
+
|
|
248
|
+
Use a small semantic project rule when the spacing genuinely belongs to an application-specific structure:
|
|
249
|
+
|
|
250
|
+
```css
|
|
251
|
+
.account-section {
|
|
252
|
+
padding-inline: var(--space-40);
|
|
253
|
+
margin-block-end: var(--space-60);
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
The goal is not zero custom CSS. The goal is CSS attached to meaningful structures instead of reconstructing every declaration in HTML.
|
|
258
|
+
|
|
259
|
+
> **Need something more specific?**
|
|
260
|
+
>
|
|
261
|
+
> 1. Tune the component or layout primitive with a public custom property.
|
|
262
|
+
> 2. Compose an Actual layout primitive.
|
|
263
|
+
> 3. Add a project class for application-specific CSS.
|
|
264
|
+
> 4. Only reach for an optional utility when the rule is genuinely generic.
|
|
265
|
+
|
|
266
|
+
Actual deliberately keeps its utility surface small and grows it only for needs
|
|
267
|
+
that recur across real migrations — a one-off is project CSS, not a new
|
|
268
|
+
framework class.
|
|
269
|
+
|
|
270
|
+
## Arbitrary values move back into CSS
|
|
271
|
+
|
|
272
|
+
Tailwind can express one-off values directly:
|
|
273
|
+
|
|
274
|
+
```html
|
|
275
|
+
<div class="grid-cols-[24rem_2.5rem_minmax(0,1fr)]">
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
or:
|
|
279
|
+
|
|
280
|
+
```html
|
|
281
|
+
<div class="top-[117px]">
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Actual has no equivalent arbitrary-value grammar.
|
|
285
|
+
|
|
286
|
+
Use a public layout hook when one exists:
|
|
287
|
+
|
|
288
|
+
```html
|
|
289
|
+
<div class="grid report-layout">
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
```css
|
|
293
|
+
.report-layout {
|
|
294
|
+
--grid-columns: 24rem 2.5rem minmax(0, 1fr);
|
|
295
|
+
}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Otherwise write the declaration normally.
|
|
299
|
+
|
|
300
|
+
This is one of the largest mechanical differences for Tailwind-heavy applications.
|
|
301
|
+
|
|
302
|
+
## State variants move into component behavior or CSS
|
|
303
|
+
|
|
304
|
+
Tailwind can keep highly specific state rules in markup:
|
|
305
|
+
|
|
306
|
+
```html
|
|
307
|
+
<button
|
|
308
|
+
class="
|
|
309
|
+
bg-neutral-100
|
|
310
|
+
hover:bg-neutral-200
|
|
311
|
+
aria-pressed:bg-blue-600
|
|
312
|
+
aria-pressed:text-white
|
|
313
|
+
disabled:opacity-50
|
|
314
|
+
"
|
|
315
|
+
>
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
For an Actual component, common interactive states should already be part of the component contract:
|
|
319
|
+
|
|
320
|
+
```html
|
|
321
|
+
<button class="btn primary" aria-pressed="true">
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
For application-specific state styling, keep the semantic attribute and style it directly:
|
|
325
|
+
|
|
326
|
+
```css
|
|
327
|
+
.view-toggle[aria-pressed="true"] {
|
|
328
|
+
/* project-specific state */
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
The same applies to Tailwind's `group-*`, `peer-*`, arbitrary selector, and complex `data-*` variants.
|
|
333
|
+
|
|
334
|
+
Do not invent an Actual variant grammar to replace them.
|
|
335
|
+
|
|
336
|
+
## Colors and dark mode
|
|
337
|
+
|
|
338
|
+
Tailwind makes low-level color selection easy:
|
|
339
|
+
|
|
340
|
+
```html
|
|
341
|
+
<div class="bg-slate-950 text-slate-100 border-slate-800">
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Actual's intent classes are not a general-purpose color palette.
|
|
345
|
+
|
|
346
|
+
Use intents where color has component meaning:
|
|
347
|
+
|
|
348
|
+
```html
|
|
349
|
+
<div class="alert danger">...</div>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
For application surfaces, define theme-aware project tokens instead:
|
|
353
|
+
|
|
354
|
+
```css
|
|
355
|
+
:root {
|
|
356
|
+
--dashboard-surface: ...;
|
|
357
|
+
--dashboard-border: ...;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.dashboard-panel {
|
|
361
|
+
background: var(--dashboard-surface);
|
|
362
|
+
border-color: var(--dashboard-border);
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Actual theme tokens should handle shared theme behavior. Local CSS remains appropriate for visual concepts that are specific to the application.
|
|
367
|
+
|
|
368
|
+
This is particularly important when migrating a Tailwind application with a large custom color palette.
|
|
369
|
+
|
|
370
|
+
## Forms
|
|
371
|
+
|
|
372
|
+
Tailwind applications often construct form controls from utility combinations or use an additional forms plugin.
|
|
373
|
+
|
|
374
|
+
Actual provides explicit form components:
|
|
375
|
+
|
|
376
|
+
```html
|
|
377
|
+
<label class="field">
|
|
378
|
+
<span class="field-label">Email</span>
|
|
379
|
+
<input class="input" type="email">
|
|
380
|
+
<span class="field-help">We'll only use this for notifications.</span>
|
|
381
|
+
</label>
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
A utility-heavy form is therefore usually easier to migrate than a utility-heavy marketing layout.
|
|
385
|
+
|
|
386
|
+
## Third-party Tailwind components
|
|
387
|
+
|
|
388
|
+
This can be a significant migration cost.
|
|
389
|
+
|
|
390
|
+
A React, Vue, Svelte, or template component containing dozens of Tailwind classes does not become framework-independent merely because those classes are hidden behind a component abstraction.
|
|
391
|
+
|
|
392
|
+
For each component:
|
|
393
|
+
|
|
394
|
+
1. identify whether an Actual component already provides the UI primitive;
|
|
395
|
+
2. replace the utility bundle with that component when possible;
|
|
396
|
+
3. preserve application-specific styling in a local component class;
|
|
397
|
+
4. keep existing application behavior unless an Actual enhancement can replace it cleanly.
|
|
398
|
+
|
|
399
|
+
Third-party component libraries authored specifically around Tailwind usually require restyling rather than automated class substitution.
|
|
400
|
+
|
|
401
|
+
## Build pipeline
|
|
402
|
+
|
|
403
|
+
Once the last Tailwind classes are gone, the Tailwind compilation and source scanning pipeline is no longer required for Actual itself.
|
|
404
|
+
|
|
405
|
+
Actual can be imported as regular CSS, with modular imports when desired.
|
|
406
|
+
|
|
407
|
+
Do not remove Tailwind from the build until migrated screens no longer depend on generated utilities.
|
|
408
|
+
|
|
409
|
+
## Where migration is genuinely harder
|
|
410
|
+
|
|
411
|
+
| Existing Tailwind usage | Migration cost |
|
|
412
|
+
|----------------------------------------------------|----------------|
|
|
413
|
+
| buttons, badges, alerts, forms | low |
|
|
414
|
+
| repeated card/panel patterns | low |
|
|
415
|
+
| basic flex/grid composition | low to medium |
|
|
416
|
+
| intrinsic responsive layouts | low to medium |
|
|
417
|
+
| breakpoint-heavy layouts | medium to high |
|
|
418
|
+
| extensive arbitrary values | high |
|
|
419
|
+
| `group`, `peer`, attribute, and arbitrary variants | high |
|
|
420
|
+
| bespoke marketing/art-direction layouts | high |
|
|
421
|
+
| large application-specific color utility palette | high |
|
|
422
|
+
| Tailwind-native third-party component libraries | high |
|
|
423
|
+
|
|
424
|
+
This is not accidental.
|
|
425
|
+
|
|
426
|
+
Actual gives you components and layout primitives but deliberately stops before becoming a complete vocabulary for expressing arbitrary CSS from HTML.
|
|
427
|
+
|
|
428
|
+
## Recommended migration order
|
|
429
|
+
|
|
430
|
+
1. Establish safe coexistence while both stylesheets are present.
|
|
431
|
+
2. Find repeated Tailwind class bundles that already represent recognizable UI components.
|
|
432
|
+
3. Replace buttons, badges, alerts, cards, and forms with Actual components.
|
|
433
|
+
4. Convert simple flex layouts to `.stack` and `.cluster`.
|
|
434
|
+
5. Convert repeated grids to Actual grid primitives.
|
|
435
|
+
6. Replace useful breakpoint choreography with intrinsic layouts or container queries.
|
|
436
|
+
7. Move remaining arbitrary values and state variants into small project classes.
|
|
437
|
+
8. Migrate application theme values to Actual tokens and project tokens.
|
|
438
|
+
9. Remove unused Tailwind-dependent components.
|
|
439
|
+
10. Remove Tailwind and its build integration.
|
|
440
|
+
|
|
441
|
+
## When Tailwind may still be the better fit
|
|
442
|
+
|
|
443
|
+
Actual is not intended to make every Tailwind workflow equally convenient.
|
|
444
|
+
|
|
445
|
+
If your application deliberately wants:
|
|
446
|
+
|
|
447
|
+
* every styling decision colocated in markup;
|
|
448
|
+
* arbitrary per-element visual values;
|
|
449
|
+
* extensive responsive changes to individual properties;
|
|
450
|
+
* complex `group`, `peer`, and selector-driven state styling;
|
|
451
|
+
* highly bespoke visual composition with few repeated UI components;
|
|
452
|
+
|
|
453
|
+
then Tailwind's utility and variant model may remain a better fit.
|
|
454
|
+
|
|
455
|
+
If the application is mostly composed from recurring UI components, forms, application layouts, and a stable theme, Actual can substantially reduce the amount of styling information carried by the markup.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# App Shell
|
|
2
|
+
|
|
3
|
+
Document landmarks for top and bottom regions, composed with layout primitives.
|
|
4
|
+
|
|
5
|
+
Header and footer are document landmarks, not required layout classes.
|
|
6
|
+
|
|
7
|
+
Use semantic elements by default.
|
|
8
|
+
|
|
9
|
+
```html demo
|
|
10
|
+
<body class="app-shell">
|
|
11
|
+
<header>Actual CSS</header>
|
|
12
|
+
<main class="grow">Main content</main>
|
|
13
|
+
<footer>Footer links</footer>
|
|
14
|
+
</body>
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
.app-shell {
|
|
19
|
+
min-block-size: 100vh;
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Sticky header/footer behavior should be opt-in and documented only if it becomes part of the shipped layout API.
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Center
|
|
2
|
+
|
|
3
|
+
Constrain readable content and center it in the viewport, with width and padding tunable per instance.
|
|
4
|
+
|
|
5
|
+
```html demo
|
|
6
|
+
<main class="center stack">
|
|
7
|
+
<header class="prose">
|
|
8
|
+
<h1>Documentation</h1>
|
|
9
|
+
<p>Guides and API notes for Actual CSS.</p>
|
|
10
|
+
</header>
|
|
11
|
+
<section class="card">
|
|
12
|
+
<h2>Getting started</h2>
|
|
13
|
+
<p>Install the stylesheet and use semantic HTML.</p>
|
|
14
|
+
</section>
|
|
15
|
+
</main>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Tune width and side padding with local variables.
|
|
19
|
+
|
|
20
|
+
```css
|
|
21
|
+
.docs-page {
|
|
22
|
+
--center-size: 48rem;
|
|
23
|
+
--center-pad: var(--space-40);
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`.center` intentionally uses `box-sizing: content-box`. The max inline size is
|
|
28
|
+
the content measure, while inline padding is added outside that measure. This is
|
|
29
|
+
deliberate even though the global reset uses `border-box`.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Cluster
|
|
2
|
+
|
|
3
|
+
Inline groups that wrap naturally, useful for action rows, tags, and toolbars.
|
|
4
|
+
|
|
5
|
+
```html demo
|
|
6
|
+
<div class="cluster">
|
|
7
|
+
<button class="btn primary" type="button">Save</button>
|
|
8
|
+
<button class="btn outline" type="button">Cancel</button>
|
|
9
|
+
<a href="#">Read docs</a>
|
|
10
|
+
</div>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Use cluster for action rows, tags, toolbar sections, metadata, and compact navigation.
|
|
14
|
+
|
|
15
|
+
Like `.stack`, cluster owns its children's spacing: direct-child margins are
|
|
16
|
+
reset, so spacing between items always comes from `--gap`.
|
|
17
|
+
|
|
18
|
+
## CSS hooks
|
|
19
|
+
|
|
20
|
+
- `--cluster-justify` — main-axis distribution (`justify-content`).
|
|
21
|
+
- `--cluster-align` — cross-axis alignment (`align-items`).
|
|
22
|
+
- `--cluster-wrap` — wrapping behavior (`flex-wrap`); set to `nowrap` to force a single row.
|
|
23
|
+
- `--gap` — space between items.
|
|
24
|
+
|
|
25
|
+
## Split / spread
|
|
26
|
+
|
|
27
|
+
For split/spread layouts — two items apart, at opposite ends, or "one left / one
|
|
28
|
+
right" — use `.cluster` with `--cluster-justify: space-between`. There is no
|
|
29
|
+
separate `.split` or `.spread` primitive; this is the same relationship.
|
|
30
|
+
|
|
31
|
+
```html demo
|
|
32
|
+
<div class="cluster" style="--cluster-justify: space-between">
|
|
33
|
+
<h2>Results</h2>
|
|
34
|
+
<button class="btn primary" type="button">New search</button>
|
|
35
|
+
</div>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
To align a row to its top edge instead of centering it — useful when one item
|
|
39
|
+
wraps to several lines — set `--cluster-align`.
|
|
40
|
+
|
|
41
|
+
```css
|
|
42
|
+
.filters {
|
|
43
|
+
--cluster-align: start;
|
|
44
|
+
--cluster-justify: space-between;
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The optional utility layer ships `.justify-content-start`, `.justify-content-center`,
|
|
49
|
+
`.justify-content-end` and `.justify-content-space-between`, which set
|
|
50
|
+
`--cluster-justify` for you. See the utilities page.
|
|
51
|
+
|
|
52
|
+
## Controls inside a wrapping cluster
|
|
53
|
+
|
|
54
|
+
A form control (`--control-size`, full width) inside a **wrapping** cluster claims
|
|
55
|
+
the full line and pushes every sibling to its own row. To keep a row of controls
|
|
56
|
+
under one label, set `--cluster-wrap: nowrap` and give the controls
|
|
57
|
+
`min-inline-size: 0` (and `flex: 1 1 0%` to share the row evenly).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Frame
|
|
2
|
+
|
|
3
|
+
Media container that holds a stable aspect ratio for images, video, or embeds.
|
|
4
|
+
|
|
5
|
+
```html demo
|
|
6
|
+
<figure class="frame">
|
|
7
|
+
<img src="https://picsum.photos/seed/actual-css-frame/1600/900" alt="Preview placeholder" width="1600" height="900" />
|
|
8
|
+
</figure>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```css
|
|
12
|
+
.frame {
|
|
13
|
+
aspect-ratio: var(--frame-ratio, 16 / 9);
|
|
14
|
+
overflow: hidden;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.frame > :where(img, video, iframe) {
|
|
18
|
+
inline-size: 100%;
|
|
19
|
+
block-size: 100%;
|
|
20
|
+
object-fit: cover;
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Use local variables for common ratios.
|
|
25
|
+
|
|
26
|
+
```css
|
|
27
|
+
.avatar-preview {
|
|
28
|
+
--frame-ratio: 1;
|
|
29
|
+
}
|
|
30
|
+
```
|