actual-css 0.2.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 +38 -1
- package/README.md +24 -9
- package/dist/actual-themes.min.css +1 -0
- package/dist/actual.css +1244 -915
- 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 +1 -1
- 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 +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 +4 -4
- package/package.json +20 -12
- package/src/css/actual.css +12 -1
- 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 +126 -99
- package/src/css/components/avatar.css +16 -10
- package/src/css/components/badge.css +55 -35
- package/src/css/components/breadcrumb.css +7 -15
- package/src/css/components/busy.css +6 -0
- package/src/css/components/button.css +78 -32
- package/src/css/components/card.css +17 -37
- 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 +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/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 +16 -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 +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 +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 +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 +74 -20
- 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 +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 +185 -136
- package/src/js/tab.js +36 -53
- package/src/js/tooltip.js +53 -31
- 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
package/docs/utilities.md
DELETED
|
@@ -1,384 +0,0 @@
|
|
|
1
|
-
# Utilities
|
|
2
|
-
|
|
3
|
-
Utilities are a small escape hatch for common single-purpose needs. They should support composition, not become the primary way to build UI.
|
|
4
|
-
|
|
5
|
-
- Prefer semantic HTML, components, and layout primitives first.
|
|
6
|
-
- Add utilities only when the rule is broadly useful and unlikely to become a component.
|
|
7
|
-
- Keep utility names stable, boring, and few.
|
|
8
|
-
- Utilities should use logical properties where relevant.
|
|
9
|
-
|
|
10
|
-
## Accessibility
|
|
11
|
-
|
|
12
|
-
### Screen Reader Only
|
|
13
|
-
|
|
14
|
-
Use `.sr-only` for text that should be available to assistive technology but visually hidden.
|
|
15
|
-
|
|
16
|
-
```html
|
|
17
|
-
<button class="btn ghost" type="button">
|
|
18
|
-
<i class="ti ti-menu-2" aria-hidden="true"></i>
|
|
19
|
-
<span class="sr-only">Open navigation</span>
|
|
20
|
-
</button>
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
```css
|
|
24
|
-
.sr-only {
|
|
25
|
-
position: absolute;
|
|
26
|
-
inline-size: 1px;
|
|
27
|
-
block-size: 1px;
|
|
28
|
-
padding: 0;
|
|
29
|
-
margin: -1px;
|
|
30
|
-
overflow: hidden;
|
|
31
|
-
clip: rect(0, 0, 0, 0);
|
|
32
|
-
white-space: nowrap;
|
|
33
|
-
border: 0;
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Overflow
|
|
38
|
-
|
|
39
|
-
> Explicit overflow handling for content that may exceed its container.
|
|
40
|
-
|
|
41
|
-
Use `.overflow-auto` when content may overflow its container, especially tables and code-like regions.
|
|
42
|
-
|
|
43
|
-
```html
|
|
44
|
-
<div class="overflow-auto">
|
|
45
|
-
<table class="table">
|
|
46
|
-
<caption>Team members</caption>
|
|
47
|
-
<thead>
|
|
48
|
-
<tr>
|
|
49
|
-
<th>Name</th>
|
|
50
|
-
<th>Role</th>
|
|
51
|
-
</tr>
|
|
52
|
-
</thead>
|
|
53
|
-
<tbody>
|
|
54
|
-
<tr>
|
|
55
|
-
<td>Alice Johnson</td>
|
|
56
|
-
<td>Admin</td>
|
|
57
|
-
</tr>
|
|
58
|
-
</tbody>
|
|
59
|
-
</table>
|
|
60
|
-
</div>
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
```css
|
|
64
|
-
.overflow-auto {
|
|
65
|
-
overflow: auto;
|
|
66
|
-
}
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Sizing And Wrapping
|
|
70
|
-
|
|
71
|
-
> Small layout corrections for controls, labels, and compact rails.
|
|
72
|
-
|
|
73
|
-
Use `.fit` when a control or element should shrink to its content instead of filling the available inline space.
|
|
74
|
-
|
|
75
|
-
```html
|
|
76
|
-
<select class="select fit" aria-label="Theme">
|
|
77
|
-
<option>System</option>
|
|
78
|
-
<option>Light</option>
|
|
79
|
-
<option>Dark</option>
|
|
80
|
-
</select>
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Use `.nowrap` on a flex layout such as `.cluster` when the group must stay on one line.
|
|
84
|
-
|
|
85
|
-
```html
|
|
86
|
-
<div class="cluster nowrap sm">
|
|
87
|
-
<select class="select sm fit" aria-label="Segment">
|
|
88
|
-
<option>All segments</option>
|
|
89
|
-
</select>
|
|
90
|
-
<button class="btn sm outline" type="button">Filter</button>
|
|
91
|
-
</div>
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Use `.truncate` on the flexible item that should ellipsize inside a constrained row.
|
|
95
|
-
|
|
96
|
-
```html
|
|
97
|
-
<header class="cluster nowrap">
|
|
98
|
-
<strong class="truncate">A long account name that should not push actions away</strong>
|
|
99
|
-
<button class="btn sm ghost" type="button">Open</button>
|
|
100
|
-
</header>
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
```css
|
|
104
|
-
.fit {
|
|
105
|
-
inline-size: fit-content;
|
|
106
|
-
max-inline-size: 100%;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.nowrap {
|
|
110
|
-
flex-wrap: nowrap;
|
|
111
|
-
white-space: nowrap;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.truncate {
|
|
115
|
-
min-inline-size: 0;
|
|
116
|
-
overflow: hidden;
|
|
117
|
-
text-overflow: ellipsis;
|
|
118
|
-
white-space: nowrap;
|
|
119
|
-
}
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Scroller
|
|
123
|
-
|
|
124
|
-
Use `.scroller` to apply optional framework scrollbar treatment to a scroll container. It styles the scrollbar only; pair it with `.overflow-auto` or a component that already creates overflow.
|
|
125
|
-
|
|
126
|
-
`scroller` is not imported by `actual.css`. Import it explicitly when a project wants Actual CSS scrollbar chrome instead of the native OS default:
|
|
127
|
-
|
|
128
|
-
```css
|
|
129
|
-
@import "actual-css/css/optional/scroller";
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
```html
|
|
133
|
-
<div class="overflow-auto scroller" style="max-block-size: 12rem">
|
|
134
|
-
<div class="stack">
|
|
135
|
-
<p>Scrollable content keeps the native scrollbar but makes it quieter.</p>
|
|
136
|
-
<p>Long content can keep flowing without forcing every component to invent its own scrollbar rules.</p>
|
|
137
|
-
<p>The utility exposes local custom properties for one-off tuning.</p>
|
|
138
|
-
</div>
|
|
139
|
-
</div>
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
```css
|
|
143
|
-
.scroller {
|
|
144
|
-
--scroller-size: 0.625rem;
|
|
145
|
-
--scroller-padding: 0.125rem;
|
|
146
|
-
--scroller-track: transparent;
|
|
147
|
-
--scroller-thumb: var(--border);
|
|
148
|
-
--scroller-thumb-hover: var(--text-muted);
|
|
149
|
-
|
|
150
|
-
scrollbar-color: var(--scroller-thumb) var(--scroller-track);
|
|
151
|
-
scrollbar-width: thin;
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
Customize locally when a scroll surface needs more contrast:
|
|
156
|
-
|
|
157
|
-
```html
|
|
158
|
-
<div class="overflow-auto scroller"
|
|
159
|
-
style="--scroller-thumb: var(--text-muted); --scroller-thumb-hover: var(--text)">
|
|
160
|
-
...
|
|
161
|
-
</div>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
For rounded scroll containers, keep the thumb visually away from the edge by tuning `--scroller-padding` rather than adding extra wrappers or masking pseudo-elements.
|
|
165
|
-
|
|
166
|
-
## Metadata Separator
|
|
167
|
-
|
|
168
|
-
> A small inline dot for separating facts in a metadata row.
|
|
169
|
-
|
|
170
|
-
Use `.dot` between short facts in a byline, timestamp row, or breadcrumb-like list of details — anywhere a plain `·` character would otherwise be typed as text (which screen readers announce awkwardly).
|
|
171
|
-
|
|
172
|
-
```html
|
|
173
|
-
<p class="cluster" style="--cluster-justify: flex-start;">
|
|
174
|
-
<span>Ada Meridian</span>
|
|
175
|
-
<span class="dot" aria-hidden="true"></span>
|
|
176
|
-
<time datetime="2026-06-12">June 12, 2026</time>
|
|
177
|
-
<span class="dot" aria-hidden="true"></span>
|
|
178
|
-
<span>8 min read</span>
|
|
179
|
-
</p>
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
```css
|
|
183
|
-
.dot {
|
|
184
|
-
flex: none;
|
|
185
|
-
inline-size: 0.25rem;
|
|
186
|
-
block-size: 0.25rem;
|
|
187
|
-
border-radius: var(--radius-full);
|
|
188
|
-
background: var(--text-subtle);
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
Always mark it `aria-hidden="true"` — it is a visual separator, not content.
|
|
193
|
-
|
|
194
|
-
## Flex Helpers
|
|
195
|
-
|
|
196
|
-
> Small flex helpers for grow behavior and other one-line needs.
|
|
197
|
-
|
|
198
|
-
### Grow
|
|
199
|
-
|
|
200
|
-
Use `.grow` when one item in a flex layout should take available space without overflowing.
|
|
201
|
-
|
|
202
|
-
```html
|
|
203
|
-
<header class="cluster">
|
|
204
|
-
<a href="/" class="brand">Actual CSS</a>
|
|
205
|
-
<nav class="grow" aria-label="Main navigation">
|
|
206
|
-
<a href="/docs">Docs</a>
|
|
207
|
-
<a href="/components">Components</a>
|
|
208
|
-
</nav>
|
|
209
|
-
<button class="btn ghost" type="button">Menu</button>
|
|
210
|
-
</header>
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
```css
|
|
214
|
-
.grow {
|
|
215
|
-
flex: 1 1 auto;
|
|
216
|
-
min-inline-size: 0;
|
|
217
|
-
}
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Item Alignment
|
|
221
|
-
|
|
222
|
-
Use `.items-start` on flex or grid layouts when children should keep their natural height and align to the block-start edge. This is especially useful for form fields in a grid, where the default grid stretch would make short fields as tall as taller neighbors.
|
|
223
|
-
|
|
224
|
-
```html
|
|
225
|
-
<div class="grid items-start">
|
|
226
|
-
<label class="field">
|
|
227
|
-
<span class="field-label">Name</span>
|
|
228
|
-
<input type="text" />
|
|
229
|
-
</label>
|
|
230
|
-
<label class="field">
|
|
231
|
-
<span class="field-label">Notes</span>
|
|
232
|
-
<textarea></textarea>
|
|
233
|
-
</label>
|
|
234
|
-
</div>
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
Use `.items-center` when a flex or grid layout should center its children on the cross axis. Use `.items-end` when children should align to the block-end edge.
|
|
238
|
-
|
|
239
|
-
## Spacing Helpers
|
|
240
|
-
|
|
241
|
-
> Semantic step helpers for gap, padding, and margin — the most common inline-style escape hatches.
|
|
242
|
-
|
|
243
|
-
Spacing helpers use a restrained semantic scale:
|
|
244
|
-
|
|
245
|
-
- `-sm` — small step, `--space-2` (0.5rem)
|
|
246
|
-
- Default — standard step, `--space-4` (1rem)
|
|
247
|
-
- `-lg` — large step, `--space-5` (1.5rem)
|
|
248
|
-
|
|
249
|
-
All map to logical properties (`padding-block`, `margin-block-end`, etc.) for writing-direction safety.
|
|
250
|
-
|
|
251
|
-
### Gap
|
|
252
|
-
|
|
253
|
-
Override the default gap from layout primitives (`.stack`, `.cluster`, `.grid`):
|
|
254
|
-
|
|
255
|
-
```html
|
|
256
|
-
<ul class="cluster">
|
|
257
|
-
<li><a href="/about" class="btn ghost">About</a></li>
|
|
258
|
-
<li><a href="/contact" class="btn ghost">Contact</a></li>
|
|
259
|
-
</ul>
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
- `.gap-none` → `gap: 0`
|
|
263
|
-
|
|
264
|
-
### Padding
|
|
265
|
-
|
|
266
|
-
Block (vertical) and inline (horizontal) padding. Values respond to the `.sm` / `.lg` modifiers.
|
|
267
|
-
|
|
268
|
-
```html
|
|
269
|
-
<section role="tabpanel" class="py">
|
|
270
|
-
Panel content with breathing room above and below.
|
|
271
|
-
</section>
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
- `.py` → `padding-block: var(--variant-space)`
|
|
275
|
-
- `.px` → `padding-inline: var(--variant-space)`
|
|
276
|
-
|
|
277
|
-
### Margin
|
|
278
|
-
|
|
279
|
-
Block-start and block-end margin for giving elements room:
|
|
280
|
-
|
|
281
|
-
```html
|
|
282
|
-
<h2 class="mbs">A section heading with space above</h2>
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
- `.mbs` → `margin-block-start: var(--variant-space)`
|
|
286
|
-
- `.mbe` → `margin-block-end: var(--variant-space)`
|
|
287
|
-
|
|
288
|
-
No `mbs-none` / `mbe-none` — use `margin: 0` via `.list-reset` for lists, or a layout primitive that already resets margins (`.stack > *`).
|
|
289
|
-
|
|
290
|
-
## Text Helpers
|
|
291
|
-
|
|
292
|
-
> Lightweight helpers for muted or secondary text without a full color scale.
|
|
293
|
-
|
|
294
|
-
### Muted
|
|
295
|
-
|
|
296
|
-
Use `.muted` for secondary text when no semantic element already carries the meaning.
|
|
297
|
-
|
|
298
|
-
```html
|
|
299
|
-
<p class="muted">Last updated June 12, 2026.</p>
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
```css
|
|
303
|
-
.muted {
|
|
304
|
-
color: var(--text-muted);
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
Do not create a full text color utility scale. Intent colors belong to components and state, not arbitrary text decoration.
|
|
309
|
-
|
|
310
|
-
### Text Alignment
|
|
311
|
-
|
|
312
|
-
Use `.text-start`, `.text-center`, or `.text-end` when a component or layout primitive does not already set alignment. These use logical properties so they follow the writing direction.
|
|
313
|
-
|
|
314
|
-
### Text Wrap
|
|
315
|
-
|
|
316
|
-
`.text-balance` and `.text-pretty` opt elements outside `.prose` into balanced or pretty text wrapping. They are safe progressive enhancements: browsers that do not support a `text-wrap` value ignore that declaration and keep normal wrapping.
|
|
317
|
-
|
|
318
|
-
### Lead
|
|
319
|
-
|
|
320
|
-
`.lead` is a slightly larger, more relaxed reading-introduction paragraph (`1.125em`, `--line-height-relaxed`). Use it for introductory text in editorial surfaces. For fluid headline sizing, use `src/css/optional/typography-fluid.css`.
|
|
321
|
-
|
|
322
|
-
## Shape Helpers
|
|
323
|
-
|
|
324
|
-
> Rare shape utilities for cases that must be perfectly round or square.
|
|
325
|
-
|
|
326
|
-
Shape utilities should be rare because shape is mostly theme-level.
|
|
327
|
-
|
|
328
|
-
### Circle
|
|
329
|
-
|
|
330
|
-
Use `.circle` only when an element must be perfectly round.
|
|
331
|
-
|
|
332
|
-
```html
|
|
333
|
-
<img class="circle" src="https://i.pravatar.cc/48?img=5" alt="Jane Doe" width="48" height="48" />
|
|
334
|
-
```
|
|
335
|
-
|
|
336
|
-
```css
|
|
337
|
-
.circle {
|
|
338
|
-
border-radius: var(--radius-full);
|
|
339
|
-
}
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
Do not add `.rounded-sm`, `.rounded-lg`, `.square`, or `.pill` unless repeated real use proves they are needed.
|
|
343
|
-
|
|
344
|
-
## Content
|
|
345
|
-
|
|
346
|
-
### Measure
|
|
347
|
-
|
|
348
|
-
`.measure` caps a block at a readable width using `--prose-measure`. It does not center — combine with `.center` for the common centered reading column, or with `margin-inline: auto` for a one-off centered block.
|
|
349
|
-
|
|
350
|
-
Use it for any content that needs a comfortable measure: prose articles, TOC lists, form fields, callouts. Avoid using it for full-bleed surfaces.
|
|
351
|
-
|
|
352
|
-
## List Helpers
|
|
353
|
-
|
|
354
|
-
### List Reset
|
|
355
|
-
|
|
356
|
-
`.list-reset` removes the native list chrome (margin, padding, markers) from any list. It is the generic version of `.nav-list` — use `.nav-list` when the list is a navigation list for semantic intent, and `.list-reset` for everything else (tag clouds, footer columns, related items, comment threads, embedded controls).
|
|
357
|
-
|
|
358
|
-
## Editorial Labels
|
|
359
|
-
|
|
360
|
-
### Overline
|
|
361
|
-
|
|
362
|
-
`.overline` is a small editorial label: muted color, smaller size, uppercase, slight letter-spacing. It does not impose a shape by itself. Add `.pill` for a bordered rounded chip, and combine with an intent class (`.primary`, `.success`, etc.) to tint the chip's text, border, and background.
|
|
363
|
-
|
|
364
|
-
The overline exists because categories, kicker labels, and section markers recur across editorial surfaces, and `.badge` is the wrong shape for them. `.badge` is a status indicator; `.overline` is metadata.
|
|
365
|
-
|
|
366
|
-
## Link Variants
|
|
367
|
-
|
|
368
|
-
### Plain
|
|
369
|
-
|
|
370
|
-
`.link-plain` removes the link's underline and inherits the surrounding color. Use for chrome that is conceptually a link (a card title, a table row action, a tag, a breadcrumb segment) but should not look like prose.
|
|
371
|
-
|
|
372
|
-
### Muted
|
|
373
|
-
|
|
374
|
-
`.link-muted` is a `.link-plain` that starts in the muted text color and shifts to the full text color on hover. Use for navigation lists, table of contents, and other secondary navigation surfaces.
|
|
375
|
-
|
|
376
|
-
## Non-Goals
|
|
377
|
-
|
|
378
|
-
> Utilities deliberately excluded to keep the surface small and intentional.
|
|
379
|
-
|
|
380
|
-
- No display scale such as `.block`, `.flex`, `.grid` — use layout primitives.
|
|
381
|
-
- No color scales such as `.text-primary` or `.bg-success` — intent colors belong to components and state.
|
|
382
|
-
- No breakpoint utility variants — use container queries or layout primitives.
|
|
383
|
-
- No utility variants for hover, focus, dark mode, or arbitrary selectors — use component states.
|
|
384
|
-
- No full spacing scale with every step and direction — the 17 spacing helpers above cover the most common escape hatches.
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Legacy dialog fallback — presentation for the dialog-fallback.js shim
|
|
3
|
-
* (browsers without native <dialog>: Safari <= 15.3, Firefox <= 97).
|
|
4
|
-
*
|
|
5
|
-
* Isolated in its own file so a custom bundle can opt out by dropping this
|
|
6
|
-
* import; the JS side is disabled with data-dialog-fallback="false" on the
|
|
7
|
-
* root element or on a dialog. Must load before component styles so ties
|
|
8
|
-
* such as dialog.modal's padding resolve in the component's favor.
|
|
9
|
-
*
|
|
10
|
-
* The whole file is inert on modern browsers: rules key on the
|
|
11
|
-
* .dialog-fallback marker written by the shim, so browsers that support
|
|
12
|
-
* <dialog> but not :modal are never affected either.
|
|
13
|
-
*/
|
|
14
|
-
@supports not (selector(dialog:modal)) {
|
|
15
|
-
/* Browsers without native <dialog> ship no UA dialog styles at all;
|
|
16
|
-
approximate them. :where() keeps specificity at dialog[open] level so
|
|
17
|
-
component styles such as dialog.modal still override the emulation. */
|
|
18
|
-
dialog[open]:where(.dialog-fallback) {
|
|
19
|
-
position: absolute;
|
|
20
|
-
inset-inline: 0;
|
|
21
|
-
width: -moz-fit-content;
|
|
22
|
-
width: fit-content;
|
|
23
|
-
max-inline-size: calc(100% - 2rem);
|
|
24
|
-
margin: auto;
|
|
25
|
-
padding: var(--space-4, 1em);
|
|
26
|
-
border: var(--border-width, 1px) solid var(--border, currentColor);
|
|
27
|
-
background: var(--surface-raised, #fff);
|
|
28
|
-
color: var(--text, inherit);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/* Open modally: fixed near the top (no fit-content in the block axis on
|
|
32
|
-
these browsers, so no vertical centering) and above menus, below
|
|
33
|
-
tooltips. Legacy-only layer, hence no z-index token. The spread shadow is
|
|
34
|
-
the visible backdrop: the dialog is its own stacking context, where a
|
|
35
|
-
negative z-index child paints above the element's background, so the
|
|
36
|
-
dimming cannot live on the ::before. */
|
|
37
|
-
dialog[open].is-fallback-modal {
|
|
38
|
-
position: fixed;
|
|
39
|
-
inset-block: 8vh auto;
|
|
40
|
-
max-block-size: 84vh;
|
|
41
|
-
overflow: auto;
|
|
42
|
-
z-index: 40;
|
|
43
|
-
box-shadow: 0 0 0 100vmax var(--backdrop-fill, rgb(0 0 0 / 35%));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/* Transparent stand-in for ::backdrop, hit-testing only. Clicks on it hit
|
|
47
|
-
the dialog element outside its own box, which dialog.js already treats as
|
|
48
|
-
a backdrop click, and it blocks pointer interaction with the page behind
|
|
49
|
-
the modal. */
|
|
50
|
-
dialog[open].is-fallback-modal::before {
|
|
51
|
-
content: "";
|
|
52
|
-
position: fixed;
|
|
53
|
-
inset: 0;
|
|
54
|
-
z-index: -1;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Dialog fallback — minimal per-element shim for browsers without native
|
|
3
|
-
* <dialog> support (Safari <= 15.3, Firefox <= 97, both below Baseline 2023).
|
|
4
|
-
*
|
|
5
|
-
* Standalone side-effect module: the default runtime imports it, a custom
|
|
6
|
-
* build opts out by omitting it — dialog.js has no dependency on this file
|
|
7
|
-
* and falls back to its acknowledgement alert without it. On supporting
|
|
8
|
-
* browsers importing this module registers nothing.
|
|
9
|
-
*
|
|
10
|
-
* Not a polyfill: no top layer, no focus trap, no inert background. It only
|
|
11
|
-
* provides show()/showModal()/close(), an `open` property, and the
|
|
12
|
-
* form[method=dialog] close-on-submit contract (otherwise those forms really
|
|
13
|
-
* navigate on legacy browsers), so dialog.js can run its normal wiring —
|
|
14
|
-
* backdrop click, Escape cancel, close events, focus restore, and the scroll
|
|
15
|
-
* lock all come from that wiring, not from here.
|
|
16
|
-
*
|
|
17
|
-
* Presentation lives in dialog-fallback.css, keyed on the classes written
|
|
18
|
-
* below. The simulated backdrop is split in two there: a spread box-shadow
|
|
19
|
-
* for the dimming, and a transparent ::before covering the viewport for
|
|
20
|
-
* hit-testing — clicks on it target the dialog element outside its own box,
|
|
21
|
-
* which dialog.js already treats as a backdrop click, and it blocks pointer
|
|
22
|
-
* interaction with the page behind the modal.
|
|
23
|
-
*
|
|
24
|
-
* An adopter-provided polyfill (e.g. dialog-polyfill) still wins: elements it
|
|
25
|
-
* already patched expose showModal() and are left untouched.
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import { targetFor } from "./command.js";
|
|
29
|
-
import enhance from "./enhance.js";
|
|
30
|
-
import { CLASSES } from "./selectors.js";
|
|
31
|
-
|
|
32
|
-
function supportsDialog() {
|
|
33
|
-
return (
|
|
34
|
-
typeof HTMLDialogElement !== "undefined" &&
|
|
35
|
-
typeof HTMLDialogElement.prototype.showModal === "function"
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function focusInitial(dialog) {
|
|
40
|
-
const target = dialog.querySelector("[autofocus]") || dialog;
|
|
41
|
-
if (target === dialog && !dialog.hasAttribute("tabindex")) {
|
|
42
|
-
dialog.setAttribute("tabindex", "-1");
|
|
43
|
-
}
|
|
44
|
-
target.focus?.();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Patch one dialog element with rudimentary show/showModal/close support.
|
|
49
|
-
* No-op when the element is already controllable (native or polyfilled).
|
|
50
|
-
*
|
|
51
|
-
* @param {HTMLElement} dialog A <dialog> element.
|
|
52
|
-
*/
|
|
53
|
-
export default function shimDialog(dialog) {
|
|
54
|
-
if (
|
|
55
|
-
typeof dialog.show === "function" &&
|
|
56
|
-
typeof dialog.showModal === "function" &&
|
|
57
|
-
typeof dialog.close === "function"
|
|
58
|
-
) {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const doc = dialog.ownerDocument;
|
|
63
|
-
let escController = null;
|
|
64
|
-
let savedScroll = null;
|
|
65
|
-
|
|
66
|
-
function stopEscapeListener() {
|
|
67
|
-
escController?.abort();
|
|
68
|
-
escController = null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function handleSubmit(event) {
|
|
72
|
-
const form = event.target;
|
|
73
|
-
// Read the attribute: on these browsers "dialog" is an invalid method
|
|
74
|
-
// value, so the reflected form.method property reports "get" and the
|
|
75
|
-
// form would otherwise really submit and navigate away.
|
|
76
|
-
if (form?.getAttribute?.("method")?.toLowerCase() !== "dialog") return;
|
|
77
|
-
event.preventDefault();
|
|
78
|
-
const submitter =
|
|
79
|
-
event.submitter || (doc.activeElement?.form === form ? doc.activeElement : null);
|
|
80
|
-
dialog.close(submitter?.value || "");
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function handleEscape(event) {
|
|
84
|
-
if (event.key !== "Escape") return;
|
|
85
|
-
event.preventDefault();
|
|
86
|
-
const cancel = new Event("cancel", { cancelable: true });
|
|
87
|
-
// dialog.js always claims the cancel event and decides itself whether to
|
|
88
|
-
// close; only an unwired dialog falls through to the direct close.
|
|
89
|
-
if (dialog.dispatchEvent(cancel)) {
|
|
90
|
-
dialog.close();
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function open(modal) {
|
|
95
|
-
if (dialog.open) return;
|
|
96
|
-
// Old WebKit clamps the scroll position to 0 when the root element gains
|
|
97
|
-
// overflow:hidden (the has-modal-open lock applied right after this),
|
|
98
|
-
// losing the user's place. Remember it and put it back after close.
|
|
99
|
-
const win = doc.defaultView;
|
|
100
|
-
savedScroll = modal && win ? { x: win.scrollX, y: win.scrollY } : null;
|
|
101
|
-
dialog.setAttribute("open", "");
|
|
102
|
-
dialog.classList.toggle(CLASSES.fallbackModal, modal);
|
|
103
|
-
if (modal) {
|
|
104
|
-
escController = new AbortController();
|
|
105
|
-
doc.addEventListener("keydown", handleEscape, { signal: escController.signal });
|
|
106
|
-
}
|
|
107
|
-
focusInitial(dialog);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
Object.defineProperty(dialog, "open", {
|
|
111
|
-
configurable: true,
|
|
112
|
-
get() {
|
|
113
|
-
return this.hasAttribute("open");
|
|
114
|
-
},
|
|
115
|
-
set(value) {
|
|
116
|
-
this.toggleAttribute("open", Boolean(value));
|
|
117
|
-
},
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
dialog.returnValue = "";
|
|
121
|
-
dialog.classList.add(CLASSES.dialogFallback);
|
|
122
|
-
dialog.addEventListener("submit", handleSubmit);
|
|
123
|
-
dialog.show = () => open(false);
|
|
124
|
-
dialog.showModal = () => open(true);
|
|
125
|
-
dialog.close = (returnValue = "") => {
|
|
126
|
-
if (!dialog.open) return;
|
|
127
|
-
stopEscapeListener();
|
|
128
|
-
dialog.returnValue = returnValue;
|
|
129
|
-
dialog.removeAttribute("open");
|
|
130
|
-
dialog.classList.remove(CLASSES.fallbackModal);
|
|
131
|
-
dialog.dispatchEvent(new Event("close"));
|
|
132
|
-
// After the close event: dialog.js has lifted the scroll lock and moved
|
|
133
|
-
// focus back to the trigger, so the restored position sticks.
|
|
134
|
-
if (savedScroll) {
|
|
135
|
-
doc.defaultView?.scrollTo?.(savedScroll.x, savedScroll.y);
|
|
136
|
-
savedScroll = null;
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (typeof document !== "undefined" && !supportsDialog()) {
|
|
142
|
-
enhance({
|
|
143
|
-
dialog: (el) => {
|
|
144
|
-
shimDialog(el);
|
|
145
|
-
},
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// A dialog inserted right before its trigger's click may not be scanned by
|
|
149
|
-
// enhance() yet, so shim command targets in capture phase too — before the
|
|
150
|
-
// delegated command routing resolves them.
|
|
151
|
-
document.addEventListener(
|
|
152
|
-
"click",
|
|
153
|
-
(event) => {
|
|
154
|
-
const trigger = event.target?.closest?.("button[commandfor][command]");
|
|
155
|
-
if (!trigger) return;
|
|
156
|
-
const dialog = targetFor(trigger);
|
|
157
|
-
if (dialog?.localName === "dialog") {
|
|
158
|
-
shimDialog(dialog);
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
true,
|
|
162
|
-
);
|
|
163
|
-
}
|