@codenhub/styles 0.3.0 → 0.4.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/docs/setup.md CHANGED
@@ -89,8 +89,8 @@ Compiled entrypoints are ready-to-import CSS and require only tooling that can r
89
89
  | `@codenhub/styles/palette` | Generated, self-contained `--palette-*` custom properties: every intent/presentation cell's fill, text, and edge color, plus the flat `border`/`surface`/`text` neutral tokens, pre-composed to a flat value. For a consumer that cannot take this package as a build-time dependency (an optional peer, for example) but wants to look consistent with it. Not a replacement for the components themselves when Tailwind is available -- see [Customizing → Generated palette](./usage/customizing.md#generated-palette). |
90
90
  | `@codenhub/styles/components` | Compiled theme tokens, typography utilities, and every component class. Includes static reduced-motion loader masks; no reset, layout/content utilities, or native mappings. |
91
91
  | `@codenhub/styles/native` | Compiled complete surface plus classless mappings for headings, text, content elements, form controls, and buttons. Applies the reset and broad element selectors. |
92
- | `@codenhub/styles/aesthetics` | Compiled `.neobrutalism`, `.glass`, `.pixel`, and `.chunky-tile` classes. Adds no tokens or components of its own; import after the base stylesheet. |
93
- | `@codenhub/styles/aesthetics/*` | One compiled aesthetic: `/neobrutalism`, `/glass`, `/pixel`, or `/chunky-tile`. Same rules and ordering as the combined file. |
92
+ | `@codenhub/styles/aesthetics` | Compiled `.neobrutalism`, `.glass`, `.pixel`, `.chunky-tile`, and `.cyber` classes, and each one's `-solo` class. Adds no tokens or components of its own; import after the base stylesheet. |
93
+ | `@codenhub/styles/aesthetics/*` | One compiled aesthetic and its solo class: `/neobrutalism`, `/glass`, `/pixel`, `/chunky-tile`, or `/cyber`. Same rules and ordering as the combined file. |
94
94
  | `@codenhub/styles/tw` | Source equivalent of the complete stylesheet. Imports Tailwind itself, theme, typography, utilities, components, and reset; applies the same global rules. |
95
95
  | `@codenhub/styles/tw/theme` | Source theme, tokens, and presentation classes. Imports Tailwind's theme layer; emits root/system/explicit theme selectors and defines the package's custom `dark:` variant. |
96
96
  | `@codenhub/styles/tw/components` | Published Tailwind source for theme, typography, buttons, feedback, forms, loaders, surfaces, and tooltips, including static reduced-motion loader masks. No reset, layout/content utilities, or native mappings. |
@@ -104,7 +104,7 @@ Compiled entrypoints are ready-to-import CSS and require only tooling that can r
104
104
  | `@codenhub/styles/tw/native` | Source complete surface plus reset and classless native-element mappings; broadest global effects. |
105
105
  | `@codenhub/styles/tw/typography` | Source theme plus typography utilities; sets theme selectors but no reset or classless mappings. |
106
106
  | `@codenhub/styles/tw/utilities` | Source theme and typography composition plus layout and content utilities; safelists the package utility names. |
107
- | `@codenhub/styles/tw/aesthetics` | Source aesthetics, combined. `/tw/aesthetics/neobrutalism`, `/glass`, `/pixel`, and `/chunky-tile` publish one apiece. Plain CSS with no theme or reset side effects. |
107
+ | `@codenhub/styles/tw/aesthetics` | Source aesthetics, combined. `/tw/aesthetics/neobrutalism`, `/glass`, `/pixel`, `/chunky-tile`, and `/cyber` publish one apiece. Plain CSS with no theme or reset side effects. |
108
108
 
109
109
  Avoid importing overlapping entrypoints in the same build unless duplicate generated CSS is acceptable; pick the narrowest entrypoint that covers what a page needs.
110
110
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Aesthetics
3
- description: The four shipped aesthetics in depth, including their documented exceptions.
3
+ description: The five shipped aesthetics in depth, their solo classes, and their documented exceptions.
4
4
  order: 4
5
5
  ---
6
6
 
@@ -30,6 +30,7 @@ Like presentation, an aesthetic class cascades to any subtree:
30
30
  | `.glass` | Translucent surfaces over a blurred backdrop with a hairline highlight edge. |
31
31
  | `.pixel` | Corners cut by one grid unit, a chunky outline drawn as an inset ring, and the consumer-supplied `--font-pixel` over monospace. |
