actual-css 0.9.2 → 0.10.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 +96 -0
- package/README.md +65 -44
- package/components.json +33 -13
- package/dist/actual.full.css +585 -499
- 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/forced-colors.md +12 -1
- package/docs/design-notes/naming.md +1 -1
- package/docs/design-notes/surface-reparenting.md +8 -9
- package/docs/navigation.json +2 -0
- package/docs/pages/components/accordion.md +55 -1
- package/docs/pages/components/alert.md +9 -15
- package/docs/pages/components/badge.md +6 -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/close.md +48 -0
- package/docs/pages/components/data-list.md +26 -1
- package/docs/pages/components/dialog.md +16 -16
- package/docs/pages/components/drawer.md +9 -9
- package/docs/pages/components/navbar.md +5 -5
- package/docs/pages/components/spinner.md +3 -3
- package/docs/pages/enhancements/overview.md +5 -0
- package/docs/pages/enhancements/tooltip.md +3 -1
- package/docs/pages/examples/overview.md +2 -1
- package/docs/pages/forms/choice-cards.md +39 -0
- package/docs/pages/forms/combobox.md +23 -11
- package/docs/pages/forms/joined-controls.md +2 -5
- package/docs/pages/forms/overview.md +6 -0
- package/docs/pages/foundations/theming.md +64 -18
- package/docs/pages/foundations/tokens.md +28 -10
- package/docs/pages/guides/introduction.md +15 -23
- package/docs/pages/guides/modular-import.md +53 -6
- package/docs/pages/guides/progressive-enhancement.md +6 -3
- package/docs/pages/guides/tailwind.md +17 -5
- 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 +24 -4
- package/package.json +6 -6
- package/reserved-classes.json +5 -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/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 +1 -0
- 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/modal.css +10 -57
- package/src/css/components/navbar.css +27 -0
- package/src/css/core/focus.css +9 -3
- package/src/css/core/theme.css +25 -43
- package/src/css/core/tokens.css +1 -1
- package/src/css/core/variants.css +3 -33
- package/src/css/forms/choice-card.css +74 -4
- package/src/css/forms/choice.css +2 -3
- package/src/css/forms/control.css +15 -8
- package/src/css/forms/custom-select.css +4 -4
- package/src/css/forms/native.css +4 -4
- package/src/css/forms/range.css +23 -8
- 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/dialog.js +95 -24
- package/src/js/enhance.js +7 -3
- 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/keys.js +14 -0
- package/src/js/menu.js +2 -1
- package/src/js/status.js +19 -0
- package/src/js/surface.js +12 -8
- package/src/js/tab.js +2 -1
- package/src/js/tooltip.js +18 -1
- package/src/js/validation.js +5 -6
- package/dist/actual.css +0 -876
- package/dist/actual.js +0 -1
- package/dist/actual.min.css +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Consumer review checklist
|
|
2
|
+
|
|
3
|
+
## Vocabulary and imports
|
|
4
|
+
|
|
5
|
+
- Every framework-looking class exists in the installed Actual CSS version.
|
|
6
|
+
- Intent/variant composition is used instead of invented compound names.
|
|
7
|
+
- Runtime `is-*` state is not authored as application API.
|
|
8
|
+
- The project imports the family/module that owns the markup it uses.
|
|
9
|
+
- `forms` vs `forms/all` is intentional.
|
|
10
|
+
- JS imports match the behavior actually enabled.
|
|
11
|
+
|
|
12
|
+
## CSS ownership
|
|
13
|
+
|
|
14
|
+
- Application CSS is not recreating an existing layout/component/focus behavior unnecessarily.
|
|
15
|
+
- Public hooks were considered before replacement CSS.
|
|
16
|
+
- Palette values remain centralized in theme/token definitions.
|
|
17
|
+
- `--gap` is only rebound when inherited rhythm should change.
|
|
18
|
+
- Component-scoped hooks are not read elsewhere without an intentional fallback.
|
|
19
|
+
|
|
20
|
+
## Layout
|
|
21
|
+
|
|
22
|
+
- Primitive choice follows child relationships, not a single mockup's item count.
|
|
23
|
+
- `.app-layout` is used only for its actual shell contract.
|
|
24
|
+
- Exact track needs use the appropriate grid hook before a new generic primitive is invented.
|
|
25
|
+
|
|
26
|
+
## Forms
|
|
27
|
+
|
|
28
|
+
- `inputmode` is not treated as validation/filtering.
|
|
29
|
+
- `data-filter` is used only when destructive rewriting is intended.
|
|
30
|
+
- Masks are not mistaken for semantic validity.
|
|
31
|
+
- Server/application validation remains authoritative for domain rules.
|
|
32
|
+
- Help/errors and invalid state are connected accessibly.
|
|
33
|
+
|
|
34
|
+
## Enhancements
|
|
35
|
+
|
|
36
|
+
- Presentation, semantics, behavior opt-in, and configuration remain separate.
|
|
37
|
+
- Enhancement tokens and feature attributes are documented by the installed version.
|
|
38
|
+
- Application code does not fight Actual for Popover or positioning ownership.
|
|
39
|
+
- Interactive floating content is not placed in a tooltip.
|
|
40
|
+
|
|
41
|
+
## Accessibility
|
|
42
|
+
|
|
43
|
+
- Keyboard focus remains visible.
|
|
44
|
+
- Icon-only controls are named.
|
|
45
|
+
- Disabled/current/selected/expanded semantics match visible state.
|
|
46
|
+
- Keyboard behavior matches claimed ARIA roles.
|
|
47
|
+
- Custom states consider forced colors and reduced motion where relevant.
|
|
48
|
+
|
|
49
|
+
## Version safety
|
|
50
|
+
|
|
51
|
+
When uncertain, inspect the installed manifests, package exports, source, and docs instead of extrapolating from this starter skill.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
function readJson(file) {
|
|
8
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function exists(file) {
|
|
12
|
+
try {
|
|
13
|
+
return fs.existsSync(file);
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function detectRoot(start = process.cwd()) {
|
|
20
|
+
const explicit = process.env.ACTUAL_CSS_ROOT;
|
|
21
|
+
if (explicit) {
|
|
22
|
+
const root = path.resolve(explicit);
|
|
23
|
+
if (!exists(path.join(root, "package.json"))) {
|
|
24
|
+
throw new Error(`ACTUAL_CSS_ROOT does not contain package.json: ${root}`);
|
|
25
|
+
}
|
|
26
|
+
return root;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let dir = path.resolve(start);
|
|
30
|
+
while (true) {
|
|
31
|
+
const localPkg = path.join(dir, "package.json");
|
|
32
|
+
if (exists(localPkg)) {
|
|
33
|
+
try {
|
|
34
|
+
const pkg = readJson(localPkg);
|
|
35
|
+
if (pkg.name === "actual-css" && exists(path.join(dir, "components.json"))) {
|
|
36
|
+
return dir;
|
|
37
|
+
}
|
|
38
|
+
} catch {
|
|
39
|
+
// Ignore malformed unrelated package.json files while walking.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const installed = path.join(dir, "node_modules", "actual-css");
|
|
44
|
+
if (exists(path.join(installed, "package.json"))) {
|
|
45
|
+
return installed;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const parent = path.dirname(dir);
|
|
49
|
+
if (parent === dir) break;
|
|
50
|
+
dir = parent;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function fail(message) {
|
|
57
|
+
process.stderr.write(`${message}\n`);
|
|
58
|
+
process.exitCode = 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function print(value) {
|
|
62
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function usage() {
|
|
66
|
+
process.stdout.write(`Actual CSS inspector\n\n`);
|
|
67
|
+
process.stdout.write(`Usage:\n`);
|
|
68
|
+
process.stdout.write(` inspect-actual-css.mjs --source\n`);
|
|
69
|
+
process.stdout.write(` inspect-actual-css.mjs <component>\n`);
|
|
70
|
+
process.stdout.write(` inspect-actual-css.mjs --class <class>\n`);
|
|
71
|
+
process.stdout.write(` inspect-actual-css.mjs --exports\n`);
|
|
72
|
+
process.stdout.write(` inspect-actual-css.mjs --enhancements\n`);
|
|
73
|
+
process.stdout.write(` inspect-actual-css.mjs --list\n\n`);
|
|
74
|
+
process.stdout.write(`Set ACTUAL_CSS_ROOT to inspect a specific checkout/package.\n`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const args = process.argv.slice(2);
|
|
78
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
79
|
+
usage();
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const root = detectRoot();
|
|
84
|
+
if (!root) {
|
|
85
|
+
fail("Could not find Actual CSS. Run inside its repository/a consuming project or set ACTUAL_CSS_ROOT.");
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const pkg = readJson(path.join(root, "package.json"));
|
|
90
|
+
const componentsPath = path.join(root, "components.json");
|
|
91
|
+
const reservedPath = path.join(root, "reserved-classes.json");
|
|
92
|
+
const components = exists(componentsPath) ? readJson(componentsPath) : [];
|
|
93
|
+
const reservedRaw = exists(reservedPath) ? readJson(reservedPath) : [];
|
|
94
|
+
const reserved = Array.isArray(reservedRaw)
|
|
95
|
+
? reservedRaw
|
|
96
|
+
: Array.isArray(reservedRaw.classes)
|
|
97
|
+
? reservedRaw.classes
|
|
98
|
+
: Object.keys(reservedRaw);
|
|
99
|
+
|
|
100
|
+
if (args.length === 0 || args[0] === "--list") {
|
|
101
|
+
print({
|
|
102
|
+
version: pkg.version,
|
|
103
|
+
root,
|
|
104
|
+
components: components.map((item) => item.name),
|
|
105
|
+
});
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (args[0] === "--source") {
|
|
110
|
+
print({
|
|
111
|
+
name: pkg.name,
|
|
112
|
+
version: pkg.version,
|
|
113
|
+
root,
|
|
114
|
+
packageJson: path.join(root, "package.json"),
|
|
115
|
+
componentsJson: exists(componentsPath) ? componentsPath : null,
|
|
116
|
+
reservedClassesJson: exists(reservedPath) ? reservedPath : null,
|
|
117
|
+
});
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (args[0] === "--exports") {
|
|
122
|
+
print({ version: pkg.version, exports: pkg.exports ?? {} });
|
|
123
|
+
process.exit(0);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (args[0] === "--enhancements") {
|
|
127
|
+
const matches = components
|
|
128
|
+
.filter((item) => item.jsImport || (item.enhancements && item.enhancements.length))
|
|
129
|
+
.map((item) => ({
|
|
130
|
+
name: item.name,
|
|
131
|
+
jsImport: item.jsImport ?? null,
|
|
132
|
+
enhancements: item.enhancements ?? [],
|
|
133
|
+
states: item.states ?? [],
|
|
134
|
+
}));
|
|
135
|
+
print({ version: pkg.version, components: matches });
|
|
136
|
+
process.exit(0);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (args[0] === "--class") {
|
|
140
|
+
const className = (args[1] ?? "").replace(/^\./, "");
|
|
141
|
+
if (!className) {
|
|
142
|
+
fail("--class requires a class name");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
const owners = components
|
|
146
|
+
.filter((item) => (item.classes ?? []).includes(className))
|
|
147
|
+
.map((item) => item.name);
|
|
148
|
+
print({
|
|
149
|
+
version: pkg.version,
|
|
150
|
+
class: className,
|
|
151
|
+
reserved: reserved.includes(className),
|
|
152
|
+
owners,
|
|
153
|
+
});
|
|
154
|
+
process.exit(0);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const name = args[0];
|
|
158
|
+
const exact = components.find((item) => item.name === name);
|
|
159
|
+
if (exact) {
|
|
160
|
+
print({ version: pkg.version, component: exact });
|
|
161
|
+
process.exit(0);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const byClass = components.filter((item) => (item.classes ?? []).includes(name));
|
|
165
|
+
if (byClass.length) {
|
|
166
|
+
print({ version: pkg.version, query: name, matchedByClass: byClass });
|
|
167
|
+
process.exit(0);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
fail(`No component or owned class matched: ${name}`);
|
|
171
|
+
process.exit(1);
|
|
@@ -15,11 +15,15 @@
|
|
|
15
15
|
*
|
|
16
16
|
* Public hooks:
|
|
17
17
|
* --accordion-radius
|
|
18
|
+
* --accordion-pad
|
|
18
19
|
* --accordion-marker-color
|
|
20
|
+
* --accordion-marker-size
|
|
19
21
|
*/
|
|
20
22
|
.accordion {
|
|
21
23
|
--accordion-radius: var(--radius);
|
|
24
|
+
--accordion-pad: var(--space-40);
|
|
22
25
|
--accordion-marker-color: var(--text-muted);
|
|
26
|
+
--accordion-marker-size: 1rem;
|
|
23
27
|
|
|
24
28
|
border: var(--border-width) solid var(--border);
|
|
25
29
|
border-radius: var(--accordion-radius);
|
|
@@ -36,7 +40,7 @@
|
|
|
36
40
|
gap: var(--space-30);
|
|
37
41
|
cursor: pointer;
|
|
38
42
|
list-style: none;
|
|
39
|
-
padding: var(--
|
|
43
|
+
padding: var(--accordion-pad);
|
|
40
44
|
color: var(--text);
|
|
41
45
|
font-weight: var(--font-weight-medium);
|
|
42
46
|
}
|
|
@@ -52,12 +56,17 @@
|
|
|
52
56
|
display: none;
|
|
53
57
|
}
|
|
54
58
|
|
|
59
|
+
/* The marker is the chevron itself: the box is filled with
|
|
60
|
+
--accordion-marker-color and then clipped to the glyph by the mask. Anything
|
|
61
|
+
painted on this pseudo-element — a background, a ring, a shadow — is clipped
|
|
62
|
+
with it, so a decorated marker (a chevron in a pill) needs a real element;
|
|
63
|
+
see the accordion docs for the opt-out. */
|
|
55
64
|
.accordion summary::after {
|
|
56
65
|
content: "";
|
|
57
66
|
flex: 0 0 auto;
|
|
58
67
|
margin-inline-start: auto;
|
|
59
|
-
inline-size:
|
|
60
|
-
block-size:
|
|
68
|
+
inline-size: var(--accordion-marker-size);
|
|
69
|
+
block-size: var(--accordion-marker-size);
|
|
61
70
|
background-color: var(--accordion-marker-color);
|
|
62
71
|
-webkit-mask: var(--icon-chevron) center / contain no-repeat;
|
|
63
72
|
mask: var(--icon-chevron) center / contain no-repeat;
|
|
@@ -68,9 +77,14 @@
|
|
|
68
77
|
transform: rotate(180deg);
|
|
69
78
|
}
|
|
70
79
|
|
|
80
|
+
/* The panel, not the item, owns the inset here — .separated paints a card on
|
|
81
|
+
<details> but leaves it unpadded — so the panel is what a .bleed child
|
|
82
|
+
escapes (components/bleed.css). */
|
|
71
83
|
.accordion details > summary + * {
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
--surface-pad: var(--accordion-pad);
|
|
85
|
+
|
|
86
|
+
padding-block: 0 var(--accordion-pad);
|
|
87
|
+
padding-inline: var(--accordion-pad);
|
|
74
88
|
color: var(--text-muted);
|
|
75
89
|
margin-block: 0;
|
|
76
90
|
}
|
|
@@ -79,6 +93,9 @@
|
|
|
79
93
|
padding-block-start: var(--space-10);
|
|
80
94
|
}
|
|
81
95
|
|
|
96
|
+
/* Also cancels the lift of a first .bleed child (bleed.css, lower
|
|
97
|
+
specificity): the panel's top edge is the summary, not the item edge, so a
|
|
98
|
+
bleed reaches only the sides and the bottom. */
|
|
82
99
|
.accordion details > summary + * > :first-child {
|
|
83
100
|
margin-block-start: 0;
|
|
84
101
|
}
|
|
@@ -103,6 +120,12 @@
|
|
|
103
120
|
padding-inline: 0;
|
|
104
121
|
}
|
|
105
122
|
|
|
123
|
+
/* Flush already sits at the embedding surface's edge, so there is no inline
|
|
124
|
+
inset left for a .bleed child to cancel. */
|
|
125
|
+
.accordion.flush details > summary + * {
|
|
126
|
+
--surface-pad: 0px;
|
|
127
|
+
}
|
|
128
|
+
|
|
106
129
|
/* Separated — each item owns a full card. The item's own border beats the
|
|
107
130
|
shared details + details separator on specificity alone, so nothing needs
|
|
108
131
|
undoing for the second item onwards. */
|
|
@@ -119,8 +142,22 @@
|
|
|
119
142
|
background: var(--surface-raised);
|
|
120
143
|
}
|
|
121
144
|
|
|
145
|
+
/* A last .bleed child inherits its bottom corners from the panel, but the
|
|
146
|
+
rounded box is the item (.separated) or the group (last item) — relay the
|
|
147
|
+
radius to the panel. Explicit rather than inherit: in browsers with
|
|
148
|
+
::details-content the panel's flat-tree parent is that slot, not
|
|
149
|
+
<details>. */
|
|
150
|
+
.accordion.separated > details > summary + *,
|
|
151
|
+
.accordion:not(.flush) > details:last-child > summary + * {
|
|
152
|
+
border-end-start-radius: var(--accordion-radius);
|
|
153
|
+
border-end-end-radius: var(--accordion-radius);
|
|
154
|
+
}
|
|
155
|
+
|
|
122
156
|
@media (forced-colors: active) {
|
|
123
|
-
|
|
124
|
-
|
|
157
|
+
/* Written on the property, not the hook: an application hook override at
|
|
158
|
+
the same specificity loads after this file and would win the variable,
|
|
159
|
+
leaving a Canvas-remapped fill. The property keeps CanvasText. */
|
|
160
|
+
.accordion summary::after {
|
|
161
|
+
background-color: CanvasText;
|
|
125
162
|
}
|
|
126
163
|
}
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
* --alert-font-size
|
|
17
17
|
* --alert-radius
|
|
18
18
|
* --alert-icon-size
|
|
19
|
-
* --alert-dismiss-size
|
|
20
19
|
* --alert-border-inline-start-color
|
|
21
20
|
* --alert-border-inline-start-width
|
|
22
21
|
*
|
|
@@ -27,7 +26,6 @@
|
|
|
27
26
|
* --alert-bg
|
|
28
27
|
* --alert-fg
|
|
29
28
|
* --alert-border
|
|
30
|
-
* --alert-dismiss-icon-size
|
|
31
29
|
*/
|
|
32
30
|
/* Intent boundary — inherited intents do not tint this component.
|
|
33
31
|
Local intent classes still win because :where() has zero specificity. */
|
|
@@ -76,13 +74,13 @@
|
|
|
76
74
|
The slots are excluded because they are anatomy, not content: an
|
|
77
75
|
admonition's title bar has to stay flush against its body. The list follows
|
|
78
76
|
the anatomy, so it grows with it. */
|
|
79
|
-
.alert > :not(:where(.alert-icon, .
|
|
77
|
+
.alert > :not(:where(.alert-icon, .close, .alert-title, .alert-body)) {
|
|
80
78
|
margin-block: 0;
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
.alert
|
|
84
|
-
> :not(:where(.alert-icon, .
|
|
85
|
-
+ :not(:where(.alert-icon, .
|
|
82
|
+
> :not(:where(.alert-icon, .close, .alert-title, .alert-body))
|
|
83
|
+
+ :not(:where(.alert-icon, .close, .alert-title, .alert-body)) {
|
|
86
84
|
margin-block-start: var(--space-10);
|
|
87
85
|
}
|
|
88
86
|
|
|
@@ -120,42 +118,24 @@
|
|
|
120
118
|
vertical-align: -0.125em;
|
|
121
119
|
}
|
|
122
120
|
|
|
123
|
-
/*
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
--
|
|
131
|
-
--alert-dismiss-icon-size: 0.875rem;
|
|
132
|
-
|
|
133
|
-
display: inline-grid;
|
|
134
|
-
place-items: center;
|
|
135
|
-
inline-size: var(--alert-dismiss-size);
|
|
136
|
-
block-size: var(--alert-dismiss-size);
|
|
137
|
-
flex: none;
|
|
138
|
-
padding: 0;
|
|
139
|
-
border: 0;
|
|
140
|
-
border-radius: var(--radius);
|
|
141
|
-
background: transparent;
|
|
142
|
-
color: currentColor;
|
|
143
|
-
opacity: 0.65;
|
|
144
|
-
cursor: pointer;
|
|
121
|
+
/* Close (components/close.css) — compact, and at reduced opacity so it stays
|
|
122
|
+
secondary inside an already-colored alert. Standard alerts place it as a
|
|
123
|
+
direct trailing child; admonitions place it inside .alert-title (see
|
|
124
|
+
below). The sizes are declared on the alert so an author can still set
|
|
125
|
+
them on the alert or on the button. */
|
|
126
|
+
.alert {
|
|
127
|
+
--close-size: 1.75rem;
|
|
128
|
+
--close-icon-size: 0.875rem;
|
|
145
129
|
}
|
|
146
130
|
|
|
147
|
-
.alert
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
block-size: var(--alert-dismiss-icon-size);
|
|
151
|
-
background: currentColor;
|
|
152
|
-
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
153
|
-
mask: var(--icon-close) center / contain no-repeat;
|
|
131
|
+
.alert > .close,
|
|
132
|
+
.alert > .alert-title > .close {
|
|
133
|
+
opacity: 0.65;
|
|
154
134
|
}
|
|
155
135
|
|
|
156
136
|
@media (hover: hover) {
|
|
157
|
-
.alert
|
|
158
|
-
|
|
137
|
+
.alert > .close:hover,
|
|
138
|
+
.alert > .alert-title > .close:hover {
|
|
159
139
|
opacity: 1;
|
|
160
140
|
}
|
|
161
141
|
}
|
|
@@ -164,7 +144,7 @@
|
|
|
164
144
|
both. Icon and dismiss are pinned to the outer columns; anything else (the
|
|
165
145
|
message content) flows into the remaining text column. */
|
|
166
146
|
@supports selector(.alert:has(> *)) {
|
|
167
|
-
.alert:has(> .alert-icon, > .
|
|
147
|
+
.alert:has(> .alert-icon, > .close) {
|
|
168
148
|
display: grid;
|
|
169
149
|
grid-template-columns: minmax(0, 1fr);
|
|
170
150
|
align-items: var(--items-align, start);
|
|
@@ -183,19 +163,19 @@
|
|
|
183
163
|
margin-block-start: 0.125em;
|
|
184
164
|
}
|
|
185
165
|
|
|
186
|
-
.alert:has(> .alert-icon) > :not(.alert-icon, .
|
|
166
|
+
.alert:has(> .alert-icon) > :not(.alert-icon, .close) {
|
|
187
167
|
grid-column: 2;
|
|
188
168
|
}
|
|
189
169
|
|
|
190
|
-
.alert:has(> .
|
|
170
|
+
.alert:has(> .close) {
|
|
191
171
|
grid-template-columns: minmax(0, 1fr) auto;
|
|
192
172
|
}
|
|
193
173
|
|
|
194
|
-
.alert:has(> .alert-icon):has(> .
|
|
174
|
+
.alert:has(> .alert-icon):has(> .close) {
|
|
195
175
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
196
176
|
}
|
|
197
177
|
|
|
198
|
-
.alert:has(> .
|
|
178
|
+
.alert:has(> .close) > .close {
|
|
199
179
|
grid-column: -2 / -1;
|
|
200
180
|
grid-row: 1;
|
|
201
181
|
}
|
|
@@ -243,8 +223,8 @@
|
|
|
243
223
|
border-start-end-radius: calc(var(--alert-radius, var(--radius-lg)) - var(--border-width));
|
|
244
224
|
}
|
|
245
225
|
|
|
246
|
-
/*
|
|
247
|
-
.alert.admonition > .alert-title > .
|
|
226
|
+
/* Close inside an admonition title bar rides the inline end. */
|
|
227
|
+
.alert.admonition > .alert-title > .close {
|
|
248
228
|
margin-inline-start: auto;
|
|
249
229
|
}
|
|
250
230
|
|
|
@@ -70,6 +70,15 @@
|
|
|
70
70
|
color: var(--text);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/* Same contextual hover as .nav-link (navbar.css): a tint of the tile's ink
|
|
74
|
+
reads on any --ui-* bar; the baseline keeps a legible light chip. */
|
|
75
|
+
@supports (color: color-mix(in oklch, red, white)) {
|
|
76
|
+
.app-nav > a:hover {
|
|
77
|
+
background: color-mix(in oklch, currentColor 10%, transparent);
|
|
78
|
+
color: var(--ui-fg, var(--text));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
73
82
|
/* Current tile is already a full selected surface. Selection never changes
|
|
74
83
|
text metrics: the shared medium weight is kept, no permanent indicator is
|
|
75
84
|
added here. */
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
* --badge-icon-size
|
|
15
15
|
* --badge-dot-size
|
|
16
16
|
* --badge-radius
|
|
17
|
-
* --badge-dismiss-icon-size
|
|
18
17
|
*
|
|
19
18
|
* Internal:
|
|
20
19
|
* --badge-default-bg
|
|
@@ -45,7 +44,10 @@
|
|
|
45
44
|
--badge-font-size: var(--font-size-sm);
|
|
46
45
|
--badge-icon-size: 1em;
|
|
47
46
|
--badge-dot-size: 0.5rem;
|
|
48
|
-
|
|
47
|
+
/* Declared on the badge, not the button, so an author can still set them
|
|
48
|
+
on either. Same element as --badge-size, so .sm/.lg carry through. */
|
|
49
|
+
--close-size: max(var(--badge-size), 24px);
|
|
50
|
+
--close-icon-size: 0.875em;
|
|
49
51
|
--badge-default-bg: var(--surface-subtle);
|
|
50
52
|
--badge-default-fg: var(--intent, var(--text));
|
|
51
53
|
--badge-default-border: var(--border);
|
|
@@ -95,8 +97,8 @@
|
|
|
95
97
|
--badge-dot-size: 0.625rem;
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
/* Decorative children follow the badge's optical size. Text and
|
|
99
|
-
|
|
100
|
+
/* Decorative children follow the badge's optical size. Text and the .close
|
|
101
|
+
button retain their own boxes. */
|
|
100
102
|
.badge > :where(svg, img, [aria-hidden="true"]:not(.dot)) {
|
|
101
103
|
flex: none;
|
|
102
104
|
/* The shared rule maps --badge-icon-size onto font-size. One em is therefore
|
|
@@ -149,45 +151,15 @@
|
|
|
149
151
|
background-color: currentColor;
|
|
150
152
|
}
|
|
151
153
|
|
|
152
|
-
.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
keep the badge visually compact through the negative margins, but never
|
|
158
|
-
shrink its action target below that literal floor. */
|
|
159
|
-
min-inline-size: max(var(--badge-size), 24px);
|
|
160
|
-
min-block-size: max(var(--badge-size), 24px);
|
|
154
|
+
/* Removable tag — a .close (components/close.css) inside the pill. WCAG 2.5.8
|
|
155
|
+
floors pointer targets at 24 CSS px: density and local size may keep the
|
|
156
|
+
badge visually compact through the negative margins, but never shrink the
|
|
157
|
+
target below that literal floor. */
|
|
158
|
+
.badge > .close {
|
|
161
159
|
margin-block: -0.25rem;
|
|
162
160
|
margin-inline-start: -0.15em;
|
|
163
161
|
margin-inline-end: -0.45em;
|
|
164
|
-
padding: 0.25rem;
|
|
165
|
-
border: 0;
|
|
166
162
|
border-radius: var(--radius-full);
|
|
167
|
-
background: transparent;
|
|
168
|
-
color: inherit;
|
|
169
|
-
font: inherit;
|
|
170
|
-
line-height: 1;
|
|
171
|
-
cursor: pointer;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/* Dismiss mark — an empty dismiss button paints its own X from the shared
|
|
175
|
-
--icon-close mask, so no icon font or text glyph is required in the markup
|
|
176
|
-
(same contract as .alert-dismiss and .dialog-close). A mask box is a block
|
|
177
|
-
box, so place-items centers the ink itself; a text glyph would only center
|
|
178
|
-
its line box and land the ink low, because a lowercase x has no descender
|
|
179
|
-
and only x-height of ascent. A button with its own content keeps it. */
|
|
180
|
-
.badge > button:empty::before {
|
|
181
|
-
content: "";
|
|
182
|
-
inline-size: var(--badge-dismiss-icon-size);
|
|
183
|
-
block-size: var(--badge-dismiss-icon-size);
|
|
184
|
-
background: currentColor;
|
|
185
|
-
-webkit-mask: var(--icon-close) center / contain no-repeat;
|
|
186
|
-
mask: var(--icon-close) center / contain no-repeat;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
.badge > button:not(:disabled):hover {
|
|
190
|
-
background: var(--hover-overlay);
|
|
191
163
|
}
|
|
192
164
|
|
|
193
165
|
/* Hover recipes — sets --ui-hover-bg consumed by the hover rule below.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Full-bleed children — the shared escape hatch for a padded surface.
|
|
3
|
+
*
|
|
4
|
+
* Docs: docs/pages/components/bleed.md
|
|
5
|
+
*
|
|
6
|
+
* A surface that insets its direct children relays that inset to them as
|
|
7
|
+
* --surface-pad; a direct .bleed child cancels it and reaches the surface
|
|
8
|
+
* edge. Card and drawer pad at the container level, so they own the token
|
|
9
|
+
* there; the accordion pads its summary and its panel instead, so the panel
|
|
10
|
+
* owns it and the item does not.
|
|
11
|
+
*
|
|
12
|
+
* Why the token is handed to the children rather than declared on the surface:
|
|
13
|
+
* custom properties inherit, so a surface-level declaration would also reach a
|
|
14
|
+
* grandchild, which sits at its wrapper's edge rather than the surface's and
|
|
15
|
+
* would pull a negative margin out of the wrong box. Relaying it one level and
|
|
16
|
+
* clearing it below keeps the child combinator's guarantee while leaving the
|
|
17
|
+
* .bleed rules themselves free of the surface list.
|
|
18
|
+
*
|
|
19
|
+
* Why the clearing rule comes first: both rules are zero-specificity, so
|
|
20
|
+
* source order decides where they overlap, and they overlap on exactly one
|
|
21
|
+
* element — a direct child of a surface nested inside another surface. That
|
|
22
|
+
* child must keep the inner surface's inset, so the relay has to win.
|
|
23
|
+
*
|
|
24
|
+
* Public hooks:
|
|
25
|
+
* --bleed-pad (read-only: the inset relayed to a surface's direct children;
|
|
26
|
+
* pad a band that is not a header/footer/figure with it)
|
|
27
|
+
*
|
|
28
|
+
* Framework plumbing:
|
|
29
|
+
* --surface-pad (declared by the surface)
|
|
30
|
+
*/
|
|
31
|
+
:where(.card, .drawer, .accordion details > summary + *) > * * {
|
|
32
|
+
--bleed-pad: initial;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
:where(.card, .drawer, .accordion details > summary + *) > * {
|
|
36
|
+
--bleed-pad: var(--surface-pad);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Inert without a surface: the fallback resolves the negative margin to zero
|
|
40
|
+
rather than dropping the declaration and inheriting a stray inset. */
|
|
41
|
+
.bleed {
|
|
42
|
+
margin-inline: calc(var(--bleed-pad, 0px) * -1);
|
|
43
|
+
max-inline-size: none;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.bleed:first-child {
|
|
47
|
+
margin-block-start: calc(var(--bleed-pad, 0px) * -1);
|
|
48
|
+
border-start-start-radius: inherit;
|
|
49
|
+
border-start-end-radius: inherit;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.bleed:last-child {
|
|
53
|
+
margin-block-end: calc(var(--bleed-pad, 0px) * -1);
|
|
54
|
+
border-end-start-radius: inherit;
|
|
55
|
+
border-end-end-radius: inherit;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/* Media bleed */
|
|
59
|
+
:where(img, picture, video, canvas, svg).bleed {
|
|
60
|
+
display: block;
|
|
61
|
+
inline-size: calc(100% + var(--bleed-pad, 0px) * 2);
|
|
62
|
+
max-inline-size: none;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
picture.bleed {
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
picture.bleed > img {
|
|
70
|
+
display: block;
|
|
71
|
+
inline-size: 100%;
|
|
72
|
+
block-size: auto;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* Padded structural bleed — the band keeps the surface's own rhythm while its
|
|
76
|
+
background reaches the edge. */
|
|
77
|
+
:where(header, footer, figure).bleed {
|
|
78
|
+
padding: var(--bleed-pad, 0px);
|
|
79
|
+
}
|