actual-css 0.9.2 → 0.11.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 +169 -1
- package/README.md +65 -44
- package/components.json +35 -14
- package/dist/actual.full.css +877 -583
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/docs/design-notes/app-ui.md +1 -1
- package/docs/design-notes/column-layout.md +103 -520
- package/docs/design-notes/enhancement-contract.md +16 -7
- package/docs/design-notes/forced-colors.md +12 -1
- package/docs/design-notes/naming.md +1 -1
- 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/design-notes/surface-reparenting.md +8 -9
- package/docs/navigation.json +4 -0
- package/docs/pages/components/accordion.md +55 -1
- package/docs/pages/components/alert.md +9 -15
- package/docs/pages/components/aura.md +12 -13
- package/docs/pages/components/badge.md +16 -7
- package/docs/pages/components/bleed.md +42 -0
- package/docs/pages/components/button.md +30 -4
- package/docs/pages/components/card.md +21 -86
- package/docs/pages/components/chat.md +5 -0
- package/docs/pages/components/close.md +48 -0
- package/docs/pages/components/data-list.md +26 -1
- package/docs/pages/components/dialog.md +21 -18
- package/docs/pages/components/drawer.md +9 -9
- package/docs/pages/components/navbar.md +5 -5
- 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 +14 -9
- package/docs/pages/components/table.md +13 -0
- package/docs/pages/enhancements/flyout.md +2 -0
- package/docs/pages/enhancements/overview.md +5 -0
- package/docs/pages/enhancements/tabs.md +4 -0
- package/docs/pages/enhancements/tooltip.md +3 -1
- package/docs/pages/examples/overview.md +2 -1
- package/docs/pages/forms/choice-cards.md +116 -4
- package/docs/pages/forms/combobox.md +39 -12
- package/docs/pages/forms/joined-controls.md +2 -5
- package/docs/pages/forms/overview.md +10 -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 +110 -18
- package/docs/pages/foundations/tokens.md +41 -13
- package/docs/pages/guides/bootstrap.md +2 -1
- package/docs/pages/guides/introduction.md +15 -23
- package/docs/pages/guides/modular-import.md +54 -7
- package/docs/pages/guides/progressive-enhancement.md +19 -17
- package/docs/pages/guides/tailwind.md +21 -7
- package/docs/pages/layout/column-layout.md +92 -54
- package/docs/pages/layout/media.md +1 -1
- package/docs/pages/layout/overview.md +1 -1
- package/docs/pages/utilities/overview.md +1 -1
- package/docs/pages/utilities/sizing-wrapping.md +5 -3
- package/llms.txt +25 -4
- package/package.json +10 -6
- package/reserved-classes.json +6 -30
- package/skills/actual-css/PROJECT.md.example +65 -0
- package/skills/actual-css/README.md +64 -0
- package/skills/actual-css/SKILL.md +234 -0
- package/skills/actual-css/references/accessibility.md +44 -0
- package/skills/actual-css/references/api-and-vocabulary.md +94 -0
- package/skills/actual-css/references/enhancements.md +59 -0
- package/skills/actual-css/references/forms.md +49 -0
- package/skills/actual-css/references/layout-and-theming.md +82 -0
- package/skills/actual-css/references/review-checklist.md +51 -0
- package/skills/actual-css/scripts/inspect-actual-css.mjs +171 -0
- package/src/css/components/accordion.css +44 -7
- package/src/css/components/alert.css +23 -43
- package/src/css/components/app-nav.css +9 -0
- package/src/css/components/avatar.css +1 -1
- package/src/css/components/badge.css +11 -39
- package/src/css/components/bleed.css +79 -0
- package/src/css/components/button.css +6 -17
- package/src/css/components/card.css +7 -40
- package/src/css/components/close.css +91 -0
- package/src/css/components/data-list.css +18 -0
- package/src/css/components/drawer.css +15 -56
- package/src/css/components/fab.css +10 -0
- package/src/css/components/flyout.css +5 -1
- package/src/css/components/index.css +2 -0
- package/src/css/components/join.css +36 -20
- package/src/css/components/menu.css +12 -0
- package/src/css/components/meter.css +1 -1
- package/src/css/components/modal.css +10 -57
- package/src/css/components/navbar.css +27 -0
- 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 +19 -3
- package/src/css/core/theme.css +33 -43
- package/src/css/core/tokens.css +9 -5
- package/src/css/core/variants.css +5 -34
- 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 +142 -24
- package/src/css/forms/choice.css +2 -3
- package/src/css/forms/control.css +17 -10
- package/src/css/forms/custom-select.css +4 -4
- package/src/css/forms/native.css +4 -4
- package/src/css/forms/otp.css +9 -1
- package/src/css/forms/range.css +72 -14
- package/src/css/forms/switch.css +2 -3
- package/src/css/layout/app-layout.css +1 -1
- package/src/css/layout/column-layout.css +26 -111
- package/src/css/utilities/extra.css +7 -6
- package/src/js/command.js +61 -32
- package/src/js/dialog.js +103 -47
- package/src/js/dismiss.js +1 -2
- package/src/js/enhance.js +91 -126
- package/src/js/escape.js +1 -0
- package/src/js/flyout.js +10 -1
- package/src/js/focus-group.js +2 -1
- package/src/js/full.js +1 -0
- package/src/js/keys.js +14 -0
- package/src/js/menu.js +2 -1
- 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 +21 -10
- package/src/js/surface.js +12 -8
- package/src/js/tab.js +2 -1
- package/src/js/tooltip.js +29 -2
- package/src/js/validation.js +8 -15
- package/dist/actual.css +0 -876
- package/dist/actual.js +0 -1
- package/dist/actual.min.css +0 -1
|
@@ -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.
|
|
@@ -30,6 +30,12 @@ an essential shape, state, or affordance.
|
|
|
30
30
|
|
|
31
31
|
## Pattern: custom property + single override
|
|
32
32
|
|
|
33
|
+
The hook-only form below is fine until an application overrides the hook: the
|
|
34
|
+
override sits at the same specificity, loads after the framework, and wins the
|
|
35
|
+
variable — leaving a non-system fill the browser then remaps to `Canvas`. For
|
|
36
|
+
a masked marker, write the forced value on the property itself so no hook
|
|
37
|
+
override can take it away (accordion) — same for range track/thumb fills.
|
|
38
|
+
|
|
33
39
|
```css
|
|
34
40
|
.component {
|
|
35
41
|
--cmp-marker-color: var(--text-muted);
|
|
@@ -48,6 +54,11 @@ an essential shape, state, or affordance.
|
|
|
48
54
|
.component {
|
|
49
55
|
--cmp-marker-color: CanvasText;
|
|
50
56
|
}
|
|
57
|
+
|
|
58
|
+
/* Masked markers: pin the property, not just the hook. */
|
|
59
|
+
.component::marker {
|
|
60
|
+
background-color: CanvasText;
|
|
61
|
+
}
|
|
51
62
|
}
|
|
52
63
|
```
|
|
53
64
|
|
|
@@ -104,7 +115,7 @@ remapping is perfect:
|
|
|
104
115
|
|
|
105
116
|
- `.card` — `background: Canvas`, `color: CanvasText` are already the defaults
|
|
106
117
|
- `.flyout` / `.menu` — `Canvas`/`CanvasText` for content, `ButtonText` for borders
|
|
107
|
-
- `.
|
|
118
|
+
- `.breadcrumb` — pure remapping gets everything right
|
|
108
119
|
- `.status-bar` — `CanvasText` on `Canvas` background
|
|
109
120
|
|
|
110
121
|
The only forced-colors rules that should remain after a full audit are the ones
|
|
@@ -47,7 +47,7 @@ never about opting in.
|
|
|
47
47
|
- **Base** (`utilities/base.css`) is a curated set of common ergonomic
|
|
48
48
|
shortcuts (`.overflow-auto`, `.gap-none`, `.text-nowrap`, `.px`, `.py`).
|
|
49
49
|
- **Extra** (`utilities/extra.css`) uses explicit property/value names
|
|
50
|
-
(`.overflow-hidden`, `.padding-context`, `.
|
|
50
|
+
(`.overflow-hidden`, `.padding-context`, `.flex-nowrap`).
|
|
51
51
|
|
|
52
52
|
Call them base and extra, after the files. "Optional" is the wrong word for
|
|
53
53
|
either: it reads as a layer an author has to opt into, which is true of every
|
|
@@ -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
|
|
@@ -20,8 +20,8 @@ every scope that reached it *by inheritance*. The panel was styled by one set of
|
|
|
20
20
|
ancestors while closed, and by a different set from the moment it became
|
|
21
21
|
visible.
|
|
22
22
|
|
|
23
|
-
Actual leans on inheritance deliberately: theme islands, density,
|
|
24
|
-
|
|
23
|
+
Actual leans on inheritance deliberately: theme islands, density, and the
|
|
24
|
+
custom properties an application scopes to its own containers all
|
|
25
25
|
travel that way. So this was not one bug about themes. It was the same bug in as
|
|
26
26
|
many forms as there are inherited contexts.
|
|
27
27
|
|
|
@@ -30,13 +30,12 @@ many forms as there are inherited contexts.
|
|
|
30
30
|
Chrome, one `.flyout` per scope, values read off the panel before and after the
|
|
31
31
|
click that opens it:
|
|
32
32
|
|
|
33
|
-
| Scope | Closed
|
|
34
|
-
| ------------------------- |
|
|
35
|
-
| `[data-theme="dark"]` | `background: rgb(30, 26, 35)`
|
|
36
|
-
| `.sm` | `--control-size: 2rem`
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
| `.sm` inside a `<dialog>` | `--control-size: 2rem` | `2.375rem` |
|
|
33
|
+
| Scope | Closed | Open |
|
|
34
|
+
| ------------------------- | ----------------------------- | -------------------- |
|
|
35
|
+
| `[data-theme="dark"]` | `background: rgb(30, 26, 35)` | `rgb(255, 255, 255)` |
|
|
36
|
+
| `.sm` | `--control-size: 2rem` | `2.375rem` |
|
|
37
|
+
| app-scoped `--app-accent` | `rgb(1, 2, 3)` | *(unset)* |
|
|
38
|
+
| `.sm` inside a `<dialog>` | `--control-size: 2rem` | `2.375rem` |
|
|
40
39
|
|
|
41
40
|
Every context broke. Three consequences were worth stating plainly:
|
|
42
41
|
|
package/docs/navigation.json
CHANGED
|
@@ -49,10 +49,12 @@
|
|
|
49
49
|
"aura",
|
|
50
50
|
"avatar",
|
|
51
51
|
"badge",
|
|
52
|
+
"bleed",
|
|
52
53
|
"breadcrumb",
|
|
53
54
|
"button",
|
|
54
55
|
"card",
|
|
55
56
|
"chat",
|
|
57
|
+
"close",
|
|
56
58
|
"dialog",
|
|
57
59
|
"drawer",
|
|
58
60
|
"data-list",
|
|
@@ -65,6 +67,7 @@
|
|
|
65
67
|
"pagination",
|
|
66
68
|
"progress",
|
|
67
69
|
"rating",
|
|
70
|
+
"shimmer",
|
|
68
71
|
"skeleton",
|
|
69
72
|
"spinner",
|
|
70
73
|
"steps",
|
|
@@ -78,6 +81,7 @@
|
|
|
78
81
|
"overview",
|
|
79
82
|
"controls",
|
|
80
83
|
"combobox",
|
|
84
|
+
"rich-text",
|
|
81
85
|
"labels",
|
|
82
86
|
"floating-field",
|
|
83
87
|
"states-and-sizes",
|
|
@@ -116,4 +116,58 @@ The end marker is a shared token-based chevron; swapping it for a local icon is
|
|
|
116
116
|
## CSS hooks
|
|
117
117
|
|
|
118
118
|
- `--accordion-radius` — outer corner radius.
|
|
119
|
-
- `--accordion-
|
|
119
|
+
- `--accordion-pad` — inset of the summary row and the panel. The panel relays it as `--surface-pad`, so a direct `.bleed` child of the panel reaches the item's sides and bottom corners (never the summary); `.flush` drops the inline half and the relay with it.
|
|
120
|
+
- `--accordion-marker-color` — color of the end marker; it reinforces to the summary text color on hover. In forced-colors mode the marker is pinned to `CanvasText` on the property itself, so an application override of the hook cannot leave a `Canvas`-remapped fill.
|
|
121
|
+
- `--accordion-marker-size` — edge length of the end marker.
|
|
122
|
+
|
|
123
|
+
### Replacing the marker
|
|
124
|
+
|
|
125
|
+
The marker is a masked box: `--accordion-marker-color` fills it and the chevron
|
|
126
|
+
mask clips it to the glyph. A background, ring, or shadow set on it is clipped
|
|
127
|
+
to the same silhouette, so a decorated marker — a chevron inside a pill — has
|
|
128
|
+
to be a real element: hide the built-in one and put a
|
|
129
|
+
`<span class="marker" aria-hidden="true"></span>` at the end of the summary. The
|
|
130
|
+
pill is the span; the chevron is its pseudo-element, which reads the same color
|
|
131
|
+
hook, so the hover reinforcement carries over. The forced-colors pin does not
|
|
132
|
+
carry over through the hook — an application hook override at the same
|
|
133
|
+
specificity loads after the framework and would win the variable — so the
|
|
134
|
+
recipe repeats it on its own property:
|
|
135
|
+
|
|
136
|
+
```css
|
|
137
|
+
.accordion.custom-marker summary::after {
|
|
138
|
+
display: none;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.accordion.custom-marker .marker {
|
|
142
|
+
display: grid;
|
|
143
|
+
place-items: center;
|
|
144
|
+
margin-inline-start: auto;
|
|
145
|
+
padding: var(--space-10);
|
|
146
|
+
border-radius: 50%;
|
|
147
|
+
background: var(--surface-subtle);
|
|
148
|
+
transition: transform var(--duration);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.accordion.custom-marker .marker::before {
|
|
152
|
+
content: "";
|
|
153
|
+
inline-size: var(--accordion-marker-size);
|
|
154
|
+
block-size: var(--accordion-marker-size);
|
|
155
|
+
background-color: var(--accordion-marker-color);
|
|
156
|
+
-webkit-mask: var(--icon-chevron) center / contain no-repeat;
|
|
157
|
+
mask: var(--icon-chevron) center / contain no-repeat;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.accordion.custom-marker summary:hover .marker {
|
|
161
|
+
--accordion-marker-color: currentColor;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.accordion.custom-marker details[open] .marker {
|
|
165
|
+
transform: rotate(180deg);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
@media (forced-colors: active) {
|
|
169
|
+
.accordion.custom-marker .marker::before {
|
|
170
|
+
background-color: CanvasText;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
- Supports intent colors.
|
|
6
6
|
- Supports longer text and lists.
|
|
7
7
|
- Links inherit alert color by default.
|
|
8
|
-
- Alerts are soft by default. Use `.surface` for neutral chrome with the intent only in the ink, `.solid` or `.outline` when the message needs stronger or quieter emphasis
|
|
8
|
+
- Alerts are soft by default. Use `.surface` for neutral chrome with the intent only in the ink, `.solid` or `.outline` when the message needs stronger or quieter emphasis.
|
|
9
9
|
- Use `<menu class="actions cluster">` for alert action lists.
|
|
10
10
|
- Use `role="alert"` only when the alert is injected dynamically and should be announced.
|
|
11
11
|
- Not a toast.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- A title and a description are plain siblings: the alert owns the small space between its direct content blocks, so neither needs a wrapper. Nested content keeps its own rhythm — wrap several paragraphs in `.prose`, or a differently spaced group in `.stack`, when the message needs a rhythm of its own.
|
|
14
14
|
- Alerts may include a decorative leading icon. Use `.alert-icon` on the icon element; the rest of the content flows into the remaining text column.
|
|
15
15
|
- Use `.compact` or `.spacious` for density changes. The inline padding stays stable.
|
|
16
|
-
- Use `.
|
|
16
|
+
- Use a [`.close`](close.md) button to make the alert dismissible. It is a direct trailing child in standard alerts and lives inside `.alert-title` in admonitions; the alert makes it compact.
|
|
17
17
|
|
|
18
18
|
Because alerts are soft by default, adding `.soft` to an intent (`.alert.soft.primary`,
|
|
19
19
|
`.alert.soft.danger`, …) is a no-op: it already resolves to a soft version of that
|
|
@@ -26,14 +26,13 @@ itself needs to change, not `.soft`.
|
|
|
26
26
|
| ---------------- | ----------- | ----------------------------------------------- |
|
|
27
27
|
| `.alert` | Component | Inline status surface; soft by default. |
|
|
28
28
|
| `.alert-icon` | Composition | Decorative leading icon, in the first column. |
|
|
29
|
-
| `.
|
|
29
|
+
| `.close` | Composition | Dismiss button, in the last column. |
|
|
30
30
|
| `.alert-title` | Composition | Admonition title bar; hosts the dismiss button. |
|
|
31
31
|
| `.alert-body` | Composition | Admonition body on the page surface. |
|
|
32
32
|
| `.callout` | Variant | Leading-edge flag; owns the border geometry. |
|
|
33
33
|
| `.admonition` | Variant | Tinted title bar, body on the page surface. |
|
|
34
34
|
| Shared intents | Intent | `.primary`, `.secondary`, `.success`, … |
|
|
35
35
|
| Shared variants | Variant | `.solid`, `.outline`, `.surface`. |
|
|
36
|
-
| `.inverted` | Surface | Inverse surface, any intent. |
|
|
37
36
|
| Density contexts | Density | Descendant rhythm; inline padding stays stable. |
|
|
38
37
|
|
|
39
38
|
## Basic usage
|
|
@@ -101,7 +100,7 @@ intent treatment when the color itself carries the meaning.
|
|
|
101
100
|
|
|
102
101
|
## Trailing action
|
|
103
102
|
|
|
104
|
-
The grid pins `.alert-icon` and `.
|
|
103
|
+
The grid pins `.alert-icon` and `.close` to the outer columns. A
|
|
105
104
|
trailing link or button is content, not anatomy, so it lives in the text column:
|
|
106
105
|
wrap the row in a `.cluster` and push the two ends apart. Same recipe as any
|
|
107
106
|
split / spread row — there is no `.alert-action` class.
|
|
@@ -129,10 +128,6 @@ center.
|
|
|
129
128
|
I'm a surface alert — theme chrome, intent in the ink
|
|
130
129
|
</div>
|
|
131
130
|
|
|
132
|
-
<div class="alert inverted" role="alert">
|
|
133
|
-
I'm an inverted alert <a href="#">with a link</a>
|
|
134
|
-
</div>
|
|
135
|
-
|
|
136
131
|
<div class="alert danger compact" role="alert">
|
|
137
132
|
I'm an error in a compact context
|
|
138
133
|
</div>
|
|
@@ -153,7 +148,7 @@ center.
|
|
|
153
148
|
|
|
154
149
|
## Dismiss
|
|
155
150
|
|
|
156
|
-
|
|
151
|
+
Add a `.close` button to let a user remove an inline alert. Inside an alert it is compact and at reduced opacity, and it follows the alert's ink with any intent. The runtime `--dismiss` command removes the alert — no custom script.
|
|
157
152
|
|
|
158
153
|
```html demo
|
|
159
154
|
<div class="alert warning" id="warning-alert">
|
|
@@ -162,7 +157,7 @@ Use `.alert-dismiss` to let a user remove an inline alert. The button is transpa
|
|
|
162
157
|
<p>Something needs your attention.</p>
|
|
163
158
|
|
|
164
159
|
<button
|
|
165
|
-
class="
|
|
160
|
+
class="close"
|
|
166
161
|
type="button"
|
|
167
162
|
commandfor="warning-alert"
|
|
168
163
|
command="--dismiss"
|
|
@@ -171,14 +166,14 @@ Use `.alert-dismiss` to let a user remove an inline alert. The button is transpa
|
|
|
171
166
|
</div>
|
|
172
167
|
```
|
|
173
168
|
|
|
174
|
-
In an admonition, place `.
|
|
169
|
+
In an admonition, place `.close` inside `.alert-title`; it rides the inline end of the title bar:
|
|
175
170
|
|
|
176
171
|
```html
|
|
177
172
|
<div class="alert admonition warning" id="notice">
|
|
178
173
|
<div class="alert-title">
|
|
179
174
|
<span>Warning</span>
|
|
180
175
|
<button
|
|
181
|
-
class="
|
|
176
|
+
class="close"
|
|
182
177
|
type="button"
|
|
183
178
|
commandfor="notice"
|
|
184
179
|
command="--dismiss"
|
|
@@ -261,8 +256,7 @@ An admonition is a structured box with a tinted title bar, an optional icon, and
|
|
|
261
256
|
- `--alert-pad-block` — block padding.
|
|
262
257
|
- `--alert-font-size` — base font size.
|
|
263
258
|
- `--alert-icon-size` — size of a leading `.alert-icon`.
|
|
264
|
-
- `--
|
|
265
|
-
- `--alert-dismiss-icon-size` — size of the dismiss glyph.
|
|
259
|
+
- `--close-size` / `--close-icon-size` — the alert declares compact values for its `.close`; set them on the alert to change them.
|
|
266
260
|
- `--alert-radius` — corner radius, when the default `--radius-lg` does not fit.
|
|
267
261
|
- `--alert-border-inline-start-color` / `--alert-border-inline-start-width` — extension points for a callout-style colored flag on the leading edge instead of a uniform border.
|
|
268
262
|
|
|
@@ -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.
|
|
@@ -177,21 +183,24 @@ Override the existing hooks when the three-step scale does not fit the content.
|
|
|
177
183
|
|
|
178
184
|
## Removable tag pattern
|
|
179
185
|
|
|
180
|
-
Use `.badge soft` for tag visuals. Add a direct
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
used instead.
|
|
194
|
+
Inside a badge the close follows the pill's size, keeps a 24px pointer target,
|
|
195
|
+
and tucks into the padding.
|
|
187
196
|
|
|
188
197
|
```html demo
|
|
189
198
|
<span class="badge primary soft">
|
|
190
199
|
Design
|
|
191
|
-
<button type="button" aria-label="Remove Design"></button>
|
|
200
|
+
<button class="close" type="button" aria-label="Remove Design"></button>
|
|
192
201
|
</span>
|
|
193
202
|
<span class="badge primary soft">
|
|
194
|
-
<button type="button" aria-label="Remove Design"></button>
|
|
203
|
+
<button class="close" type="button" aria-label="Remove Design"></button>
|
|
195
204
|
Design
|
|
196
205
|
</span>
|
|
197
206
|
```
|
|
@@ -204,4 +213,4 @@ used instead.
|
|
|
204
213
|
- `--badge-font-size` — label font size.
|
|
205
214
|
- `--badge-icon-size` — decorative child size; `1em` follows the label by default.
|
|
206
215
|
- `--badge-pad-x` — inline padding. Derived from `--badge-size`, so it stays proportional to the pill; set it to opt out.
|
|
207
|
-
- `--
|
|
216
|
+
- `--close-size` / `--close-icon-size` — the badge declares them for its `.close`; set them on the badge to change them.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Bleed
|
|
2
|
+
|
|
3
|
+
`.bleed` lets a direct child of a padded surface reach its edges. Use it for a
|
|
4
|
+
full-width image, a colored header or footer, or a notice band.
|
|
5
|
+
|
|
6
|
+
It works inside `.card`, `.drawer`, and an accordion panel. The accordion panel
|
|
7
|
+
owns the padding, so place `.bleed` directly inside the panel, after the summary.
|
|
8
|
+
A deeper descendant stays within its wrapper. A modular build imports
|
|
9
|
+
`actual-css/css/components/bleed` alongside the surface component.
|
|
10
|
+
|
|
11
|
+
```html demo
|
|
12
|
+
<article class="card stack" style="--card-max-inline-size: 24rem">
|
|
13
|
+
<img class="bleed" src="https://picsum.photos/seed/actual-css-card/600/300" alt="" />
|
|
14
|
+
<header><h3>Full-width image</h3></header>
|
|
15
|
+
<p>The image reaches the card edges.</p>
|
|
16
|
+
<footer class="bleed" style="background: var(--surface-subtle)">
|
|
17
|
+
<button type="button" class="btn outline">View</button>
|
|
18
|
+
</footer>
|
|
19
|
+
</article>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
An `<img>`, `<picture>`, `<video>`, `<canvas>`, or `<svg>` with `.bleed` expands
|
|
23
|
+
to the surface width. For a responsive image, put `.bleed` on `<picture>`; its
|
|
24
|
+
image fills the picture and is clipped by it.
|
|
25
|
+
|
|
26
|
+
Structural `header`, `footer`, and `figure` bands keep the surface's inner
|
|
27
|
+
padding while their background reaches the edge. Other bands can read the
|
|
28
|
+
relayed inset with `--bleed-pad`:
|
|
29
|
+
|
|
30
|
+
```css
|
|
31
|
+
.status-row {
|
|
32
|
+
padding: var(--bleed-pad);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The participating surface relays its inset to direct children only. Read
|
|
37
|
+
`--bleed-pad` on one of those children when a custom band needs matching
|
|
38
|
+
padding. An author-defined surface needs its own direct-child relay to join
|
|
39
|
+
this contract.
|
|
40
|
+
|
|
41
|
+
See [Card](card.md), [Drawer](drawer.md), and [Accordion](accordion.md) for
|
|
42
|
+
surface examples.
|
|
@@ -61,10 +61,37 @@ Reach for `.surface` when a secondary action must sit on an opaque, neutral
|
|
|
61
61
|
chip: `.outline` lets whatever is behind it show through, and `.soft` is the
|
|
62
62
|
intent-tinted treatment (its baseline fallback uses `--surface-subtle`).
|
|
63
63
|
|
|
64
|
+
None of the three gives an opaque fill *under* an intent-colored border —
|
|
65
|
+
`.surface` keeps the neutral `--border`, and `.outline` has no fill. Set
|
|
66
|
+
`--ui-bg` on the element for that combination; it is the variant relay every
|
|
67
|
+
component with shared variants reads, so the same declaration works on a badge:
|
|
68
|
+
|
|
69
|
+
```css
|
|
70
|
+
.chip-on-tint {
|
|
71
|
+
--ui-bg: var(--surface);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The rule ties with the variant's own `--ui-bg` on specificity, so load it after
|
|
76
|
+
Actual CSS or import `actual.layer.css`.
|
|
77
|
+
|
|
64
78
|
Transparent treatments (`.outline`, `.ghost`, `.link`) follow the surrounding
|
|
65
|
-
foreground by default, including on a
|
|
66
|
-
intent class still wins: `.danger.outline` uses the danger color in
|
|
67
|
-
context
|
|
79
|
+
foreground by default, including on a band your application paints. A local
|
|
80
|
+
intent class still wins: `.danger.outline` uses the danger color in any
|
|
81
|
+
context, and nothing tunes that color for a surface you paint yourself. For
|
|
82
|
+
intent ink on a dark band, use a `data-theme` island, whose palette is tuned
|
|
83
|
+
for its surface. Filled buttons own their surface and keep their normal intent
|
|
84
|
+
recipe.
|
|
85
|
+
|
|
86
|
+
A dark button (inverse, contrast) is a custom intent on the button itself, so
|
|
87
|
+
every variant keeps working — `.outline` and `.ghost` take the solid ink:
|
|
88
|
+
|
|
89
|
+
```css
|
|
90
|
+
.btn.app-dark {
|
|
91
|
+
--intent: var(--surface-solid);
|
|
92
|
+
--intent-fg: var(--surface-solid-fg);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
68
95
|
|
|
69
96
|
`.btn.link` is a link-style action: it keeps button behavior and states, but
|
|
70
97
|
remains intrinsically sized like text instead of stretching with its layout
|
|
@@ -251,5 +278,4 @@ For exclusive groups, manage a single pressed button:
|
|
|
251
278
|
## CSS hooks
|
|
252
279
|
|
|
253
280
|
- `--btn-radius` — corner radius.
|
|
254
|
-
- `--btn-focus-color` — focus ring base color; the ring itself is derived from it.
|
|
255
281
|
- `--btn-gap` — space between an icon and the label text (default `0.375em`).
|