32
32
  | `.chunky-tile` | Rounded slabs seated on a darker shade of themselves, with a press that moves the element down into its own bar. |
33
+ | `.cyber` | Bevelled corners, a thin bright edge, and a glow in the component's own colour. |
33
34
 
34
35
  Aesthetics compose with every supported fill and edge pair. `.ghost.edgeless` intentionally removes the visible material traits from components whose aesthetic is expressed only through their fill, edge, or elevation.
35
36
 
@@ -119,3 +120,76 @@ The bar is a darker shade of the element's own colour rather than a shadow under
119
120
  - `.card.interactive` presses like a `.btn`, so a chunky answer tile drops onto its bar on click; `.card.pressable` gets the same press on its own. A plain `.card` stays put.
120
121
  - The shipped `.primary` is a near-black-or-near-white monochrome depending on theme, and the bar mixes toward a fixed black regardless of theme. In light theme this leaves a primary button's bar almost invisible against its own near-black plate (measured `1.04:1`, 10 units of sRGB distance); in dark theme the near-white plate against the same black-anchored bar reads fine (`2.48:1`, 154 units). Give `.primary` a hue of its own if it is the call to action and needs the depth cue in both themes.
121
122
  - Depth in this aesthetic is the bar, never a blurred drop shadow. Anything the registry rests flat sits flush on the page.
