actual-css 0.4.0 → 0.4.2
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 +84 -35
- package/dist/actual.css +1 -20
- package/dist/actual.full.css +692 -221
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/dist/actual.min.css +1 -1
- package/docs/design-notes/app-ui.md +33 -0
- package/docs/design-notes/enhancement-contract.md +25 -0
- package/docs/design-notes/grid.md +177 -4
- package/docs/design-notes/references.md +1 -1
- package/docs/navigation.json +7 -1
- package/docs/pages/components/alert.md +5 -0
- package/docs/pages/components/app-navigation.md +34 -0
- package/docs/pages/components/avatar.md +10 -0
- package/docs/pages/components/badge.md +2 -0
- package/docs/pages/components/button.md +30 -0
- package/docs/pages/components/card.md +5 -1
- package/docs/pages/components/fab.md +2 -0
- package/docs/pages/components/indicator.md +52 -0
- package/docs/pages/components/list.md +62 -0
- package/docs/pages/components/navbar.md +6 -0
- package/docs/pages/components/rating.md +68 -0
- package/docs/pages/components/steps.md +57 -0
- package/docs/pages/enhancements/flyout.md +2 -2
- package/docs/pages/enhancements/tabs.md +1 -2
- package/docs/pages/examples/overview.md +8 -0
- package/docs/pages/forms/otp.md +32 -0
- package/docs/pages/forms/states-and-sizes.md +3 -3
- package/docs/pages/guides/integrating-actual-css.md +6 -0
- package/docs/pages/layout/app-layout.md +57 -0
- package/docs/pages/layout/app-shell.md +28 -7
- package/docs/pages/layout/center.md +12 -1
- package/docs/pages/layout/choosing.md +15 -0
- package/docs/pages/layout/grid.md +41 -8
- package/docs/pages/layout/sidebar-layout.md +19 -0
- package/docs/pages/layout/stack.md +17 -0
- package/docs/pages/patterns/icon-slot.md +61 -0
- package/docs/pages/patterns/structured-lists.md +38 -40
- package/package.json +3 -2
- package/scripts/reserved-classes.json +17 -0
- package/src/css/components/actions.css +1 -0
- package/src/css/components/app-nav.css +76 -0
- package/src/css/components/avatar.css +4 -0
- package/src/css/components/badge.css +1 -13
- package/src/css/components/breadcrumb.css +1 -0
- package/src/css/components/button.css +8 -38
- package/src/css/components/card.css +11 -11
- package/src/css/components/chat.css +0 -6
- package/src/css/components/drawer.css +0 -6
- package/src/css/components/fab.css +1 -7
- package/src/css/components/index.css +5 -0
- package/src/css/components/indicator.css +47 -0
- package/src/css/components/list.css +83 -0
- package/src/css/components/menu.css +0 -7
- package/src/css/components/modal.css +0 -17
- package/src/css/components/navbar.css +8 -15
- package/src/css/components/pagination.css +1 -0
- package/src/css/components/rating.css +101 -0
- package/src/css/components/steps.css +123 -0
- package/src/css/components/tab.css +21 -22
- package/src/css/components/tooltip.css +1 -10
- package/src/css/core/focus.css +0 -8
- package/src/css/core/theme.css +0 -10
- package/src/css/core/tokens.css +1 -2
- package/src/css/effects/aura.css +1 -10
- package/src/css/forms/choice-card.css +6 -15
- package/src/css/forms/form-actions.css +1 -0
- package/src/css/forms/otp.css +7 -2
- package/src/css/forms/range.css +14 -4
- package/src/css/layout/app-layout.css +102 -0
- package/src/css/layout/app-shell.css +1 -1
- package/src/css/layout/center.css +1 -0
- package/src/css/layout/cluster.css +1 -0
- package/src/css/layout/container-query.css +6 -4
- package/src/css/layout/grid.css +25 -11
- package/src/css/layout/index.css +4 -2
- package/src/css/layout/scroll-snap.css +1 -0
- package/src/css/layout/sidebar-layout.css +23 -1
- package/src/css/layout/switcher.css +1 -0
- package/src/css/layout/topbar.css +1 -0
- package/src/css/typography/prose.css +4 -0
- package/src/js/tooltip.js +2 -1
- package/docs/pages/layout/list.md +0 -41
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Rating
|
|
2
|
+
|
|
3
|
+
> Integer star rating built from native radio inputs.
|
|
4
|
+
|
|
5
|
+
Each star is a real `input[type="radio"]`, so submission, `required`, disabled
|
|
6
|
+
state, form reset, and keyboard navigation remain native. Keep values and DOM
|
|
7
|
+
order ascending. The cumulative fill and hover preview use `:has()` as a
|
|
8
|
+
progressive enhancement; older supported browsers show only the selected star
|
|
9
|
+
filled while the control remains fully functional.
|
|
10
|
+
|
|
11
|
+
## Class reference
|
|
12
|
+
|
|
13
|
+
| Class | Kind | Description |
|
|
14
|
+
|---------------|-----------|-------------------------------------|
|
|
15
|
+
| `.rating` | Component | Radio group presented as stars. |
|
|
16
|
+
| `.sm` / `.lg` | Size | Smaller or larger star geometry. |
|
|
17
|
+
|
|
18
|
+
```html demo
|
|
19
|
+
<fieldset class="field">
|
|
20
|
+
<legend class="field-label">Rate your experience</legend>
|
|
21
|
+
<div class="rating">
|
|
22
|
+
<input type="radio" name="experience" value="1" aria-label="1 star" />
|
|
23
|
+
<input type="radio" name="experience" value="2" aria-label="2 stars" />
|
|
24
|
+
<input type="radio" name="experience" value="3" aria-label="3 stars" checked />
|
|
25
|
+
<input type="radio" name="experience" value="4" aria-label="4 stars" />
|
|
26
|
+
<input type="radio" name="experience" value="5" aria-label="5 stars" />
|
|
27
|
+
</div>
|
|
28
|
+
</fieldset>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Use a visible `<legend>` to name the group and an `aria-label` on every radio
|
|
32
|
+
to name its value. Add `required` to the radios when the form must receive a
|
|
33
|
+
rating. In forced-colors mode the stars deliberately return to native platform
|
|
34
|
+
radios so checked and focus states remain unambiguous.
|
|
35
|
+
|
|
36
|
+
Intent classes change the active color without changing the interaction model.
|
|
37
|
+
|
|
38
|
+
```html demo
|
|
39
|
+
<div class="cluster">
|
|
40
|
+
<fieldset class="field">
|
|
41
|
+
<legend class="field-label">Small rating</legend>
|
|
42
|
+
<div class="rating primary sm">
|
|
43
|
+
<input type="radio" name="small-rating" value="1" aria-label="1 star" />
|
|
44
|
+
<input type="radio" name="small-rating" value="2" aria-label="2 stars" checked />
|
|
45
|
+
<input type="radio" name="small-rating" value="3" aria-label="3 stars" />
|
|
46
|
+
</div>
|
|
47
|
+
</fieldset>
|
|
48
|
+
|
|
49
|
+
<fieldset class="field">
|
|
50
|
+
<legend class="field-label">Large rating</legend>
|
|
51
|
+
<div class="rating danger lg">
|
|
52
|
+
<input type="radio" name="large-rating" value="1" aria-label="1 star" />
|
|
53
|
+
<input type="radio" name="large-rating" value="2" aria-label="2 stars" />
|
|
54
|
+
<input type="radio" name="large-rating" value="3" aria-label="3 stars" />
|
|
55
|
+
<input type="radio" name="large-rating" value="4" aria-label="4 stars" checked />
|
|
56
|
+
<input type="radio" name="large-rating" value="5" aria-label="5 stars" />
|
|
57
|
+
</div>
|
|
58
|
+
</fieldset>
|
|
59
|
+
</div>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Hooks
|
|
63
|
+
|
|
64
|
+
- `--rating-size` — size of each star; `.sm` and `.lg` set this hook.
|
|
65
|
+
- `--rating-gap` — visual space between stars without creating inactive hover zones.
|
|
66
|
+
- `--rating-color` — star color. Prefer an intent class for semantic color.
|
|
67
|
+
- `--rating-empty` — color of unselected stars; use a surface or border token.
|
|
68
|
+
- `--rating-star` — mask image used to draw a star; override it when a strict CSP disallows data URLs.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Steps
|
|
2
|
+
|
|
3
|
+
> Semantic ordered-list progression for a multi-step process.
|
|
4
|
+
|
|
5
|
+
Use an `<ol>` because order is part of the meaning. Add `.complete` to finished
|
|
6
|
+
steps and `aria-current="step"` to exactly one current step. Unmarked items are
|
|
7
|
+
future steps. The component needs no JavaScript.
|
|
8
|
+
|
|
9
|
+
## Class reference
|
|
10
|
+
|
|
11
|
+
| Class | Kind | Description |
|
|
12
|
+
|-------------------------|-----------|--------------------------------------------|
|
|
13
|
+
| `.steps` | Component | Ordered progression with numbered markers. |
|
|
14
|
+
| `.complete` | State | A finished step. |
|
|
15
|
+
| `[aria-current="step"]` | State | The current step. |
|
|
16
|
+
|
|
17
|
+
```html demo
|
|
18
|
+
<ol class="steps">
|
|
19
|
+
<li class="complete">Account</li>
|
|
20
|
+
<li aria-current="step">Payment</li>
|
|
21
|
+
<li>Confirm</li>
|
|
22
|
+
</ol>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Intrinsic responsiveness
|
|
26
|
+
|
|
27
|
+
`.steps` fills its row when the steps fit and holds each item at `--step-min`
|
|
28
|
+
otherwise, so the sequence scrolls horizontally only when it genuinely cannot
|
|
29
|
+
fit — no breakpoint. The threshold is space-driven: how many steps fit depends
|
|
30
|
+
on their number and the configurable minimum item width `--step-min`. Labels
|
|
31
|
+
wrap inside their item when needed.
|
|
32
|
+
|
|
33
|
+
Because `.steps` is an `<ol>` that can scroll horizontally, its overflow is not
|
|
34
|
+
focusable by default in every browser. Prefer a `--step-min` that fits the
|
|
35
|
+
common case, and only give a step flow `tabindex="0"` when your content
|
|
36
|
+
genuinely needs keyboard access to the overflow — the framework adds neither
|
|
37
|
+
JavaScript nor a tabindex automatically.
|
|
38
|
+
|
|
39
|
+
Keep state classes aligned with the real process state; they are visual, while
|
|
40
|
+
`aria-current="step"` also exposes the current position to assistive technology.
|
|
41
|
+
Do not put links or buttons around a step unless the workflow genuinely allows
|
|
42
|
+
the user to navigate to it.
|
|
43
|
+
|
|
44
|
+
### Hooks
|
|
45
|
+
|
|
46
|
+
- `--step-size` — marker diameter.
|
|
47
|
+
- `--step-min` — minimum item width; below this the sequence scrolls instead of
|
|
48
|
+
squishing labels.
|
|
49
|
+
- `--step-gap` — space between each marker and label.
|
|
50
|
+
- `--step-line-size` — connector thickness.
|
|
51
|
+
- `--step-marker-radius` — marker corner radius (round by default).
|
|
52
|
+
- `--step-connector` — connector background; when set it overrides the color
|
|
53
|
+
(defaults to each step's own line, so a `.complete` step's connector follows
|
|
54
|
+
its selected color). A plain color by default, but any background (e.g. a
|
|
55
|
+
dashed `repeating-linear-gradient`) works.
|
|
56
|
+
- `--step-complete-mark` — replaces the number on a `.complete` step with any
|
|
57
|
+
`content` value (e.g. `"✓"`); defaults to the step number.
|
|
@@ -350,12 +350,12 @@ delay; a number sets the delay in milliseconds.
|
|
|
350
350
|
More
|
|
351
351
|
</button>
|
|
352
352
|
</div>
|
|
353
|
-
<p class="
|
|
353
|
+
<p class="muted">Right click a detail below, press the context-menu key, or use More.</p>
|
|
354
354
|
<div class="cluster">
|
|
355
355
|
<span class="badge" data-context-item="File name">File.pdf</span>
|
|
356
356
|
<span class="badge" data-context-item="Owner">Design team</span>
|
|
357
357
|
</div>
|
|
358
|
-
<output id="context-result" class="
|
|
358
|
+
<output id="context-result" class="muted" aria-live="polite">No context selected.</output>
|
|
359
359
|
|
|
360
360
|
<menu class="flyout menu"
|
|
361
361
|
id="file-actions"
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
- A `.tab` with an icon uses `--tab-gap` (default `0.375em`) for the space between icon and label.
|
|
21
21
|
|
|
22
22
|
```html demo
|
|
23
|
-
<div class="
|
|
24
|
-
<div class="tabs" data-enhance="tabs" role="tablist" aria-label="Settings">
|
|
23
|
+
<div class="tabs" data-enhance="tabs" role="tablist" aria-label="Settings">
|
|
25
24
|
<button class="tab primary"
|
|
26
25
|
type="button"
|
|
27
26
|
role="tab"
|
|
@@ -26,6 +26,14 @@ and `actual.full.js` from `dist/`, plus the theme palettes bundle from
|
|
|
26
26
|
|
|
27
27
|
Full sites demo living in `demo/sites`.
|
|
28
28
|
|
|
29
|
+
### Mobile PWA
|
|
30
|
+
|
|
31
|
+
[Actual Tasks](../../demo/sites/actual-tasks/index.html) is a mobile task demo
|
|
32
|
+
with one bottom/side navigation landmark, application lists, tabs,
|
|
33
|
+
dialog validation, project cards, an activity feed, loading and empty states,
|
|
34
|
+
theme preferences, switches, and passive status feedback. Its local
|
|
35
|
+
stylesheet includes a rule-by-rule framework friction audit.
|
|
36
|
+
|
|
29
37
|
### Admin application
|
|
30
38
|
|
|
31
39
|
`demo/sites/admini/` is a complete admin interface: application shell, navigation,
|
package/docs/pages/forms/otp.md
CHANGED
|
@@ -40,6 +40,38 @@ The component follows shared `.sm` and `.lg` density through `--control-size`.
|
|
|
40
40
|
Use `aria-invalid="true"` for explicit invalid state, or place it in the
|
|
41
41
|
standard `.needs-validation.was-validated` form flow.
|
|
42
42
|
|
|
43
|
+
The `<input>` must be the **first** direct child, with one `<span>` per
|
|
44
|
+
character after it — the state rules (focus, invalid, disabled) select the
|
|
45
|
+
cells as following siblings, so that order is part of the contract.
|
|
46
|
+
|
|
47
|
+
## Disabled
|
|
48
|
+
|
|
49
|
+
Set `disabled` on the input. The cells rest on `--surface-subtle` with a muted
|
|
50
|
+
`--state-disabled` border at `--disabled-opacity`, the code glyph turns
|
|
51
|
+
`--state-disabled`, and the cursor is `not-allowed` — a clearly distinct,
|
|
52
|
+
non-interactive look that follows the shared disabled contract. A disabled
|
|
53
|
+
`<fieldset>` disables it the same way.
|
|
54
|
+
|
|
55
|
+
```html demo
|
|
56
|
+
<div class="otp" aria-label="Disabled verification code">
|
|
57
|
+
<input
|
|
58
|
+
class="input"
|
|
59
|
+
type="text"
|
|
60
|
+
autocomplete="one-time-code"
|
|
61
|
+
inputmode="numeric"
|
|
62
|
+
maxlength="6"
|
|
63
|
+
disabled
|
|
64
|
+
aria-label="Disabled verification code"
|
|
65
|
+
/>
|
|
66
|
+
<span aria-hidden="true"></span>
|
|
67
|
+
<span aria-hidden="true"></span>
|
|
68
|
+
<span aria-hidden="true"></span>
|
|
69
|
+
<span aria-hidden="true"></span>
|
|
70
|
+
<span aria-hidden="true"></span>
|
|
71
|
+
<span aria-hidden="true"></span>
|
|
72
|
+
</div>
|
|
73
|
+
```
|
|
74
|
+
|
|
43
75
|
## CSS hooks
|
|
44
76
|
|
|
45
77
|
- `--otp-cell-size` — width and height of every cell; follows `--control-size`.
|
|
@@ -96,7 +96,7 @@ type instead.
|
|
|
96
96
|
</label>
|
|
97
97
|
</div>
|
|
98
98
|
|
|
99
|
-
<div class="grid
|
|
99
|
+
<div class="grid items-start">
|
|
100
100
|
<label class="choice">
|
|
101
101
|
<input class="check" type="checkbox" />
|
|
102
102
|
<span>
|
|
@@ -133,7 +133,7 @@ type instead.
|
|
|
133
133
|
<input class="check" type="checkbox" id="indet-demo" />
|
|
134
134
|
<span>
|
|
135
135
|
<span class="field-label">Indeterminate checkbox</span>
|
|
136
|
-
<span class="field-help">Partially selected — set via <code>element.indeterminate = true</code>. Try it: <button class="btn
|
|
136
|
+
<span class="field-help">Partially selected — set via <code>element.indeterminate = true</code>. Try it: <button class="btn link sm" type="button" onclick="document.getElementById('indet-demo').indeterminate = !document.getElementById('indet-demo').indeterminate">toggle</button></span>
|
|
137
137
|
</span>
|
|
138
138
|
</label>
|
|
139
139
|
</div>
|
|
@@ -291,7 +291,7 @@ type instead.
|
|
|
291
291
|
</label>
|
|
292
292
|
<label class="choice">
|
|
293
293
|
<input class="check" type="checkbox" id="indet-size" />
|
|
294
|
-
<span>Indeterminate <button class="btn
|
|
294
|
+
<span>Indeterminate <button class="btn link sm" type="button" onclick="document.getElementById('indet-size').indeterminate = !document.getElementById('indet-size').indeterminate">toggle</button></span>
|
|
295
295
|
</label>
|
|
296
296
|
</div>
|
|
297
297
|
|
|
@@ -4,6 +4,12 @@ Use Actual CSS as a vocabulary before writing application CSS.
|
|
|
4
4
|
|
|
5
5
|
The goal is not to eliminate custom CSS. The goal is to keep framework concerns in the framework and application identity in the application.
|
|
6
6
|
|
|
7
|
+
In short: **use Actual for generic structure and behavior; add product-specific
|
|
8
|
+
classes for brand and compositions rather than forcing a generic primitive.** A
|
|
9
|
+
local rule is not a failure of Actual — overfitting the framework to a
|
|
10
|
+
product-specific look is. Reach for an Actual primitive first, and write
|
|
11
|
+
application CSS when the need is genuinely product-specific (see §6–7).
|
|
12
|
+
|
|
7
13
|
## 1. Start with the theme
|
|
8
14
|
|
|
9
15
|
Define palette values once, then derive decorative colors from them.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# App Layout
|
|
2
|
+
|
|
3
|
+
> Specialized application recipe with a topbar, independently scrolling content, and one adaptive primary navigation landmark.
|
|
4
|
+
|
|
5
|
+
Use `.app-layout` only when all three direct regions are intentional:
|
|
6
|
+
|
|
7
|
+
- `.topbar` is persistent application chrome;
|
|
8
|
+
- `.app-main` is the page's independent scroll container;
|
|
9
|
+
- `.app-nav` changes from bottom navigation to labelled side navigation.
|
|
10
|
+
|
|
11
|
+
This is not a generic wrapper for a website, dashboard, or anything described
|
|
12
|
+
as an "app." It fixes the shell to the dynamic viewport and hides overflow on
|
|
13
|
+
that shell. A public, marketing, editorial, or normally scrolling page should
|
|
14
|
+
use semantic landmarks, optionally `.app-shell` for full-height document flow,
|
|
15
|
+
and `.navbar` for top-level site navigation.
|
|
16
|
+
|
|
17
|
+
```html demo
|
|
18
|
+
<div class="app-shell app-layout" style="block-size: 30rem">
|
|
19
|
+
<header class="topbar">
|
|
20
|
+
<strong>Tasks</strong>
|
|
21
|
+
</header>
|
|
22
|
+
<main class="app-main padding-context">
|
|
23
|
+
<div class="stack">
|
|
24
|
+
<h2>Today</h2>
|
|
25
|
+
<p>The main region scrolls without displacing the application chrome.</p>
|
|
26
|
+
</div>
|
|
27
|
+
</main>
|
|
28
|
+
<nav class="app-nav" aria-label="Primary">
|
|
29
|
+
<a href="#today" aria-current="page"><span aria-hidden="true">☀</span><span>Today</span></a>
|
|
30
|
+
<a href="#tasks"><span aria-hidden="true">✓</span><span>Tasks</span></a>
|
|
31
|
+
<a href="#settings"><span aria-hidden="true">⚙</span><span>Settings</span></a>
|
|
32
|
+
</nav>
|
|
33
|
+
<div class="fab">
|
|
34
|
+
<button class="btn primary circle icon-only" type="button" aria-label="Add task">+</button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The direct-child relationship is required, not illustrative: `.app-layout`
|
|
40
|
+
assigns those children to named grid areas and changes `.app-nav` from a bottom
|
|
41
|
+
bar to labelled side navigation. Missing, renamed, or wrapped regions mean a
|
|
42
|
+
different layout contract should be used.
|
|
43
|
+
|
|
44
|
+
A direct-child `.fab` overlays the end of the main grid area. It therefore
|
|
45
|
+
clears both bottom and side navigation without copying the navigation size or
|
|
46
|
+
bottom safe-area inset. When navigation moves aside, the layout applies the
|
|
47
|
+
bottom inset at the viewport edge. Outside this recipe, FAB keeps its
|
|
48
|
+
viewport-fixed placement.
|
|
49
|
+
|
|
50
|
+
The recipe uses the dynamic viewport token and gives scrolling to `.app-main`,
|
|
51
|
+
which avoids fixed-position offsets and keeps mobile keyboard resizing within
|
|
52
|
+
the same layout contract. The topbar preserves its normal spacing below the
|
|
53
|
+
top safe-area inset. The recipe provides no routing or page lifecycle.
|
|
54
|
+
|
|
55
|
+
### Hooks
|
|
56
|
+
|
|
57
|
+
- `--app-nav-side-size` controls the side navigation track.
|
|
@@ -1,16 +1,30 @@
|
|
|
1
1
|
# App Shell
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Full-height, single-column document flow. Despite its historical name, this is
|
|
4
|
+
the general shell for ordinary pages as well as simple applications.
|
|
4
5
|
|
|
5
6
|
Header and footer are document landmarks, not required layout classes.
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
Use semantic elements by default. `.app-shell` already supplies the outer
|
|
8
|
+
column, so do not add a `.stack` solely to arrange the page landmarks.
|
|
8
9
|
|
|
9
10
|
```html demo
|
|
10
11
|
<body class="app-shell">
|
|
11
|
-
<header>
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
<header>
|
|
13
|
+
<div class="center">
|
|
14
|
+
<nav class="navbar" aria-label="Main">
|
|
15
|
+
<a class="navbar-brand" href="/">Actual CSS</a>
|
|
16
|
+
</nav>
|
|
17
|
+
</div>
|
|
18
|
+
</header>
|
|
19
|
+
<main class="grow">
|
|
20
|
+
<div class="center stack">
|
|
21
|
+
<h1>Page title</h1>
|
|
22
|
+
<p>Main content</p>
|
|
23
|
+
</div>
|
|
24
|
+
</main>
|
|
25
|
+
<footer>
|
|
26
|
+
<div class="center">Footer links</div>
|
|
27
|
+
</footer>
|
|
14
28
|
</body>
|
|
15
29
|
```
|
|
16
30
|
|
|
@@ -22,4 +36,11 @@ Use semantic elements by default.
|
|
|
22
36
|
}
|
|
23
37
|
```
|
|
24
38
|
|
|
25
|
-
|
|
39
|
+
Keep `.center` inside each full-width landmark when their backgrounds or
|
|
40
|
+
borders should span the viewport. Sticky header/footer behavior remains an
|
|
41
|
+
application choice.
|
|
42
|
+
|
|
43
|
+
Do not upgrade this shell to `.app-layout` merely because the product is an
|
|
44
|
+
application. Use App Layout only for its complete specialized contract: a
|
|
45
|
+
direct-child `.topbar`, independently scrolling `.app-main`, and direct-child
|
|
46
|
+
`.app-nav` that changes from a bottom bar to a labelled side menu.
|
|
@@ -26,4 +26,15 @@ Tune width and side padding with local variables.
|
|
|
26
26
|
|
|
27
27
|
`.center` intentionally uses `box-sizing: content-box`. The max inline size is
|
|
28
28
|
the content measure, while inline padding is added outside that measure. This is
|
|
29
|
-
deliberate even though the global reset uses `border-box`.
|
|
29
|
+
deliberate even though the global reset uses `border-box`. Its explicit inline
|
|
30
|
+
size subtracts that padding, so `.center` also keeps its available width as a
|
|
31
|
+
flex or grid item instead of collapsing to its min-content width.
|
|
32
|
+
|
|
33
|
+
Do not override it with `inline-size: 100%`: because the padding sits outside
|
|
34
|
+
the content box, that would make its outer size exceed the available width by
|
|
35
|
+
both padding edges.
|
|
36
|
+
|
|
37
|
+
As a child of `.stack`, `.center` sizes to its content measure and centers via
|
|
38
|
+
`margin-inline: auto` instead of stretching to the full row. For full-width page
|
|
39
|
+
regions, keep `.center` regions in normal block flow rather than inside a
|
|
40
|
+
`.stack` wrapper (see [stack](stack.md)).
|
|
@@ -3,6 +3,21 @@
|
|
|
3
3
|
Choose a primitive from the relationship between its children, not from the
|
|
4
4
|
number of children visible in one mockup.
|
|
5
5
|
|
|
6
|
+
## Page and application shells
|
|
7
|
+
|
|
8
|
+
| Situation | Recipe |
|
|
9
|
+
|---|---|
|
|
10
|
+
| Public, marketing, editorial, or normally scrolling page | Semantic landmarks; optionally `.app-shell`, with `.navbar` for site navigation |
|
|
11
|
+
| Persistent topbar + independently scrolling main + bottom-to-side primary navigation | `.app-layout` with its exact direct-child contract |
|
|
12
|
+
|
|
13
|
+
Do not choose `.app-layout` from the word "app" or from the presence of a
|
|
14
|
+
header. Its viewport-sized grid, hidden shell overflow, internal main scroll,
|
|
15
|
+
and adaptive `.app-nav` are one indivisible behavior. If any of those is not
|
|
16
|
+
wanted, compose the ordinary page from semantic landmarks and the primitives
|
|
17
|
+
below.
|
|
18
|
+
|
|
19
|
+
## Content relationships
|
|
20
|
+
|
|
6
21
|
| Situation | Primitive |
|
|
7
22
|
|--------------------------------------------------------------|-------------------|
|
|
8
23
|
| Vertical flow | `.stack` |
|
|
@@ -34,7 +34,7 @@ Tune the item minimum with `--grid-min`.
|
|
|
34
34
|
own: it never exceeds N columns, never overflows, and every item keeps the same
|
|
35
35
|
width.
|
|
36
36
|
|
|
37
|
-
```html demo
|
|
37
|
+
```html demo bare
|
|
38
38
|
<div class="grid-4">
|
|
39
39
|
<div class="card">Exercitation</div>
|
|
40
40
|
<div class="card">Amet mollit</div>
|
|
@@ -55,8 +55,10 @@ density is `.grid-N`'s. Choose the preset that suits your item size:
|
|
|
55
55
|
## Balanced subdivision
|
|
56
56
|
|
|
57
57
|
Left alone, a preset collapses one column at a time, so a six-item `.grid-6`
|
|
58
|
-
can pass through five columns — five items and a lone sixth.
|
|
59
|
-
|
|
58
|
+
can pass through five columns — five items and a lone sixth. The example above
|
|
59
|
+
is that bare version. Placed inside a container named **`actual-grid`** — the
|
|
60
|
+
`.container-query` helper, or a region of your own that declares
|
|
61
|
+
`container: actual-grid / inline-size` — it only ever enters a **divisor of N**:
|
|
60
62
|
|
|
61
63
|
```text
|
|
62
64
|
.grid-2 2 → 1
|
|
@@ -80,12 +82,43 @@ Every state splits the collection evenly, and all items keep the same width.
|
|
|
80
82
|
</div>
|
|
81
83
|
```
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
### Container sizes
|
|
86
|
+
|
|
87
|
+
The steps are read from the **container's content box** — not the viewport, and
|
|
88
|
+
not the grid's own width. Padding and borders on the container come off before
|
|
89
|
+
the query resolves.
|
|
90
|
+
|
|
91
|
+
| Container content box | `.grid-2` | `.grid-3` | `.grid-4` | `.grid-6` |
|
|
92
|
+
|-----------------------|-----------|-----------|-----------|-----------|
|
|
93
|
+
| under `28rem` | 1 | 1 | 1 | 1 |
|
|
94
|
+
| `28rem` to `48rem` | 2 | 1 | 2 | 2 |
|
|
95
|
+
| `48rem` to `64rem` | 2 | 3 | 2 | 3 |
|
|
96
|
+
| `64rem` and up | 2 | 3 | 4 | 6 |
|
|
97
|
+
|
|
98
|
+
Size a region that hosts a `.grid-N` so it lands on the step you want, with room
|
|
99
|
+
to spare. The step is a cliff, not a ramp: a container at `47rem` gives
|
|
100
|
+
`.grid-3` a single column, the same as one at `20rem`. `.grid-3` has no
|
|
101
|
+
intermediate state by design — it never shows `2 + 1`.
|
|
102
|
+
|
|
103
|
+
Budget backwards from the content box. A region that must reach a step needs
|
|
104
|
+
that width **plus its own padding and borders**:
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
3 columns 48rem + 2 x 1rem padding ~= 50rem outer
|
|
108
|
+
4 or 6 64rem + 2 x 1rem padding ~= 66rem outer
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Round up past the figure rather than matching it exactly, so a later padding
|
|
112
|
+
change cannot drop the region a state.
|
|
113
|
+
|
|
114
|
+
Balanced subdivision is precision, never a prerequisite for responsive
|
|
115
|
+
behavior — forgetting the container costs you the exact chain, nothing else.
|
|
116
|
+
The grid responds only to a container named `actual-grid`, never to an
|
|
117
|
+
incidental query container, so a region that sets `container-type: inline-size`
|
|
118
|
+
for its own reasons leaves the grid untouched. The container has to be an
|
|
119
|
+
ancestor: an element cannot query its own size without taking inline-size
|
|
86
120
|
containment, which would collapse the grid inside a `.cluster`, a float, or any
|
|
87
|
-
shrink-to-fit box. Put
|
|
88
|
-
the same reason.
|
|
121
|
+
shrink-to-fit box. Put it on a normal block-level wrapper for the same reason.
|
|
89
122
|
|
|
90
123
|
The chain balances the **declared density**, not your actual item count. Eight
|
|
91
124
|
items in `.grid-6` still give `6 + 2`. For a paginated collection, choose a page
|
|
@@ -22,6 +22,25 @@ otherwise stack. No `.container-query` ancestor is required.
|
|
|
22
22
|
The first child is the flexible main region and the last child is the aside.
|
|
23
23
|
Use a custom `.grid` template only when an exact editorial ratio is structural.
|
|
24
24
|
|
|
25
|
+
To place the aside on the inline start instead, add `.reverse` and put the
|
|
26
|
+
aside first in the DOM — visual, reading and keyboard order then all agree:
|
|
27
|
+
|
|
28
|
+
```html demo
|
|
29
|
+
<div class="sidebar-layout reverse">
|
|
30
|
+
<aside class="stack">
|
|
31
|
+
<h3>On this page</h3>
|
|
32
|
+
<ol class="list-reset"><li><a class="link-muted" href="#">Section</a></li></ol>
|
|
33
|
+
</aside>
|
|
34
|
+
|
|
35
|
+
<article class="stack">
|
|
36
|
+
<h1>Long-form article</h1>
|
|
37
|
+
<p>Article body that benefits from a related table of contents.</p>
|
|
38
|
+
</article>
|
|
39
|
+
</div>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
When the layout stacks (narrow space), the aside stays on top.
|
|
43
|
+
|
|
25
44
|
### Hooks
|
|
26
45
|
|
|
27
46
|
- `--sidebar-layout-size` sets the aside's preferred width.
|
|
@@ -22,6 +22,23 @@ block margins are reset, so vertical rhythm always comes from `--gap`. Inline
|
|
|
22
22
|
margins stay free — for example `margin-inline: auto` on a child centers it
|
|
23
23
|
without fighting the primitive.
|
|
24
24
|
|
|
25
|
+
## Don't wrap full-width regions with `.stack`
|
|
26
|
+
|
|
27
|
+
`.stack` is a column flex container, so a `.center` child — which sets its own
|
|
28
|
+
`inline-size` and `margin-inline: auto` — becomes a flex item that will not
|
|
29
|
+
stretch across the full row; it sizes to its content measure and centers inside
|
|
30
|
+
the flex line. For page-level full-width regions that each constrain their own
|
|
31
|
+
content, keep them in normal block flow instead:
|
|
32
|
+
|
|
33
|
+
```html
|
|
34
|
+
<header class="center">…</header>
|
|
35
|
+
<main class="center">…</main>
|
|
36
|
+
<footer class="center">…</footer>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Use `.stack` for the vertical rhythm *between* related items inside one region,
|
|
40
|
+
not as the wrapper that lays out independent full-width regions of a page.
|
|
41
|
+
|
|
25
42
|
## Choosing a density
|
|
26
43
|
|
|
27
44
|
`--gap` is the primitive's channel, and the density variants set it at the point
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Icon slot
|
|
2
|
+
|
|
3
|
+
> Contained icon zones — a leading icon, an icon disc, a status marker — appear
|
|
4
|
+
> across product UI: notifications, activity feeds, list actions, cards. There
|
|
5
|
+
> is no dedicated primitive; the same small contract is composed from existing
|
|
6
|
+
> pieces.
|
|
7
|
+
|
|
8
|
+
A contained icon zone must be a **stable box** that never shrinks, always
|
|
9
|
+
centers its glyph, and clips overflow:
|
|
10
|
+
|
|
11
|
+
- fixed `inline-size` / `block-size` (or `aspect-ratio: 1` for a disc);
|
|
12
|
+
- `display: grid; place-items: center;` for reliable centering;
|
|
13
|
+
- `flex: 0 0 auto` so a flex row never compresses it;
|
|
14
|
+
- `overflow: hidden`;
|
|
15
|
+
- a child `svg` constrained to the box.
|
|
16
|
+
|
|
17
|
+
## The contract
|
|
18
|
+
|
|
19
|
+
```css
|
|
20
|
+
.product-icon {
|
|
21
|
+
flex: 0 0 auto;
|
|
22
|
+
inline-size: 1.5rem;
|
|
23
|
+
block-size: 1.5rem;
|
|
24
|
+
display: grid;
|
|
25
|
+
place-items: center;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.product-icon svg {
|
|
30
|
+
inline-size: 100%;
|
|
31
|
+
block-size: 100%;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For a round disc, add the `.circle` utility — it supplies the `border-radius`:
|
|
36
|
+
|
|
37
|
+
```css
|
|
38
|
+
.product-icon.circle {
|
|
39
|
+
background: var(--surface-subtle);
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Reuse the existing icon slots
|
|
44
|
+
|
|
45
|
+
Several components already embody this slot — reach for them before composing
|
|
46
|
+
your own:
|
|
47
|
+
|
|
48
|
+
- `.alert-icon` — the leading icon in an alert.
|
|
49
|
+
- `.avatar` — initials or image in a fixed circular box.
|
|
50
|
+
- `.btn.icon-only` — a square icon button sized to the control height.
|
|
51
|
+
- `.input-icon` — a leading icon in a search/filter input.
|
|
52
|
+
|
|
53
|
+
```html demo
|
|
54
|
+
<div class="media">
|
|
55
|
+
<span class="avatar primary"><abbr>AM</abbr></span>
|
|
56
|
+
<div class="stack">
|
|
57
|
+
<strong>Ada Meridian</strong>
|
|
58
|
+
<span class="muted">Senior designer.</span>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
```
|