@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
|
@@ -1,113 +1,155 @@
|
|
|
1
1
|
/* Cyber: bevelled corners, a thin bright edge, and a glow in the component's own
|
|
2
|
-
colour. See docs/internal/
|
|
2
|
+
colour. See docs/internal/model.md#cyber for the decision.
|
|
3
3
|
|
|
4
4
|
Tier 1 only. The bevel is `corner-shape`, not `clip-path`: a clip removes
|
|
5
5
|
every outer shadow, so a clipped chamfer has nothing left to glow, and an
|
|
6
6
|
inset ring cannot follow a diagonal cut the way `.pixel`'s follows its square
|
|
7
7
|
one. `corner-shape` draws the corner itself, and the border, the glow, and the
|
|
8
|
-
focus outline all follow it.
|
|
9
|
-
.cyber {
|
|
10
|
-
/* Knobs, read with a fallback so an ancestor can set them (R8; see
|
|
11
|
-
model.md#rules-for-aesthetics). One cut size for controls and surfaces
|
|
12
|
-
alike, so a button and the card around it read as the same material; the
|
|
13
|
-
two shapes then place that cut on opposite diagonals, so they read as
|
|
14
|
-
different things. A shape knob takes any `border-radius` value -- the
|
|
15
|
-
documented presets are a hexagon, a parallelogram, and a single notch --
|
|
16
|
-
and the cut knob scales the default pair without restating it.
|
|
17
|
-
|
|
18
|
-
The cut is a ceiling, not a size: the default pair caps it at a quarter of
|
|
19
|
-
the element's own box, so a 40px button or a card takes the full cut and a
|
|
20
|
-
19px icon button about 5px rather than having its corners eaten. Nothing in
|
|
21
|
-
the model scales a corner with a component's size -- `.sm` and `.dense`
|
|
22
|
-
change padding and height, never the radius -- and a radius percentage is
|
|
23
|
-
the one length that already knows the box. Horizontal percentages resolve
|
|
24
|
-
against the width and vertical ones against the height, so on a small wide
|
|
25
|
-
button the cut runs slightly shallower than 45 degrees; at those sizes that
|
|
26
|
-
is not visible. A shape set through the knob is taken as written. */
|
|
27
|
-
--_cyber-cut: var(--cyber-cut, 0.625rem);
|
|
28
|
-
--_cyber-glow: var(--cyber-glow, 8px);
|
|
29
|
-
--_cyber-shape: var(--cyber-shape, min(var(--_cyber-cut), 25%) 0 / min(var(--_cyber-cut), 25%) 0);
|
|
30
|
-
--_cyber-shape-surface: var(--cyber-shape-surface, 0 min(var(--_cyber-cut), 25%) / 0 min(var(--_cyber-cut), 25%));
|
|
31
|
-
|
|
32
|
-
/* The neutral line. A neon hue is the look's signature, but hue is intent's
|
|
33
|
-
(R1), so the default follows the theme the way neobrutalism's and pixel's
|
|
34
|
-
ink does, and `--cyber-ink` lets an application choose a neon neutral
|
|
35
|
-
without the aesthetic imposing one on every palette. A named intent keeps
|
|
36
|
-
its own edge colour. */
|
|
37
|
-
--ui-ink: var(--cyber-ink, light-dark(var(--color-neutral-950), var(--color-neutral-50)));
|
|
38
|
-
|
|
39
|
-
/* Controls cut top-left and bottom-right, surfaces top-right and bottom-left.
|
|
40
|
-
Anything fully round -- a badge, a switch, the progress track, the tooltip
|
|
41
|
-
icon, and through their own full radius a radio and a `.btn.pill` -- cuts
|
|
42
|
-
all the way to points: a diamond, or a hexagon on something wider than it
|
|
43
|
-
is tall. Chips square outright, which is what tells the checkbox from the
|
|
44
|
-
radio beside it. */
|
|
45
|
-
--ui-corner-shape: bevel;
|
|
46
|
-
--ui-radius: var(--_cyber-shape);
|
|
47
|
-
--ui-radius-surface: var(--_cyber-shape-surface);
|
|
48
|
-
--ui-radius-pill: calc(infinity * 1px);
|
|
49
|
-
--ui-radius-tight: 0;
|
|
50
|
-
--ui-border-width: 1px;
|
|
51
|
-
--ui-border-max: 100px;
|
|
52
|
-
|
|
53
|
-
/* The glow: the component's own ink, blurred, with no offset. `box` mixes
|
|
54
|
-
depth as `color-mix(in oklab, var(--intent-border) var(--ui-shadow-ink),
|
|
55
|
-
var(--elevation-color))`, so a transparent depth colour turns 40% ink into
|
|
56
|
-
the intent colour at 40% alpha rather than into a darker shade of it.
|
|
57
|
-
Elevation scales the blur like every shadow, so the glow lights what rests
|
|
58
|
-
above zero -- buttons and cards -- and anything raised, and leaves fields
|
|
59
|
-
and chips crisp. */
|
|
60
|
-
--elevation-color: transparent;
|
|
61
|
-
--ui-shadow-ink: 40%;
|
|
62
|
-
--ui-shadow-x: 0px;
|
|
63
|
-
--ui-shadow-y: 0px;
|
|
64
|
-
--ui-shadow-blur: var(--_cyber-glow);
|
|
65
|
-
--ui-shadow-spread: 0px;
|
|
66
|
-
|
|
67
|
-
/* The glow holds still under the pointer and under a press; the press is the
|
|
68
|
-
base scale, restated because a region nested inside another aesthetic must
|
|
69
|
-
not inherit that one's travel. Reduced motion drops it below. */
|
|
70
|
-
--ui-hover-transform: none;
|
|
71
|
-
--ui-active-shadow-x: 0px;
|
|
72
|
-
--ui-active-shadow-y: 0px;
|
|
73
|
-
--ui-active-transform: scale(0.97);
|
|
74
|
-
|
|
75
|
-
/* Nothing here clips, blurs, thins its fill, or draws its edge as a ring, and
|
|
76
|
-
an aesthetic nested inside one that does has to say so rather than inherit
|
|
77
|
-
it. */
|
|
78
|
-
--ui-clip: none;
|
|
79
|
-
--ui-clip-tight: none;
|
|
80
|
-
--ui-shadow-inset: ;
|
|
81
|
-
--ui-shadow-edge: initial;
|
|
82
|
-
--ui-backdrop: none;
|
|
83
|
-
--ui-bg-alpha: 1;
|
|
84
|
-
|
|
85
|
-
/* Consumer-supplied, the way `.pixel` reads `--font-pixel`. The package ships
|
|
86
|
-
no font binary; the monospace stack is the closest technical face every
|
|
87
|
-
platform already has. */
|
|
88
|
-
font-family: var(--font-cyber, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
89
|
-
}
|
|
8
|
+
focus outline all follow it. */
|
|
90
9
|
|
|
91
|
-
/*
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@
|
|
10
|
+
/* The layer map, stated before anything uses a layer -- see `theme.css`. The
|
|
11
|
+
aesthetic's token class sits in `components`, below every utility, so a
|
|
12
|
+
consumer's own utility or unlayered CSS beats it; the solo class stays
|
|
13
|
+
unlayered, because it has to beat a foreign component's unlayered CSS. See
|
|
14
|
+
docs/internal/model.md#cascade-layers. */
|
|
15
|
+
@layer theme, base, components, utilities;
|
|
16
|
+
|
|
17
|
+
@layer components {
|
|
97
18
|
.cyber {
|
|
98
|
-
|
|
99
|
-
|
|
19
|
+
/* Knobs, read with a fallback so an ancestor can set them (R8; see
|
|
20
|
+
model.md#rules-for-aesthetics). One cut size for controls and surfaces
|
|
21
|
+
alike, so a button and the card around it read as the same material; the
|
|
22
|
+
two shapes then place that cut on opposite diagonals, so they read as
|
|
23
|
+
different things. A shape knob takes any `border-radius` value -- the
|
|
24
|
+
documented presets are a hexagon, a parallelogram, and a single notch --
|
|
25
|
+
and the cut knob sizes the default pair without restating it.
|
|
26
|
+
|
|
27
|
+
The cut is the corner length at scale 1, and `box` multiplies it by the
|
|
28
|
+
size step, so a `.p-xs` icon button takes half the cut and a `.lg` button
|
|
29
|
+
a quarter more -- always a true 45 degrees, because it stays a length. A
|
|
30
|
+
shape set through a knob is taken as written, unscaled: it is read as
|
|
31
|
+
`--ui-radius`, which `box` honours over the computed corner, and left
|
|
32
|
+
unset the reference makes the token guaranteed-invalid so the computed
|
|
33
|
+
corner draws. */
|
|
34
|
+
--_cyber-cut: var(--cyber-cut, 0.625rem);
|
|
35
|
+
--_cyber-glow: var(--cyber-glow, 8px);
|
|
36
|
+
|
|
37
|
+
/* The neutral line. A neon hue is the look's signature, but hue is intent's
|
|
38
|
+
(R1), so the default follows the theme the way neobrutalism's and pixel's
|
|
39
|
+
ink does, and `--cyber-ink` lets an application choose a neon neutral
|
|
40
|
+
without the aesthetic imposing one on every palette. A named intent keeps
|
|
41
|
+
its own edge colour. */
|
|
42
|
+
--ui-ink: var(--cyber-ink, light-dark(var(--color-neutral-950), var(--color-neutral-50)));
|
|
43
|
+
|
|
44
|
+
/* Controls cut top-left and bottom-right, through the four corner switches
|
|
45
|
+
every component reads; surfaces cut top-right and bottom-left, which the
|
|
46
|
+
switches cannot say at the same time, so their shape is written whole and
|
|
47
|
+
does not scale -- a card is large enough that the full cut is right at
|
|
48
|
+
every padding. Anything fully round -- a badge, a switch, the progress
|
|
49
|
+
track, the tooltip icon, and through their own full radius a radio and a
|
|
50
|
+
`.btn.pill` -- cuts all the way to points: a diamond, or a hexagon on
|
|
51
|
+
something wider than it is tall. Chips square outright, which is what
|
|
52
|
+
tells the checkbox from the radio beside it. */
|
|
53
|
+
--ui-corner-shape: bevel;
|
|
54
|
+
--ui-corner: var(--_cyber-cut);
|
|
55
|
+
--ui-corner-surface: var(--_cyber-cut);
|
|
56
|
+
--ui-corner-tl: 1;
|
|
57
|
+
--ui-corner-tr: 0;
|
|
58
|
+
--ui-corner-br: 1;
|
|
59
|
+
--ui-corner-bl: 0;
|
|
60
|
+
--ui-radius: var(--cyber-shape);
|
|
61
|
+
--ui-radius-surface: var(--cyber-shape-surface, 0 var(--_cyber-cut) / 0 var(--_cyber-cut));
|
|
62
|
+
--ui-radius-pill: calc(infinity * 1px);
|
|
63
|
+
--ui-radius-tight: 0;
|
|
64
|
+
--ui-border-width: 1px;
|
|
65
|
+
--ui-border-max: 100px;
|
|
66
|
+
--ui-line-style: initial;
|
|
67
|
+
--ui-rule-style: initial;
|
|
68
|
+
|
|
69
|
+
/* The glow is light, not depth, so it is the halo: the component's own intent
|
|
70
|
+
colour at 40%, blurred, with no offset. Elevation does not scale it, so
|
|
71
|
+
every component glows -- fields and chips included -- and `.flat` does
|
|
72
|
+
not put it out. There is no depth, and the transparent depth colour keeps
|
|
73
|
+
`.raised` and `.floating` from drawing any. */
|
|
74
|
+
--elevation-color: transparent;
|
|
75
|
+
--ui-shadow-ink: 0%;
|
|
76
|
+
--ui-shadow-x: 0px;
|
|
77
|
+
--ui-shadow-y: 0px;
|
|
78
|
+
--ui-shadow-blur: 0px;
|
|
79
|
+
--ui-shadow-spread: 0px;
|
|
80
|
+
--ui-halo-blur: var(--_cyber-glow);
|
|
81
|
+
--ui-halo-spread: 0px;
|
|
82
|
+
--ui-halo-ink: 40%;
|
|
83
|
+
|
|
84
|
+
/* The glow holds still under the pointer and under a press; the press is the
|
|
85
|
+
base scale, restated because a region nested inside another aesthetic must
|
|
86
|
+
not inherit that one's travel. Reduced motion drops it below. */
|
|
87
|
+
--ui-hover-transform: none;
|
|
88
|
+
--ui-active-shadow-x: 0px;
|
|
89
|
+
--ui-active-shadow-y: 0px;
|
|
90
|
+
--ui-active-transform: scale(0.97);
|
|
91
|
+
--ui-active-translate-x: initial;
|
|
92
|
+
--ui-active-translate-y: initial;
|
|
93
|
+
|
|
94
|
+
/* Nothing here clips, blurs, thins its fill, or draws its edge as a ring, and
|
|
95
|
+
an aesthetic nested inside one that does has to say so rather than inherit
|
|
96
|
+
it. */
|
|
97
|
+
--ui-clip: none;
|
|
98
|
+
--ui-clip-tight: none;
|
|
99
|
+
--ui-shadow-inset: ;
|
|
100
|
+
--ui-shadow-edge: initial;
|
|
101
|
+
/* The bevel is a corner shape, not a clip, so the global focus outline
|
|
102
|
+
follows it; pixel's inset focus layer is cleared rather than inherited. */
|
|
103
|
+
--ui-focus-inset: initial;
|
|
104
|
+
--ui-backdrop: none;
|
|
105
|
+
--ui-surface-ground: initial;
|
|
106
|
+
--ui-surface-shadow: initial;
|
|
107
|
+
--ui-surface-image: initial;
|
|
108
|
+
--ui-bg-alpha: 1;
|
|
109
|
+
|
|
110
|
+
/* Consumer-supplied, the way `.pixel` reads `--font-pixel`. The package ships
|
|
111
|
+
no font binary; the monospace stack is the closest technical face every
|
|
112
|
+
platform already has. */
|
|
113
|
+
font-family: var(--font-cyber, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
|
|
114
|
+
|
|
115
|
+
/* A control's neutral line is the theme's control border, not this
|
|
116
|
+
aesthetic's ink: that ink is the primary colour in both themes, so every
|
|
117
|
+
unchecked toggle and resting field drew its boundary as loudly as a
|
|
118
|
+
primary one. Named rather than cleared, so a region nested inside glass
|
|
119
|
+
or chunky tile does not inherit theirs either. */
|
|
120
|
+
--ui-control-ink: var(--color-control-border);
|
|
121
|
+
--ui-rule-ink: initial;
|
|
122
|
+
|
|
123
|
+
/* Chunky tile's heavier label, cleared so a region nested inside it keeps
|
|
124
|
+
the button's and the badge's own label. */
|
|
125
|
+
--ui-label-weight: initial;
|
|
126
|
+
--ui-label-tracking: initial;
|
|
127
|
+
--ui-label-case: initial;
|
|
128
|
+
--ui-label-shadow: initial;
|
|
100
129
|
}
|
|
101
|
-
}
|
|
102
130
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
131
|
+
/* Firefox and Safari do not draw `corner-shape` yet, and there the radius would
|
|
132
|
+
round the corners instead. A rounded cyber element reads as a different
|
|
133
|
+
aesthetic; a square one reads as the same aesthetic simplified. What is fully
|
|
134
|
+
round stays round rather than square: a square radio is a checkbox, and a
|
|
135
|
+
pill is the nearest a diamond gets without a bevel. */
|
|
136
|
+
@supports not (corner-shape: bevel) {
|
|
137
|
+
.cyber {
|
|
138
|
+
--ui-corner: 0;
|
|
139
|
+
--ui-radius: 0;
|
|
140
|
+
--ui-radius-surface: 0;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
@media (prefers-reduced-motion: reduce) {
|
|
145
|
+
.cyber {
|
|
146
|
+
--ui-active-transform: none;
|
|
147
|
+
}
|
|
106
148
|
}
|
|
107
149
|
}
|
|
108
150
|
|
|
109
151
|
/* Solo: the same look painted onto the one element carrying the class, for an
|
|
110
|
-
element the package does not style. See docs/internal/solo-
|
|
152
|
+
element the package does not style. See docs/internal/model.md#solo-classes and
|
|
111
153
|
the note on `.glass-solo`: own knobs and foundation tokens with literal
|
|
112
154
|
fallbacks, never a shared `--ui-*`, `--intent-*`, or `--elevation-color`
|
|
113
155
|
token, unlayered, and not for this package's own components. */
|
|
@@ -1,80 +1,290 @@
|
|
|
1
1
|
/* Glass: translucent surfaces over a blurred backdrop with a hairline highlight
|
|
2
|
-
edge. It needs something behind it to blur -- on a flat page background it is
|
|
3
|
-
translucent panel and nothing more.
|
|
4
|
-
|
|
5
|
-
Tier 1 only. The scope that used to take a hand-written selector list is now a
|
|
6
|
-
token: only `surface` resolves `--ui-backdrop`, so blur reaches cards,
|
|
7
|
-
panels, alerts and tooltips while controls stay solid. An active blur costs a
|
|
8
|
-
compositing layer apiece, and one under every control of a dense cluster reads
|
|
9
|
-
as noise. */
|
|
10
|
-
.glass {
|
|
11
|
-
/* Knobs read with a fallback so an ancestor can set them -- declared here they
|
|
12
|
-
outrank `:root` (R8; see model.md#rules-for-aesthetics). Glass is softer
|
|
13
|
-
than the base geometry because a translucent tight corner reads as a
|
|
14
|
-
cut-out; a control/surface pair rather than one value, because they do not
|
|
15
|
-
scale together and 12/16 keeps a button looking at home inside a card. */
|
|
16
|
-
--ui-radius: var(--glass-radius, 0.75rem);
|
|
17
|
-
--ui-radius-surface: var(--glass-radius-surface, 1rem);
|
|
18
|
-
--ui-border-width: 1px;
|
|
19
|
-
--ui-border-max: 100px;
|
|
20
|
-
|
|
21
|
-
/* Glass catches light from above whatever is under it, so the edge is a light
|
|
22
|
-
hairline in both themes rather than a theme-following ink. */
|
|
23
|
-
--ui-ink: light-dark(rgb(255 255 255 / 0.65), rgb(255 255 255 / 0.22));
|
|
24
|
-
|
|
25
|
-
--ui-bg-alpha: 0.8;
|
|
26
|
-
--ui-backdrop: blur(14px) saturate(1.6);
|
|
27
|
-
|
|
28
|
-
/* R6. Controls carry no shadow of their own -- the depth in this aesthetic is
|
|
29
|
-
the blur, and a drop shadow under a translucent button reads as grime. All
|
|
30
|
-
general parts are explicitly zero so inherited or future geometry cannot
|
|
31
|
-
leak onto controls. `--ui-surface-shadow` below replaces the shadow of
|
|
32
|
-
surfaces only. */
|
|
33
|
-
--ui-shadow-x: 0px;
|
|
34
|
-
--ui-shadow-y: 0px;
|
|
35
|
-
--ui-shadow-blur: 0px;
|
|
36
|
-
--ui-shadow-spread: 0px;
|
|
37
|
-
/* `--ui-bg-alpha` multiplies the fill, and a surface's default fill is 0%:
|
|
38
|
-
without a translucent ground the blur ends up behind an opaque background
|
|
39
|
-
and nothing shows through. */
|
|
40
|
-
--ui-surface-ground: color-mix(in srgb, var(--color-background) 45%, transparent);
|
|
41
|
-
|
|
42
|
-
/* Two layers pulling in with negative spread, so the shadow tucks under the
|
|
43
|
-
surface instead of haloing onto the backdrop. It is the one shipped aesthetic
|
|
44
|
-
that cannot be described with the parts, so it takes `surface`'s
|
|
45
|
-
complete-value slot -- which is also what keeps it off buttons and chips --
|
|
46
|
-
and the registry records that `.flat` does not reach it. */
|
|
47
|
-
--ui-surface-shadow: 0 1px 2px -1px var(--elevation-color), 0 18px 40px -16px var(--elevation-color);
|
|
48
|
-
|
|
49
|
-
--ui-clip: none;
|
|
50
|
-
--ui-clip-tight: none;
|
|
51
|
-
/* Round corners, said explicitly, and the pill and chip corners cleared back
|
|
52
|
-
to `--ui-radius`: a region nested inside `.cyber` would otherwise inherit
|
|
53
|
-
its bevel, its diamond pills, and its square chips. */
|
|
54
|
-
--ui-corner-shape: round;
|
|
55
|
-
--ui-radius-pill: initial;
|
|
56
|
-
--ui-radius-tight: initial;
|
|
57
|
-
/* Nothing here draws its edge as a ring, and an aesthetic nested inside pixel
|
|
58
|
-
has to say so rather than inherit it. */
|
|
59
|
-
--ui-shadow-edge: initial;
|
|
60
|
-
--ui-hover-transform: none;
|
|
61
|
-
--ui-active-transform: none;
|
|
62
|
-
}
|
|
2
|
+
edge. It needs something behind it to blur -- on a flat page background it is
|
|
3
|
+
a translucent panel and nothing more.
|
|
63
4
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
5
|
+
Tier 1 only: the scope is a token. Only `surface` resolves `--ui-backdrop`,
|
|
6
|
+
so blur reaches cards, panels, alerts and tooltips while controls stay solid.
|
|
7
|
+
An active blur costs a compositing layer apiece, and one under every control
|
|
8
|
+
of a dense cluster reads as noise. */
|
|
9
|
+
|
|
10
|
+
/* The layer map, stated before anything uses a layer -- see `theme.css`. The
|
|
11
|
+
aesthetic's token class sits in `components`, below every utility, so a
|
|
12
|
+
consumer's own utility or unlayered CSS beats it; the solo class stays
|
|
13
|
+
unlayered, because it has to beat a foreign component's unlayered CSS. See
|
|
14
|
+
docs/internal/model.md#cascade-layers. */
|
|
15
|
+
@layer theme, base, components, utilities;
|
|
16
|
+
|
|
17
|
+
@layer components {
|
|
67
18
|
.glass {
|
|
19
|
+
/* Knobs read with a fallback so an ancestor can set them -- declared here they
|
|
20
|
+
outrank `:root` (R8; see model.md#rules-for-aesthetics). Glass is softer
|
|
21
|
+
than the base geometry because a translucent tight corner reads as a
|
|
22
|
+
cut-out; a control/surface pair rather than one value, because they do not
|
|
23
|
+
scale together and 12/16 keeps a button looking at home inside a card. */
|
|
24
|
+
--ui-corner: var(--glass-radius, 0.75rem);
|
|
25
|
+
--ui-corner-surface: var(--glass-radius-surface, 1rem);
|
|
26
|
+
--ui-border-width: 1px;
|
|
27
|
+
--ui-border-max: 100px;
|
|
28
|
+
--ui-line-style: initial;
|
|
29
|
+
--ui-rule-style: initial;
|
|
30
|
+
|
|
31
|
+
/* Glass catches light from above whatever is under it, so the edge is a light
|
|
32
|
+
hairline in both themes rather than a theme-following ink. */
|
|
33
|
+
--ui-ink: light-dark(rgb(255 255 255 / 0.65), rgb(255 255 255 / 0.22));
|
|
34
|
+
/* A control's boundary is not a pane's edge. The hairline erases an
|
|
35
|
+
unchecked checkbox on a light page (1.33:1 inside a glass card) and leaves
|
|
36
|
+
a field with nothing marking where typing goes, so controls take the
|
|
37
|
+
theme's ink at 55% instead: translucent, so it tints whatever is behind
|
|
38
|
+
the pane rather than drawing a flat grey, and 6.31:1 on a checkbox, 2.31:1
|
|
39
|
+
on a field's 60% resting line, in light. See
|
|
40
|
+
docs/internal/model.md#presentation. */
|
|
41
|
+
--ui-control-ink: light-dark(rgb(0 0 0 / 0.55), rgb(255 255 255 / 0.55));
|
|
42
|
+
/* Nor is a line drawn on a plate: the hairline vanishes on a table's light
|
|
43
|
+
plate, and its default, `.ruled`, and `.ruleless` forms would render as
|
|
44
|
+
one. A table's frame and rules take the theme's ink at a fifth,
|
|
45
|
+
translucent for the same reason the control line is. */
|
|
46
|
+
--ui-rule-ink: light-dark(rgb(0 0 0 / 0.2), rgb(255 255 255 / 0.2));
|
|
47
|
+
|
|
48
|
+
/* The glass is the ground, not the fill. Thinning fills with
|
|
49
|
+
`--ui-bg-alpha` would make how much colour a presentation asked for
|
|
50
|
+
depend on the aesthetic, which is presentation's question (R1): a
|
|
51
|
+
`.solid` button, a checked toggle, and a `.solid` card stay opaque.
|
|
52
|
+
Translucency lives in `--ui-surface-ground` below, the plate a partial
|
|
53
|
+
fill is painted over. */
|
|
68
54
|
--ui-bg-alpha: 1;
|
|
55
|
+
--ui-backdrop: blur(14px) saturate(1.6);
|
|
56
|
+
--ui-surface-image: initial;
|
|
57
|
+
|
|
58
|
+
/* R6. Controls carry no shadow of their own -- the depth in this aesthetic is
|
|
59
|
+
the blur, and a drop shadow under a translucent button reads as grime. All
|
|
60
|
+
general parts are explicitly zero so inherited or future geometry cannot
|
|
61
|
+
leak onto controls. `--ui-surface-shadow` below replaces the shadow of
|
|
62
|
+
surfaces only. */
|
|
63
|
+
--ui-shadow-x: 0px;
|
|
64
|
+
--ui-shadow-y: 0px;
|
|
65
|
+
--ui-shadow-blur: 0px;
|
|
66
|
+
--ui-shadow-spread: 0px;
|
|
67
|
+
--ui-shadow-inset: initial;
|
|
68
|
+
--ui-shadow-ink: initial;
|
|
69
|
+
--ui-active-shadow-x: initial;
|
|
70
|
+
--ui-active-shadow-y: initial;
|
|
71
|
+
/* The theme's depth colour, restated: the surface shadow below resolves it
|
|
72
|
+
here, on the region, so inside chunky tile it inherited opaque black and
|
|
73
|
+
cast a black slab under every card, and inside cyber it inherited
|
|
74
|
+
transparent and cast none. */
|
|
75
|
+
--elevation-color: light-dark(rgb(15 23 42 / 0.08), rgb(0 0 0 / 0.55));
|
|
76
|
+
/* No halo, cleared so a region nested inside another
|
|
77
|
+
aesthetic does not inherit one. */
|
|
78
|
+
--ui-halo-blur: initial;
|
|
79
|
+
--ui-halo-spread: initial;
|
|
80
|
+
--ui-halo-ink: initial;
|
|
81
|
+
/* `--ui-bg-alpha` multiplies the fill, and a surface's default fill is 0%:
|
|
82
|
+
without a translucent ground the blur ends up behind an opaque background
|
|
83
|
+
and nothing shows through. */
|
|
84
|
+
--ui-surface-ground: color-mix(in srgb, var(--color-background) 45%, transparent);
|
|
85
|
+
|
|
86
|
+
/* Two layers pulling in with negative spread, so the shadow tucks under the
|
|
87
|
+
surface instead of haloing onto the backdrop. It is the one shipped aesthetic
|
|
88
|
+
that cannot be described with the parts, so it takes `surface`'s
|
|
89
|
+
complete-value slot -- which is also what keeps it off buttons and chips --
|
|
90
|
+
and the registry records that `.flat` does not reach it. */
|
|
91
|
+
--ui-surface-shadow: 0 1px 2px -1px var(--elevation-color), 0 18px 40px -16px var(--elevation-color);
|
|
92
|
+
|
|
93
|
+
--ui-clip: none;
|
|
94
|
+
--ui-clip-tight: none;
|
|
95
|
+
/* Round corners, said explicitly, and the pill and chip corners cleared back
|
|
96
|
+
to `--ui-corner`: a region nested inside `.cyber` would otherwise inherit
|
|
97
|
+
its bevel, its diagonal, its diamond pills, and its square chips. */
|
|
98
|
+
--ui-corner-shape: round;
|
|
99
|
+
--ui-radius-pill: initial;
|
|
100
|
+
--ui-radius-tight: initial;
|
|
101
|
+
--ui-radius: initial;
|
|
102
|
+
--ui-radius-surface: initial;
|
|
103
|
+
--ui-corner-tl: initial;
|
|
104
|
+
--ui-corner-tr: initial;
|
|
105
|
+
--ui-corner-br: initial;
|
|
106
|
+
--ui-corner-bl: initial;
|
|
107
|
+
/* Nothing here draws its edge as a ring, and an aesthetic nested inside pixel
|
|
108
|
+
has to say so rather than inherit it. */
|
|
109
|
+
--ui-shadow-edge: initial;
|
|
110
|
+
/* Nothing clips, so the global focus outline shows; pixel's inset focus
|
|
111
|
+
layer is cleared rather than inherited. */
|
|
112
|
+
--ui-focus-inset: initial;
|
|
113
|
+
--ui-hover-transform: none;
|
|
114
|
+
/* The base press, stated because every token here is: the depth in this
|
|
115
|
+
aesthetic is the blur, so a control has no slab to press into, and a pane
|
|
116
|
+
that gives slightly under the pointer is the one answer a press gets. */
|
|
117
|
+
--ui-active-transform: scale(0.97);
|
|
118
|
+
--ui-active-translate-x: initial;
|
|
119
|
+
--ui-active-translate-y: initial;
|
|
120
|
+
|
|
121
|
+
/* Chunky tile's heavier label, cleared so a region nested inside it keeps
|
|
122
|
+
the button's and the badge's own label. */
|
|
123
|
+
--ui-label-weight: initial;
|
|
124
|
+
--ui-label-tracking: initial;
|
|
125
|
+
--ui-label-case: initial;
|
|
126
|
+
--ui-label-shadow: initial;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* Transparency is the whole aesthetic, so the honest degradation is an opaque
|
|
130
|
+
surface rather than a softer blur. */
|
|
131
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
132
|
+
.glass {
|
|
133
|
+
--ui-backdrop: none;
|
|
134
|
+
--ui-surface-ground: var(--color-background);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@media (prefers-reduced-motion: reduce) {
|
|
139
|
+
.glass {
|
|
140
|
+
--ui-active-transform: none;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Liquid glass: a modifier of glass, the way `.sketch-rounded` is of sketch.
|
|
145
|
+
Continuous corners, a bright rim where light catches the pane, a lighter
|
|
146
|
+
blur, and -- in engines that render an SVG filter in `backdrop-filter`,
|
|
147
|
+
Chromium today -- a lens that bends the backdrop at the edges. On the region
|
|
148
|
+
beside `.glass`, or on one surface inside a glass region.
|
|
149
|
+
|
|
150
|
+
Only the corners apply on their own. The ink, ground, and blur are glass
|
|
151
|
+
material, so they are set below only where `.glass` is in scope: a lone
|
|
152
|
+
`.glass-liquid` would otherwise make every surface translucent with nothing
|
|
153
|
+
to blur and every field's line a near-invisible hairline.
|
|
154
|
+
|
|
155
|
+
The corner knobs are glass's own, with larger defaults: a continuous corner
|
|
156
|
+
reads smaller than a round one of the same radius. Where `corner-shape` is
|
|
157
|
+
not drawn the corners stay round. */
|
|
158
|
+
.glass-liquid {
|
|
159
|
+
--ui-corner: var(--glass-radius, 1rem);
|
|
160
|
+
--ui-corner-surface: var(--glass-radius-surface, 1.5rem);
|
|
161
|
+
--ui-corner-shape: squircle;
|
|
162
|
+
|
|
163
|
+
--_glass-liquid-blur: blur(2px) saturate(1.8);
|
|
164
|
+
|
|
165
|
+
/* The lens is its own layer rather than a second function in the blur's
|
|
166
|
+
list: Firefox and WebKit accept `url()` there and draw nothing for it, and
|
|
167
|
+
a filter list with an unusable reference is dropped whole -- the blur with
|
|
168
|
+
it. Kept apart, an engine that ignores the lens still blurs.
|
|
169
|
+
|
|
170
|
+
The displacement map is built inside the filter from the box itself:
|
|
171
|
+
erode a flood of the filter region by 8px, blur it into a ramp, and take
|
|
172
|
+
the ramp's slope across x and y as the two displacement channels. Every
|
|
173
|
+
length is absolute, so the band is the same width on a tooltip and on a
|
|
174
|
+
page-wide card; no length is a percentage of the box. The blur is wider
|
|
175
|
+
than the erosion so the ramp's corners round off -- otherwise the square
|
|
176
|
+
core shows through as loops at every corner. A filter cannot read a custom
|
|
177
|
+
property, so the band and the strength are fixed, not knobs. */
|
|
178
|
+
--_glass-liquid-lens: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='lens' x='0' y='0' width='100%' height='100%' color-interpolation-filters='sRGB'><feFlood flood-color='white'/><feMorphology operator='erode' radius='8'/><feGaussianBlur stdDeviation='10' result='ramp'/><feOffset in='ramp' dx='-3' result='l'/><feOffset in='ramp' dx='3' result='r'/><feComposite in='l' in2='r' operator='arithmetic' k2='2' k3='-2' k4='0.5' result='dx'/><feOffset in='ramp' dy='-3' result='u'/><feOffset in='ramp' dy='3' result='d'/><feComposite in='u' in2='d' operator='arithmetic' k2='2' k3='-2' k4='0.5' result='dy'/><feColorMatrix in='dx' values='0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1' result='x'/><feColorMatrix in='dy' values='0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1'/><feComposite in='x' operator='arithmetic' k2='1' k3='1'/><feDisplacementMap in='SourceGraphic' scale='110' xChannelSelector='R' yChannelSelector='G'/></filter></svg>#lens");
|
|
179
|
+
|
|
180
|
+
/* The rim: light caught on the top-left edge and, fainter, the bottom-right,
|
|
181
|
+
over a soft inner glow. Drawn on the lens layer, the topmost, because a
|
|
182
|
+
rim drawn on the surface itself would sit under the layers and blur. */
|
|
183
|
+
--_glass-liquid-rim:
|
|
184
|
+
inset 1px 1px 0 light-dark(rgb(255 255 255 / 0.75), rgb(255 255 255 / 0.35)),
|
|
185
|
+
inset -1px -1px 0 light-dark(rgb(255 255 255 / 0.35), rgb(255 255 255 / 0.12)),
|
|
186
|
+
inset 0 0 18px light-dark(rgb(255 255 255 / 0.22), rgb(255 255 255 / 0.05));
|
|
187
|
+
--_glass-liquid-sheen: linear-gradient(
|
|
188
|
+
160deg,
|
|
189
|
+
light-dark(rgb(255 255 255 / 0.22), rgb(255 255 255 / 0.06)),
|
|
190
|
+
transparent 45%
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* The glass material the modifier changes, where glass is in scope: on the
|
|
195
|
+
same element, or on a surface inside a glass region. The blur moves off the
|
|
196
|
+
surface onto a layer behind its content (below): a surface with a backdrop
|
|
197
|
+
filter of its own is a backdrop root, and a layer inside one sees only the
|
|
198
|
+
surface, never the page behind it. */
|
|
199
|
+
.glass.glass-liquid,
|
|
200
|
+
.glass .glass-liquid {
|
|
201
|
+
--ui-ink: light-dark(rgb(255 255 255 / 0.45), rgb(255 255 255 / 0.16));
|
|
202
|
+
--ui-surface-ground: color-mix(in srgb, var(--color-background) 30%, transparent);
|
|
203
|
+
--ui-surface-image: initial;
|
|
69
204
|
--ui-backdrop: none;
|
|
70
|
-
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
208
|
+
.glass.glass-liquid,
|
|
209
|
+
.glass .glass-liquid {
|
|
210
|
+
--ui-surface-ground: var(--color-background);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.glass-liquid {
|
|
214
|
+
--_glass-liquid-blur: none;
|
|
215
|
+
--_glass-liquid-lens: none;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* The layers: a treatment no token reaches, so a selector list glass owns and
|
|
220
|
+
the registry records. Two pseudo-elements behind a surface's content, the
|
|
221
|
+
blur beneath and the lens with the rim and sheen above it. `isolation`
|
|
222
|
+
keeps them above the surface's own plate, which they then blur with the
|
|
223
|
+
page; `position` anchors them, and a component that positions itself --
|
|
224
|
+
the tooltip bubble -- keeps its own. A default or `.soft` table frosts
|
|
225
|
+
under glass, so it takes the layers too. Both slots are spent, so a surface
|
|
226
|
+
inside a liquid region carries no `::before` or `::after` of the
|
|
227
|
+
consumer's; the fields cannot host one, and controls stay solid as they do
|
|
228
|
+
under glass.
|
|
229
|
+
|
|
230
|
+
A region of another aesthetic nested inside, or of plain glass, keeps its
|
|
231
|
+
own material and takes no layers. This list names the other aesthetics, and
|
|
232
|
+
`registry.test.ts` fails when one is missing. */
|
|
233
|
+
:is(.glass-liquid, .glass-liquid *):is(.glass, .glass *):is(
|
|
234
|
+
.card,
|
|
235
|
+
.panel,
|
|
236
|
+
.alert,
|
|
237
|
+
.tooltip-bubble,
|
|
238
|
+
.surface,
|
|
239
|
+
.data-table:not(.solid, .ghost)
|
|
240
|
+
):not(
|
|
241
|
+
.glass-liquid :is(.neobrutalism, .pixel, .chunky-tile, .cyber, .sketch),
|
|
242
|
+
.glass-liquid :is(.neobrutalism, .pixel, .chunky-tile, .cyber, .sketch) *,
|
|
243
|
+
.glass-liquid .glass:not(.glass-liquid),
|
|
244
|
+
.glass-liquid .glass:not(.glass-liquid) *
|
|
245
|
+
) {
|
|
246
|
+
position: relative;
|
|
247
|
+
isolation: isolate;
|
|
248
|
+
|
|
249
|
+
&::before,
|
|
250
|
+
&::after {
|
|
251
|
+
position: absolute;
|
|
252
|
+
inset: 0;
|
|
253
|
+
z-index: -1;
|
|
254
|
+
border-radius: inherit;
|
|
255
|
+
corner-shape: inherit;
|
|
256
|
+
content: "";
|
|
257
|
+
pointer-events: none;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/* Prefixed first, for the reason `surface` gives. */
|
|
261
|
+
&::before {
|
|
262
|
+
-webkit-backdrop-filter: var(--_glass-liquid-blur);
|
|
263
|
+
backdrop-filter: var(--_glass-liquid-blur);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
&::after {
|
|
267
|
+
backdrop-filter: var(--_glass-liquid-lens);
|
|
268
|
+
background-image: var(--_glass-liquid-sheen);
|
|
269
|
+
box-shadow: var(--_glass-liquid-rim);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* Forced colours paint the plain surface: no layer blurs, bends, or lights it. */
|
|
275
|
+
@media (forced-colors: active) {
|
|
276
|
+
.glass-liquid {
|
|
277
|
+
--_glass-liquid-blur: none;
|
|
278
|
+
--_glass-liquid-lens: none;
|
|
279
|
+
--_glass-liquid-rim: none;
|
|
280
|
+
--_glass-liquid-sheen: none;
|
|
71
281
|
}
|
|
72
282
|
}
|
|
73
283
|
|
|
74
284
|
/* Solo: the same look painted onto the one element carrying the class, for an
|
|
75
285
|
element the package does not style -- a toast from another library, a plain
|
|
76
286
|
`<div>` on a page that never loaded the base stylesheet. See
|
|
77
|
-
docs/internal/solo-
|
|
287
|
+
docs/internal/model.md#solo-classes.
|
|
78
288
|
|
|
79
289
|
It reads the aesthetic's own knobs and the theme's foundation tokens, each
|
|
80
290
|
with its shipped literal as the fallback, and never a `--ui-*`, `--intent-*`,
|
|
@@ -110,3 +320,59 @@
|
|
|
110
320
|
backdrop-filter: none;
|
|
111
321
|
}
|
|
112
322
|
}
|
|
323
|
+
|
|
324
|
+
/* With `.glass-liquid`, the liquid pane: its corner and ground, and the same two
|
|
325
|
+
layers behind the element's content. The element's own blur goes, for the
|
|
326
|
+
backdrop-root reason the modifier gives. Both pseudo-elements are spent, so a
|
|
327
|
+
foreign element that draws its own `::before` or `::after` loses them.
|
|
328
|
+
|
|
329
|
+
The layers need a positioned element to anchor to. That one declaration is
|
|
330
|
+
layered, where the element's own `fixed` or `absolute` beats it -- a toast
|
|
331
|
+
keeps its placement, and an unpositioned element is anchored. */
|
|
332
|
+
@layer components {
|
|
333
|
+
.glass-solo.glass-liquid {
|
|
334
|
+
position: relative;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.glass-solo.glass-liquid {
|
|
339
|
+
isolation: isolate;
|
|
340
|
+
background-color: color-mix(
|
|
341
|
+
in srgb,
|
|
342
|
+
var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0))) 30%,
|
|
343
|
+
transparent
|
|
344
|
+
);
|
|
345
|
+
border-color: light-dark(rgb(255 255 255 / 0.45), rgb(255 255 255 / 0.16));
|
|
346
|
+
border-radius: var(--glass-radius-surface, 1.5rem);
|
|
347
|
+
corner-shape: squircle;
|
|
348
|
+
-webkit-backdrop-filter: none;
|
|
349
|
+
backdrop-filter: none;
|
|
350
|
+
|
|
351
|
+
&::before,
|
|
352
|
+
&::after {
|
|
353
|
+
position: absolute;
|
|
354
|
+
inset: 0;
|
|
355
|
+
z-index: -1;
|
|
356
|
+
border-radius: inherit;
|
|
357
|
+
corner-shape: inherit;
|
|
358
|
+
content: "";
|
|
359
|
+
pointer-events: none;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
&::before {
|
|
363
|
+
-webkit-backdrop-filter: var(--_glass-liquid-blur);
|
|
364
|
+
backdrop-filter: var(--_glass-liquid-blur);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
&::after {
|
|
368
|
+
backdrop-filter: var(--_glass-liquid-lens);
|
|
369
|
+
background-image: var(--_glass-liquid-sheen);
|
|
370
|
+
box-shadow: var(--_glass-liquid-rim);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@media (prefers-reduced-transparency: reduce) {
|
|
375
|
+
.glass-solo.glass-liquid {
|
|
376
|
+
background-color: var(--color-background, light-dark(oklch(98.5% 0 0), oklch(14.5% 0 0)));
|
|
377
|
+
}
|
|
378
|
+
}
|