@codenhub/styles 0.4.0 → 0.5.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.
Files changed (53) hide show
  1. package/README.md +1 -1
  2. package/dist/aesthetics/chunky-tile.css +1 -1
  3. package/dist/aesthetics/cyber.css +1 -1
  4. package/dist/aesthetics/glass.css +1 -1
  5. package/dist/aesthetics/index.css +1 -1
  6. package/dist/aesthetics/neobrutalism.css +1 -1
  7. package/dist/aesthetics/pixel.css +1 -1
  8. package/dist/aesthetics/sketch.css +1 -0
  9. package/dist/components.css +1 -1
  10. package/dist/index.css +1 -1
  11. package/dist/native.css +1 -1
  12. package/dist/palette.css +1 -1
  13. package/dist/theme.css +1 -1
  14. package/dist/tw/aesthetics/chunky-tile.css +142 -126
  15. package/dist/tw/aesthetics/cyber.css +132 -95
  16. package/dist/tw/aesthetics/glass.css +97 -56
  17. package/dist/tw/aesthetics/index.css +1 -0
  18. package/dist/tw/aesthetics/neobrutalism.css +99 -52
  19. package/dist/tw/aesthetics/pixel.css +127 -87
  20. package/dist/tw/aesthetics/sketch.css +326 -0
  21. package/dist/tw/box.css +100 -35
  22. package/dist/tw/components/button.css +40 -22
  23. package/dist/tw/components/feedback.css +29 -14
  24. package/dist/tw/components/form.css +151 -155
  25. package/dist/tw/components/index.css +15 -3
  26. package/dist/tw/components/loader.css +34 -25
  27. package/dist/tw/components/surface.css +28 -71
  28. package/dist/tw/components/tooltip.css +20 -10
  29. package/dist/tw/corner.css +26 -0
  30. package/dist/tw/elevation.css +8 -9
  31. package/dist/tw/intent.css +17 -9
  32. package/dist/tw/native.css +45 -39
  33. package/dist/tw/palette.css +452 -446
  34. package/dist/tw/presentation.css +9 -4
  35. package/dist/tw/surface.css +62 -0
  36. package/dist/tw/theme.css +106 -75
  37. package/dist/tw/utilities/content.css +25 -16
  38. package/dist/tw/utilities/layout.css +2 -2
  39. package/docs/accessibility.md +16 -16
  40. package/docs/changelog/0.5.0.md +38 -0
  41. package/docs/changelog/index.md +1 -0
  42. package/docs/index.md +3 -3
  43. package/docs/setup.md +23 -3
  44. package/docs/usage/aesthetics.md +56 -13
  45. package/docs/usage/buttons.md +13 -13
  46. package/docs/usage/composing.md +11 -11
  47. package/docs/usage/content-and-layout.md +13 -13
  48. package/docs/usage/customizing.md +90 -52
  49. package/docs/usage/feedback.md +1 -1
  50. package/docs/usage/forms.md +11 -11
  51. package/docs/usage/theming.md +1 -1
  52. package/llms-full.txt +274 -136
  53. package/package.json +12 -2
@@ -1,113 +1,150 @@
1
1
  /* Cyber: bevelled corners, a thin bright edge, and a glow in the component's own
2
- colour. See docs/internal/cyber-aesthetic.md for the decision.
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
8
  focus outline all follow it. Load after the base stylesheet. */
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
- }
90
9
 
