@codenhub/styles 0.4.0 → 0.6.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/README.md +7 -7
- package/dist/aesthetics/chunky-tile.css +1 -1
- package/dist/aesthetics/cyber.css +1 -1
- package/dist/aesthetics/glass.css +1 -1
- package/dist/aesthetics/index.css +1 -1
- package/dist/aesthetics/neobrutalism.css +1 -1
- package/dist/aesthetics/pixel.css +1 -1
- package/dist/aesthetics/sketch.css +1 -0
- package/dist/components.css +1 -1
- package/dist/index.css +1 -1
- package/dist/native.css +1 -1
- package/dist/palette.css +1 -1
- package/dist/theme.css +1 -1
- package/dist/tw/aesthetics/chunky-tile.css +144 -128
- package/dist/tw/aesthetics/cyber.css +138 -96
- package/dist/tw/aesthetics/glass.css +332 -66
- package/dist/tw/aesthetics/index.css +3 -1
- package/dist/tw/aesthetics/neobrutalism.css +113 -54
- package/dist/tw/aesthetics/pixel.css +137 -87
- package/dist/tw/aesthetics/sketch.css +333 -0
- package/dist/tw/box.css +139 -52
- package/dist/tw/components/button.css +67 -28
- package/dist/tw/components/feedback.css +85 -67
- package/dist/tw/components/form.css +300 -230
- package/dist/tw/components/index.css +17 -9
- package/dist/tw/components/loader.css +38 -30
- package/dist/tw/components/surface.css +89 -129
- package/dist/tw/components/tooltip.css +91 -47
- package/dist/tw/corner.css +26 -0
- package/dist/tw/elevation.css +19 -28
- package/dist/tw/index.css +15 -19
- package/dist/tw/intent.css +47 -32
- package/dist/tw/native.css +58 -42
- package/dist/tw/palette.css +455 -446
- package/dist/tw/presentation.css +20 -29
- package/dist/tw/reset.css +21 -28
- package/dist/tw/surface.css +61 -0
- package/dist/tw/theme.css +156 -153
- package/dist/tw/utilities/content.css +283 -205
- package/dist/tw/utilities/layout.css +17 -27
- package/docs/accessibility.md +63 -46
- package/docs/changelog/0.5.0.md +38 -0
- package/docs/changelog/0.6.0.md +52 -0
- package/docs/changelog/index.md +2 -0
- package/docs/concepts.md +6 -6
- package/docs/index.md +12 -3
- package/docs/integrating/nextjs.md +1 -1
- package/docs/integrating/tailwind.md +14 -6
- package/docs/setup.md +28 -8
- package/docs/usage/aesthetics.md +88 -18
- package/docs/usage/buttons.md +14 -14
- package/docs/usage/composing.md +12 -12
- package/docs/usage/content-and-layout.md +25 -24
- package/docs/usage/customizing.md +92 -55
- package/docs/usage/feedback.md +5 -3
- package/docs/usage/forms.md +61 -45
- package/docs/usage/theming.md +41 -42
- package/docs/usage/tooltips.md +37 -17
- package/llms-full.txt +590 -304
- package/llms.txt +6 -0
- package/package.json +15 -5
package/dist/tw/presentation.css
CHANGED
|
@@ -1,22 +1,17 @@
|
|
|
1
|
+
/* Imported into the `components` layer by `theme.css`, below every utility, so
|
|
2
|
+
a consumer's own utility or unlayered CSS beats these classes. See
|
|
3
|
+
docs/internal/model.md#cascade-layers. */
|
|
4
|
+
|
|
1
5
|
/* Presentation answers two independent questions, and each is its own closed
|
|
2
6
|
set: how much of the intent color fills the box, and whether the box draws a
|
|
3
7
|
line at its boundary. Exactly one value from each set applies, so the six
|
|
4
8
|
combinations are the whole vocabulary.
|
|
5
9
|
|
|
6
|
-
A fill class never decides an edge and an edge class never decides a fill
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
so every capped intent that drew a line drew a visible ring over its own plate
|
|
12
|
-
-- at the cost of the one property that makes the six combinations readable:
|
|
13
|
-
that each half means only what it says. Two axes that are independent in two
|
|
14
|
-
cases out of three are not axes.
|
|
15
|
-
|
|
16
|
-
The ring is a fault in the blend rather than in the class, and it is recorded
|
|
17
|
-
as such: `.solid.edged` on a capped intent measures 1.53:1 against its own
|
|
18
|
-
plate in light and 1.82:1 in dark. `.solid.edgeless` is how a consumer asks
|
|
19
|
-
for a filled box with no line.
|
|
10
|
+
A fill class never decides an edge and an edge class never decides a fill, so
|
|
11
|
+
each half means only what it says. The line fades toward `transparent` as the
|
|
12
|
+
fill fills in (`box.css`; docs/internal/model.md#presentation), so
|
|
13
|
+
`.solid.edged` and `.solid.edgeless` render one box without either class
|
|
14
|
+
deciding the other's question.
|
|
20
15
|
|
|
21
16
|
Presentation declares only unitless numbers and percentages. A custom
|
|
22
17
|
property's value is resolved on the element that declares it, so anything
|
|
@@ -28,8 +23,8 @@
|
|
|
28
23
|
container set the look for its subtree while any element still opts out.
|
|
29
24
|
|
|
30
25
|
Hover is derived from these values rather than declared beside them:
|
|
31
|
-
`min(100%, resting fill + --hover-step)`, always in `--intent-hover`. There
|
|
32
|
-
no hover token and no per-presentation branch. */
|
|
26
|
+
`min(100%, resting fill + --hover-step)`, always in `--intent-hover`. There
|
|
27
|
+
is no hover token and no per-presentation branch. */
|
|
33
28
|
|
|
34
29
|
/* Filled with the intent color. Text is the contrast tone. */
|
|
35
30
|
.solid {
|
|
@@ -43,11 +38,9 @@
|
|
|
43
38
|
--ui-fg-on-fill: 0%;
|
|
44
39
|
}
|
|
45
40
|
|
|
46
|
-
/* No fill at rest; the derived hover gives it one. Text is the intent color.
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
as "less of something", so an author wanting no border reached for `.bare`
|
|
50
|
-
first and got no background instead. `.ghost` says fill and only fill.
|
|
41
|
+
/* No fill at rest; the derived hover gives it one. Text is the intent color. It
|
|
42
|
+
names a fill and only a fill: `.bare` and `.edgeless` both read as "less of
|
|
43
|
+
something", so an author wanting no border would reach for the wrong one.
|
|
51
44
|
|
|
52
45
|
A component that rests on a ground rather than on nothing keeps that ground
|
|
53
46
|
here: `.ghost` is zero fill, not zero paint. `.data-table` is the exception
|
|
@@ -57,16 +50,14 @@
|
|
|
57
50
|
--ui-fg-on-fill: 0%;
|
|
58
51
|
}
|
|
59
52
|
|
|
60
|
-
/* A line in the intent color, at the aesthetic's material width. The width
|
|
61
|
-
from the aesthetic and nothing else: there is no presentation scale on
|
|
62
|
-
small components need no ceilings to survive a thick one.
|
|
53
|
+
/* A line in the intent color, at the aesthetic's material width. The width
|
|
54
|
+
comes from the aesthetic and nothing else: there is no presentation scale on
|
|
55
|
+
it, so small components need no ceilings to survive a thick one.
|
|
63
56
|
|
|
64
57
|
The edge is the silhouette and nothing else. Rules *inside* a component that
|
|
65
|
-
has an inside are a separate decision with
|
|
66
|
-
--
|
|
67
|
-
|
|
68
|
-
consumer got them depended on a default they had not read and could not name.
|
|
69
|
-
Two questions, two switches. */
|
|
58
|
+
has an inside are a separate decision with their own class --
|
|
59
|
+
`.data-table.ruled` -- so a consumer's own markup says whether they asked for
|
|
60
|
+
them. */
|
|
70
61
|
.edged {
|
|
71
62
|
--ui-border: 100%;
|
|
72
63
|
}
|
package/dist/tw/reset.css
CHANGED
|
@@ -3,13 +3,12 @@
|
|
|
3
3
|
@import "./typography.css";
|
|
4
4
|
|
|
5
5
|
@layer base {
|
|
6
|
-
/* The package's own components
|
|
6
|
+
/* The package's own components do not lean on this: `@utility box` sets
|
|
7
7
|
`box-sizing: border-box` on itself, so a sized control is right in every
|
|
8
8
|
entrypoint including the reset-less `/components` and granular `/tw/*`
|
|
9
|
-
ones.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
than a correctness prop the components need. */
|
|
9
|
+
ones. The universal rule covers the consumer's own markup -- a plain
|
|
10
|
+
`<div>` laid out beside our components -- as part of the opinionated
|
|
11
|
+
baseline. */
|
|
13
12
|
*,
|
|
14
13
|
*::before,
|
|
15
14
|
*::after {
|
|
@@ -52,12 +51,16 @@
|
|
|
52
51
|
margin: 0;
|
|
53
52
|
}
|
|
54
53
|
|
|
54
|
+
/* The whole font, not only the family: the user agent gives a button and a
|
|
55
|
+
field their own smaller size, so a `<button>`, a `.btn` link, and the
|
|
56
|
+
same `.btn` on another entrypoint each printed a different label size.
|
|
57
|
+
Tailwind's Preflight does the same on the entries that carry it. */
|
|
55
58
|
button,
|
|
56
59
|
input,
|
|
57
60
|
optgroup,
|
|
58
61
|
select,
|
|
59
62
|
textarea {
|
|
60
|
-
font
|
|
63
|
+
font: inherit;
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
button,
|
|
@@ -72,9 +75,20 @@
|
|
|
72
75
|
color: var(--color-primary-contrast);
|
|
73
76
|
}
|
|
74
77
|
|
|
78
|
+
/* Negative, so the ring is drawn over the edge rather than beside it: at a
|
|
79
|
+
positive offset the outline floats a gap away and reads as a second,
|
|
80
|
+
detached frame around the control. Pulled in by exactly the line width the
|
|
81
|
+
aesthetic draws, the ring lands on top of the line and a focused control
|
|
82
|
+
reads as its own edge thickening -- which is what a focus indicator is
|
|
83
|
+
meant to look like. Computed here, on the focused element, because that is
|
|
84
|
+
where an aesthetic's `--ui-border-width` resolves; `--focus-ring-offset`,
|
|
85
|
+
when a consumer sets it, still wins.
|
|
86
|
+
|
|
87
|
+
A component drawing no line at rest loses nothing: the outline moves inward
|
|
88
|
+
over its own fill by a hairline, which is not a visible change. */
|
|
75
89
|
:focus-visible {
|
|
76
90
|
outline: var(--focus-ring-width) solid var(--focus-ring);
|
|
77
|
-
outline-offset: var(--focus-ring-offset);
|
|
91
|
+
outline-offset: var(--focus-ring-offset, calc(-1 * var(--ui-border-width, var(--border-width))));
|
|
78
92
|
}
|
|
79
93
|
|
|
80
94
|
@media (forced-colors: active) {
|
|
@@ -127,24 +141,3 @@
|
|
|
127
141
|
border-color: CanvasText;
|
|
128
142
|
}
|
|
129
143
|
}
|
|
130
|
-
|
|
131
|
-
/* The user agent already promises both of these for a native `<dialog>` --
|
|
132
|
-
`dialog:not([open]) { display: none }` and a centered `margin: auto` on the
|
|
133
|
-
open box -- but both are user-agent origin, which loses to any author rule
|
|
134
|
-
at all. `.card`'s own `display: block` (`surface`'s `@apply box block`)
|
|
135
|
-
beats the first outright, so a `<dialog class="card">` renders open with no
|
|
136
|
-
`open` attribute and no `.showModal()` call -- the registry has no
|
|
137
|
-
dialog/modal component of its own, and `.card` is the closest primitive a
|
|
138
|
-
consumer reaches for. A margin reset on every element -- Tailwind's own
|
|
139
|
-
Preflight on the entries that carry it -- beats the second, so an opened
|
|
140
|
-
dialog pins to the top-left instead of centering. Restated here, unlayered like
|
|
141
|
-
the block above, for the same reason: a behavior override has to win over
|
|
142
|
-
any layered rule whatever the specificity, and `.card` is layered in the
|
|
143
|
-
utilities layer regardless of how it composes `box`. */
|
|
144
|
-
dialog:not([open]) {
|
|
145
|
-
display: none;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
dialog[open] {
|
|
149
|
-
margin: auto;
|
|
150
|
-
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
@reference "./box.css";
|
|
2
|
+
|
|
3
|
+
/* What every surface shares. Two slots exist here and nowhere else, and that is
|
|
4
|
+
how an aesthetic reaches surfaces without naming one: only a surface resolves
|
|
5
|
+
`--ui-backdrop`, so glass blurs cards and panels while controls stay solid,
|
|
6
|
+
and only a surface resolves `--ui-surface-shadow`, so a two-layer drop shadow
|
|
7
|
+
does not land under every button on the page.
|
|
8
|
+
|
|
9
|
+
A primitive beside `box`, and like `box` it imports neither the theme nor
|
|
10
|
+
Tailwind: `feedback.css` and `tooltip.css` `@reference` it, and a referenced
|
|
11
|
+
file that reaches the theme strips its tokens from every entry that pulls it
|
|
12
|
+
in. See docs/internal/model.md (Which files import the theme). */
|
|
13
|
+
@utility surface {
|
|
14
|
+
@apply box block;
|
|
15
|
+
|
|
16
|
+
/* Bare and edged. `box` reads these three with no fallback, so `surface` --
|
|
17
|
+
typeable on its own -- owes all three; `card` restates its own. No shadow
|
|
18
|
+
geometry: depth is asked for (`.raised`/`.floating`, an aesthetic, the
|
|
19
|
+
registry elevation) rather than carried, so a plain card is flat. */
|
|
20
|
+
--_d-fill: 0%;
|
|
21
|
+
--_d-fg-on-fill: 0%;
|
|
22
|
+
--_d-border: 100%;
|
|
23
|
+
|
|
24
|
+
/* The public token first, so an aesthetic (glass's translucent ground) or a
|
|
25
|
+
consumer names the ground outright; then the component's own quiet default,
|
|
26
|
+
which `.card.soft`, a neutral `.panel`, and a neutral `.alert` write as a
|
|
27
|
+
private rather than as the public token. Written as the public token, their
|
|
28
|
+
own declaration would beat glass's inherited one in a glass region, and
|
|
29
|
+
beat glass on the same element too (`.card.soft.glass` would go opaque).
|
|
30
|
+
Restated `initial` here so a quiet ground cannot inherit into a surface
|
|
31
|
+
nested inside one, the way `box` restates `--_d-ground`. See
|
|
32
|
+
docs/internal/model.md#cascade-layers. */
|
|
33
|
+
--_d-surface-ground: initial;
|
|
34
|
+
--_d-ground: var(--ui-surface-ground, var(--_d-surface-ground, var(--color-background)));
|
|
35
|
+
|
|
36
|
+
/* The complete-shadow slot for a shadow that is not one layer (glass tucks two
|
|
37
|
+
under with negative spread). Only `surface` reads it -- through `--ui-shadow`
|
|
38
|
+
it would land an 18px drop under every button and chip -- like the backdrop. */
|
|
39
|
+
--_shadow: var(--ui-surface-shadow, var(--_shadow-parts)) var(--_halo-layer,);
|
|
40
|
+
|
|
41
|
+
/* The surface pair of `box`'s corner: the aesthetic's surface length, scaled
|
|
42
|
+
by the padding step and patterned by the same four corner switches. */
|
|
43
|
+
--_corner-surface: calc(var(--ui-corner-surface, var(--radius-surface)) * var(--_scale));
|
|
44
|
+
border-radius: var(
|
|
45
|
+
--ui-radius-surface,
|
|
46
|
+
calc(var(--_corner-surface) * var(--ui-corner-tl, 1)) calc(var(--_corner-surface) * var(--ui-corner-tr, 1))
|
|
47
|
+
calc(var(--_corner-surface) * var(--ui-corner-br, 1)) calc(var(--_corner-surface) * var(--ui-corner-bl, 1))
|
|
48
|
+
);
|
|
49
|
+
/* The painted layer: a pattern, a texture, or a gradient an aesthetic lays
|
|
50
|
+
over the plate. Surfaces only, like the backdrop, so a control's label and
|
|
51
|
+
`.select`'s chevron never sit on one. The aesthetic that sets it owns the
|
|
52
|
+
contrast of the text over it, and a consumer's own `background-image` still
|
|
53
|
+
beats this declaration. */
|
|
54
|
+
background-image: var(--ui-surface-image, none);
|
|
55
|
+
/* Prefixed property first, deliberately: written second the minifier folds the
|
|
56
|
+
pair into the prefixed one alone, and the standard property never reaches
|
|
57
|
+
`dist/` -- which drops the blur in Firefox (the one engine with
|
|
58
|
+
`backdrop-filter` and not the `-webkit-` alias). Measured on the built CSS. */
|
|
59
|
+
-webkit-backdrop-filter: var(--ui-backdrop, none);
|
|
60
|
+
backdrop-filter: var(--ui-backdrop, none);
|
|
61
|
+
}
|
package/dist/tw/theme.css
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/* The package's layer map, declared before anything uses a layer. A browser
|
|
2
|
+
orders layers by the first time it meets each name, so a sheet that opened
|
|
3
|
+
`components` first would rank it below `theme` and `base`; stating Tailwind's
|
|
4
|
+
own order here, and at the top of every entrypoint, makes every load order
|
|
5
|
+
rank the same. The four class families -- intent, presentation, elevation,
|
|
6
|
+
and every aesthetic -- sit in `components`, below every utility, so a
|
|
7
|
+
consumer's own utility or unlayered CSS beats them. Components are
|
|
8
|
+
`@utility` rules in `utilities`. See docs/internal/model.md#cascade-layers. */
|
|
9
|
+
@layer theme, base, components, utilities;
|
|
10
|
+
|
|
11
|
+
@import "tailwindcss/theme.css" layer(theme);
|
|
12
|
+
@import "./intent.css" layer(components);
|
|
13
|
+
@import "./presentation.css" layer(components);
|
|
14
|
+
@import "./elevation.css" layer(components);
|
|
15
|
+
@import "./corner.css" layer(components);
|
|
5
16
|
/* Everything that is not `src/` -- see the note in `index.css`. Keeps a
|
|
6
17
|
`--color-*` reference in a docs example from shipping as a real theme entry. */
|
|
7
18
|
|
|
@@ -24,30 +35,22 @@
|
|
|
24
35
|
--color-accent-hover: light-dark(var(--color-neutral-700), var(--color-neutral-300));
|
|
25
36
|
--color-accent-subtle: light-dark(var(--color-neutral-100), var(--color-neutral-800));
|
|
26
37
|
--color-accent-strong: light-dark(var(--color-neutral-800), var(--color-neutral-200));
|
|
27
|
-
/*
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
same tone landed the two 4 RGB units apart, which reads as no boundary at
|
|
32
|
-
all. Dark's neutral-700 pairing already separated -- 27 units, still under
|
|
33
|
-
3:1 but at least legible -- so only light moved. Neutral-400 is not a new
|
|
34
|
-
number: `.chunky-tile` already measured it at 2.48:1 against the light
|
|
35
|
-
page and picked it for the same reason, "the heaviest grey that still
|
|
36
|
-
leaves a field legible." Dark is unchanged. */
|
|
38
|
+
/* Neutral-400 in light: the heaviest grey that still leaves a field legible
|
|
39
|
+
(2.48:1 against the page). A lighter line lands within a few RGB units of a
|
|
40
|
+
`.card.soft.edged`'s own fill and reads as no boundary. Dark's neutral-700
|
|
41
|
+
already separates (27 units). */
|
|
37
42
|
--color-border: light-dark(var(--color-neutral-400), var(--color-neutral-700));
|
|
38
43
|
|
|
39
44
|
/* A text control's line is the only thing identifying it: the box is
|
|
40
45
|
transparent at rest, so nothing else marks where typing goes.
|
|
41
|
-
`--color-border` is
|
|
42
|
-
which is a line you can barely see.
|
|
46
|
+
`--color-border` is the quiet surface line, too light for that.
|
|
43
47
|
|
|
44
|
-
One value rather than a `light-dark()` pair
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
docs/accessibility.md. */
|
|
48
|
+
One value rather than a `light-dark()` pair: the page is neutral-50 in
|
|
49
|
+
light and neutral-950 in dark, so a tone halfway between them reads on both
|
|
50
|
+
grounds at once. A text control draws it at `--_line-rest` rather than
|
|
51
|
+
whole, which keeps the field quiet while leaving the pointer somewhere to
|
|
52
|
+
go, so the line as painted lands under the 3:1 WCAG 1.4.11 asks of a
|
|
53
|
+
control boundary. Recorded in docs/accessibility.md. */
|
|
51
54
|
--color-control-border: var(--color-neutral-500);
|
|
52
55
|
--color-background: light-dark(var(--color-neutral-50), var(--color-neutral-950));
|
|
53
56
|
--color-foreground: light-dark(var(--color-neutral-100), var(--color-neutral-900));
|
|
@@ -57,40 +60,30 @@
|
|
|
57
60
|
--color-text-contrast: light-dark(var(--color-neutral-50), var(--color-neutral-950));
|
|
58
61
|
--color-text-hover: light-dark(var(--color-neutral-700), var(--color-neutral-200));
|
|
59
62
|
/* No `--color-text-subtle`: the neutral intent points `--intent-subtle` at
|
|
60
|
-
`--color-surface`, not at a text-family tint
|
|
61
|
-
fill had no reader here. */
|
|
63
|
+
`--color-surface`, not at a text-family tint. */
|
|
62
64
|
--color-text-strong: light-dark(var(--color-neutral-900), var(--color-neutral-100));
|
|
63
65
|
|
|
64
|
-
/* The tooltip bubble is
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
light got a mid-grey slab two steps darker than every card on the screen,
|
|
70
|
-
carrying dark ink. Measured, the light bubble was neutral-400 at 2.01:1
|
|
71
|
-
against the page while the dark one was 2.27:1 the other way.
|
|
66
|
+
/* The tooltip bubble's plate is chosen per theme rather than derived: 20% of
|
|
67
|
+
the page's ink over the surface tone steps lighter than the surface on a
|
|
68
|
+
dark page and darker on a light one, and one derivation cannot serve both
|
|
69
|
+
directions. Both ends read as lifted: near-white over a light page, a mid
|
|
70
|
+
grey over a dark one.
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
one. Near-white is only 1.04:1 against the page, so the bubble draws a
|
|
76
|
-
hairline as well -- see `components/tooltip.css`. */
|
|
72
|
+
Near-white is only 1.04:1 against the page, so the bubble draws a hairline
|
|
73
|
+
as well -- see `components/tooltip.css`. */
|
|
77
74
|
--color-tooltip: light-dark(var(--color-white), var(--color-neutral-700));
|
|
78
75
|
--color-tooltip-contrast: light-dark(var(--color-neutral-900), var(--color-neutral-100));
|
|
79
76
|
|
|
80
|
-
/* Emerald-700 rather than 600
|
|
81
|
-
WCAG 1.4.3 asks 4.5:1, and white on emerald-600 measures 3.50:1
|
|
82
|
-
only as large text, which `.btn` is not. One step darker carries the white
|
|
83
|
-
label and leaves the hue where it was. */
|
|
77
|
+
/* Emerald-700 rather than 600: a filled button's label is normal-size text
|
|
78
|
+
(WCAG 1.4.3 asks 4.5:1), and white on emerald-600 measures 3.50:1. */
|
|
84
79
|
--color-success: var(--color-emerald-700);
|
|
85
80
|
--color-success-contrast: var(--color-neutral-50);
|
|
86
|
-
/* Baked rather than mixed live
|
|
87
|
-
levels deeper to compose the fill and the edge, and three
|
|
88
|
-
`color-mix()` over a `light-dark()`
|
|
89
|
-
hover.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
screenshotted back so the blend stays exact; re-bake it if the base or
|
|
93
|
-
strong tone it was mixed from changes. */
|
|
81
|
+
/* Baked rather than mixed live: `box-hover` nests this token two
|
|
82
|
+
`color-mix()` levels deeper to compose the fill and the edge, and three
|
|
83
|
+
levels of `color-mix()` over a `light-dark()` crash the WebKit renderer on
|
|
84
|
+
hover. Each pinned pair below is `color-mix(in srgb, <base> 82%,
|
|
85
|
+
<strong>)`, resolved once per theme; re-bake it if the base or strong tone
|
|
86
|
+
it was mixed from changes. */
|
|
94
87
|
--color-success-hover: light-dark(#007552, #008e66);
|
|
95
88
|
/* The dark ground is `-900` to make room for the `-300` ink above it. `-subtle`
|
|
96
89
|
is what `kbd`, `code`, `pre` and the tooltip bubble rest on, and those are the
|
|
@@ -99,21 +92,16 @@
|
|
|
99
92
|
WCAG 1.4.3 asks of normal text -- and the other three sat at 4.9 to 5.0 with
|
|
100
93
|
no headroom. One step darker puts all four past 9:1. */
|
|
101
94
|
--color-success-subtle: light-dark(var(--color-emerald-100), var(--color-emerald-900));
|
|
102
|
-
/* The dark tone is `-300`, not `-100
|
|
103
|
-
-- the label of a soft badge, the dot of a soft radio -- and on a
|
|
104
|
-
the `-100` shade of every hue is a pale tint that reads white at
|
|
105
|
-
sizes.
|
|
106
|
-
|
|
107
|
-
radio's dot stopped matching its own ring. `-300` keeps the hue and stays
|
|
108
|
-
far above the contrast a mark or a label needs. Light keeps `-800`, which
|
|
109
|
-
was never the problem: it is saturated and dark against a light page. */
|
|
95
|
+
/* The dark tone is `-300`, not `-100`: `-strong` is what a partial fill
|
|
96
|
+
prints -- the label of a soft badge, the dot of a soft radio -- and on a
|
|
97
|
+
dark page the `-100` shade of every hue is a pale tint that reads white at
|
|
98
|
+
small sizes. `-300` keeps the hue. Light keeps `-800`, which is saturated
|
|
99
|
+
and dark against a light page. */
|
|
110
100
|
--color-success-strong: light-dark(var(--color-emerald-800), var(--color-emerald-300));
|
|
111
|
-
/* Amber
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
what a warning looks like nearly everywhere, and it is the only intent whose
|
|
116
|
-
contrast tone is not the page colour. */
|
|
101
|
+
/* Amber cannot solve contrast by darkening: white on amber-600 measures
|
|
102
|
+
3.07:1, and amber dark enough to carry white reads brown. So the ink moves
|
|
103
|
+
instead of the hue: dark ink on amber is what a warning looks like, and
|
|
104
|
+
warning is the only intent whose contrast tone is not the page colour. */
|
|
117
105
|
--color-warning: var(--color-amber-600);
|
|
118
106
|
--color-warning-contrast: var(--color-neutral-950);
|
|
119
107
|
/* Same crash, same fix as `--color-success-hover` above. */
|
|
@@ -134,7 +122,6 @@
|
|
|
134
122
|
--color-info-strong: light-dark(var(--color-indigo-800), var(--color-indigo-300));
|
|
135
123
|
|
|
136
124
|
--breakpoint-xs: 30rem;
|
|
137
|
-
--breakpoint-2xl: 90rem;
|
|
138
125
|
}
|
|
139
126
|
|
|
140
127
|
/* Tailwind only keeps a `@theme` variable in the compiled output when it
|
|
@@ -151,111 +138,112 @@
|
|
|
151
138
|
--color-border-hover: light-dark(var(--color-neutral-600), var(--color-neutral-500));
|
|
152
139
|
}
|
|
153
140
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
--layout-section-block: clamp(4rem, 9vw, 7rem);
|
|
164
|
-
--layout-gap: 1rem;
|
|
165
|
-
--layout-grid-min: 16rem;
|
|
166
|
-
|
|
167
|
-
--radius-small: 0.25rem;
|
|
168
|
-
--radius-control: 0.5rem;
|
|
169
|
-
--radius-surface: 0.875rem;
|
|
170
|
-
--control-height: 2.5rem;
|
|
171
|
-
--border-width: 1px;
|
|
141
|
+
/* The foundation tokens and the colour-scheme selectors, in `theme` so every
|
|
142
|
+
class family in `components` beats them on the element both land on -- an
|
|
143
|
+
aesthetic on `<html>` keeps its own press and depth colour over `:root`'s,
|
|
144
|
+
whatever order the sheets load in. */
|
|
145
|
+
@layer theme {
|
|
146
|
+
:root {
|
|
147
|
+
/* Both keywords: the system preference decides, and the explicit theme
|
|
148
|
+
selectors below override it at any depth because `color-scheme` inherits. */
|
|
149
|
+
color-scheme: light dark;
|
|
172
150
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
modifier classes (`.elevation-none`/`.elevation-sm`/`.elevation-md` in
|
|
181
|
-
elevation.css) share this naming on purpose -- same weight, same word --
|
|
182
|
-
though they multiply an aesthetic's shadow parts rather than declaring a
|
|
183
|
-
fixed value, so the two are not read from the same place. */
|
|
184
|
-
--elevation-none: none;
|
|
185
|
-
--elevation-sm: 0 1px 2px var(--elevation-color), 0 1px 3px var(--elevation-color);
|
|
186
|
-
--elevation-md: 0 4px 6px -1px var(--elevation-color), 0 2px 4px -2px var(--elevation-color);
|
|
187
|
-
--elevation-lg: 0 20px 25px -5px var(--elevation-color), 0 8px 10px -6px var(--elevation-color);
|
|
151
|
+
--container-narrow: 48rem;
|
|
152
|
+
--container-max: 80rem;
|
|
153
|
+
--container-wide: 90rem;
|
|
154
|
+
--layout-gutter: clamp(1rem, 4vw, 2rem);
|
|
155
|
+
--layout-section-block: clamp(4rem, 9vw, 7rem);
|
|
156
|
+
--layout-gap: 1rem;
|
|
157
|
+
--layout-grid-min: 16rem;
|
|
188
158
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
thickening -- which is what a focus indicator is meant to look like.
|
|
159
|
+
--radius-small: 0.25rem;
|
|
160
|
+
--radius-control: 0.5rem;
|
|
161
|
+
--radius-surface: 0.875rem;
|
|
162
|
+
--control-height: 2.5rem;
|
|
163
|
+
--border-width: 1px;
|
|
195
164
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
165
|
+
/* `light-dark()` takes colors only, so the shadow color is the token and the
|
|
166
|
+
three elevations compose from it. Dark themes need a much heavier color to
|
|
167
|
+
read at all against a dark background. */
|
|
168
|
+
--elevation-color: light-dark(rgb(15 23 42 / 0.08), rgb(0 0 0 / 0.55));
|
|
169
|
+
/* Four rungs: no shadow, then a Tailwind-style sm/md/lg progression, so a
|
|
170
|
+
consumer unsure which step to reach for has a named "no depth" value
|
|
171
|
+
instead of inferring it by omitting a shadow declaration. These are raw
|
|
172
|
+
shadows for elements a consumer styles; the elevation modifier classes
|
|
173
|
+
(`.flat`/`.raised`/`.floating` in elevation.css) multiply an aesthetic's
|
|
174
|
+
shadow parts instead, and are not read from here. */
|
|
175
|
+
--elevation-none: none;
|
|
176
|
+
--elevation-sm: 0 1px 2px var(--elevation-color), 0 1px 3px var(--elevation-color);
|
|
177
|
+
--elevation-md: 0 4px 6px -1px var(--elevation-color), 0 2px 4px -2px var(--elevation-color);
|
|
178
|
+
--elevation-lg: 0 20px 25px -5px var(--elevation-color), 0 8px 10px -6px var(--elevation-color);
|
|
200
179
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
180
|
+
--focus-ring: color-mix(in srgb, var(--color-primary) 64%, transparent);
|
|
181
|
+
/* `--focus-ring-offset` is not declared: its default is the negative of the
|
|
182
|
+
line width of whatever is focused, which only the focused element knows,
|
|
183
|
+
so the outline computes it there -- see `reset.css`. Declared here it
|
|
184
|
+
resolved against the base 1px once and inherited, and under a 2px
|
|
185
|
+
aesthetic the ring covered half the edge and left a stripe of it inside. */
|
|
186
|
+
--focus-ring-width: 3px;
|
|
205
187
|
|
|
206
|
-
|
|
188
|
+
--motion-duration-fast: 120ms;
|
|
189
|
+
--motion-duration-normal: 200ms;
|
|
190
|
+
--motion-duration-slow: 400ms;
|
|
191
|
+
--motion-ease: cubic-bezier(0.2, 0, 0, 1);
|
|
207
192
|
|
|
208
|
-
|
|
209
|
-
on top of whatever the element rests at. */
|
|
210
|
-
--hover-step: 14%;
|
|
211
|
-
}
|
|
193
|
+
--z-popover: 40;
|
|
212
194
|
|
|
213
|
-
/* The
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
inside it. */
|
|
218
|
-
:root {
|
|
219
|
-
--ui-active-transform: scale(0.97);
|
|
220
|
-
}
|
|
195
|
+
/* The one number the derived-hover formula spends: how much fill a hover adds
|
|
196
|
+
on top of whatever the element rests at. */
|
|
197
|
+
--hover-step: 14%;
|
|
198
|
+
}
|
|
221
199
|
|
|
222
|
-
|
|
200
|
+
/* The base look answers a press with a small scale-in. `box-active` applies it on
|
|
201
|
+
`.btn` and `.card.interactive`; every aesthetic sets its own
|
|
202
|
+
`--ui-active-transform` on its class, in `components`, so those win over
|
|
203
|
+
this whichever sheet loads first. Reduced motion opts out. Kept outside `@theme` because a media query cannot go
|
|
204
|
+
inside it. */
|
|
223
205
|
:root {
|
|
224
|
-
--ui-active-transform:
|
|
206
|
+
--ui-active-transform: scale(0.97);
|
|
225
207
|
}
|
|
226
|
-
}
|
|
227
208
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
209
|
+
@media (prefers-reduced-motion: reduce) {
|
|
210
|
+
:root {
|
|
211
|
+
--ui-active-transform: none;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
233
214
|
|
|
234
|
-
.
|
|
235
|
-
.theme-
|
|
236
|
-
[data-theme="
|
|
237
|
-
|
|
215
|
+
.light,
|
|
216
|
+
.theme-light,
|
|
217
|
+
[data-theme="light"] {
|
|
218
|
+
color-scheme: light;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.dark,
|
|
222
|
+
.theme-dark,
|
|
223
|
+
[data-theme="dark"] {
|
|
224
|
+
color-scheme: dark;
|
|
225
|
+
}
|
|
238
226
|
}
|
|
239
227
|
|
|
240
228
|
/* The first arm mirrors the `color-scheme: dark` block above, all three
|
|
241
|
-
spellings included
|
|
242
|
-
was otherwise left without the matching `dark:` variant.
|
|
229
|
+
spellings included.
|
|
243
230
|
|
|
244
|
-
The second arm
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
light
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
231
|
+
The second arm follows the OS preference. `--color-*` tokens resolve through
|
|
232
|
+
`light-dark()` against the computed `color-scheme`, so with no explicit
|
|
233
|
+
selector `:root`'s `color-scheme: light dark` already follows it -- see
|
|
234
|
+
`concepts.md#theme-selection`. A selector list cannot query computed style,
|
|
235
|
+
but it can match "OS prefers dark, and not under a forced-light subtree" with
|
|
236
|
+
a plain descendant exclusion: `:not(:where(.light, .light *, ...))` matches
|
|
237
|
+
only when no ancestor (or the element itself) carries one of the three light
|
|
238
|
+
spellings. The exclusion cannot see a subtree forced light by other means,
|
|
239
|
+
where `light-dark()` would still honor it; that is out of contract, since
|
|
240
|
+
those three spellings are the only supported way to force a theme.
|
|
241
|
+
|
|
242
|
+
Each arm is independent, so an explicit selector wins over the OS preference
|
|
243
|
+
at any depth. It does not win over an explicit selector of the opposite
|
|
244
|
+
theme further up: `.dark *` matches every descendant of a `.dark` element,
|
|
245
|
+
including one inside a nested `.light`, so a `dark:` utility still fires
|
|
246
|
+
there although its tokens are light. `setup.md#dark-variant` documents it. */
|
|
259
247
|
@custom-variant dark {
|
|
260
248
|
&:where(.dark, .dark *, .theme-dark, .theme-dark *, [data-theme="dark"], [data-theme="dark"] *) {
|
|
261
249
|
@slot;
|
|
@@ -267,3 +255,18 @@
|
|
|
267
255
|
}
|
|
268
256
|
}
|
|
269
257
|
}
|
|
258
|
+
|
|
259
|
+
/* Forced colours draw the plain version of every structural part: a solid line
|
|
260
|
+
and nothing an aesthetic layers on top. Unlayered, so it beats every layered
|
|
261
|
+
declaration of these tokens -- an aesthetic's and a consumer's utility alike --
|
|
262
|
+
the way the other `forced-colors` blocks do. See
|
|
263
|
+
docs/internal/model.md#material-tokens. */
|
|
264
|
+
@media (forced-colors: active) {
|
|
265
|
+
* {
|
|
266
|
+
--ui-line-style: solid;
|
|
267
|
+
--ui-rule-style: solid;
|
|
268
|
+
--ui-halo-ink: initial;
|
|
269
|
+
--ui-surface-image: none;
|
|
270
|
+
--ui-label-shadow: none;
|
|
271
|
+
}
|
|
272
|
+
}
|