actual-css 0.2.0 → 0.3.1
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 +49 -1
- package/README.md +24 -9
- package/dist/actual-themes.min.css +1 -0
- package/dist/actual.css +1532 -1114
- 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 +94 -14
- package/docs/design-notes/cascade-layer.md +7 -6
- package/docs/design-notes/enhancement-contract.md +11 -14
- package/docs/design-notes/forced-colors.md +105 -0
- package/docs/design-notes/naming.md +2 -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 +37 -1
- 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 +210 -0
- package/docs/pages/components/card.md +195 -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 +43 -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 +99 -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 +59 -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 +28 -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 +55 -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 +32 -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 +4 -4
- package/package.json +21 -12
- package/src/css/actual.css +16 -2
- package/src/css/base.css +3 -5
- package/src/css/components/accordion.css +18 -30
- package/src/css/components/actions.css +4 -1
- package/src/css/components/alert.css +139 -102
- package/src/css/components/avatar.css +16 -10
- package/src/css/components/badge.css +69 -42
- package/src/css/components/breadcrumb.css +7 -15
- package/src/css/components/busy.css +6 -0
- package/src/css/components/button.css +84 -34
- package/src/css/components/card.css +26 -38
- package/src/css/components/drawer.css +30 -13
- package/src/css/components/flyout.css +39 -109
- package/src/css/components/index.css +1 -0
- package/src/css/components/join.css +14 -2
- 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 +30 -23
- package/src/css/components/overline.css +8 -9
- package/src/css/components/pagination.css +1 -1
- package/src/css/components/progress.css +8 -26
- package/src/css/components/skeleton.css +7 -0
- package/src/css/components/spinner.css +6 -7
- package/src/css/components/status-bar.css +15 -24
- package/src/css/components/surface.css +2 -8
- package/src/css/components/tab.css +19 -48
- package/src/css/components/table.css +7 -5
- package/src/css/components/tooltip.css +14 -7
- package/src/css/focus.css +14 -38
- package/src/css/forms/choice-card.css +13 -14
- package/src/css/forms/choice.css +21 -27
- package/src/css/forms/control.css +18 -17
- 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 +4 -4
- package/src/css/forms/input-icon.css +30 -12
- package/src/css/forms/native.css +50 -11
- package/src/css/forms/range.css +17 -9
- package/src/css/forms/select.css +8 -8
- package/src/css/forms/switch.css +14 -12
- package/src/css/forms/validation.css +32 -5
- package/src/css/grid.css +32 -18
- package/src/css/intents.css +2 -2
- package/src/css/layout.css +45 -2
- 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 +13 -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 +21 -5
- package/src/css/themes/cyberpunk.css +1 -1
- package/src/css/themes/dim.css +3 -3
- package/src/css/themes/edge.css +8 -10
- package/src/css/themes/index.css +8 -4
- 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 +74 -20
- package/src/css/utilities.css +8 -10
- package/src/css/variants.css +51 -66
- package/src/js/command.js +1 -3
- package/src/js/context-menu.js +67 -35
- package/src/js/dialog.js +68 -79
- package/src/js/dismiss.js +15 -0
- package/src/js/enhance.js +54 -23
- 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 +104 -87
- 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 +87 -34
- package/src/js/parse-config.js +47 -0
- package/src/js/password.js +9 -1
- package/src/js/scrollspy.js +12 -4
- package/src/js/selectors.js +0 -7
- package/src/js/status.js +3 -1
- package/src/js/surface.js +187 -136
- package/src/js/tab.js +36 -53
- package/src/js/tooltip.js +70 -35
- package/src/js/validation.js +34 -8
- package/dist/actual.js.map +0 -31
- package/docs/README.md +0 -28
- 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 -496
- package/docs/layout.md +0 -480
- package/docs/patterns.md +0 -504
- package/docs/reserved.md +0 -7
- package/docs/tokens.md +0 -455
- package/docs/ui.md +0 -929
- 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,142 @@
|
|
|
1
|
+
# Grid
|
|
2
|
+
|
|
3
|
+
Responsive equal-width item grids with a tunable minimum item width.
|
|
4
|
+
|
|
5
|
+
```html demo
|
|
6
|
+
<section class="grid">
|
|
7
|
+
<article class="card">
|
|
8
|
+
<h3>Starter</h3>
|
|
9
|
+
<p>For small projects.</p>
|
|
10
|
+
</article>
|
|
11
|
+
<article class="card">
|
|
12
|
+
<h3>Team</h3>
|
|
13
|
+
<p>For shared products.</p>
|
|
14
|
+
</article>
|
|
15
|
+
<article class="card">
|
|
16
|
+
<h3>Scale</h3>
|
|
17
|
+
<p>For larger systems.</p>
|
|
18
|
+
</article>
|
|
19
|
+
</section>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Tune the minimum item width with `--grid-min`.
|
|
23
|
+
|
|
24
|
+
```css
|
|
25
|
+
.pricing-grid {
|
|
26
|
+
--grid-min: 20rem;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or replace the responsive template entirely with `--grid-columns` for a
|
|
31
|
+
custom or asymmetric layout. Because this overrides the intrinsic
|
|
32
|
+
`auto-fit/minmax` algorithm, you own any narrow-container collapse:
|
|
33
|
+
|
|
34
|
+
```css
|
|
35
|
+
.feature-grid {
|
|
36
|
+
--grid-columns: minmax(0, 2fr) minmax(0, 1fr);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@container (width < 40rem) {
|
|
40
|
+
.feature-grid {
|
|
41
|
+
--grid-columns: 1fr;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`--grid-columns` applies to `.grid` only. The `.grid-2`, `.grid-3`,
|
|
47
|
+
`.grid-4`, and `.grid-6` presets retain their fixed-column contracts.
|
|
48
|
+
|
|
49
|
+
## Fixed columns
|
|
50
|
+
|
|
51
|
+
Predictable 2/3/4/6-column grids. Full count by default; wrap in `.container-query` to make the column count collapse on narrow wrappers.
|
|
52
|
+
|
|
53
|
+
Use `.grid-2`, `.grid-3`, `.grid-4`, and `.grid-6` when the column count matters more than the item minimum width — for example, when a row should always read as "three items side by side", or when an empty cell should not stretch to fill space. Unlike `.grid`, the columns are fixed; if you have only two items in a `.grid-3`, the third cell stays empty rather than redistributing.
|
|
54
|
+
|
|
55
|
+
A `.grid-2/3/4/6` without a wrapper is a **fixed grid** (always at its full column count); with a `.container-query` wrapper, it's a **responsive grid** (the column count collapses on narrow wrappers). The wrapper is the query container; the grid itself is a plain block grid.
|
|
56
|
+
|
|
57
|
+
```html demo
|
|
58
|
+
<div class="container-query">
|
|
59
|
+
<div class="grid-3">
|
|
60
|
+
<article class="card">
|
|
61
|
+
<h3>Starter</h3>
|
|
62
|
+
<p>For small projects.</p>
|
|
63
|
+
</article>
|
|
64
|
+
<article class="card">
|
|
65
|
+
<h3>Team</h3>
|
|
66
|
+
<p>For shared products.</p>
|
|
67
|
+
</article>
|
|
68
|
+
<article class="card">
|
|
69
|
+
<h3>Scale</h3>
|
|
70
|
+
<p>For larger systems.</p>
|
|
71
|
+
</article>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A `grid-4` demo:
|
|
77
|
+
|
|
78
|
+
```html demo
|
|
79
|
+
<div class="container-query">
|
|
80
|
+
<div class="grid-4">
|
|
81
|
+
<article class="card">
|
|
82
|
+
<h3>Starter</h3>
|
|
83
|
+
<p>For small projects.</p>
|
|
84
|
+
</article>
|
|
85
|
+
<article class="card">
|
|
86
|
+
<h3>Team</h3>
|
|
87
|
+
<p>For shared products.</p>
|
|
88
|
+
</article>
|
|
89
|
+
<article class="card">
|
|
90
|
+
<h3>Scale</h3>
|
|
91
|
+
<p>For larger systems.</p>
|
|
92
|
+
</article>
|
|
93
|
+
<article class="card">
|
|
94
|
+
<h3>Enterprise</h3>
|
|
95
|
+
<p>Contact us.</p>
|
|
96
|
+
</article>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
A `grid-6` demo for compact items:
|
|
102
|
+
|
|
103
|
+
```html demo
|
|
104
|
+
<div class="container-query">
|
|
105
|
+
<div class="grid-6">
|
|
106
|
+
<article class="card compact">
|
|
107
|
+
<span class="muted">Open</span>
|
|
108
|
+
<strong>18</strong>
|
|
109
|
+
</article>
|
|
110
|
+
<article class="card compact">
|
|
111
|
+
<span class="muted">Queued</span>
|
|
112
|
+
<strong>7</strong>
|
|
113
|
+
</article>
|
|
114
|
+
<article class="card compact">
|
|
115
|
+
<span class="muted">Blocked</span>
|
|
116
|
+
<strong>2</strong>
|
|
117
|
+
</article>
|
|
118
|
+
<article class="card compact">
|
|
119
|
+
<span class="muted">Shipped</span>
|
|
120
|
+
<strong>41</strong>
|
|
121
|
+
</article>
|
|
122
|
+
<article class="card compact">
|
|
123
|
+
<span class="muted">Owners</span>
|
|
124
|
+
<strong>6</strong>
|
|
125
|
+
</article>
|
|
126
|
+
<article class="card compact">
|
|
127
|
+
<span class="muted">SLA</span>
|
|
128
|
+
<strong>99%</strong>
|
|
129
|
+
</article>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Behavior
|
|
135
|
+
|
|
136
|
+
- Without `.container-query`: `.grid-2` is always 2 cols, `.grid-3` is always 3 cols, `.grid-4` is always 4 cols, and `.grid-6` is always 6 cols.
|
|
137
|
+
- With `.container-query`: `.grid-2`, `.grid-3`, and `.grid-4` collapse to one column on a narrow wrapper (under 28rem). `.grid-3` and `.grid-4` step through 2 columns between 28rem and 48rem. `.grid-6` starts at 2 columns under 28rem, steps through 3 columns between 28rem and 64rem, then returns to 6 columns.
|
|
138
|
+
- The intermediate 2-col step is useful for lists of items divisible by both 2 and 3 (or 4) — a 12-item list reads as 12 lines, then 6 lines of 2, then 4 lines of 3 (or 3 lines of 4), depending on the grid.
|
|
139
|
+
- `.grid-6` is for compact items such as stats, shortcut tiles, swatches, avatar summaries, and small settings cards. It does not collapse to one column by default. If item width matters more than an exact six-column rhythm, prefer `.grid` with a smaller `--grid-min`.
|
|
140
|
+
- The wrapper is the query container, so the same class behaves differently in a narrow sidebar than in a wide main area. No viewport breakpoints are involved.
|
|
141
|
+
|
|
142
|
+
Do not add `.row`, `.col-6`, `.offset-2`, or other fixed grid-system classes unless the project later proves it needs a formal grid system.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# List
|
|
2
|
+
|
|
3
|
+
Lists stay native by default. Compose visual list rows from existing primitives.
|
|
4
|
+
|
|
5
|
+
Use `.list-reset` only when markers are not part of the content. Combine `.stack`, `.media`, `.cluster`, spacing helpers, and local styles for application-style rows.
|
|
6
|
+
|
|
7
|
+
```html demo
|
|
8
|
+
<figure class="stack">
|
|
9
|
+
<figcaption><strong>Most played songs this week</strong></figcaption>
|
|
10
|
+
|
|
11
|
+
<ul class="list-reset stack gap-none">
|
|
12
|
+
<li class="media items-center py" style="border-block-end: var(--border-width) solid var(--border)">
|
|
13
|
+
<div class="avatar">
|
|
14
|
+
<img src="https://i.pravatar.cc/48?img=5" alt="Dio Lupa" />
|
|
15
|
+
</div>
|
|
16
|
+
<div class="cluster">
|
|
17
|
+
<div class="stack grow" style="--gap: var(--space-10)">
|
|
18
|
+
<strong>Dio Lupa</strong>
|
|
19
|
+
<span class="muted">Remaining Reason</span>
|
|
20
|
+
</div>
|
|
21
|
+
<span class="muted">3:45</span>
|
|
22
|
+
</div>
|
|
23
|
+
</li>
|
|
24
|
+
|
|
25
|
+
<li class="media items-center py" style="border-block-end: var(--border-width) solid var(--border)">
|
|
26
|
+
<div class="avatar">
|
|
27
|
+
<img src="https://i.pravatar.cc/48?img=10" alt="Astral Planes" />
|
|
28
|
+
</div>
|
|
29
|
+
<div class="cluster">
|
|
30
|
+
<div class="stack grow" style="--gap: var(--space-10)">
|
|
31
|
+
<strong>Astral Planes</strong>
|
|
32
|
+
<span class="muted">Neon Drift</span>
|
|
33
|
+
</div>
|
|
34
|
+
<span class="muted">4:12</span>
|
|
35
|
+
</div>
|
|
36
|
+
</li>
|
|
37
|
+
</ul>
|
|
38
|
+
</figure>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Keep semantic lists unstyled when the markers carry meaning.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Media Object
|
|
2
|
+
|
|
3
|
+
Fixed-width leading element (avatar, icon, image) paired with flexible trailing content.
|
|
4
|
+
|
|
5
|
+
`.media` is the media object: a grid with `auto minmax(0, 1fr)` columns. Use it for author cards, comments, meta rows, and any composition that pairs a small leading element with a flexible content block. The trailing column takes the remaining space and never overflows.
|
|
6
|
+
|
|
7
|
+
Give `.media` exactly two children. Trailing metadata or actions go inside the content column — nest a `.cluster` and put `.grow` on the flexible part — never as additional children, which silently wrap onto a second grid row.
|
|
8
|
+
|
|
9
|
+
Add `.items-center` to align the leading element with short trailing content. For multi-line content, the default `align-items: start` is more comfortable.
|
|
10
|
+
|
|
11
|
+
```html demo
|
|
12
|
+
<article class="card media">
|
|
13
|
+
<span class="avatar"><abbr>AM</abbr></span>
|
|
14
|
+
<div class="stack gap-none">
|
|
15
|
+
<strong>Ada Meridian</strong>
|
|
16
|
+
<p class="muted">Senior designer working on design systems.</p>
|
|
17
|
+
</div>
|
|
18
|
+
</article>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```html demo
|
|
22
|
+
<div class="media items-center">
|
|
23
|
+
<span class="avatar"><abbr>AM</abbr></span>
|
|
24
|
+
<span>Ada Meridian</span>
|
|
25
|
+
</div>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The media object is the underlying pattern of `.blog-author`, `.blog-comment`, and similar compositions in templates. Components can adopt it instead of repeating the grid rule.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Layout Overview
|
|
2
|
+
|
|
3
|
+
Layout provides a small set of composable building blocks for common page, app, and content structure. It is not a utility-first framework and it is not a twelve-column grid system.
|
|
4
|
+
|
|
5
|
+
- Layout classes compose components; components do not own page spacing.
|
|
6
|
+
- Use custom properties to tune a layout instance.
|
|
7
|
+
- Prefer intrinsic and container-friendly behavior over breakpoint-heavy APIs.
|
|
8
|
+
- Keep class names semantic enough to remember and generic enough to reuse.
|
|
9
|
+
- Add a new layout primitive only when a pattern appears often and is awkward to express with existing primitives.
|
|
10
|
+
|
|
11
|
+
## Spacing tokens
|
|
12
|
+
|
|
13
|
+
Layout shares the global spacing surface and exposes a small set of gap and rhythm tokens.
|
|
14
|
+
|
|
15
|
+
```css
|
|
16
|
+
:root {
|
|
17
|
+
--gap: 0.75rem;
|
|
18
|
+
--space-10: 0.25rem;
|
|
19
|
+
--space-20: 0.5rem;
|
|
20
|
+
--space-30: 0.75rem;
|
|
21
|
+
--space-40: 1rem;
|
|
22
|
+
--space-50: 1.5rem;
|
|
23
|
+
--space-60: 2rem;
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Most layout primitives read `--gap` and allow local overrides.
|
|
28
|
+
|
|
29
|
+
```css
|
|
30
|
+
.settings-form {
|
|
31
|
+
--gap: var(--space-50);
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Prefer a local custom property over adding many one-off utility classes. Use inline styles only for demos, prototypes, or truly dynamic values.
|
|
36
|
+
|
|
37
|
+
There are no `.gap-sm` / `.gap-md` / `.gap-lg` density variants on the core layout primitives; set `--gap` locally when only spacing should change. The optional `utilities-extra.css` layer ships those gap utilities for row/column containers.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Scroll Snap
|
|
2
|
+
|
|
3
|
+
Horizontal rails that stay useful without JavaScript and snap in modern browsers.
|
|
4
|
+
|
|
5
|
+
> **Optional** — import `actual-css/css/optional/scroll-snap` or `actual-css/css/optional/index`.
|
|
6
|
+
|
|
7
|
+
Use `.scroll-snap` for a niche row of items that should scroll horizontally, such as card rails, media strips, overflow tabs, or a simple touch-friendly carousel baseline. Scrolling only appears when the items are wider than the container; set `--scroll-snap-item-size` for card rails.
|
|
8
|
+
|
|
9
|
+
```html demo
|
|
10
|
+
<section class="stack" aria-labelledby="featured-title">
|
|
11
|
+
<h2 id="featured-title">Featured articles</h2>
|
|
12
|
+
|
|
13
|
+
<ul class="scroll-snap list-reset" style="--scroll-snap-item-size: min(85%, 18rem)">
|
|
14
|
+
<li class="card">Article one</li>
|
|
15
|
+
<li class="card">Article two</li>
|
|
16
|
+
<li class="card">Article three</li>
|
|
17
|
+
<li class="card">Article four</li>
|
|
18
|
+
<li class="card">Article five</li>
|
|
19
|
+
</ul>
|
|
20
|
+
</section>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
The baseline is flex layout plus `overflow-x: auto`, so older browsers still get a usable horizontal scroll area. Browsers with scroll snap support add `scroll-snap-type` and item alignment.
|
|
24
|
+
|
|
25
|
+
Items snap to the inline start edge by default. Use `data-snap="center"` for visual rails where centered cards are the better fit.
|
|
26
|
+
|
|
27
|
+
```html demo
|
|
28
|
+
<div class="scroll-snap" data-snap="center" style="--scroll-snap-item-size: min(85%, 18rem)">
|
|
29
|
+
<article class="card">One</article>
|
|
30
|
+
<article class="card">Two</article>
|
|
31
|
+
<article class="card">Three</article>
|
|
32
|
+
<article class="card">Four</article>
|
|
33
|
+
<article class="card">Five</article>
|
|
34
|
+
</div>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## CSS hooks
|
|
38
|
+
|
|
39
|
+
- `--scroll-snap-item-size` — item `flex` basis; set it for card rails.
|
|
40
|
+
- `--scroll-snap-gap` — space between items.
|
|
41
|
+
- `--scroll-snap-padding` — inline scroll padding, so a snapped item does not sit
|
|
42
|
+
flush against the container edge. Not declared; falls back to `--scroll-snap-gap`.
|
|
43
|
+
- `--scroll-snap-align` — item snap alignment. Prefer `data-snap="center"`
|
|
44
|
+
over setting this directly.
|
|
45
|
+
|
|
46
|
+
Keep scrollbars visible by default. If a product deliberately hides them, make that choice explicit with `data-scrollbar="hidden"`.
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<div class="scroll-snap" data-scrollbar="hidden">
|
|
50
|
+
...
|
|
51
|
+
</div>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`.scroll-snap` is not a full interactive carousel. For previous/next controls, pagination, active state, mouse drag, looping, autoplay, or robust accessibility behavior, use a dedicated carousel library such as Swiper and compose it with Actual components.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Sidebar Layout
|
|
2
|
+
|
|
3
|
+
Two-column layout where one side has a preferred width and the other takes the remaining space.
|
|
4
|
+
|
|
5
|
+
Use `.sidebar-layout` for two-column layouts where the aside should align to the top of the content, stay sticky, or keep a predictable declared width.
|
|
6
|
+
|
|
7
|
+
Wrap it in `.container-query` to make the switch depend on the container width. Below 64rem the columns stack; at and above it the aside takes `--sidebar-layout-size` and the content takes the remaining space. Older browsers use the viewport fallback.
|
|
8
|
+
|
|
9
|
+
```html demo
|
|
10
|
+
<div class="container-query">
|
|
11
|
+
<div class="sidebar-layout" style="--sidebar-layout-size: 18rem">
|
|
12
|
+
<article class="stack">
|
|
13
|
+
<h1>Long-form article</h1>
|
|
14
|
+
<p>Article body that benefits from a sticky table of contents.</p>
|
|
15
|
+
</article>
|
|
16
|
+
|
|
17
|
+
<aside class="stack">
|
|
18
|
+
<h3>On this page</h3>
|
|
19
|
+
<ol class="list-reset">
|
|
20
|
+
<li><a class="link-muted" href="#">Section</a></li>
|
|
21
|
+
</ol>
|
|
22
|
+
</aside>
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Tune the side width with `--sidebar-layout-size`.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Stack
|
|
2
|
+
|
|
3
|
+
Vertical flow with consistent spacing, ideal for forms, card content, and content blocks.
|
|
4
|
+
|
|
5
|
+
```html demo
|
|
6
|
+
<section class="stack">
|
|
7
|
+
<h2>Account</h2>
|
|
8
|
+
<p class="muted">Manage profile and billing settings.</p>
|
|
9
|
+
<form>
|
|
10
|
+
<label class="field">
|
|
11
|
+
<span class="field-label">Email</span>
|
|
12
|
+
<input class="input" type="email" placeholder="you@example.com" />
|
|
13
|
+
</label>
|
|
14
|
+
</form>
|
|
15
|
+
</section>
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Use stack for forms, card content, modal bodies, side panels, and documentation blocks.
|
|
19
|
+
|
|
20
|
+
Like `.cluster`, stack owns its children's block-axis spacing: direct-child
|
|
21
|
+
block margins are reset, so vertical rhythm always comes from `--gap`. Inline
|
|
22
|
+
margins stay free — for example `margin-inline: auto` on a child centers it
|
|
23
|
+
without fighting the primitive.
|
|
24
|
+
|
|
25
|
+
## Grouping items tightly
|
|
26
|
+
|
|
27
|
+
Stack spacing is a `--gap` between every direct child; the primitive cannot
|
|
28
|
+
tighten two specific children by itself. To group a few items into one unit
|
|
29
|
+
(a title and its description), nest a `.stack gap-none`:
|
|
30
|
+
|
|
31
|
+
```html demo
|
|
32
|
+
<div class="stack">
|
|
33
|
+
<div class="stack gap-none">
|
|
34
|
+
<strong>Ada Meridian</strong>
|
|
35
|
+
<span class="muted">Senior designer working on design systems.</span>
|
|
36
|
+
</div>
|
|
37
|
+
<span class="badge success soft">Active</span>
|
|
38
|
+
</div>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The outer stack keeps its rhythm between groups; the inner `gap-none` unit owns
|
|
42
|
+
the tight pairing.
|
|
43
|
+
|
|
44
|
+
For a one-off adjacency — pull a single child closer to its neighbour without
|
|
45
|
+
adding a wrapper — the escape hatch is app CSS. `gap` and `margin-block` are
|
|
46
|
+
additive, so a negative block margin on the child cancels the following gap:
|
|
47
|
+
|
|
48
|
+
```css
|
|
49
|
+
.stack > .profile-row {
|
|
50
|
+
margin-block-end: calc(var(--gap) * -1);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The stack resets child block margins to zero, so this re-adds a margin on that
|
|
55
|
+
one child only; the rest of the stack keeps its rhythm.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Switcher
|
|
2
|
+
|
|
3
|
+
Row that becomes a column when space gets tight, useful for small sets of panels.
|
|
4
|
+
|
|
5
|
+
> **Optional** — import `actual-css/css/optional/layout-extra` or `actual-css/css/optional/index`.
|
|
6
|
+
|
|
7
|
+
Use `.switcher` for rows that should become columns when space gets tight.
|
|
8
|
+
|
|
9
|
+
```html demo
|
|
10
|
+
<section class="switcher">
|
|
11
|
+
<article class="card">
|
|
12
|
+
<h3>Profile</h3>
|
|
13
|
+
<p>Personal details and preferences.</p>
|
|
14
|
+
</article>
|
|
15
|
+
<article class="card">
|
|
16
|
+
<h3>Security</h3>
|
|
17
|
+
<p>Password and login settings.</p>
|
|
18
|
+
</article>
|
|
19
|
+
<article class="card">
|
|
20
|
+
<h3>Billing</h3>
|
|
21
|
+
<p>Invoices and payment methods.</p>
|
|
22
|
+
</article>
|
|
23
|
+
</section>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This is useful for small sets of panels, not large card collections. Use `.grid` for repeatable collections.
|
|
27
|
+
|
|
28
|
+
## CSS hooks
|
|
29
|
+
|
|
30
|
+
- `--switcher-threshold` — container width at which the row becomes a column.
|
|
31
|
+
|
|
32
|
+
```css
|
|
33
|
+
.settings-panels {
|
|
34
|
+
--switcher-threshold: 55rem;
|
|
35
|
+
}
|
|
36
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Topbar
|
|
2
|
+
|
|
3
|
+
Sticky, frosted top bar shell for app/dashboard layouts with a persistent sidebar.
|
|
4
|
+
|
|
5
|
+
> **Optional** — import `actual-css/css/optional/layout-extra` or `actual-css/css/optional/index`.
|
|
6
|
+
|
|
7
|
+
Use `.topbar` for the structural shell of an app-shell header: sticky positioning, stacking, and a frosted background. Row content — search, breadcrumb, actions — stays local to the page.
|
|
8
|
+
|
|
9
|
+
```html demo
|
|
10
|
+
<header class="topbar">
|
|
11
|
+
<button class="btn neutral ghost" type="button" aria-label="Open navigation">
|
|
12
|
+
<i class="ti ti-menu-2" aria-hidden="true"></i>
|
|
13
|
+
</button>
|
|
14
|
+
<ol class="breadcrumb" aria-label="Breadcrumb">
|
|
15
|
+
<li><a href="#">Overview</a></li>
|
|
16
|
+
<li aria-current="page">Accounts</li>
|
|
17
|
+
</ol>
|
|
18
|
+
<div class="grow"></div>
|
|
19
|
+
<span class="avatar sm"><abbr>LW</abbr></span>
|
|
20
|
+
</header>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This is for the app-shell topbar specifically — a structural need that recurs unchanged across app UIs regardless of brand. It is not for marketing or editorial site headers, which are one-off visual identity per page.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Actions
|
|
2
|
+
|
|
3
|
+
Use `.actions` for a semantic list of actions.
|
|
4
|
+
|
|
5
|
+
It removes native list chrome and provides a baseline row layout (`display: flex`, wrap, shared gap). Compose with layout primitives only when you need a different direction or distribution.
|
|
6
|
+
|
|
7
|
+
```html demo
|
|
8
|
+
<div class="stack">
|
|
9
|
+
<menu class="actions cluster">
|
|
10
|
+
<li><button class="btn primary" type="button">Save</button></li>
|
|
11
|
+
<li><button class="btn outline" type="button">Cancel</button></li>
|
|
12
|
+
</menu>
|
|
13
|
+
|
|
14
|
+
<menu class="actions stack">
|
|
15
|
+
<li><a class="btn primary" href="/billing">Update billing</a></li>
|
|
16
|
+
<li><a href="/support">Contact support</a></li>
|
|
17
|
+
</menu>
|
|
18
|
+
</div>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Use `.actions` for:
|
|
22
|
+
|
|
23
|
+
- dialog actions
|
|
24
|
+
- alert actions
|
|
25
|
+
- card actions
|
|
26
|
+
- toolbar actions
|
|
27
|
+
- form submit/cancel groups
|
|
28
|
+
|
|
29
|
+
Use layout primitives for contextual spacing.
|
|
30
|
+
|
|
31
|
+
```html demo
|
|
32
|
+
<div class="alert warning stack">
|
|
33
|
+
<p>Your billing method has expired.</p>
|
|
34
|
+
|
|
35
|
+
<menu class="actions cluster">
|
|
36
|
+
<li><a class="btn danger sm" href="/billing">Update billing</a></li>
|
|
37
|
+
<li><a href="/support">Contact support</a></li>
|
|
38
|
+
</menu>
|
|
39
|
+
</div>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Prefer composition over making each component reimplement action-list layout.
|
|
43
|
+
|
|
44
|
+
```css
|
|
45
|
+
/* Avoid */
|
|
46
|
+
.alert > menu {
|
|
47
|
+
display: flex;
|
|
48
|
+
gap: var(--space-20);
|
|
49
|
+
padding: 0;
|
|
50
|
+
list-style: none;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Code Block With Copy Button
|
|
2
|
+
|
|
3
|
+
A self-contained copy-button recipe composed from `.btn`, a `pre > code` block, and the `enhance()` lifecycle.
|
|
4
|
+
|
|
5
|
+
```html
|
|
6
|
+
<div class="code-block">
|
|
7
|
+
<pre><code>npm install actual-css</code></pre>
|
|
8
|
+
<button class="btn sm ghost" data-copy>Copy</button>
|
|
9
|
+
</div>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```css
|
|
13
|
+
.code-block { position: relative; }
|
|
14
|
+
.code-block > [data-copy] {
|
|
15
|
+
position: absolute;
|
|
16
|
+
inset-block-start: var(--space-20);
|
|
17
|
+
inset-inline-end: var(--space-20);
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
import enhance from "actual-css/js/enhance";
|
|
23
|
+
|
|
24
|
+
enhance({
|
|
25
|
+
"[data-copy]": (button) => {
|
|
26
|
+
const controller = new AbortController();
|
|
27
|
+
button.addEventListener("click", async () => {
|
|
28
|
+
const code = button.closest(".code-block")?.querySelector("code");
|
|
29
|
+
if (code) await navigator.clipboard.writeText(code.textContent);
|
|
30
|
+
}, { signal: controller.signal });
|
|
31
|
+
return () => controller.abort();
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
No core JS, no syntax highlighter — the recipe is the documentation.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Exclusive Accordion
|
|
2
|
+
|
|
3
|
+
Browsers that support the `name` attribute on `<details>` get native exclusive accordions — no JS engine needed. Omit `name` to allow several open at once.
|
|
4
|
+
|
|
5
|
+
```html demo
|
|
6
|
+
<details name="faq" open>
|
|
7
|
+
<summary>What is this?</summary>
|
|
8
|
+
<p>A CSS framework.</p>
|
|
9
|
+
</details>
|
|
10
|
+
<details name="faq">
|
|
11
|
+
<summary>Does it need JS?</summary>
|
|
12
|
+
<p>Only the progressive enhancers you import.</p>
|
|
13
|
+
</details>
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Not shipping an accordion JS engine is a design decision, not a gap.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Hierarchical Scrollable Nav
|
|
2
|
+
|
|
3
|
+
App shell with a collapsible nav tree. Uses existing primitives only — no treeview component.
|
|
4
|
+
|
|
5
|
+
```html demo
|
|
6
|
+
<aside>
|
|
7
|
+
<nav class="nav-list scroller" data-enhance="scrollspy" aria-label="Docs">
|
|
8
|
+
<details open>
|
|
9
|
+
<summary>Getting started</summary>
|
|
10
|
+
<a class="nav-link" href="#intro">Introduction</a>
|
|
11
|
+
<a class="nav-link" href="#install">Installation</a>
|
|
12
|
+
</details>
|
|
13
|
+
<details>
|
|
14
|
+
<summary>Components</summary>
|
|
15
|
+
<a class="nav-link" href="#alert">Alert</a>
|
|
16
|
+
<a class="nav-link" href="#badge">Badge</a>
|
|
17
|
+
</details>
|
|
18
|
+
</nav>
|
|
19
|
+
</aside>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- `.nav-list` + `.nav-link`: Actual's nav chrome
|
|
23
|
+
- Native `<details>`: exclusive accordion with `<details name>` (no JS engine)
|
|
24
|
+
- `data-enhance="scrollspy"`: scroll-driven `aria-current`
|
|
25
|
+
- `.scroller`: thin, theme-aware scrollbar
|
|
26
|
+
- No `.nav-heading` / `.nav-sublist` classes — the existing primitives are enough
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Icon Input
|
|
2
|
+
|
|
3
|
+
Use `.input-icon` for a search or filter input with a leading icon.
|
|
4
|
+
|
|
5
|
+
```html demo
|
|
6
|
+
<label class="input-icon">
|
|
7
|
+
<i class="ti ti-search" aria-hidden="true"></i>
|
|
8
|
+
<input class="input" type="search" placeholder="Search accounts, invoices, notes" aria-label="Search">
|
|
9
|
+
</label>
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
It positions the wrapper's first child absolutely inside a relative container and grows the input's start padding to clear it. `pointer-events: none` on the icon keeps clicks passing through to the input underneath. It is icon-library agnostic — an `<i>` webfont icon, an inline `<svg>`, or a `<span>` all work as the wrapper's first child.
|
|
13
|
+
|
|
14
|
+
The icon keeps a fixed `--input-icon-size` (1.25rem); density contexts shrink the control geometry around it, not the pictogram. Apply the density class to the wrapper and to the input so the control height follows:
|
|
15
|
+
|
|
16
|
+
```html demo
|
|
17
|
+
<div class="stack">
|
|
18
|
+
<label class="input-icon sm">
|
|
19
|
+
<i class="ti ti-search" aria-hidden="true"></i>
|
|
20
|
+
<input class="input sm" type="search" aria-label="Search">
|
|
21
|
+
</label>
|
|
22
|
+
</div>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Trailing icons are supported too: an icon that follows the input is positioned at the end and the input grows its end padding. It stays decorative — `pointer-events: none` means an interactive trailing control such as a clear button is not covered by this class.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# Intent Nesting
|
|
2
|
+
|
|
3
|
+
Intent on an ancestor must not tint nested components, and a local intent inside an intent-carrying ancestor must win. Both directions are verified in `tests/css-audit.test.js` and the kitchen-sink demo template. Intent classes only expose `--intent` and `--intent-fg`; each consuming component opens a local intent boundary in a `:where()` block that resets exactly the tokens it consumes to initial. Parent intents do not leak into nested components, and a local intent class still wins because it has higher specificity than `:where()`.
|