91
- /* Firefox and Safari do not draw `corner-shape` yet, and there the radius would
92
- round the corners instead. A rounded cyber element reads as a different
93
- aesthetic; a square one reads as the same aesthetic simplified. What is fully
94
- round stays round rather than square: a square radio is a checkbox, and a
95
- pill is the nearest a diamond gets without a bevel. */
96
- @supports not (corner-shape: bevel) {
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
- --ui-radius: 0;
99
- --ui-radius-surface: 0;
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. It
30
+ used to be capped at a quarter of the element's box, a percentage, which
31
+ ran shallower than 45 degrees on a small wide button; the step retired
32
+ the cap. A shape set through a knob is taken as written, unscaled: it is
33
+ read as `--ui-radius`, which `box` honours over the computed corner, and
34
+ left unset the reference makes the token guaranteed-invalid so the
35
+ computed corner draws. */
36
+ --_cyber-cut: var(--cyber-cut, 0.625rem);
37
+ --_cyber-glow: var(--cyber-glow, 8px);
38
+
39
+ /* The neutral line. A neon hue is the look's signature, but hue is intent's
40
+ (R1), so the default follows the theme the way neobrutalism's and pixel's
41
+ ink does, and `--cyber-ink` lets an application choose a neon neutral
42
+ without the aesthetic imposing one on every palette. A named intent keeps
43
+ its own edge colour. */
44
+ --ui-ink: var(--cyber-ink, light-dark(var(--color-neutral-950), var(--color-neutral-50)));
45
+
46
+ /* Controls cut top-left and bottom-right, through the four corner switches
47
+ every component reads; surfaces cut top-right and bottom-left, which the
48
+ switches cannot say at the same time, so their shape is written whole and
49
+ does not scale -- a card is large enough that the full cut is right at
50
+ every padding. Anything fully round -- a badge, a switch, the progress
51
+ track, the tooltip icon, and through their own full radius a radio and a
52
+ `.btn.pill` -- cuts all the way to points: a diamond, or a hexagon on
53
+ something wider than it is tall. Chips square outright, which is what
54
+ tells the checkbox from the radio beside it. */
55
+ --ui-corner-shape: bevel;
56
+ --ui-corner: var(--_cyber-cut);
57
+ --ui-corner-surface: var(--_cyber-cut);
58
+ --ui-corner-tl: 1;
59
+ --ui-corner-tr: 0;
60
+ --ui-corner-br: 1;
61
+ --ui-corner-bl: 0;
62
+ --ui-radius: var(--cyber-shape);
63
+ --ui-radius-surface: var(--cyber-shape-surface, 0 var(--_cyber-cut) / 0 var(--_cyber-cut));
64
+ --ui-radius-pill: calc(infinity * 1px);
65
+ --ui-radius-tight: 0;
66
+ --ui-border-width: 1px;
67
+ --ui-border-max: 100px;
68
+ --ui-line-style: initial;
69
+ --ui-rule-style: initial;
70
+
71
+ /* The glow is light, not depth, so it is the halo: the component's own intent
72
+ colour at 40%, blurred, with no offset. Elevation does not scale it, so
73
+ every component glows -- fields and chips included -- and `.flat` does
74
+ not put it out. There is no depth, and the transparent depth colour keeps
75
+ `.raised` and `.floating` from drawing any. */
76
+ --elevation-color: transparent;
77
+ --ui-shadow-ink: 0%;
78
+ --ui-shadow-x: 0px;
79
+ --ui-shadow-y: 0px;
80
+ --ui-shadow-blur: 0px;
81
+ --ui-shadow-spread: 0px;
82
+ --ui-halo-blur: var(--_cyber-glow);
83
+ --ui-halo-spread: 0px;
84
+ --ui-halo-ink: 40%;
85
+
86
+ /* The glow holds still under the pointer and under a press; the press is the
87
+ base scale, restated because a region nested inside another aesthetic must
88
+ not inherit that one's travel. Reduced motion drops it below. */
89
+ --ui-hover-transform: none;
90
+ --ui-active-shadow-x: 0px;
91
+ --ui-active-shadow-y: 0px;
92
+ --ui-active-transform: scale(0.97);
93
+ --ui-active-translate-x: initial;
94
+ --ui-active-translate-y: initial;
95
+
96
+ /* Nothing here clips, blurs, thins its fill, or draws its edge as a ring, and
97
+ an aesthetic nested inside one that does has to say so rather than inherit
98
+ it. */
99
+ --ui-clip: none;
100
+ --ui-clip-tight: none;
101
+ --ui-shadow-inset: ;
102
+ --ui-shadow-edge: initial;
103
+ --ui-backdrop: none;
104
+ --ui-surface-ground: initial;
105
+ --ui-surface-shadow: initial;
106
+ --ui-surface-image: initial;
107
+ --ui-bg-alpha: 1;
108
+
109
+ /* Consumer-supplied, the way `.pixel` reads `--font-pixel`. The package ships
110
+ no font binary; the monospace stack is the closest technical face every
111
+ platform already has. */
112
+ font-family: var(--font-cyber, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
113
+
114
+ /* The control ink glass and chunky tile name, cleared so a region nested
115
+ inside either draws its controls in this aesthetic's own ink. */
116
+ --ui-control-ink: initial;
117
+
118
+ /* Chunky tile's heavier label, cleared so a region nested inside it keeps
119
+ the button's and the badge's own label. */
120
+ --ui-label-weight: initial;
121
+ --ui-label-tracking: initial;
122
+ --ui-label-case: initial;
123
+ --ui-label-shadow: initial;
100
124
  }
101
- }
102
125
 