123
+
124
+ ## Cyber
125
+
126
+ Bevelled corners, a 1px edge, and a glow in the component's own colour: a success button glows green, a destructive card red. The bevel is drawn with `corner-shape`, so the border, the glow, and the focus outline all follow the cut rather than being clipped by it.
127
+
128
+ Controls and surfaces take the cut on opposite diagonals, so a button and the card around it read as the same material and as different things: buttons and fields cut top-left and bottom-right, cards, panels, and alerts top-right and bottom-left. Anything fully round cuts to points -- a radio, a switch knob, and the tooltip icon become diamonds, a pill button or a badge a pointed hexagon -- and chips square, so the checkbox reads as a plain square beside the diamond radio.
129
+
130
+ `--cyber-cut` is the largest cut, `0.625rem`: the default shapes cap it at a quarter of the element's own box, so a normal button or a card takes the full cut and a small icon button a smaller one rather than losing its corners. `--cyber-shape` and `--cyber-shape-surface` place it, and take any `border-radius` value. `--cyber-glow` is the glow's blur, `8px`. `--cyber-ink` is the neutral line and glow colour, which follows the theme by default -- near-black on light, near-white on dark -- and is where a neon neutral goes if you want one.
131
+
132
+ ```html
133
+ <section class="cyber" style="--cyber-ink: rgb(0 229 255); --cyber-cut: 0.75rem">
134
+ <button class="btn">Jack in</button>
135
+ </section>
136
+ ```
137
+
138
+ Other shapes are a value away. Set them on the `.cyber` element or any ancestor; `--ui-radius` set on one element changes just that element. A shape you set is used as written, so wrap a length in `min(..., 25%)` if small controls should keep the cap.
139
+
140
+ | Shape | Value |
141
+ | ---------------------- | --------------------- |
142
+ | All four corners | `0.625rem` |
143
+ | The other diagonal | `0 0.625rem` |
144
+ | Hexagon (pointed ends) | `0.625rem / 50%` |
145
+ | Parallelogram | `0.625rem 0 / 100% 0` |
146
+ | One notch, top-right | `0 0.625rem 0 0` |
147
+
148
+ ```html
149
+ <section class="cyber" style="--cyber-shape: 0.625rem / 50%">
150
+ <button class="btn primary">Hexagonal buttons</button>
151
+ </section>
152
+ ```
153
+
154
+ A parallelogram slants its sides into the content, so give a surface that takes one extra inline padding.
155
+
156
+ **Exceptions:**
157
+
158
+ - The bevel is Chromium-only today. Firefox and Safari do not draw `corner-shape` yet, and there the aesthetic squares its cut corners instead of rounding them, and what is fully round stays round, so the look degrades to square, lined, and glowing. It picks up the bevel with no change once an engine ships it.
159
+ - The glow is depth, so it reaches what the registry rests above zero -- buttons and cards -- and anything you raise with `.raised` or `.floating`. Fields, badges, and alerts keep the edge without the glow, and `.flat` takes it off anything.
160
+ - In the light theme the glow of a neutral or `.primary` component is its near-black ink, which reads as a soft shadow rather than as light. The look is at its strongest on a dark page, or with a hued `--cyber-ink`.
161
+ - A plain `.btn.icon` is a square control, so it takes the control diagonal; a `.btn.icon.pill` is round, so it becomes a diamond.
162
+ - `--font-cyber` is yours to supply. The package ships no font binary, so the aesthetic falls back to the monospace stack.
163
+ - Casing is left alone, for the reason [Chunky tile](#chunky-tile) gives.
164
+
165
+ ## Solo classes
166
+
167
+ An aesthetic class sets material tokens and nothing paints until a component reads them, so `.glass` on an element this package does not style -- a toast from another library, a dialog, a plain `<div>` on a page without the base stylesheet -- changes nothing. Each aesthetic also ships a solo class that paints its look directly onto the one element carrying it:
168
+
169
+ | Aesthetic | Solo class | Paints |
170
+ | --------------- | -------------------- | ----------------------------------------------------------------------------------------- |
171
+ | `.glass` | `.glass-solo` | Translucent ground, hairline edge, blur, the tucked two-layer shadow, the surface corner. |
172
+ | `.neobrutalism` | `.neobrutalism-solo` | 2px ink edge, square corners, the hard offset slab. |
173
+ | `.pixel` | `.pixel-solo` | The stepped silhouette, the inset ring in place of a border, the pixel font. |
174
+ | `.chunky-tile` | `.chunky-tile-solo` | The tile corner, 2px edge, the bar under it, the rounded font. |
175
+ | `.cyber` | `.cyber-solo` | The surface diagonal (the control one on an action), 1px edge, the glow, the font. |
176
+
177
+ ```css
178
+ /* The only stylesheet from this package on the page. */
179
+ @import "@codenhub/styles/aesthetics/glass";
180
+ ```
181
+
182
+ ```html
183
+ <div class="toast glass-solo">A pane over whatever is behind it.</div>
184
+ ```
185
+
186
+ A solo class ships in the same entrypoint as its aesthetic, and needs nothing else loaded:
187
+
188
+ - **It paints material, not colour.** Edge, corner, depth, silhouette, backdrop, font, and press. No intent, no fill amount, no hover tint -- the element has no presentation to compose. Glass's translucent ground is the one fill, because it is glass's material.
189
+ - **It reads its aesthetic's knobs and the theme's tokens, with the shipped values as fallbacks.** `--glass-radius-surface`, `--neo-offset`, `--pixel-unit`, `--tile-lift`, `--tile-radius`, `--cyber-cut`, `--cyber-shape`, `--cyber-shape-surface`, `--cyber-glow`, `--cyber-ink`, and the font knobs all work, set on the element or any ancestor. With the theme loaded it follows it; without it, it renders the shipped look.
190
+ - **It ignores the aesthetic around it.** It reads no `--ui-*` or `--elevation-color`, so a `.glass-solo` inside a `.pixel` region keeps its corners. The elevation modifiers do not reach it either.
191
+ - **It beats a foreign component's own styles.** The rules are unlayered and one class deep, so they win over a component's zero-specificity or layered rules wherever the two load. The flip side is that a Tailwind utility on the same element (`rounded-none`) loses to it: tune a solo class through its knobs.
192
+ - **It presses only an action.** Neobrutalism, chunky tile, and cyber press a `button`, `a[href]`, `[role="button"]`, `summary`, or button-type `input` that is not disabled; a container stays put, so a toast does not sink when clicked. Reduced motion drops the movement. Chunky tile's heavier label follows the same rule.
193
+ - **Dark values need a colour scheme.** Light and dark pairs are `light-dark()`, which follows the element's `color-scheme`. The package theme sets it; a page without it gets the light values.
194
+
195
+ Do not put a solo class on this package's own components. On a `.card` it would replace the composed fill, edge, and intent with material alone; use the aesthetic class there.
@@ -38,35 +38,38 @@ Material tokens describe what a component is _made of_ rather than which intent
38
38
 
39
39
  Components read each with a fallback, so leaving them unset produces the default look. Setting them on a container restyles the whole subtree. Only shadow geometry is split into colorless parts that inherit safely; color-capable inputs such as `--ui-ink` and `--ui-surface-shadow` may include their own colors.
40
40
 
41
- | Token | Purpose | Fallback |
42
- | ----------------------- | ------------------------------------------------------------------------------------------------------------- | -------------------- |
43
- | `--ui-radius` | Corner radius for controls. | `--radius-control` |
44
- | `--ui-radius-surface` | Corner radius for surfaces. | `--radius-surface` |
45
- | `--ui-border-width` | Edge thickness. | `--border-width` |
46
- | `--ui-border-max` | Ceiling on computed edge width. | `100px` |
47
- | `--ui-ink` | Neutral line color when no intent is set. | `--color-border` |
48
- | `--ui-rule` | Lines inside a table. Unset draws the head and foot boundaries only; `0%` draws none; `100%` rules every row. | unset |
49
- | `--ui-shadow-x` | Shadow horizontal offset. | `0px` |
50
- | `--ui-shadow-y` | Shadow vertical offset. | `0px` |
51
- | `--ui-shadow-blur` | Shadow blur radius. | `0px` |
52
- | `--ui-shadow-spread` | Shadow spread radius. | `0px` |
53
- | `--ui-shadow-inset` | The `inset` keyword for an inner ring. | Empty |
54
- | `--ui-hover-shadow-x` | Shadow horizontal offset while hovered. | `--ui-shadow-x` |
55
- | `--ui-hover-shadow-y` | Shadow vertical offset while hovered. | `--ui-shadow-y` |
56
- | `--ui-active-shadow-x` | Shadow horizontal offset while pressed. | `--ui-shadow-x` |
57
- | `--ui-active-shadow-y` | Shadow vertical offset while pressed. | `--ui-shadow-y` |
58
- | `--ui-active-transform` | Transform while pressed. `none` under `prefers-reduced-motion`. | `scale(0.97)` |
59
- | `--ui-shadow-ink` | Percentage of shadow color taken from intent ink. | `0%` |
60
- | `--ui-shadow-edge` | Declared, even empty, when the shadow is the element's edge rather than its depth. Read for presence. | Unset |
61
- | `--ui-elevation` | Unitless multiplier over shadow geometry. | `1` |
62
- | `--ui-surface-shadow` | Complete multi-layer shadow accepted by surfaces only. | Unset |
63
- | `--ui-surface-ground` | Ground a surface sits on. | `--color-background` |
64
- | `--ui-bg-alpha` | Multiplier over fill for translucent materials. | `1` |
65
- | `--ui-backdrop` | Backdrop filter accepted by surfaces only. | `none` |
66
- | `--ui-hover-transform` | Transform while an interactive element is hovered. | `none` |
67
- | `--ui-clip` | Structural component silhouette. | `none` |
68
- | `--ui-clip-tight` | Compact component silhouette. | `--ui-clip` |
69
- | `--ui-focus-inset` | Inset focus layer width. | Unset |
41
+ | Token | Purpose | Fallback |
42
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------- |
43
+ | `--ui-radius` | Corner radius for controls. | `--radius-control` |
44
+ | `--ui-radius-surface` | Corner radius for surfaces. | `--radius-surface` |
45
+ | `--ui-corner-shape` | What every corner the two radii set draws: `round`, or `bevel` for a straight cut. Chromium only today. | `round` |
46
+ | `--ui-radius-pill` | Corner for what is fully round by default: badges, switches, progress, the tooltip icon. | `--ui-radius`, then full |
47
+ | `--ui-radius-tight` | Corner for chips -- checkbox, `.kbd`, `.code` -- before their `--radius-small` cap. | `--ui-radius`, then small |
48
+ | `--ui-border-width` | Edge thickness. | `--border-width` |
49
+ | `--ui-border-max` | Ceiling on computed edge width. | `100px` |
50
+ | `--ui-ink` | Neutral line color when no intent is set. | `--color-border` |
51
+ | `--ui-rule` | Lines inside a table. Unset draws the head and foot boundaries only; `0%` draws none; `100%` rules every row. | unset |
52
+ | `--ui-shadow-x` | Shadow horizontal offset. | `0px` |
53
+ | `--ui-shadow-y` | Shadow vertical offset. | `0px` |
54
+ | `--ui-shadow-blur` | Shadow blur radius. | `0px` |
55
+ | `--ui-shadow-spread` | Shadow spread radius. | `0px` |
56
+ | `--ui-shadow-inset` | The `inset` keyword for an inner ring. | Empty |
57
+ | `--ui-hover-shadow-x` | Shadow horizontal offset while hovered. | `--ui-shadow-x` |
58
+ | `--ui-hover-shadow-y` | Shadow vertical offset while hovered. | `--ui-shadow-y` |
59
+ | `--ui-active-shadow-x` | Shadow horizontal offset while pressed. | `--ui-shadow-x` |
60
+ | `--ui-active-shadow-y` | Shadow vertical offset while pressed. | `--ui-shadow-y` |
61
+ | `--ui-active-transform` | Transform while pressed. `none` under `prefers-reduced-motion`. | `scale(0.97)` |
62
+ | `--ui-shadow-ink` | Percentage of shadow color taken from intent ink. | `0%` |
63
+ | `--ui-shadow-edge` | Declared, even empty, when the shadow is the element's edge rather than its depth. Read for presence. | Unset |
64
+ | `--ui-elevation` | Unitless multiplier over shadow geometry. | `1` |
65
+ | `--ui-surface-shadow` | Complete multi-layer shadow accepted by surfaces only. | Unset |
66
+ | `--ui-surface-ground` | Ground a surface sits on. | `--color-background` |
67
+ | `--ui-bg-alpha` | Multiplier over fill for translucent materials. | `1` |
68
+ | `--ui-backdrop` | Backdrop filter accepted by surfaces only. | `none` |
69
+ | `--ui-hover-transform` | Transform while an interactive element is hovered. | `none` |
70
+ | `--ui-clip` | Structural component silhouette. | `none` |
71
+ | `--ui-clip-tight` | Compact component silhouette. | `--ui-clip` |
72
+ | `--ui-focus-inset` | Inset focus layer width. | Unset |
70
73
 
71
74
  ```html
72
75
  <section style="--ui-radius: 0; --ui-border-width: 3px">
@@ -81,6 +84,10 @@ Components read each with a fallback, so leaving them unset produces the default
81
84
 
82
85
  Clipping removes borders, outlines, and outer shadows. An aesthetic that uses a clip can replace the border with an inset shadow by setting `--ui-shadow-inset` and the four shadow geometry parts. Set `--ui-border-max: 0px` when that ring replaces the component border. Focusable components restore the clipped focus outline through `--ui-focus-inset`.
83
86
 
87
+ A clip is not the only way to cut a corner. `--ui-corner-shape: bevel` draws the corner the radius sets as a straight cut instead of an arc, and the border, outer shadow, focus outline, and backdrop all follow it -- so a bevelled element keeps its glow and its line, where a clipped one loses both. `.cyber` uses it. Engines without `corner-shape` ignore it and draw the radius round, so an aesthetic that bevels should square its radius under `@supports not (corner-shape: bevel)`. A bevel on a full radius cuts to points: a radio, a `.btn.pill`, and anything reading `--ui-radius-pill` become diamonds or pointed hexagons.
88
+
89
+ Because `--ui-radius` accepts any `border-radius` value, it can also place the corner per side: `10px 0` cuts only top-left and bottom-right. The pill and chip corners are separate tokens for that reason -- a pill's full radius and a chip's `min()` cap cannot take a per-corner value -- and each falls back to `--ui-radius` when unset.
90
+
84
91
  Declare `--ui-shadow-edge` when that ring _is_ the border. The ring then takes the element's edge color, which carries the edge axis as its alpha, so `.edged` draws it and `.edgeless` does not — the same answer a real border gives. Without it the ring paints on every component the aesthetic reaches and the edge classes have no effect. Use `--ui-shadow-ink` instead when the shadow is depth cast in the intent's own ink, such as a hard offset slab, which an `.edgeless` element still gets.
85
92
 
86
93
  The token is read for its presence, not its value: declare it empty to switch the ring on, and `initial` to switch it back off in an aesthetic nested inside one that declares it.
@@ -106,18 +113,24 @@ Variables prefixed `--_` are internal composition outputs — `--_bg`, `--_fg`,
106
113
 
107
114
  The shipped [aesthetic classes](./aesthetics.md) set material tokens for you. Only aesthetics with a token listed below expose an aesthetic-specific token. Tune all other behavior through the shared material tokens above. Listed tokens are available only where that aesthetic's stylesheet is imported.
108
115
 
109
- | Token | Aesthetic | Purpose | Default |
110
- | ------------------------ | --------------- | ----------------------------------------------------------- | --------------------------------- |
111
- | `--neo-offset` | `.neobrutalism` | Hard shadow offset and distance the press travels. | `4px` |
112
- | `--glass-radius` | `.glass` | Control corner radius. | `0.75rem` |
113
- | `--glass-radius-surface` | `.glass` | Surface corner radius. | `1rem` |
114
- | `--pixel-unit` | `.pixel` | One pixel of the grid. The corner cut and the ring are one. | `4px` |
115
- | `--font-pixel` | `.pixel` | Consumer-supplied pixel font stack. | Falls back to the monospace stack |
116
- | `--tile-radius` | `.chunky-tile` | Corner radius, on controls and surfaces alike. | `0.75rem` |
117
- | `--tile-lift` | `.chunky-tile` | Depth of the seated bar, and how far a press travels. | `4px` |
118
- | `--font-rounded` | `.chunky-tile` | Consumer-supplied rounded font stack. | Falls back to the page stack |
119
-
120
- All four aesthetics reach the same way: each knob is _read_ with its default as a `var()` fallback rather than declared, so it resolves once into a private on the aesthetic's own class (`--_pixel-unit`, `--_neo-offset`, `--_tile-radius`, `--_tile-lift`, and so on). Set a knob on the element carrying the aesthetic class, or on any ancestor including `:root`:
116
+ | Token | Aesthetic | Purpose | Default |
117
+ | ------------------------ | --------------- | --------------------------------------------------------------------------------------------- | ---------------------------------- |
118
+ | `--neo-offset` | `.neobrutalism` | Hard shadow offset and distance the press travels. | `4px` |
119
+ | `--glass-radius` | `.glass` | Control corner radius. | `0.75rem` |
120
+ | `--glass-radius-surface` | `.glass` | Surface corner radius. | `1rem` |
121
+ | `--pixel-unit` | `.pixel` | One pixel of the grid. The corner cut and the ring are one. | `4px` |
122
+ | `--font-pixel` | `.pixel` | Consumer-supplied pixel font stack. | Falls back to the monospace stack |
123
+ | `--tile-radius` | `.chunky-tile` | Corner radius, on controls and surfaces alike. | `0.75rem` |
124
+ | `--tile-lift` | `.chunky-tile` | Depth of the seated bar, and how far a press travels. | `4px` |
125
+ | `--font-rounded` | `.chunky-tile` | Consumer-supplied rounded font stack. | Falls back to the page stack |
126
+ | `--cyber-cut` | `.cyber` | Largest cut, on controls and surfaces alike; the default shapes cap it at 25% of the element. | `0.625rem` |
127
+ | `--cyber-shape` | `.cyber` | Where controls take the cut: any `border-radius` value. | `<cut> 0` (top-left, bottom-right) |
128
+ | `--cyber-shape-surface` | `.cyber` | Where surfaces take the cut: any `border-radius` value. | `0 <cut>` (top-right, bottom-left) |
129
+ | `--cyber-glow` | `.cyber` | Blur of the glow. | `8px` |
130
+ | `--cyber-ink` | `.cyber` | Neutral line and glow colour, where no intent sets one. | Theme ink (near-black, near-white) |
131
+ | `--font-cyber` | `.cyber` | Consumer-supplied technical font stack. | Falls back to the monospace stack |
132
+
133
+ Every aesthetic's knobs reach the same way: each knob is _read_ with its default as a `var()` fallback rather than declared, so it resolves once into a private on the aesthetic's own class (`--_pixel-unit`, `--_neo-offset`, `--_tile-radius`, `--_tile-lift`, and so on). Set a knob on the element carrying the aesthetic class, or on any ancestor including `:root`:
121
134
 
122
135
  ```html
123
136
  <section class="pixel" style="--pixel-unit: 6px">
@@ -135,7 +148,9 @@ All four aesthetics reach the same way: each knob is _read_ with its default as
135
148
 
136
149
  Knobs and material tokens reach in opposite directions, and telling them apart matters. A knob resolves once at the aesthetic's own class and inherits down already-resolved, so it reaches the aesthetic **from above**: setting it on the element or any ancestor works, but setting it on a _descendant_ of the aesthetic class changes nothing — the value has already been read. `--ui-radius` and the other material tokens above are read fresh by each component as it draws, so they reach **at or below** the element carrying them, which is how you round one card differently from its neighbours without touching the knob at all.
137
150
 
138
- Shipped aesthetics set the shared material tokens above. `.neobrutalism` and `.pixel` set `--ui-ink` for their neutral outline; intent classes still override the intent slots on each component.
151
+ Shipped aesthetics set the shared material tokens above. `.neobrutalism`, `.pixel`, and `.cyber` set `--ui-ink` for their neutral outline; intent classes still override the intent slots on each component.
152
+
153
+ The same knobs tune each aesthetic's [solo class](./aesthetics.md#solo-classes), which reads them on its own element.
139
154
 
140
155
  ## Generated palette
141
156