actual-css 0.10.0 → 0.11.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 +99 -1
- package/README.md +6 -2
- package/components.json +14 -3
- package/dist/actual.full.css +483 -128
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/docs/design-notes/enhancement-contract.md +16 -7
- package/docs/design-notes/platform-alignment.md +8 -4
- package/docs/design-notes/rich-text.md +61 -0
- package/docs/design-notes/steps.md +2 -2
- package/docs/navigation.json +4 -1
- package/docs/pages/components/aura.md +12 -13
- package/docs/pages/components/badge.md +10 -0
- package/docs/pages/components/bleed.md +5 -4
- package/docs/pages/components/button.md +1 -1
- package/docs/pages/components/chat.md +5 -0
- package/docs/pages/components/close.md +9 -3
- package/docs/pages/components/dialog.md +145 -13
- package/docs/pages/components/meter.md +2 -0
- package/docs/pages/components/pagination.md +15 -15
- package/docs/pages/components/shimmer.md +57 -0
- package/docs/pages/components/skeleton.md +2 -1
- package/docs/pages/components/spinner.md +11 -6
- package/docs/pages/components/table.md +13 -0
- package/docs/pages/enhancements/flyout.md +2 -0
- package/docs/pages/enhancements/tabs.md +4 -0
- package/docs/pages/examples/overview.md +1 -0
- package/docs/pages/forms/choice-cards.md +77 -4
- package/docs/pages/forms/combobox.md +16 -1
- package/docs/pages/forms/overview.md +4 -1
- package/docs/pages/forms/range.md +110 -5
- package/docs/pages/forms/rich-text.md +110 -0
- package/docs/pages/forms/validation.md +5 -1
- package/docs/pages/foundations/theming.md +49 -3
- package/docs/pages/foundations/tokens.md +15 -5
- package/docs/pages/guides/bootstrap.md +2 -1
- package/docs/pages/guides/design-tools.md +36 -0
- package/docs/pages/guides/introduction.md +1 -1
- package/docs/pages/guides/modular-import.md +1 -1
- package/docs/pages/guides/progressive-enhancement.md +13 -14
- package/docs/pages/guides/tailwind.md +4 -2
- package/llms.txt +4 -3
- package/package.json +6 -1
- package/reserved-classes.json +1 -0
- package/skills/actual-css/references/enhancements.md +35 -0
- package/src/cli/actual-css.js +49 -6
- package/src/css/components/avatar.css +1 -1
- package/src/css/components/bleed.css +5 -5
- package/src/css/components/button.css +14 -1
- package/src/css/components/close.css +17 -6
- package/src/css/components/flyout.css +4 -1
- package/src/css/components/meter.css +6 -3
- package/src/css/components/modal.css +123 -7
- package/src/css/components/pagination.css +3 -2
- package/src/css/components/rating.css +1 -1
- package/src/css/components/status-bar.css +2 -2
- package/src/css/components/steps.css +3 -3
- package/src/css/components/tab.css +3 -3
- package/src/css/components/table.css +6 -0
- package/src/css/components/tooltip.css +1 -1
- package/src/css/core/focus.css +14 -4
- package/src/css/core/theme.css +8 -0
- package/src/css/core/tokens.css +8 -4
- package/src/css/core/variants.css +2 -1
- package/src/css/effects/aura.css +51 -33
- package/src/css/effects/index.css +1 -0
- package/src/css/effects/shimmer.css +57 -0
- package/src/css/forms/choice-card.css +71 -23
- package/src/css/forms/control.css +2 -2
- package/src/css/forms/native.css +5 -1
- package/src/css/forms/otp.css +9 -1
- package/src/css/forms/range.css +64 -21
- package/src/js/command.js +61 -32
- package/src/js/dialog.js +8 -23
- package/src/js/dismiss.js +1 -2
- package/src/js/enhance.js +84 -123
- package/src/js/full.js +1 -0
- package/src/js/password.js +1 -8
- package/src/js/range.js +151 -0
- package/src/js/scrollspy.js +4 -5
- package/src/js/status.js +2 -10
- package/src/js/tooltip.js +11 -1
- package/src/js/validation.js +4 -10
- package/src/tooling/design-components.js +286 -0
- package/src/tooling/design-export.js +69 -0
- package/src/tooling/design-tokens.js +466 -0
- package/src/tooling/penpot-plugin/manifest.json +7 -0
- package/src/tooling/penpot-plugin/plugin.js +272 -0
- package/src/tooling/penpot-plugin/ui.html +58 -0
|
@@ -57,10 +57,11 @@ than the registration root, without crossing shadow boundaries.
|
|
|
57
57
|
|
|
58
58
|
> `data-enhance` is a generic opt-in for when HTML does not already provide an unambiguous one.
|
|
59
59
|
|
|
60
|
-
- **A token** is for a
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
- **A token** is for a named behavior whose opt-in carries no configuration,
|
|
61
|
+
whether it owns a subtree or enhances one leaf (tabs, flyout, range,
|
|
62
|
+
scrollspy, validation).
|
|
63
|
+
- **A self-describing `data-*`** is for a leaf whose attribute is simultaneously
|
|
64
|
+
the opt-in and the configuration (`data-tooltip`, `data-mask`, `data-filter`,
|
|
64
65
|
`data-context-menu`).
|
|
65
66
|
- **Elements that are their own opt-in** need nothing (`<dialog>`).
|
|
66
67
|
|
|
@@ -72,6 +73,13 @@ than the registration root, without crossing shadow boundaries.
|
|
|
72
73
|
on the same root throws; `disconnect()` releases the name; re-registration
|
|
73
74
|
after disconnect is allowed; the same token on a different root is independent.
|
|
74
75
|
|
|
76
|
+
Named registrations share one `[data-enhance]` discovery gate per root: however
|
|
77
|
+
many behaviors are registered, the observer queries that single selector and
|
|
78
|
+
each registration matches its own `[data-enhance~="name"]` against what it finds.
|
|
79
|
+
`enhance()` records share the same observer and lifecycle with their arbitrary
|
|
80
|
+
selectors. Behaviors on one element start in no guaranteed order and must not
|
|
81
|
+
depend on each other.
|
|
82
|
+
|
|
75
83
|
`enhance()` stays generic and unguarded — registering the same selector twice
|
|
76
84
|
creates two independent records that both run.
|
|
77
85
|
|
|
@@ -95,8 +103,8 @@ those registrations explicitly.
|
|
|
95
103
|
|
|
96
104
|
- Use a command for an immediate trigger-to-current-target action. Commands
|
|
97
105
|
resolve the DOM at event time and do not own element lifecycle.
|
|
98
|
-
- Use `registerEnhancement()` for
|
|
99
|
-
state for a subtree.
|
|
106
|
+
- Use `registerEnhancement()` for a named, opted-in behavior that owns listeners
|
|
107
|
+
or state for a subtree or leaf.
|
|
100
108
|
- Use `enhance()` for a self-describing leaf behavior that needs connection and
|
|
101
109
|
cleanup lifecycle without a named token.
|
|
102
110
|
- Prefer delegation when no persistent per-element state is required.
|
|
@@ -145,7 +153,8 @@ of the contract itself.
|
|
|
145
153
|
| ------------ | ------------------------------------------------ | ------------------- |
|
|
146
154
|
| `tabs` | Tab panel switching, arrow-key navigation | `.tabs` |
|
|
147
155
|
| `flyout` | Trigger → panel positioning, open/close | `.flyout` |
|
|
156
|
+
| `range` | Fill share and named-value synchronization | `.range` |
|
|
148
157
|
| `scrollspy` | Scroll-driven nav highlighting | `.scrollspy` |
|
|
149
158
|
| `validation` | Form validation, focus management, server errors | `.needs-validation` |
|
|
150
159
|
|
|
151
|
-
|
|
160
|
+
Five tokens. Everything else stays self-describing.
|
|
@@ -386,12 +386,16 @@ Prefer native controls and progressive styling.
|
|
|
386
386
|
|
|
387
387
|
The concrete signal to watch is `appearance: base-slider` with `::slider-track`, `::slider-fill`, and `::slider-thumb`. Checked on Chrome 152: `CSS.supports('appearance', 'base-slider')` and `CSS.supports('selector(::slider-fill)')` are both false, so there is nothing to enhance against yet.
|
|
388
388
|
|
|
389
|
-
Until then, `.range` intentionally omits
|
|
389
|
+
Until then, `.range` intentionally omits tick marks, and its filled track comes
|
|
390
|
+
from the `range` enhancement (one `--range-progress` share driving a
|
|
391
|
+
track gradient) rather than engine-specific pseudo-elements:
|
|
390
392
|
|
|
391
|
-
- The Chromium filled-track trick needs `overflow: hidden` on the runnable track plus a large `box-shadow` on the thumb. It collides twice with `range.css`: the thumb is wider than the track and would be clipped, and the
|
|
392
|
-
- CSS-only tick marks need a step count duplicated out of `min`/`max`/`step` into a custom property, so markup carries the same contract twice and the two can diverge. `datalist` cannot carry it either: its native ticks are part of the appearance that `appearance: none` removes, and no engine renders `<option label>` for a range.
|
|
393
|
+
- The Chromium filled-track trick needs `overflow: hidden` on the runnable track plus a large `box-shadow` on the thumb. It collides twice with `range.css`: the thumb is wider than the track and would be clipped, and the thumb's `box-shadow` is already owned by the focus ring.
|
|
394
|
+
- CSS-only tick marks need a step count duplicated out of `min`/`max`/`step` into a custom property, so markup carries the same contract twice and the two can diverge. `datalist` cannot carry it either: its native ticks are part of the appearance that `appearance: none` removes, and no engine renders `<option label>` for a range — Actual reads the datalist as data only.
|
|
393
395
|
|
|
394
|
-
When the base-slider parts become interoperable,
|
|
396
|
+
When the base-slider parts become interoperable, the `--range-progress`
|
|
397
|
+
calculation becomes a `@supports` enhancement in the shape of
|
|
398
|
+
`custom-select.css` — less engine-specific CSS, not more.
|
|
395
399
|
|
|
396
400
|
### Navigation
|
|
397
401
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Rich text: three levels and why a package demos level 2
|
|
2
|
+
|
|
3
|
+
Editing text and editing a document are different problems with different
|
|
4
|
+
costs. Actual draws the line at the document boundary, in three levels:
|
|
5
|
+
|
|
6
|
+
```text
|
|
7
|
+
1. TEXTAREA (native)
|
|
8
|
+
text, autosize, actions row, plain-text caret helpers
|
|
9
|
+
→ the composer pattern: demo/templates/composer.html
|
|
10
|
+
|
|
11
|
+
2. HOSTED RICH-TEXT PACKAGE (integration)
|
|
12
|
+
bold / italic / lists / links / quote, undo, paste cleanup,
|
|
13
|
+
structured @mentions, / snippets
|
|
14
|
+
→ the rich-text recipe: demo/templates/rich-text.html + @lekoala/rich-text
|
|
15
|
+
|
|
16
|
+
3. STRUCTURED DOCUMENT ENGINE (integration, heavier)
|
|
17
|
+
custom document nodes, annotations, collaborative state
|
|
18
|
+
→ no demo; the bridge contract from level 2 applies unchanged
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Level 1 stays a real `<textarea>`: form value, IME, undo, mobile,
|
|
22
|
+
accessibility, validation. `contenteditable="plaintext-only"` buys nothing over
|
|
23
|
+
it and loses the native form control, so the framework does not use it. The
|
|
24
|
+
Markdown round-trip some engines offer is still beta upstream and is not the
|
|
25
|
+
storage contract.
|
|
26
|
+
|
|
27
|
+
## Why a package for the level-2 demo
|
|
28
|
+
|
|
29
|
+
The demo exists to prove the bridge (tokens, pressed states, focus, invalid,
|
|
30
|
+
composer chrome), not the engine. Owning the toolbar, sanitizer, roving focus
|
|
31
|
+
and lifecycle inside the demo meant Actual did involuntarily much of the
|
|
32
|
+
editor's job. Extracting that into `@lekoala/rich-text` restored the boundary:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
Actual CSS presentation, tokens, composer chrome, dialogs, validation
|
|
36
|
+
@lekoala/rich-text textarea enhancement, Squire, sanitization, toolbar
|
|
37
|
+
semantics + keyboard, links, mentions, suggestions,
|
|
38
|
+
form/reset/readonly/disabled, lifecycle
|
|
39
|
+
application people lookup, templates, attachments, dictation, send
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`@lekoala/rich-text` owns the editor UI semantics; Actual owns its visual
|
|
43
|
+
language. Squire is an implementation detail of the integration package: it
|
|
44
|
+
owns selection, editing, paste normalisation and undo/redo, while the package
|
|
45
|
+
owns the product-facing contract around it (progressive enhancement of a real
|
|
46
|
+
`<textarea>`, mandatory sanitization, accessible generated UI, lifecycle and
|
|
47
|
+
cleanup). HTML stays the source of truth, which keeps the storage decoupled
|
|
48
|
+
and the engine choice reversible.
|
|
49
|
+
|
|
50
|
+
## What was deliberately not demoed
|
|
51
|
+
|
|
52
|
+
- A second engine demo would prove nothing more about the bridge, and one
|
|
53
|
+
candidate changes fundamental choices between minor versions — a pinned demo
|
|
54
|
+
would rot. Spikes belong in `tmp/`, not `demo/`.
|
|
55
|
+
- Plain-text caret helpers stay a textarea-level improvement at level 1.
|
|
56
|
+
Structured `@mentions` live at level 2 because they are sanitized, atomic
|
|
57
|
+
entities inserted as HTML — the demo's Snippet/Mention buttons reuse the
|
|
58
|
+
same suggestion seam as `/` and `@`, so one capability keeps two surfaces
|
|
59
|
+
without duplicating its logic.
|
|
60
|
+
- Syntax highlighting inside inputs is a search-DSL concern, not a composer
|
|
61
|
+
concern; layering it onto a textarea rebuilds a fake rich editor.
|
|
@@ -630,8 +630,8 @@ so an author who lowers `--step-min` below `--step-size` narrows the scroll
|
|
|
630
630
|
budget without collapsing items behind their own markers.
|
|
631
631
|
|
|
632
632
|
The cost of scrolling is that clipping happens at the padding edge, and a focus
|
|
633
|
-
ring is drawn outside its element's border box — `--focus-outline` is
|
|
634
|
-
|
|
633
|
+
ring is drawn outside its element's border box — `--focus-outline` is
|
|
634
|
+
`--focus-ring-width` wide at `--focus-outline-offset`, so 4px past a label that sits
|
|
635
635
|
flush with the bottom of a stacked row. A navigable row therefore reserves
|
|
636
636
|
exactly that much `padding-block`, gated on `.steps-horizontal:has(a[href])`. An
|
|
637
637
|
informational stepper — the common case, with no ring to protect — keeps its
|
package/docs/navigation.json
CHANGED
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"modular-import",
|
|
10
10
|
"progressive-enhancement",
|
|
11
11
|
"bootstrap",
|
|
12
|
-
"tailwind"
|
|
12
|
+
"tailwind",
|
|
13
|
+
"design-tools"
|
|
13
14
|
]
|
|
14
15
|
},
|
|
15
16
|
{
|
|
@@ -67,6 +68,7 @@
|
|
|
67
68
|
"pagination",
|
|
68
69
|
"progress",
|
|
69
70
|
"rating",
|
|
71
|
+
"shimmer",
|
|
70
72
|
"skeleton",
|
|
71
73
|
"spinner",
|
|
72
74
|
"steps",
|
|
@@ -80,6 +82,7 @@
|
|
|
80
82
|
"overview",
|
|
81
83
|
"controls",
|
|
82
84
|
"combobox",
|
|
85
|
+
"rich-text",
|
|
83
86
|
"labels",
|
|
84
87
|
"floating-field",
|
|
85
88
|
"states-and-sizes",
|
|
@@ -84,22 +84,22 @@ The frame color comes from an intent such as `.primary`, `.success`, or
|
|
|
84
84
|
a neutral intent produces a neutral aura. Use a chromatic intent when you want
|
|
85
85
|
a visibly luminous effect.
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
of the glow.
|
|
87
|
+
The default is a thin lit line with a faint halo. Three hooks tune how loud it
|
|
88
|
+
is: `--aura-width` sets the line, `--aura-intensity` scales both halos at once,
|
|
89
|
+
resting and breathing alike, and `--aura-duration` sets one full rotation of
|
|
90
|
+
the accent, or one breath of the glow.
|
|
91
91
|
|
|
92
92
|
```html demo
|
|
93
93
|
<div class="cluster" style="gap: 2rem">
|
|
94
|
-
<span class="aura aura-glow primary" style="--aura-intensity: 0
|
|
95
|
-
<span class="card" style="inline-size: 8rem">
|
|
94
|
+
<span class="aura aura-glow primary" style="--aura-intensity: 0">
|
|
95
|
+
<span class="card" style="inline-size: 8rem">Line only</span>
|
|
96
96
|
</span>
|
|
97
97
|
|
|
98
98
|
<span class="aura aura-glow primary">
|
|
99
99
|
<span class="card" style="inline-size: 8rem">Default</span>
|
|
100
100
|
</span>
|
|
101
101
|
|
|
102
|
-
<span class="aura aura-glow primary" style="--aura-intensity:
|
|
102
|
+
<span class="aura aura-glow primary" style="--aura-width: 3px; --aura-intensity: 1">
|
|
103
103
|
<span class="card" style="inline-size: 8rem">Loud</span>
|
|
104
104
|
</span>
|
|
105
105
|
|
|
@@ -113,11 +113,10 @@ Keep exactly one direct child and set `--aura-radius` when its shape differs
|
|
|
113
113
|
from the common button, card, alert, and badge shapes detected as a visual
|
|
114
114
|
enhancement.
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
wrapper around it.
|
|
116
|
+
A transparent or translucent child — an `.outline` alert, a `.ghost` button, a
|
|
117
|
+
glass surface — shows `--surface-opaque` beneath it, the theme's opaque
|
|
118
|
+
neutral, rather than the frame color. On a differently painted band, give the
|
|
119
|
+
child its own surface so it matches the band.
|
|
121
120
|
|
|
122
121
|
## CSS hooks
|
|
123
122
|
|
|
@@ -125,7 +124,7 @@ wrapper around it.
|
|
|
125
124
|
- `--aura-width` — frame thickness.
|
|
126
125
|
- `--aura-blur` — near halo blur; the far halo derives its own blur and its
|
|
127
126
|
spread beyond the frame from this value, so one hook scales both.
|
|
128
|
-
- `--aura-intensity` — multiplies both halo
|
|
127
|
+
- `--aura-intensity` — multiplies both halo strengths, resting and breathing.
|
|
129
128
|
It does not touch the frame itself, which stays a crisp line at any setting.
|
|
130
129
|
- `--aura-radius` — child corner radius used to derive the outer radius.
|
|
131
130
|
- `--aura-duration` — one rotation of the accent, or one breath of the glow.
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
> Compact label for counts, status, or category tags, with shared intents and variants.
|
|
4
4
|
|
|
5
|
+
A badge is **information, never a control**: counts, status, categories. It is
|
|
6
|
+
non-interactive by nature — for a selectable option use
|
|
7
|
+
[`.choice-card.chip`](../forms/choice-cards.md#chips); for a value produced by
|
|
8
|
+
a widget that can be removed (combobox multi-value), the widget owns the
|
|
9
|
+
removable tag (see [combobox](../forms/combobox.md)).
|
|
10
|
+
|
|
5
11
|
- Supports intent colors.
|
|
6
12
|
- Soft by default. Use `.solid` for counters and notification badges, or `.outline` for quieter emphasis.
|
|
7
13
|
- Empty badges are solid status dots: visual variants such as `.soft` and `.outline` do not change their fill.
|
|
@@ -180,6 +186,10 @@ Override the existing hooks when the three-step scale does not fit the content.
|
|
|
180
186
|
Use `.badge soft` for tag visuals. Add a direct [`.close`](close.md) button only when the tag can actually be removed. There is no separate chip component.
|
|
181
187
|
For a compact action or filter, use `.btn.sm` instead; badges describe content
|
|
182
188
|
and must not be turned into toggle controls merely to obtain a compact shape.
|
|
189
|
+
Never put a `.close` inside a `.choice-card.chip` label: the label is already
|
|
190
|
+
an interactive target driving its checkbox/radio, and a nested button would mix
|
|
191
|
+
two interactive targets. A removable tag next to a selectable chip is a sibling
|
|
192
|
+
(a widget tag or a `.badge` + `.close`), never a child of the label.
|
|
183
193
|
|
|
184
194
|
Inside a badge the close follows the pill's size, keeps a 24px pointer target,
|
|
185
195
|
and tucks into the padding.
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
`.bleed` lets a direct child of a padded surface reach its edges. Use it for a
|
|
4
4
|
full-width image, a colored header or footer, or a notice band.
|
|
5
5
|
|
|
6
|
-
It works inside `.card`, `.drawer`,
|
|
7
|
-
owns the padding, so place `.bleed`
|
|
8
|
-
|
|
9
|
-
`actual-css/css/components/bleed`
|
|
6
|
+
It works inside `.card`, `.drawer`, a modal's direct `<form>` or `.stack`, and
|
|
7
|
+
an accordion panel. The accordion panel owns the padding, so place `.bleed`
|
|
8
|
+
directly inside the panel, after the summary. A deeper descendant stays within
|
|
9
|
+
its wrapper. A modular build imports `actual-css/css/components/bleed`
|
|
10
|
+
alongside the surface component.
|
|
10
11
|
|
|
11
12
|
```html demo
|
|
12
13
|
<article class="card stack" style="--card-max-inline-size: 24rem">
|
|
@@ -34,6 +34,11 @@ For a live conversation, put an appropriate live-region or `role="log"` on the
|
|
|
34
34
|
conversation container according to the product's announcement needs. Do not
|
|
35
35
|
put `role="log"` on individual messages.
|
|
36
36
|
|
|
37
|
+
For the message input itself, keep a native `<textarea>` inside a surface with
|
|
38
|
+
an actions row — see the [composer template](../../demo/templates/composer.html).
|
|
39
|
+
Formatting needs a hosted editor instead — see the
|
|
40
|
+
[rich-text template](../../demo/templates/rich-text.html).
|
|
41
|
+
|
|
37
42
|
## Class reference
|
|
38
43
|
|
|
39
44
|
| Class | Description |
|
|
@@ -28,7 +28,9 @@
|
|
|
28
28
|
In a `.modal` or a `.drawer`, the close sits out of the content flow at the
|
|
29
29
|
panel's top inline-end corner. Place it as a direct child of the dialog, of its
|
|
30
30
|
top-level `form` or `header`, or of a `form` inside that header; a `.close`
|
|
31
|
-
deeper in the content keeps its own place
|
|
31
|
+
deeper in the content keeps its own place, so wrapping it in a `.cluster` keeps
|
|
32
|
+
it in the header's row. A header that hosts the corner close reserves inline
|
|
33
|
+
room so a title never runs under it. See [Dialog](dialog.md) and
|
|
32
34
|
[Drawer](drawer.md).
|
|
33
35
|
|
|
34
36
|
```html
|
|
@@ -43,6 +45,10 @@ deeper in the content keeps its own place. See [Dialog](dialog.md) and
|
|
|
43
45
|
|
|
44
46
|
- `--close-size` — inline and block size of the button; `--control-size` unless the container sets it.
|
|
45
47
|
- `--close-icon-size` — size of the X.
|
|
48
|
+
- `--close-bg` — background of the dialog corner close; `--surface-subtle` unless the dialog or a painted band sets it, e.g. `transparent` for a bare X.
|
|
49
|
+
- `--close-color` — ink of the X; `--text-muted` at the dialog corner, otherwise inherited from the context.
|
|
50
|
+
- `--close-hover-color` — ink of the dialog corner close on hover; `--text` unless a band sets its own foreground.
|
|
51
|
+
- `--close-hover-overlay` — hover and active wash that tints the fill the context gave the control; the dialog corner sets it `transparent` so a hover changes only the ink.
|
|
46
52
|
|
|
47
|
-
Alerts and badges declare both hooks on themselves, so set them on the
|
|
48
|
-
or on the button.
|
|
53
|
+
Alerts and badges declare both size hooks on themselves, so set them on the
|
|
54
|
+
container or on the button.
|
|
@@ -14,15 +14,44 @@
|
|
|
14
14
|
| `.dialog-icon` | Component | Circular intent-aware icon well. |
|
|
15
15
|
| `.close` | Composition | Icon-only close button, at the top end. |
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
## Anatomy
|
|
18
|
+
|
|
19
|
+
`.modal` is the outer surface and deliberately has no inner padding. Wrap its
|
|
20
|
+
content in a direct `<form>` or `.stack`; that wrapper owns the padding, rhythm,
|
|
21
|
+
and overflow. Put `.stack` on the wrapper, never on `dialog.modal` itself.
|
|
22
|
+
|
|
23
|
+
For a header band, place `<header>` directly under `dialog.modal`, before the
|
|
24
|
+
body wrapper. Put a flexible `.media` title region first and a `.cluster` of
|
|
25
|
+
controls second. The title has the close control's minimum height; its leading
|
|
26
|
+
icon and every trailing control share the same center axis. A wrapped title
|
|
27
|
+
simply makes the row taller.
|
|
28
|
+
|
|
29
|
+
A direct `dialog > header` is dialog chrome and forms a distinct header band.
|
|
30
|
+
Put an ordinary content heading inside the padded `form` or `.stack` instead.
|
|
31
|
+
|
|
32
|
+
Keep a corner [`.close`](close.md) as a direct child of the dialog and put the
|
|
33
|
+
title in the wrapper's direct `<header>`. The button then stays outside the
|
|
34
|
+
content flow while the header reserves enough inline space to keep its title
|
|
35
|
+
clear.
|
|
36
|
+
|
|
37
|
+
A `.close` placed as a direct child of the chrome header is still the corner
|
|
38
|
+
overlay. That header reserves the same inline room for it, never below its own
|
|
39
|
+
`--modal-pad`, so a wrapping title stays clear either way.
|
|
40
|
+
|
|
41
|
+
The corner close overlays the panel edge, so a body tall enough to scroll would
|
|
42
|
+
run its scrollbar under the button. Give such a dialog the `scrollable` anatomy:
|
|
43
|
+
the close then sits over the fixed header and the scrollbar starts below it.
|
|
44
|
+
|
|
45
|
+
Add `.bleed` to that header when it should form a full-width band. It reaches
|
|
46
|
+
the dialog edges, keeps the modal's inner padding, and can take a surface token
|
|
47
|
+
such as `background: var(--surface-subtle)`.
|
|
20
48
|
|
|
21
49
|
## Usage
|
|
22
50
|
|
|
23
51
|
Modals use the platform-native `<dialog class="modal">` element with `commandfor` and `command` buttons.
|
|
24
52
|
|
|
25
|
-
Use `command="show-modal"` to open a modal dialog
|
|
53
|
+
Use `command="show-modal"` to open a modal dialog, or `command="--show"` to open
|
|
54
|
+
it non-modally.
|
|
26
55
|
|
|
27
56
|
Add `aria-haspopup="dialog"` and `aria-controls="<id>"` to opening buttons so
|
|
28
57
|
their semantics are present before either native or framework JavaScript runs.
|
|
@@ -84,7 +113,8 @@ For a compact destructive confirmation with a leading status icon, compose
|
|
|
84
113
|
`dialog-confirmation` with the media object. The icon well accepts the shared
|
|
85
114
|
intent and emphasis classes; `dialog-icon danger soft` creates the tinted red
|
|
86
115
|
circle while keeping the glyph centered. The footer becomes a separate action
|
|
87
|
-
band
|
|
116
|
+
band whose two actions sit at its edges, so the cancel-style control never sits
|
|
117
|
+
next to the committing one.
|
|
88
118
|
|
|
89
119
|
```html demo
|
|
90
120
|
<button class="btn danger"
|
|
@@ -120,6 +150,49 @@ band without changing the semantics of the form or its buttons.
|
|
|
120
150
|
</dialog>
|
|
121
151
|
```
|
|
122
152
|
|
|
153
|
+
For a trailing illustration, use a direct `<header>` as the message region and
|
|
154
|
+
compose it as a non-wrapping split cluster. `.grow` keeps the text column
|
|
155
|
+
flexible while `.dialog-icon` stays at the inline end. The confirmation variant
|
|
156
|
+
pads either this header or the leading-icon `.media` shape above.
|
|
157
|
+
|
|
158
|
+
Use the same anatomy in a `.card` instead when the surface belongs in page
|
|
159
|
+
content and does not need modal focus or a backdrop.
|
|
160
|
+
|
|
161
|
+
```html demo
|
|
162
|
+
<button class="btn"
|
|
163
|
+
type="button"
|
|
164
|
+
commandfor="customize-dialog"
|
|
165
|
+
command="show-modal"
|
|
166
|
+
aria-haspopup="dialog"
|
|
167
|
+
aria-controls="customize-dialog">
|
|
168
|
+
Customize workspace
|
|
169
|
+
</button>
|
|
170
|
+
|
|
171
|
+
<dialog class="modal dialog-confirmation"
|
|
172
|
+
id="customize-dialog"
|
|
173
|
+
closedby="none"
|
|
174
|
+
aria-labelledby="customize-dialog-title">
|
|
175
|
+
<form method="dialog">
|
|
176
|
+
<header class="cluster"
|
|
177
|
+
style="--cluster-align: start; --cluster-justify: space-between; --cluster-wrap: nowrap">
|
|
178
|
+
<hgroup class="grow">
|
|
179
|
+
<h3 id="customize-dialog-title">Make this workspace your own</h3>
|
|
180
|
+
<p class="muted">Choose colors and defaults now, or continue with the current settings.</p>
|
|
181
|
+
</hgroup>
|
|
182
|
+
|
|
183
|
+
<span class="dialog-icon success soft" aria-hidden="true">
|
|
184
|
+
<i class="ti ti-check"></i>
|
|
185
|
+
</span>
|
|
186
|
+
</header>
|
|
187
|
+
|
|
188
|
+
<footer>
|
|
189
|
+
<button class="btn primary" value="customize">Customize</button>
|
|
190
|
+
<button class="btn ghost" value="later">Later</button>
|
|
191
|
+
</footer>
|
|
192
|
+
</form>
|
|
193
|
+
</dialog>
|
|
194
|
+
```
|
|
195
|
+
|
|
123
196
|
## Information modal
|
|
124
197
|
|
|
125
198
|
Use this shape for contextual information or lightweight secondary content. It
|
|
@@ -162,6 +235,67 @@ has no action button; the header close button dismisses the dialog.
|
|
|
162
235
|
</dialog>
|
|
163
236
|
```
|
|
164
237
|
|
|
238
|
+
## Action modal
|
|
239
|
+
|
|
240
|
+
Use this shape when contextual information leads to one non-destructive next
|
|
241
|
+
step, such as reviewing a setup before continuing. Unlike an alert dialog, it
|
|
242
|
+
can be dismissed without choosing the action; unlike an information modal, it
|
|
243
|
+
has a primary action in a footer.
|
|
244
|
+
|
|
245
|
+
Use `.dialog-icon` with the media object for a leading title icon. An `.alert`
|
|
246
|
+
belongs in the body only when the dialog contains a distinct status message;
|
|
247
|
+
the dialog title itself is not an alert.
|
|
248
|
+
|
|
249
|
+
```html demo
|
|
250
|
+
<button class="btn primary"
|
|
251
|
+
type="button"
|
|
252
|
+
commandfor="setup-dialog"
|
|
253
|
+
command="show-modal"
|
|
254
|
+
aria-haspopup="dialog"
|
|
255
|
+
aria-controls="setup-dialog">
|
|
256
|
+
Continue setup
|
|
257
|
+
</button>
|
|
258
|
+
|
|
259
|
+
<dialog class="modal"
|
|
260
|
+
id="setup-dialog"
|
|
261
|
+
closedby="any"
|
|
262
|
+
data-dialog-dismissible
|
|
263
|
+
aria-labelledby="setup-dialog-title">
|
|
264
|
+
<header>
|
|
265
|
+
<div class="media items-center grow">
|
|
266
|
+
<span class="dialog-icon warning soft" aria-hidden="true">
|
|
267
|
+
<i class="ti ti-alert-triangle"></i>
|
|
268
|
+
</span>
|
|
269
|
+
|
|
270
|
+
<h2 id="setup-dialog-title">Finish workspace setup</h2>
|
|
271
|
+
</div>
|
|
272
|
+
|
|
273
|
+
<div class="cluster">
|
|
274
|
+
<button class="close"
|
|
275
|
+
type="button"
|
|
276
|
+
commandfor="setup-dialog"
|
|
277
|
+
command="request-close"
|
|
278
|
+
aria-controls="setup-dialog"
|
|
279
|
+
aria-label="Close dialog"></button>
|
|
280
|
+
</div>
|
|
281
|
+
</header>
|
|
282
|
+
|
|
283
|
+
<form method="dialog" class="stack">
|
|
284
|
+
<div class="card stack">
|
|
285
|
+
<p><strong>Acme workspace</strong></p>
|
|
286
|
+
<p>Team plan</p>
|
|
287
|
+
<p>Starts October 1</p>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<p>Confirm these settings to finish setting up the workspace.</p>
|
|
291
|
+
|
|
292
|
+
<footer>
|
|
293
|
+
<button class="btn primary inline-size-full" value="continue">Continue</button>
|
|
294
|
+
</footer>
|
|
295
|
+
</form>
|
|
296
|
+
</dialog>
|
|
297
|
+
```
|
|
298
|
+
|
|
165
299
|
## Scrollable modal
|
|
166
300
|
|
|
167
301
|
Use `modal scrollable` when the header and footer should stay visible while the
|
|
@@ -297,7 +431,9 @@ Available options:
|
|
|
297
431
|
light dismiss and closes the dialog (or rewrites `closedby="any"` to
|
|
298
432
|
`closedby="closerequest"` so the native dialog does not double-handle).
|
|
299
433
|
It never affects Escape or explicit close requests.
|
|
300
|
-
* `data-dialog-modal="false"`
|
|
434
|
+
* `data-dialog-modal="false"` makes direct `openDialog()` calls use `show()`
|
|
435
|
+
instead of `showModal()`. The `show-modal` and `--show` commands override
|
|
436
|
+
this default.
|
|
301
437
|
* `data-dialog-view-transition` enables a view transition that morphs the dialog to/from its trigger. Only active when the browser supports `document.startViewTransition` and the user allows motion.
|
|
302
438
|
* `closedby` keeps its native meaning: `"any"` closes on backdrop click and
|
|
303
439
|
Escape, `"closerequest"` closes on Escape only, `"none"` disables both. The
|
|
@@ -368,20 +504,16 @@ Prefer native dialog behavior whenever possible. The framework runtime should
|
|
|
368
504
|
not replace the platform modal system; it should only make dialogs declarative,
|
|
369
505
|
animation-friendly, and consistent across supported browsers.
|
|
370
506
|
|
|
371
|
-
The close button is anchored to the dialog surface, not the content flow: it
|
|
372
|
-
sits at the top `inline-end`, inside the panel. The button itself is out of
|
|
373
|
-
flow, while the header reserves enough inline padding to keep its title clear.
|
|
374
|
-
Keep the button a direct child of `dialog.modal` so the scrolling content
|
|
375
|
-
(`> form` or `> .stack`) never competes with it.
|
|
376
|
-
|
|
377
507
|
## CSS hooks
|
|
378
508
|
|
|
379
509
|
- `--modal-size` — maximum dialog width. Fallback-only, so a class on the
|
|
380
510
|
dialog or an inherited value both reach it. A dialog is shrink-to-fit: a
|
|
381
511
|
composition that must fill this width sets `inline-size` as well.
|
|
512
|
+
- `--modal-pad` — padding of the direct `<form>` or `.stack` content wrapper.
|
|
513
|
+
- `--modal-header-bg` — background of a direct header band.
|
|
382
514
|
- `--dialog-viewport-gap` — distance kept between the dialog and the viewport edges.
|
|
383
515
|
- `--dialog-icon-size` — diameter of the `.dialog-icon` circle.
|
|
384
516
|
- `--dialog-icon-glyph-size` — size of the glyph centered inside `.dialog-icon`.
|
|
385
517
|
- `--close-size` / `--close-icon-size` — size of the corner `.close` and of its
|
|
386
|
-
X. The header reserves the close size
|
|
518
|
+
X. The header reserves the close size, its edge inset, and a content gap so
|
|
387
519
|
the title never runs under it.
|
|
@@ -70,6 +70,8 @@ marker to that background rather than to the neutral intent.
|
|
|
70
70
|
|
|
71
71
|
### Hooks
|
|
72
72
|
|
|
73
|
+
- `--meter-track` — track color; raise it when the meter sits on a
|
|
74
|
+
`--surface-subtle` card, where the default track would disappear.
|
|
73
75
|
- `--bar-height` — bar thickness, shared with `.progress`.
|
|
74
76
|
- `--meter-value` — relative weight of a direct child in a segmented meter.
|
|
75
77
|
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
- Use `aria-current="page"` for the current page.
|
|
8
8
|
- Put `.sm` or `.lg` on `.pagination` to scale its controls.
|
|
9
9
|
- Numeric links may use `aria-label="Page N"` for clearer screen reader output.
|
|
10
|
-
- Prefer text labels for Previous and Next. Icon-only controls need an accessible name.
|
|
11
|
-
-
|
|
10
|
+
- Prefer text labels for Previous and Next; a chevron icon may accompany them. Icon-only controls need an accessible name.
|
|
11
|
+
- Compose page links with `.btn.ghost`; `aria-current="page"` fills the current one, so it is the only boxed item.
|
|
12
12
|
- Arrows, page numbers, and truncation ellipses share one geometric system: every item gets the same minimum footprint, and wider content (100, 1000, text labels) grows past it naturally.
|
|
13
13
|
- Truncated ranges are plain text with `.muted` and `aria-hidden="true"`, not `.btn` — they are decorative, not actionable.
|
|
14
14
|
|
|
@@ -24,12 +24,12 @@
|
|
|
24
24
|
```html demo
|
|
25
25
|
<nav aria-label="Pagination">
|
|
26
26
|
<ol class="pagination sm">
|
|
27
|
-
<li><a href="?page=2" class="btn
|
|
28
|
-
<li><a href="?page=1" class="btn
|
|
29
|
-
<li><a href="?page=2" class="btn
|
|
30
|
-
<li><a href="?page=3" class="btn" aria-current="page" aria-label="Page 3">3</a></li>
|
|
31
|
-
<li><a href="?page=4" class="btn
|
|
32
|
-
<li><a href="?page=5" class="btn
|
|
27
|
+
<li><a href="?page=2" class="btn ghost" rel="prev"><i class="ti ti-chevron-left" aria-hidden="true"></i> Previous</a></li>
|
|
28
|
+
<li><a href="?page=1" class="btn ghost" aria-label="Page 1">1</a></li>
|
|
29
|
+
<li><a href="?page=2" class="btn ghost" aria-label="Page 2">2</a></li>
|
|
30
|
+
<li><a href="?page=3" class="btn ghost" aria-current="page" aria-label="Page 3">3</a></li>
|
|
31
|
+
<li><a href="?page=4" class="btn ghost" aria-label="Page 4">4</a></li>
|
|
32
|
+
<li><a href="?page=5" class="btn ghost" rel="next">Next <i class="ti ti-chevron-right" aria-hidden="true"></i></a></li>
|
|
33
33
|
</ol>
|
|
34
34
|
</nav>
|
|
35
35
|
```
|
|
@@ -42,15 +42,15 @@ buttons — they are decorative, not actionable.
|
|
|
42
42
|
```html demo
|
|
43
43
|
<nav aria-label="Pagination">
|
|
44
44
|
<ol class="pagination sm">
|
|
45
|
-
<li><a href="?page=
|
|
46
|
-
<li><a href="?page=1" class="btn
|
|
45
|
+
<li><a href="?page=7" class="btn ghost" rel="prev"><i class="ti ti-chevron-left" aria-hidden="true"></i> Previous</a></li>
|
|
46
|
+
<li><a href="?page=1" class="btn ghost" aria-label="Page 1">1</a></li>
|
|
47
47
|
<li><span class="muted" aria-hidden="true">…</span></li>
|
|
48
|
-
<li><a href="?page=7" class="btn
|
|
49
|
-
<li><a href="?page=8" class="btn" aria-current="page" aria-label="Page 8">8</a></li>
|
|
50
|
-
<li><a href="?page=9" class="btn
|
|
48
|
+
<li><a href="?page=7" class="btn ghost" aria-label="Page 7">7</a></li>
|
|
49
|
+
<li><a href="?page=8" class="btn ghost" aria-current="page" aria-label="Page 8">8</a></li>
|
|
50
|
+
<li><a href="?page=9" class="btn ghost" aria-label="Page 9">9</a></li>
|
|
51
51
|
<li><span class="muted" aria-hidden="true">…</span></li>
|
|
52
|
-
<li><a href="?page=20" class="btn
|
|
53
|
-
<li><a href="?page=9" class="btn
|
|
52
|
+
<li><a href="?page=20" class="btn ghost" aria-label="Page 20">20</a></li>
|
|
53
|
+
<li><a href="?page=9" class="btn ghost" rel="next">Next <i class="ti ti-chevron-right" aria-hidden="true"></i></a></li>
|
|
54
54
|
</ol>
|
|
55
55
|
</nav>
|
|
56
56
|
```
|