103
- @media (prefers-reduced-motion: reduce) {
104
- .cyber {
105
- --ui-active-transform: none;
126
+ /* Firefox and Safari do not draw `corner-shape` yet, and there the radius would
127
+ round the corners instead. A rounded cyber element reads as a different
128
+ aesthetic; a square one reads as the same aesthetic simplified. What is fully
129
+ round stays round rather than square: a square radio is a checkbox, and a
130
+ pill is the nearest a diamond gets without a bevel. */
131
+ @supports not (corner-shape: bevel) {
132
+ .cyber {
133
+ --ui-corner: 0;
134
+ --ui-radius: 0;
135
+ --ui-radius-surface: 0;
136
+ }
137
+ }
138
+
139
+ @media (prefers-reduced-motion: reduce) {
140
+ .cyber {
141
+ --ui-active-transform: none;
142
+ }
106
143
  }
107
144
  }
108
145
 
109
146
  /* 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-utilities.md and
147
+ element the package does not style. See docs/internal/model.md#solo-classes and
111
148
  the note on `.glass-solo`: own knobs and foundation tokens with literal
112
149
  fallbacks, never a shared `--ui-*`, `--intent-*`, or `--elevation-color`
113
150
  token, unlayered, and not for this package's own components. */
@@ -7,74 +7,115 @@
7
7
  panels, alerts and tooltips while controls stay solid. An active blur costs a
8
8
  compositing layer apiece, and one under every control of a dense cluster reads
9
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
10
 
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));
11
+ /* The layer map, stated before anything uses a layer -- see `theme.css`. The
12
+ aesthetic's token class sits in `components`, below every utility, so a
13
+ consumer's own utility or unlayered CSS beats it; the solo class stays
14
+ unlayered, because it has to beat a foreign component's unlayered CSS. See
15
+ docs/internal/model.md#cascade-layers. */
16
+ @layer theme, base, components, utilities;
24
17
 
25
- --ui-bg-alpha: 0.8;
26
- --ui-backdrop: blur(14px) saturate(1.6);
18
+ @layer components {
19
+ .glass {
20
+ /* Knobs read with a fallback so an ancestor can set them -- declared here they
21
+ outrank `:root` (R8; see model.md#rules-for-aesthetics). Glass is softer
22
+ than the base geometry because a translucent tight corner reads as a
23
+ cut-out; a control/surface pair rather than one value, because they do not
24
+ scale together and 12/16 keeps a button looking at home inside a card. */
25
+ --ui-corner: var(--glass-radius, 0.75rem);
26
+ --ui-corner-surface: var(--glass-radius-surface, 1rem);
27
+ --ui-border-width: 1px;
28
+ --ui-border-max: 100px;
29
+ --ui-line-style: initial;
30
+ --ui-rule-style: initial;
27
31
 
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);
32
+ /* Glass catches light from above whatever is under it, so the edge is a light
33
+ hairline in both themes rather than a theme-following ink. */
34
+ --ui-ink: light-dark(rgb(255 255 255 / 0.65), rgb(255 255 255 / 0.22));
35
+ /* A control's boundary is not a pane's edge. The hairline erases an
36
+ unchecked checkbox on a light page (1.33:1 inside a glass card) and leaves
37
+ a field with nothing marking where typing goes, so controls take the
38
+ theme's ink at 55% instead: translucent, so it tints whatever is behind
39
+ the pane rather than drawing a flat grey, and 6.31:1 on a checkbox, 2.31:1
40
+ on a field's 60% resting line, in light. See
41
+ docs/internal/model.md#presentation. */
42
+ --ui-control-ink: light-dark(rgb(0 0 0 / 0.55), rgb(255 255 255 / 0.55));
41
43
 
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);
44
+ --ui-bg-alpha: 0.8;
45
+ --ui-backdrop: blur(14px) saturate(1.6);
46
+ --ui-surface-image: initial;
48
47
 
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
- }
48
+ /* R6. Controls carry no shadow of their own -- the depth in this aesthetic is
49
+ the blur, and a drop shadow under a translucent button reads as grime. All
50
+ general parts are explicitly zero so inherited or future geometry cannot
51
+ leak onto controls. `--ui-surface-shadow` below replaces the shadow of
52
+ surfaces only. */
53
+ --ui-shadow-x: 0px;
54
+ --ui-shadow-y: 0px;
55
+ --ui-shadow-blur: 0px;
56
+ --ui-shadow-spread: 0px;
57
+ /* No halo, cleared so a region nested inside another
58
+ aesthetic does not inherit one. */
59
+ --ui-halo-blur: initial;
60
+ --ui-halo-spread: initial;
61
+ --ui-halo-ink: initial;
62
+ /* `--ui-bg-alpha` multiplies the fill, and a surface's default fill is 0%:
63
+ without a translucent ground the blur ends up behind an opaque background
64
+ and nothing shows through. */
65
+ --ui-surface-ground: color-mix(in srgb, var(--color-background) 45%, transparent);
63
66
 
64
- /* Transparency is the whole aesthetic, so the honest degradation is an opaque
65
- surface rather than a softer blur. */
66
- @media (prefers-reduced-transparency: reduce) {
67
- .glass {
68
- --ui-bg-alpha: 1;
69
- --ui-backdrop: none;
70
- --ui-surface-ground: var(--color-background);
67
+ /* Two layers pulling in with negative spread, so the shadow tucks under the
68
+ surface instead of haloing onto the backdrop. It is the one shipped aesthetic
69
+ that cannot be described with the parts, so it takes `surface`'s
70
+ complete-value slot -- which is also what keeps it off buttons and chips --
71
+ and the registry records that `.flat` does not reach it. */
72
+ --ui-surface-shadow: 0 1px 2px -1px var(--elevation-color), 0 18px 40px -16px var(--elevation-color);
73
+
74
+ --ui-clip: none;
75
+ --ui-clip-tight: none;
76
+ /* Round corners, said explicitly, and the pill and chip corners cleared back
77
+ to `--ui-corner`: a region nested inside `.cyber` would otherwise inherit
78
+ its bevel, its diagonal, its diamond pills, and its square chips. */
79
+ --ui-corner-shape: round;
80
+ --ui-radius-pill: initial;
81
+ --ui-radius-tight: initial;
82
+ --ui-radius: initial;
83
+ --ui-radius-surface: initial;
84
+ --ui-corner-tl: initial;
85
+ --ui-corner-tr: initial;
86
+ --ui-corner-br: initial;
87
+ --ui-corner-bl: initial;
88
+ /* Nothing here draws its edge as a ring, and an aesthetic nested inside pixel
89
+ has to say so rather than inherit it. */
90
+ --ui-shadow-edge: initial;
91
+ --ui-hover-transform: none;
92
+ --ui-active-transform: none;
93
+ --ui-active-translate-x: initial;
94
+ --ui-active-translate-y: initial;
95
+
96
+ /* Chunky tile's heavier label, cleared so a region nested inside it keeps
97
+ the button's and the badge's own label. */
98
+ --ui-label-weight: initial;
99
+ --ui-label-tracking: initial;
100
+ --ui-label-case: initial;
101
+ --ui-label-shadow: initial;
102
+ }
103
+
104
+ /* Transparency is the whole aesthetic, so the honest degradation is an opaque
105
+ surface rather than a softer blur. */
106
+ @media (prefers-reduced-transparency: reduce) {
107
+ .glass {
108
+ --ui-bg-alpha: 1;
109
+ --ui-backdrop: none;
110
+ --ui-surface-ground: var(--color-background);
111
+ }
71
112
  }
72
113
  }
73
114
 
74
115
  /* Solo: the same look painted onto the one element carrying the class, for an
75
116
  element the package does not style -- a toast from another library, a plain
76
117
  `<div>` on a page that never loaded the base stylesheet. See
77
- docs/internal/solo-utilities.md.
118
+ docs/internal/model.md#solo-classes.
78
119
 
79
120
  It reads the aesthetic's own knobs and the theme's foundation tokens, each
80
121
  with its shipped literal as the fallback, and never a `--ui-*`, `--intent-*`,
@@ -8,3 +8,4 @@
8
8
  @import "./pixel.css";
9
9
  @import "./chunky-tile.css";
10
10
  @import "./cyber.css";
11
+ @import "./sketch.css";
@@ -3,59 +3,100 @@
3
3
  named -- the ink reaches everything through the intent reset, the shadow is
4
4
  colourless geometry each component colours against its own intent. Load after
5
5
  the base stylesheet; source order decides against the zero-specificity reset. */
6
- .neobrutalism {
7
- /* Read with a fallback, not declared, so an ancestor (`:root`, where an app
8
- themes) can set `--neo-offset`; the private carries the resolved value to
9
- the six places below. See model.md#rules-for-aesthetics (R8). */
10
- --_neo-offset: var(--neo-offset, 4px);
11
6
 
12
- /* Pure black reads as ink on a light page and disappears on a dark one, so the
13
- ink follows the theme rather than the palette. A thick border in the neutral
14
- border grey is a thick grey box, not a brutalist one. */
15
- --ui-ink: light-dark(var(--color-neutral-950), var(--color-neutral-50));
7
+ /* The layer map, stated before anything uses a layer -- see `theme.css`. The
8
+ aesthetic's token class sits in `components`, below every utility, so a
9
+ consumer's own utility or unlayered CSS beats it; the solo class stays
10
+ unlayered, because it has to beat a foreign component's unlayered CSS. See
11
+ docs/internal/model.md#cascade-layers. */
12
+ @layer theme, base, components, utilities;
13
+
14
+ @layer components {
15
+ .neobrutalism {
16
+ /* Read with a fallback, not declared, so an ancestor (`:root`, where an app
17
+ themes) can set `--neo-offset`; the private carries the resolved value to
18
+ the six places below. See model.md#rules-for-aesthetics (R8). */
19
+ --_neo-offset: var(--neo-offset, 4px);
20
+
21
+ /* Pure black reads as ink on a light page and disappears on a dark one, so the
22
+ ink follows the theme rather than the palette. A thick border in the neutral
23
+ border grey is a thick grey box, not a brutalist one. */
24
+ --ui-ink: light-dark(var(--color-neutral-950), var(--color-neutral-50));
25
+
26
+ --ui-corner: 0;
27
+ --ui-corner-surface: 0;
28
+ --ui-border-width: 2px;
29
+ --ui-border-max: 100px;
30
+ --ui-line-style: initial;
31
+ --ui-rule-style: initial;
16
32
 
17
- --ui-radius: 0;
18
- --ui-radius-surface: 0;
19
- --ui-border-width: 2px;
20
- --ui-border-max: 100px;
33
+ /* Hard, unblurred, and offset. Scaled by each component's own elevation, so
34
+ the registry decides which components sit on a slab and `.flat` takes one
35
+ off without this file knowing which component it landed on. */
36
+ /* The slab is ink, not shade: a hard offset of the element's own line colour
37
+ is the whole look, so this aesthetic asks for the intent instead of the
38
+ neutral depth colour every other shadow uses. */
39
+ --ui-shadow-ink: 100%;
40
+ --ui-shadow-x: var(--_neo-offset);
41
+ --ui-shadow-y: var(--_neo-offset);
42
+ --ui-shadow-blur: 0px;
43
+ --ui-shadow-spread: 0px;
44
+ /* No halo, cleared so a region nested inside another
45
+ aesthetic does not inherit one. */
46
+ --ui-halo-blur: initial;
47
+ --ui-halo-spread: initial;
48
+ --ui-halo-ink: initial;
21
49
 
22
- /* Hard, unblurred, and offset. Scaled by each component's own elevation, so
23
- the registry decides which components sit on a slab and `.flat` takes one
24
- off without this file knowing which component it landed on. */
25
- /* The slab is ink, not shade: a hard offset of the element's own line colour
26
- is the whole look, so this aesthetic asks for the intent instead of the
27
- neutral depth colour every other shadow uses. */
28
- --ui-shadow-ink: 100%;
29
- --ui-shadow-x: var(--_neo-offset);
30
- --ui-shadow-y: var(--_neo-offset);
31
- --ui-shadow-blur: 0px;
32
- --ui-shadow-spread: 0px;
50
+ /* The press: travel the offset, shrink the shadow to nothing, so the element
51
+ lands flat where its slab was. A translation `box` scales with the slab, so
52
+ the two stay the same length at every size and elevation. Hover keeps the slab still under the pointer;
53
+ `box-hover`'s derived fill tint is the whole hover response. One token pair,
54
+ no `:hover` list, R3 intact. */
55
+ --ui-active-shadow-x: 0px;
56
+ --ui-active-shadow-y: 0px;
57
+ --ui-active-transform: none;
58
+ --ui-active-translate-x: var(--_neo-offset);
59
+ --ui-active-translate-y: var(--_neo-offset);
33
60
 
34
- /* The press: travel the offset, shrink the shadow to nothing, so the element
35
- lands flat where its slab was. Hover keeps the slab still under the pointer;
36
- `box-hover`'s derived fill tint is the whole hover response. One token pair,
37
- no `:hover` list, R3 intact. */
38
- --ui-active-shadow-x: 0px;
39
- --ui-active-shadow-y: 0px;
40
- --ui-active-transform: translate(var(--_neo-offset), var(--_neo-offset));
61
+ /* Nothing here clips or blurs, and an aesthetic nested inside another has to
62
+ say so rather than inherit it. */
63
+ --ui-clip: none;
64
+ --ui-clip-tight: none;
65
+ /* Round corners, said explicitly, and the pill and chip corners cleared back
66
+ to `--ui-corner`: a region nested inside `.cyber` would otherwise inherit
67
+ its bevel, its diagonal, its diamond pills, and its square chips. */
68
+ --ui-corner-shape: round;
69
+ --ui-radius-pill: initial;
70
+ --ui-radius-tight: initial;
71
+ --ui-radius: initial;
72
+ --ui-radius-surface: initial;
73
+ --ui-corner-tl: initial;
74
+ --ui-corner-tr: initial;
75
+ --ui-corner-br: initial;
76
+ --ui-corner-bl: initial;
77
+ --ui-shadow-inset: ;
78
+ /* The slab is depth cast in ink, not this element's line, so an `.edgeless`
79
+ button keeps it. Cleared rather than omitted, because a neobrutalist region
80
+ nested inside a pixel one would otherwise inherit pixel's answer and lose the
81
+ slab on everything the edge axis rests at zero. */
82
+ --ui-shadow-edge: initial;
83
+ --ui-backdrop: none;
84
+ --ui-surface-ground: initial;
85
+ --ui-surface-shadow: initial;
86
+ --ui-bg-alpha: 1;
87
+ --ui-surface-image: initial;
41
88
 
42
- /* Nothing here clips or blurs, and an aesthetic nested inside another has to
43
- say so rather than inherit it. */
44
- --ui-clip: none;
45
- --ui-clip-tight: none;
46
- /* Round corners, said explicitly, and the pill and chip corners cleared back
47
- to `--ui-radius`: a region nested inside `.cyber` would otherwise inherit
48
- its bevel, its diamond pills, and its square chips. */
49
- --ui-corner-shape: round;
50
- --ui-radius-pill: initial;
51
- --ui-radius-tight: initial;
52
- --ui-shadow-inset: ;
53
- /* The slab is depth cast in ink, not this element's line, so an `.edgeless`
54
- button keeps it. Cleared rather than omitted, because a neobrutalist region
55
- nested inside a pixel one would otherwise inherit pixel's answer and lose the
56
- slab on everything the edge axis rests at zero. */
57
- --ui-shadow-edge: initial;
58
- --ui-backdrop: none;
89
+ /* The control ink glass and chunky tile name, cleared so a region nested
90
+ inside either draws its controls in this aesthetic's own ink. */
91
+ --ui-control-ink: initial;
92
+
93
+ /* Chunky tile's heavier label, cleared so a region nested inside it keeps
94
+ the button's and the badge's own label. */
95
+ --ui-label-weight: initial;
96
+ --ui-label-tracking: initial;
97
+ --ui-label-case: initial;
98
+ --ui-label-shadow: initial;
99
+ }
59
100
  }
60
101
 
61
102
  /* The one R3 exception, recorded in the registry as `selectors`. The slab here
@@ -63,13 +104,19 @@
63
104
  registry rests flat -- looks unfinished without it. It writes the component's
64
105
  resting default rather than the modifier, so `.flat`/`.raised`/`.floating`
65
106
  still win on the element; a token would name the component through a longer
66
- route. `--_d-elevation` inheriting is inert -- it equals `box`'s own fallback. */
67
- .neobrutalism .alert {
68
- --_d-elevation: 1;
107
+ route. `--_d-elevation` inheriting is inert -- it equals `box`'s own fallback.
108
+
109
+ In `utilities`, not with the token class: it has to beat `@utility alert`'s
110
+ own `--_d-elevation: 0`, which it does there at 0-2-0, and it writes only a
111
+ private nothing a consumer sets. */
112
+ @layer utilities {
113
+ .neobrutalism .alert {
114
+ --_d-elevation: 1;
115
+ }
69
116
  }
70
117
 
71
118
  /* Solo: the same look painted onto the one element carrying the class, for an
72
- element the package does not style. See docs/internal/solo-utilities.md and
119
+ element the package does not style. See docs/internal/model.md#solo-classes and
73
120
  the note on `.glass-solo`: own knobs and foundation tokens with literal
74
121
  fallbacks, never a shared `--ui-*`, `--intent-*`, or `--elevation-color`
75
122
  token, unlayered, and not for this package's own components. */