@codenhub/styles 0.6.0 → 0.8.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 (151) hide show
  1. package/NOTICE +18 -16
  2. package/README.es.md +62 -0
  3. package/README.md +28 -22
  4. package/README.pt-BR.md +62 -0
  5. package/dist/aesthetics/chunky-tile.css +1 -1
  6. package/dist/aesthetics/cyber.css +1 -1
  7. package/dist/aesthetics/glass.css +1 -1
  8. package/dist/aesthetics/index.css +2 -2
  9. package/dist/aesthetics/neobrutalism.css +1 -1
  10. package/dist/aesthetics/pixel.css +1 -1
  11. package/dist/aesthetics/sketch.css +1 -1
  12. package/dist/cli.js +189 -0
  13. package/dist/components.css +2 -2
  14. package/dist/index.css +2 -2
  15. package/dist/index.d.ts +1 -0
  16. package/dist/native.css +2 -2
  17. package/dist/palette.css +1 -1
  18. package/dist/theme.css +2 -2
  19. package/docs/accessibility.md +108 -60
  20. package/docs/changelog/0.1.1.md +1 -0
  21. package/docs/changelog/0.2.0.md +1 -0
  22. package/docs/changelog/0.3.0.md +1 -0
  23. package/docs/changelog/0.4.0.md +1 -0
  24. package/docs/changelog/0.5.0.md +8 -3
  25. package/docs/changelog/0.6.0.md +2 -1
  26. package/docs/changelog/0.7.0.md +50 -0
  27. package/docs/changelog/0.8.0.md +70 -0
  28. package/docs/changelog/index.md +2 -0
  29. package/docs/concepts.md +188 -46
  30. package/docs/es/accessibility.md +169 -0
  31. package/docs/es/concepts.md +212 -0
  32. package/docs/es/index.md +105 -0
  33. package/docs/es/integrating/astro.md +35 -0
  34. package/docs/es/integrating/index.md +75 -0
  35. package/docs/es/integrating/nextjs.md +67 -0
  36. package/docs/es/integrating/svelte.md +45 -0
  37. package/docs/es/integrating/tailwind.md +148 -0
  38. package/docs/es/integrating/vue.md +31 -0
  39. package/docs/es/integrating/without-a-bundler.md +238 -0
  40. package/docs/es/setup.md +188 -0
  41. package/docs/es/usage/aesthetics.md +282 -0
  42. package/docs/es/usage/buttons.md +135 -0
  43. package/docs/es/usage/composing.md +168 -0
  44. package/docs/es/usage/content-and-layout.md +219 -0
  45. package/docs/es/usage/customizing.md +304 -0
  46. package/docs/es/usage/feedback.md +157 -0
  47. package/docs/es/usage/forms.md +256 -0
  48. package/docs/es/usage/index.md +33 -0
  49. package/docs/es/usage/presets.md +92 -0
  50. package/docs/es/usage/solo.md +86 -0
  51. package/docs/es/usage/theming.md +309 -0
  52. package/docs/es/usage/tooltips.md +127 -0
  53. package/docs/index.md +86 -26
  54. package/docs/integrating/astro.md +3 -4
  55. package/docs/integrating/index.md +46 -13
  56. package/docs/integrating/nextjs.md +3 -4
  57. package/docs/integrating/svelte.md +5 -4
  58. package/docs/integrating/tailwind.md +89 -6
  59. package/docs/integrating/vue.md +3 -4
  60. package/docs/integrating/without-a-bundler.md +237 -0
  61. package/docs/pt-BR/accessibility.md +169 -0
  62. package/docs/pt-BR/concepts.md +212 -0
  63. package/docs/pt-BR/index.md +105 -0
  64. package/docs/pt-BR/integrating/astro.md +35 -0
  65. package/docs/pt-BR/integrating/index.md +75 -0
  66. package/docs/pt-BR/integrating/nextjs.md +67 -0
  67. package/docs/pt-BR/integrating/svelte.md +45 -0
  68. package/docs/pt-BR/integrating/tailwind.md +148 -0
  69. package/docs/pt-BR/integrating/vue.md +31 -0
  70. package/docs/pt-BR/integrating/without-a-bundler.md +238 -0
  71. package/docs/pt-BR/setup.md +188 -0
  72. package/docs/pt-BR/usage/aesthetics.md +282 -0
  73. package/docs/pt-BR/usage/buttons.md +135 -0
  74. package/docs/pt-BR/usage/composing.md +168 -0
  75. package/docs/pt-BR/usage/content-and-layout.md +219 -0
  76. package/docs/pt-BR/usage/customizing.md +304 -0
  77. package/docs/pt-BR/usage/feedback.md +157 -0
  78. package/docs/pt-BR/usage/forms.md +256 -0
  79. package/docs/pt-BR/usage/index.md +33 -0
  80. package/docs/pt-BR/usage/presets.md +92 -0
  81. package/docs/pt-BR/usage/solo.md +86 -0
  82. package/docs/pt-BR/usage/theming.md +309 -0
  83. package/docs/pt-BR/usage/tooltips.md +127 -0
  84. package/docs/setup.md +142 -91
  85. package/docs/usage/aesthetics.md +89 -73
  86. package/docs/usage/buttons.md +112 -40
  87. package/docs/usage/composing.md +109 -92
  88. package/docs/usage/content-and-layout.md +160 -125
  89. package/docs/usage/customizing.md +117 -41
  90. package/docs/usage/feedback.md +138 -71
  91. package/docs/usage/forms.md +201 -107
  92. package/docs/usage/index.md +22 -14
  93. package/docs/usage/presets.md +91 -0
  94. package/docs/usage/solo.md +85 -0
  95. package/docs/usage/theming.md +273 -118
  96. package/docs/usage/tooltips.md +90 -49
  97. package/llms-full.txt +2820 -1298
  98. package/llms.txt +24 -22
  99. package/package.json +134 -91
  100. package/{dist/tw → tw}/aesthetics/chunky-tile.css +107 -29
  101. package/{dist/tw → tw}/aesthetics/cyber.css +88 -40
  102. package/{dist/tw → tw}/aesthetics/glass.css +257 -61
  103. package/{dist/tw → tw}/aesthetics/neobrutalism.css +113 -20
  104. package/{dist/tw → tw}/aesthetics/pixel.css +93 -8
  105. package/tw/aesthetics/sketch.css +417 -0
  106. package/{dist/tw → tw}/box.css +105 -9
  107. package/tw/components/button.css +11 -0
  108. package/tw/components/button.rules.css +187 -0
  109. package/tw/components/feedback.css +11 -0
  110. package/{dist/tw/components/feedback.css → tw/components/feedback.rules.css} +136 -32
  111. package/tw/components/form.css +11 -0
  112. package/{dist/tw/components/form.css → tw/components/form.rules.css} +577 -125
  113. package/tw/components/index.css +16 -0
  114. package/tw/components/rules.css +41 -0
  115. package/tw/components/surface.css +11 -0
  116. package/tw/components/surface.rules.css +222 -0
  117. package/tw/components/tooltip.css +11 -0
  118. package/{dist/tw/components/tooltip.css → tw/components/tooltip.rules.css} +43 -27
  119. package/{dist/tw → tw}/corner.css +18 -12
  120. package/{dist/tw → tw}/elevation.css +23 -17
  121. package/{dist/tw → tw}/index.css +12 -10
  122. package/tw/index.d.ts +1 -0
  123. package/tw/intent.css +312 -0
  124. package/tw/modifiers.css +127 -0
  125. package/tw/native.css +320 -0
  126. package/tw/presentation.css +139 -0
  127. package/tw/reset.css +10 -0
  128. package/{dist/tw/reset.css → tw/reset.rules.css} +21 -22
  129. package/tw/size.css +34 -0
  130. package/tw/surface.css +9 -0
  131. package/tw/surface.rules.css +212 -0
  132. package/{dist/tw → tw}/theme.css +121 -45
  133. package/tw/typography.css +8 -0
  134. package/{dist/tw/typography.css → tw/typography.rules.css} +0 -3
  135. package/{dist/tw → tw}/utilities/content.css +212 -50
  136. package/tw/utilities/index.css +11 -0
  137. package/tw/utilities/layout.css +208 -0
  138. package/tw/utilities/rules.css +8 -0
  139. package/dist/tw/aesthetics/sketch.css +0 -333
  140. package/dist/tw/components/button.css +0 -139
  141. package/dist/tw/components/index.css +0 -36
  142. package/dist/tw/components/loader.css +0 -116
  143. package/dist/tw/components/surface.css +0 -175
  144. package/dist/tw/intent.css +0 -228
  145. package/dist/tw/native.css +0 -203
  146. package/dist/tw/palette.css +0 -460
  147. package/dist/tw/presentation.css +0 -68
  148. package/dist/tw/surface.css +0 -61
  149. package/dist/tw/utilities/index.css +0 -6
  150. package/dist/tw/utilities/layout.css +0 -130
  151. /package/{dist/tw → tw}/aesthetics/index.css +0 -0
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: Solo classes
3
+ description: Painting an aesthetic onto one element from another library, such as a toast or a dialog, and what a solo class reads and ignores.
4
+ order: 7
5
+ ---
6
+
7
+ # Solo classes
8
+
9
+ Suppose the settings screen shows "Profile saved" in a toast from a notification library, not in this package's alert. Under `.neobrutalism`, every card and button has the thick outline and the hard shadow, and the toast does not: an aesthetic class only sets values that this package's components read, so it changes nothing on an element the package does not style. A solo class paints the aesthetic directly onto the one element that carries it:
10
+
11
+ ```html
12
+ <div class="toast neobrutalism-solo success" role="status">Profile saved.</div>
13
+ ```
14
+
15
+ ## The six solo classes
16
+
17
+ Each aesthetic ships its solo class in the same entrypoint, and a solo class needs nothing else loaded:
18
+
19
+ ```css
20
+ /* The only stylesheet from this package on the page. */
21
+ @import "@codenhub/styles/aesthetics/glass";
22
+ ```
23
+
24
+ ```html
25
+ <div class="toast glass-solo">A pane over whatever is behind it.</div>
26
+ ```
27
+
28
+ | Aesthetic | Solo class | Paints |
29
+ | --------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
30
+ | `.glass` | `.glass-solo` | A translucent background, a thin light edge, the blur, a soft two-layer shadow, and the surface corner. With `.glass-liquid`, the liquid pane, drawn on its `::before` and `::after`. |
31
+ | `.neobrutalism` | `.neobrutalism-solo` | A 2px ink edge, square corners, and the hard offset shadow. |
32
+ | `.pixel` | `.pixel-solo` | The stepped outline, the inner ring in place of a border, and the pixel font. |
33
+ | `.chunky-tile` | `.chunky-tile-solo` | The tile corner, a 2px edge, the bar under it, and the rounded font. |
34
+ | `.cyber` | `.cyber-solo` | The surface diagonal (the control diagonal on a button or link), a 1px edge, the glow, and the font. |
35
+ | `.sketch` | `.sketch-solo` | Uneven elliptical corners (a sharper outline on a pane, a looser one on a button or link, and small rounded ones with `.sketch-rounded`), a 1px solid ink line, the 2px offset shadow, and the handwriting font. |
36
+
37
+ Do not put a solo class on this package's own components. A `.card` already draws its fill, edge, and intent, and a solo class would paint a second edge and fill over them. Use the aesthetic class there; it also works on a single element: `<button class="btn pixel">`.
38
+
39
+ ## What a solo class reads
40
+
41
+ A solo element takes the same presentation, intent, and elevation classes a `.card` would:
42
+
43
+ ```html
44
+ <div class="toast pixel-solo edgeless">No ring</div>
45
+ <div class="toast glass-solo solid success">An opaque green pane</div>
46
+ <div class="toast neobrutalism-solo flat">No offset shadow</div>
47
+
48
+ <section class="soft edged">
49
+ <div class="toast chunky-tile-solo info">Soft and edged from the container, info from itself</div>
50
+ </section>
51
+ ```
52
+
53
+ | What | Read from |
54
+ | ------------------------------------- | --------------------------------------------------------------------------------------------------- |
55
+ | Fill and edge (`.solid`, `.edged`, …) | The element, or any container: presentation passes down, as it does to components. |
56
+ | Intent (`.success`, …) | The element only, as on components. A `.success` container leaves a solo element inside it neutral. |
57
+ | Elevation (`.flat`, `.raised`, …) | The element only, as on components. It scales the offset shadow, the bar, and sketch's shadow. |
58
+ | The aesthetic around it (`.pixel`, …) | Never. See [What a solo class ignores](#what-a-solo-class-ignores). |
59
+
60
+ With none of those classes, a solo class paints the material alone (edge, corner, depth, outline, blur, font, and press) and leaves the element's own background and text color as they were.
61
+
62
+ - **Only the six built-in intents.** A solo class takes an intent from `.primary`, `.secondary`, `.success`, `.warning`, `.destructive`, and `.info` only. An intent class you defined yourself, as in [Theming → Your own intent](./theming.md#your-own-intent), is not read, and the element is drawn neutral. To give a solo element a custom color, retheme one of the built-in intents on it or a container, as that section describes.
63
+ - **The fill is a layer over the element's own background.** Five of the six paint the fill as an inner shadow and do not set `background`, so a `.soft` solo toast is a tint over whatever the toast already was, and a `.solid` one with a colored intent is opaque. Glass sets its own background, because the translucent background is its material, and mixes the fill over it. A neutral `.solid` is a 20% tint, not the opaque gray a component draws.
64
+ - **The text color assumes the theme.** It is set only where a fill or an intent is asked for, and it is the color the theme uses on the page or on the fill. On an element whose own background does not follow the theme, such as a white toast on a dark page, a `.soft` or `.ghost` intent can put light text on a light background; use `.solid`, or leave the intent off.
65
+ - **There is no hover tint.** A solo element is not known to be clickable, so it does not get the hover color a `.btn` gets.
66
+ - **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`, `--sketch-radius`, `--sketch-radius-surface`, `--sketch-radius-rounded`, `--sketch-radius-surface-rounded`, and the font tokens such as `--font-sketch` all work, set on the element or any ancestor. With the theme loaded, it follows the theme; without it, it renders the shipped look.
67
+ - **Dark values need a color scheme.** Light and dark pairs use `light-dark()`, which follows the element's `color-scheme`. The package's theme sets it; a page without the theme gets the light values.
68
+ - **The classes it reads come from the theme.** `.solid`, `.edgeless`, `.success`, `.flat`, and the rest ship in the base stylesheet and in `@codenhub/styles/theme`. With an aesthetic entrypoint alone on the page, those classes do nothing, and the solo class paints its unclassed look.
69
+
70
+ ## What a solo class ignores
71
+
72
+ - **The aesthetic around it.** It reads none of the values an aesthetic class sets, and not `--elevation-color`. A `.glass-solo` inside a `.pixel` region keeps its own corners, and `--ui-radius` or `--ui-border-width` set on it or above it do nothing. Tune it through its knobs.
73
+ - **Its own children.** A solo element is not an aesthetic region. A component inside it keeps the look of the page around it, even with `.glass-liquid` or `.sketch-rounded` beside the solo class. It passes down only what any container does: the solo class's font, and the fill or edge of a presentation class on it, so a `.card` inside a `.glass-solo.solid` pane is solid, as inside any `.solid` container.
74
+ - **Glass's shadow and `.flat`.** `.flat` does not remove the shadow from `.glass-solo`, as it does not under `.glass`.
75
+
76
+ ## How it wins and when it moves
77
+
78
+ - **It beats the other library's styles.** The rules are unlayered and one class deep, so they win over a component's layered rules, and over its zero-specificity ones, wherever the two load. An unlayered foreign rule that is more specific, or as specific and later in the page, still wins. The other side of this is that a Tailwind utility on the same element, such as `rounded-none`, loses to the solo class: tune it through its knobs instead.
79
+ - **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. Anything else stays put, so a toast does not sink when clicked. Chunky tile's heavier label follows the same rule.
80
+ - **It leaves a container's motion alone.** Only an action takes the press's `transition`, and the press moves the element through the `translate` and `scale` properties, not `transform`. A toast keeps its own transitions and its own `transform`. On an action, the press's `transition` replaces any the element had.
81
+ - **Reduced motion keeps the press and drops its motion.** Under `prefers-reduced-motion: reduce`, neobrutalism, chunky tile, and cyber still press, but at once: the element lands in its shadow or bar, or shrinks, without travelling there.
82
+
83
+ ## Next
84
+
85
+ The guides end here. [Composing](./composing.md), [Customizing](./customizing.md), and [Content and layout](./content-and-layout.md) are the lookup pages, and [Accessibility](../accessibility.md) lists what the CSS does for your users and what your application still owes them.
@@ -1,153 +1,308 @@
1
1
  ---
2
2
  title: Theming
3
- description: The color, intent, and foundation token reference.
4
- order: 2
3
+ description: Light and dark mode, replacing the palette with brand colors, defining an intent of your own, and every color, intent, foundation, and font token with its default.
4
+ order: 5
5
5
  ---
6
6
 
7
7
  # Theming
8
8
 
9
- Color tokens are how you restyle the palette; see [Concepts → Theme selection](../concepts.md#theme-selection) for how a theme is chosen. Intent tokens are how a component reads that palette once an intent class is applied. Foundation tokens are the shared layout, shape, and motion defaults underneath everything.
9
+ The settings screen works and looks like the package's defaults: near-black actions on a near-white page. This page makes it yours. It covers light and dark mode, then replaces the primary color with a brand color in both modes, then defines an intent the package does not ship. The second half is the lookup reference: every color, intent, foundation, and font token, with its default.
10
10
 
11
- ## Color tokens
11
+ ## Light and dark
12
12
 
13
- Each color token holds both of its theme values at once through `light-dark()`, and the element's `color-scheme` picks one.
13
+ Out of the box, the page follows the operating system: light when the user's system is light, dark when it is dark. You do nothing for that.
14
14
 
15
- Every row is an override point: set it on `:root` to retheme globally, or on any element to retheme that subtree. The `-hover`, `-subtle`, `-strong`, and `-contrast` rows are companion tones wired into the intent slots, so overriding a base color such as `--color-primary` on its own does not shift its companions — retheme the whole family. The `text` family has no `-subtle` row: the neutral intent draws its subtle tone from `--color-surface`.
15
+ To let the user choose, put a theme selector on the root element:
16
16
 
17
17
  ```html
18
- <section data-theme="dark">
19
- <article>Dark token subtree</article>
20
- </section>
18
+ <html data-theme="dark">
19
+ …
20
+ </html>
21
21
  ```
22
22
 
23
- | Token | Purpose |
24
- | ------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
25
- | `--color-primary` | Primary action/content color. |
26
- | `--color-primary-contrast` | Text/icon color on primary filled surface. |
27
- | `--color-primary-hover` | Primary hover state. |
28
- | `--color-primary-subtle` | Low-emphasis companion tone for primary. Use as soft surface or subtle background. |
29
- | `--color-primary-strong` | High-emphasis companion tone for primary. Use as readable text, icon, or border on subtle surfaces. |
30
- | `--color-accent` | Secondary/accent surface. |
31
- | `--color-accent-contrast` | Text/icon color on accent filled surface. |
32
- | `--color-accent-hover` | Accent hover state. |
33
- | `--color-accent-subtle` | Low-emphasis companion tone for accent. Use as soft surface or subtle background. |
34
- | `--color-accent-strong` | High-emphasis companion tone for accent. Use as readable text, icon, or border on subtle surfaces. |
35
- | `--color-border` | Default border color. |
36
- | `--color-border-hover` | Interactive border color. |
37
- | `--color-control-border` | Resting line on the six text controls; one value, drawn at a fraction on text inputs. |
38
- | `--color-background` | Page and default raised surface background. |
39
- | `--color-foreground` | Subtle raised foreground surface. |
40
- | `--color-surface` | Nested or muted surface. |
41
- | `--color-text` | Primary text color. |
42
- | `--color-text-secondary` | Secondary text color. |
43
- | `--color-text-contrast` | Readable color on top of a full fill of `--color-text`. A checked toggle prints it. |
44
- | `--color-text-hover` | Text hover state. |
45
- | `--color-text-strong` | High-emphasis companion tone for text. Use as readable text, icon, or border on subtle surfaces. |
46
- | `--color-tooltip` | Tooltip bubble plate with no intent. Chosen per theme rather than derived: near-white in light, mid grey in dark. |
47
- | `--color-tooltip-contrast` | Ink on `--color-tooltip`. |
48
- | `--color-success` | Success state color. |
49
- | `--color-success-contrast` | Text/icon color on success filled surface. Meets 4.5:1 as normal text on `--color-success` (5.14:1). |
50
- | `--color-success-hover` | Success hover tone. |
51
- | `--color-success-subtle` | Low-emphasis companion tone for success. |
52
- | `--color-success-strong` | High-emphasis companion tone for success. |
53
- | `--color-warning` | Warning state color. |
54
- | `--color-warning-contrast` | The near-black tone, not the page tone. Meets 4.5:1 as normal text on `--color-warning` (6.19:1). |
55
- | `--color-warning-hover` | Warning hover tone. |
56
- | `--color-warning-subtle` | Low-emphasis companion tone for warning. |
57
- | `--color-warning-strong` | High-emphasis companion tone for warning. |
58
- | `--color-destructive` | Destructive/error state color. |
59
- | `--color-destructive-contrast` | Text/icon color on destructive filled surface. Meets 4.5:1 as normal text on `--color-destructive` (5.78:1). |
60
- | `--color-destructive-hover` | Destructive hover tone. |
61
- | `--color-destructive-subtle` | Low-emphasis companion tone for destructive. |
62
- | `--color-destructive-strong` | High-emphasis companion tone for destructive. |
63
- | `--color-info` | Informational state color. |
64
- | `--color-info-contrast` | Text/icon color on info filled surface. Meets 4.5:1 as normal text on `--color-info` (6.19:1). |
65
- | `--color-info-hover` | Info hover tone. |
66
- | `--color-info-subtle` | Low-emphasis companion tone for info. |
67
- | `--color-info-strong` | High-emphasis companion tone for info. |
68
-
69
- > **Intent token contract**: color intent tokens own meaning and tone variants. Intent classes map one family onto the shared intent slots below, and presentation classes decide how much of it a component shows. Theme changes belong in token values, not broad component-level theme checks. Component-level theme handling should exist only when a component has an internal structure that cannot be expressed through the token palette alone.
23
+ | Theme | Equivalent selectors |
24
+ | ----- | ------------------------------------------------ |
25
+ | Light | `.light`, `.theme-light`, `[data-theme="light"]` |
26
+ | Dark | `.dark`, `.theme-dark`, `[data-theme="dark"]` |
70
27
 
71
- ## Intent tokens
28
+ A settings screen with a "System / Light / Dark" choice removes the attribute for System and sets it for the other two.
29
+
30
+ How it resolves:
31
+
32
+ - A theme selector sets the CSS `color-scheme` property, and the element's text color to `--color-text`. Every color token holds its light and its dark value at once, through `light-dark()`, and the element's `color-scheme` picks one.
33
+ - `color-scheme` inherits, so a selector themes everything inside it. A selector on any element themes that part of the page: a dark sidebar on a light page is `<aside class="dark">`, and the text inside it takes the dark theme's text color.
34
+ - A selector sets no background. Give the region its ground yourself, such as `background: var(--color-background)`. Any utility or component class on the same element overrides the selector's text color.
35
+ - The nearest selector wins, at any depth. A `.light` card inside a `.dark` page is light.
36
+ - With no selector, the root has `color-scheme: light dark`, which follows the system.
37
+ - Put one theme on an element. If an element carries both a light and a dark selector, dark wins, but do not rely on that.
38
+ - Setting `color-scheme` yourself on an element themes the tokens the same way, because it is the only thing the palette reads. It also themes native parts such as scrollbars and the inside of form controls. Plain text inside keeps the color it inherited until you also set `color: var(--color-text)`.
39
+
40
+ Every component color comes from a token or from `currentColor`, so components follow the theme with no dark-mode rules of their own. The marks the package draws, such as the checkbox tick and the select arrow, use `currentColor` too. Icons and images you add are yours to theme.
72
41
 
73
- Intent classes set these seven slots; components read them. Because every supporting component reads the same seven names, an intent works on every component that supports intent, and a custom intent needs no component changes.
42
+ `light-dark()` works from Chrome 123, Safari 17.5, and Firefox 120. The package as a whole needs Firefox 128; see [Setup → Requirements](../setup.md#requirements).
74
43
 
75
- | Token | Purpose |
76
- | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
77
- | `--intent-color` | The intent's base color. What a fill is made of. |
78
- | `--intent-contrast` | Readable color on top of a filled `--intent-color`. |
79
- | `--intent-hover` | The intent's hovered base color. |
80
- | `--intent-strong` | The intent printed on a page. Text, icon, or border wherever the fill is not full. |
81
- | `--intent-subtle` | Low-emphasis tone. Tinted surfaces and tracks. Neutral resolves to `--color-surface`. |
82
- | `--intent-border` | Line color. With no intent it resolves to the aesthetic's `--ui-ink`, then `--color-border`; text controls and toggles read `--ui-control-ink`, then `--ui-ink`, then `--color-control-border`, and tables read `--ui-rule-ink` first. |
83
- | `--intent-fill-max` | How far a fill of this intent is allowed to go. `100%` for every intent but neutral. |
44
+ ### Printing
84
45
 
85
- `--intent-color` and `--intent-strong` are two ends of the same family and not interchangeable. The base is a ground, chosen to be filled with; the strong tone is chosen to be read against a page. A `.soft` or `.ghost` component prints the strong one, which is why an unfilled warning reads in amber-800 rather than the amber-600 its solid sibling is filled with.
46
+ A page prints in the light theme, whatever theme it shows on screen. Under `@media print`, the root and every dark selector set `color-scheme: light`, so every token resolves to its light value, a forced-dark sidebar's included. Paper is light, and dark text on it costs less ink than a printed dark page.
86
47
 
87
- The strong tone has to stay recognisably its own hue, not just readable. On a dark page the `-100` shade of every family is a pale tint that carries almost no chroma at small sizes: a soft success badge and a soft destructive one printed labels that both read white, and a soft radio's dot stopped matching the ring around it. The dark tones are `-300` for that reason, and the tinted grounds under them are `-900` so there is room — measured at 5.0:1 or better everywhere a partial fill prints text, and past 9:1 on the page.
48
+ Filled components print their background. A browser drops backgrounds from a printed page by default, which leaves a solid button's contrast text on nothing, so every component sets `print-color-adjust: exact`. The property inherits, so the content inside a component keeps its colors too.
88
49
 
89
- `--intent-border` exists because a neutral border must stay the quiet border gray rather than the text color. Everything that draws a line reads it: control borders, table rules, dividers, key caps, and quote bars.
50
+ One limit: a `dark:` utility still applies in print. It matches by selector, not by `color-scheme`, so `<html class="dark">` with `dark:bg-black` on an element prints that background black. Write a `print:` utility beside it if the printed page matters.
51
+
52
+ ### The `dark:` variant
53
+
54
+ With the Tailwind source entrypoints (`/tw`), the package replaces Tailwind's `dark:` variant so your own utilities follow the same theme as the tokens. A `dark:` utility applies under an explicit dark selector on the element or an ancestor, or under a dark system preference when no ancestor sets a theme:
55
+
56
+ ```html
57
+ <html>
58
+ <body>
59
+ <p class="dark:text-white">Follows the system preference</p>
60
+ <div class="light">
61
+ <p class="dark:text-white">Stays light: this part of the page chose a theme</p>
62
+ </div>
63
+ </body>
64
+ </html>
65
+ ```
90
66
 
91
- `--intent-fill-max` exists for the neutral intent, whose color is the page's own ink. Every other intent is a hue, legible pale at 12% and saturated at 100%; a grey filled to 100% is a black or white slab, which is the loudest thing on the page and the wrong look for the intent that means _nothing in particular_. Neutral stops at `20%`, so `.solid` gives it a quiet plate that still reads as a step past `.soft`. Hover adds its step on top of the cap, so a capped fill still deepens under the pointer.
67
+ It differs from the tokens in one case: an explicit theme nested inside the opposite one. A `.light` element inside a `.dark` one has light tokens, because the nearest `color-scheme` wins. But `dark:` matches by selector, and `.dark *` matches every element inside `.dark`, so a `dark:` utility inside that `.light` element still applies. If a page uses both tokens and `dark:` utilities, do not nest opposite themes.
92
68
 
93
- The foreground is gated by the plate rather than tied to the fill. `--intent-contrast` is the ink for a _full_ fill, so printing it at full strength on a fifth of one puts white on light grey. Contrast ink therefore appears only once the fill is past halfway and reaches full at a full fill; below that a component prints `--intent-strong`, the tone chosen to be read.
69
+ The compiled entrypoints do not define a `dark:` variant; they have no Tailwind build to define it in.
94
70
 
95
- So a capped neutral takes the strong tone whole rather than a fraction of the way toward the page. A state that lifts the cap on purpose — a checked checkbox is filled with its own intent by definition — reaches a full fill and gets the contrast whole.
71
+ ## Change the brand color
96
72
 
97
- Set them directly to define an intent this package does not ship. **State every slot**, including `--intent-fill-max`: components reset all seven to the neutral values, so a slot your intent leaves out keeps neutral's — and an intent that omits the cap fills to 20% of its color instead of 100%.
73
+ The primary color is near-black on a light page and near-white on a dark one. To make it a brand color, replace the whole primary family: the base color and its four companions. Each holds a light and a dark value:
98
74
 
99
75
  ```css
100
- .brand {
101
- --intent-color: var(--color-violet-600);
102
- --intent-contrast: var(--color-neutral-50);
103
- --intent-hover: var(--color-violet-700);
104
- --intent-strong: var(--color-violet-800);
105
- --intent-subtle: var(--color-violet-100);
106
- --intent-border: var(--color-violet-600);
76
+ :root {
77
+ --color-primary: light-dark(#0f766e, #2dd4bf);
78
+ --color-primary-contrast: light-dark(#ffffff, #042f2e);
79
+ --color-primary-hover: light-dark(#115e59, #5eead4);
80
+ --color-primary-subtle: light-dark(#ccfbf1, #134e4a);
81
+ --color-primary-strong: light-dark(#115e59, #5eead4);
82
+ }
83
+ ```
84
+
85
+ What each one is for:
86
+
87
+ | Token | Role | Check |
88
+ | -------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------- |
89
+ | `--color-primary` | The fill of a solid primary button, a checked toggle, a progress fill. | Text in `-contrast` on it reaches 4.5:1. |
90
+ | `--color-primary-contrast` | Text and icons on a full primary fill. | See above. |
91
+ | `--color-primary-hover` | The fill under the pointer. | Text in `-contrast` on it still reaches 4.5:1. |
92
+ | `--color-primary-subtle` | The light tint behind soft components. | Text in `-strong` on it reaches 4.5:1. |
93
+ | `--color-primary-strong` | Text, icons, and borders where the fill is not full: soft and ghost. | Reaches 4.5:1 on the page and on `-subtle`. |
94
+
95
+ The teal values above measure, in light and dark mode: 5.47:1 and 7.77:1 for the fill and its text, 7.27:1 and 13.38:1 for the strong tone on the page, and 6.73:1 and 6.41:1 for the strong tone on the subtle tint.
96
+
97
+ Replace the whole family. Changing only `--color-primary` leaves its companions on the old near-black, so a soft primary badge keeps gray text and a hovered button flashes black. The focus ring is drawn from `--color-primary` at 64% opacity, so it follows the brand color without a change of its own.
98
+
99
+ The same pattern works for every family in [Color tokens](#color-tokens): `--color-accent` is what `.secondary` reads, and the four status families are `success`, `warning`, `destructive`, and `info`. Set them on `:root` for the whole page, or on any element to retheme only what is inside it.
100
+
101
+ The values must be complete colors: hex, `rgb()`, `oklch()`, or a variable you declared yourself. Tailwind palette variables such as `--color-teal-700` exist in a `/tw` build, from your Tailwind theme, but the compiled CSS declares only the palette colors the package itself uses. With compiled CSS, a reference to any other palette variable is empty, and the color it feeds is lost.
102
+
103
+ With `/tw`, you can drop the Tailwind hues your design does not use, so `bg-red-500` and its siblings stop being classes. Keep the ones the theme reads, `neutral`, `emerald`, `amber`, `rose`, `indigo`, and `white`: cleared, the colors built from them are lost without an error. [Tailwind CSS v4 → Drop Tailwind's palette](../integrating/tailwind.md#drop-tailwinds-palette) has the block to copy.
104
+
105
+ ## Your own intent
106
+
107
+ The package ships six intents: `.primary`, `.secondary`, `.success`, `.warning`, `.destructive`, and `.info`, and the default, `.neutral`. To add one, such as a "Beta" color for features in preview, write a class that sets the seven intent slots. Use complete colors, with `light-dark()` for anything that changes between themes:
108
+
109
+ ```css
110
+ .beta {
111
+ --intent-color: #7c3aed;
112
+ --intent-contrast: #ffffff;
113
+ --intent-hover: #6d28d9;
114
+ --intent-strong: light-dark(#5b21b6, #c4b5fd);
115
+ --intent-subtle: light-dark(#ede9fe, #4c1d95);
116
+ --intent-border: light-dark(#7c3aed, #a78bfa);
107
117
  --intent-fill-max: 100%;
108
118
  }
109
119
  ```
110
120
 
111
121
  ```html
112
- <button class="btn brand">Custom intent</button> <span class="badge brand soft">Works everywhere</span>
122
+ <span class="badge beta">Beta</span> <button class="btn beta" type="button">Try the new editor</button>
113
123
  ```
114
124
 
115
- > **Intent does not cascade.** Components reset these slots to the text palette at their own root, so a `.success` container never recolors a nested destructive button. Put the intent class on the element that shows the intent.
125
+ These values measure 5.7:1 for white on the fill, 7.1:1 for white on the hover color, 8.61:1 (light) and 10.72:1 (dark) for the strong tone on the page, and 7.57:1 and 5.93:1 for the strong tone on the subtle tint.
126
+
127
+ **Set every slot**, including `--intent-fill-max`. Each component resets all seven slots to the neutral values at its own root, so a slot you leave out keeps neutral's value, and without `--intent-fill-max: 100%` your intent fills only to 20%, like neutral. What each slot does is in [Intent tokens](#intent-tokens).
128
+
129
+ The class works on everything that takes an intent: buttons, badges, fields and toggles, solid and ghost cards, progress bars, skeletons, tooltip bubbles, and the rest. A few behaviors are written for the six built-in names, and a custom intent does not get them:
130
+
131
+ - **A quiet, untinted rest.** A neutral `.card.soft`, `.panel`, and `.alert` rest on the quiet background tone, and they recognize an intent only by the six built-in classes. `.card.soft.beta`, `.panel.beta`, and `.alert.beta` are drawn like the neutral one: the quiet background tone instead of a violet tint. Your intent still colors their text and border.
132
+ - **The chip tint.** A `.beta` key cap or code block shows your `--intent-subtle` tint, but not the extra 12% of `--intent-color` a built-in intent adds over it.
133
+ - **Solo classes.** A [solo class](./solo.md) such as `.neobrutalism-solo` takes an intent only from the six built-in classes, so `.neobrutalism-solo.beta` is drawn neutral.
134
+ - **Hint text.** A [`.hint`](./forms.md#show-a-validation-error) prints in an intent's readable tone only for the six built-in classes, so `.hint.beta` stays secondary-colored.
135
+ - **Table rows.** A body row recolors its text, line, and hover only for the built-in intent classes. `<tr class="beta">` looks like the rest of its table.
136
+
137
+ If you need those, there are two ways:
138
+
139
+ - **Retheme a built-in intent instead.** Set the `--color-info` family (or another one) to your colors, as in [Change the brand color](#change-the-brand-color), and write `.info`. Everything, including the cases above, then works. The cost is that the change reaches every `.info` inside the element you set it on, so set it on the narrowest element that works, and do not do it with an intent the page already uses for its own meaning.
140
+ - **Paint it yourself.** Write your own CSS for that one component, such as a `background-color` on `.panel.beta`.
141
+
142
+ > **Intent does not pass down.** Each component resets the intent slots at its own root, so a `.success` container does not recolor a destructive button inside it. Put the intent class on the element that shows the color.
143
+
144
+ ## Color tokens
145
+
146
+ Every color token holds a light and a dark value through `light-dark()`; one value means both themes use it. Palette names such as `neutral-950` are Tailwind's default palette, which the package's compiled CSS declares for the colors it uses.
147
+
148
+ Each row is an override point: set it on `:root` to retheme the page, or on any element to retheme what is inside it. The `-hover`, `-subtle`, `-strong`, and `-contrast` rows are companions of their family's base color and do not follow it, so retheme the whole family. The `text` family has no `-subtle` row: the neutral intent takes its subtle tone from `--color-surface`.
149
+
150
+ | Token | Light, dark | Purpose |
151
+ | ------------------------------ | ---------------------------- | ---------------------------------------------------------------------------------------------- |
152
+ | `--color-primary` | `neutral-950`, `neutral-50` | Fill of `.primary`. |
153
+ | `--color-primary-contrast` | `neutral-50`, `neutral-950` | Text and icons on a full primary fill. |
154
+ | `--color-primary-hover` | `neutral-700`, `neutral-200` | Primary fill under the pointer. |
155
+ | `--color-primary-subtle` | `neutral-200`, `neutral-900` | Primary tint behind soft components. |
156
+ | `--color-primary-strong` | `neutral-900`, `neutral-100` | Primary text, icons, and borders where the fill is not full. |
157
+ | `--color-accent` | `neutral-600`, `neutral-400` | Fill of `.secondary`. |
158
+ | `--color-accent-contrast` | `neutral-50`, `neutral-950` | Text and icons on a full secondary fill. |
159
+ | `--color-accent-hover` | `neutral-700`, `neutral-300` | Secondary fill under the pointer. |
160
+ | `--color-accent-subtle` | `neutral-100`, `neutral-800` | Secondary tint behind soft components. |
161
+ | `--color-accent-strong` | `neutral-800`, `neutral-200` | Secondary text, icons, and borders where the fill is not full. |
162
+ | `--color-border` | `neutral-400`, `neutral-700` | Border of neutral surfaces, tables, dividers, and key caps. |
163
+ | `--color-border-hover` | `neutral-600`, `neutral-500` | Border under the pointer. |
164
+ | `--color-control-border` | `neutral-500` | Resting border of neutral text controls and toggles, drawn at a fraction on text controls. |
165
+ | `--color-background` | `neutral-50`, `neutral-950` | The page. |
166
+ | `--color-foreground` | `neutral-100`, `neutral-900` | The quiet background tone one step off the page: neutral soft cards, panels, and alerts. |
167
+ | `--color-surface` | `neutral-200`, `neutral-800` | A further step: nested and muted surfaces, and the neutral intent's tint. |
168
+ | `--color-text` | `neutral-950`, `neutral-50` | Body text, and the neutral intent's color. |
169
+ | `--color-text-secondary` | `neutral-700`, `neutral-400` | Secondary text, such as `.hint`. |
170
+ | `--color-text-contrast` | `neutral-50`, `neutral-950` | Text on a full fill of `--color-text`, such as the mark of a checked neutral toggle. |
171
+ | `--color-text-hover` | `neutral-700`, `neutral-200` | Neutral color under the pointer. |
172
+ | `--color-text-strong` | `neutral-900`, `neutral-100` | Neutral text, icons, and borders where the fill is not full. |
173
+ | `--color-tooltip` | `white`, `neutral-700` | Background of a tooltip bubble with no intent. Chosen per theme so it reads as lifted in both. |
174
+ | `--color-tooltip-contrast` | `neutral-900`, `neutral-100` | Text on `--color-tooltip`. |
175
+ | `--color-success` | `emerald-700` | Fill of `.success`. |
176
+ | `--color-success-contrast` | `neutral-50` | Text on a full success fill: 5.14:1. |
177
+ | `--color-success-hover` | `#007552`, `#008e66` | Success fill under the pointer. |
178
+ | `--color-success-subtle` | `emerald-100`, `emerald-900` | Success tint behind soft components. |
179
+ | `--color-success-strong` | `emerald-800`, `emerald-300` | Success text, icons, and borders where the fill is not full. |
180
+ | `--color-warning` | `amber-600` | Fill of `.warning`. |
181
+ | `--color-warning-contrast` | `neutral-950` | Text on a full warning fill: near-black, not the page color. 6.19:1. |
182
+ | `--color-warning-hover` | `#d46700`, `#e78300` | Warning fill under the pointer. |
183
+ | `--color-warning-subtle` | `amber-100`, `amber-900` | Warning tint behind soft components. |
184
+ | `--color-warning-strong` | `amber-800`, `amber-300` | Warning text, icons, and borders where the fill is not full. |
185
+ | `--color-destructive` | `rose-700` | Fill of `.destructive`. |
186
+ | `--color-destructive-contrast` | `neutral-50` | Text on a full destructive fill: 5.78:1. |
187
+ | `--color-destructive-hover` | `#c00036`, `#d1054b` | Destructive fill under the pointer. |
188
+ | `--color-destructive-subtle` | `rose-100`, `rose-900` | Destructive tint behind soft components. |
189
+ | `--color-destructive-strong` | `rose-800`, `rose-300` | Destructive text, icons, and borders where the fill is not full. |
190
+ | `--color-info` | `indigo-600` | Fill of `.info`. |
191
+ | `--color-info-contrast` | `neutral-50` | Text on a full info fill: 6.19:1. |
192
+ | `--color-info-hover` | `#4b36e9`, `#5e4ff9` | Info fill under the pointer. |
193
+ | `--color-info-subtle` | `indigo-100`, `indigo-900` | Info tint behind soft components. |
194
+ | `--color-info-strong` | `indigo-800`, `indigo-300` | Info text, icons, and borders where the fill is not full. |
195
+
196
+ Theme changes belong in these values, not in component rules that check the theme.
197
+
198
+ ## Intent tokens
199
+
200
+ An intent class sets these seven slots, and components read them. Because every component reads the same seven names, a class that sets all seven works as an intent, as [Your own intent](#your-own-intent) shows.
201
+
202
+ | Token | Purpose |
203
+ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
204
+ | `--intent-color` | The intent's base color, what a fill is made of. |
205
+ | `--intent-contrast` | Text on a full fill of `--intent-color`. |
206
+ | `--intent-hover` | The base color under the pointer. |
207
+ | `--intent-strong` | The intent as text, icon, or border wherever the fill is not full. |
208
+ | `--intent-subtle` | The light tint behind soft components and progress tracks. Neutral uses `--color-surface`. |
209
+ | `--intent-border` | Line color. With no intent it is the aesthetic's `--ui-ink`, then `--color-border`; text controls and toggles read `--ui-control-ink`, then `--ui-ink`, then `--color-control-border`, and tables read `--ui-rule-ink` first. |
210
+ | `--intent-fill-max` | How far a fill of this intent may go. `100%` for every intent except neutral. |
211
+
212
+ `--intent-color` and `--intent-strong` are not interchangeable. The base color is chosen to be filled with; the strong tone is chosen to be read against the page. A soft or ghost component prints the strong tone, which is why a soft warning reads in amber-800 while a solid one is filled with amber-600.
213
+
214
+ The strong tone also has to stay recognizably its own hue. On a dark page, the `-100` shade of every family is a pale tint with almost no color at small sizes: a soft success badge and a soft destructive one would both read as white. The dark strong tones are `-300` for that reason, and the tints under them are `-900`, which measures 5.0:1 or better wherever a partial fill holds text, and over 9:1 on the page.
215
+
216
+ `--intent-border` exists so a neutral border stays the quiet border gray rather than the text color. Everything that draws a line reads it: control borders, table rules, dividers, key caps, and quote bars.
217
+
218
+ `--intent-fill-max` exists for the neutral intent, whose color is the page's own text color. Every other intent is a hue, readable as a pale tint at 12% and saturated at 100%; a gray filled to 100% is a black or white block, the loudest thing on the page for the intent that means nothing in particular. Neutral stops at `20%`, so `.solid` gives it a quiet gray that is still a step past `.soft`. Hover adds its step on top of the cap, so a capped fill still deepens under the pointer.
219
+
220
+ The text color follows the fill. `--intent-contrast` is the text color for a full fill; printed on a fifth of one, it would be white on light gray. So contrast text appears only once the fill is past halfway, and reaches full strength at a full fill; below that, a component prints `--intent-strong`. A capped neutral therefore prints the strong tone. A state that lifts the cap on purpose, such as a checked checkbox, reaches a full fill and prints the contrast color.
116
221
 
117
222
  ## Foundation tokens
118
223
 
119
- Foundation tokens are not aliases for one color. They define layout, shape, motion, focus, depth, and layering behavior used by helper classes.
120
-
121
- The four `--elevation-*` values below are raw shadows for elements you style yourself. They are a separate thing from `.flat`/`.raised`/`.floating`, which scale a _component's own_ shadow geometry and default to no shadow at all until an aesthetic or one of those classes asks for depth — see [Composing → Elevation](./composing.md#elevation) for that system. The two share `none`/`sm`/`md`/`lg` naming for the same weight of depth, but one is a fixed value and the other multiplies shadow parts, so they will not always paint identically.
122
-
123
- | Token | Purpose |
124
- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
125
- | `--font-default` | Default app font family. |
126
- | `--container-narrow` | Narrow content width. |
127
- | `--container-max` | Default content width. |
128
- | `--container-wide` | Wide content width. |
129
- | `--layout-gutter` | Responsive inline page padding. |
130
- | `--layout-section-block` | Responsive section vertical padding. |
131
- | `--layout-gap` | Shared gap for `.view`, `.stack`, `.cluster`, and `.auto-grid`. |
132
- | `--layout-grid-min` | Minimum column width for `.auto-grid`. |
133
- | `--radius-small` | Small radius for compact utility surfaces such as code elements. |
134
- | `--radius-control` | Radius for buttons, inputs, tooltips, and compact UI. |
135
- | `--radius-surface` | Radius for cards, panels, and alerts. |
136
- | `--control-height` | Default minimum height for controls. |
137
- | `--border-width` | Default border width. |
138
- | `--elevation-color` | Shadow color the four elevations compose from. Heavier in dark themes. |
139
- | `--elevation-none` | No shadow. Use on an element that should read as flat against its page. |
140
- | `--elevation-sm` | A light shadow for your own surface-level elements, such as cards. |
141
- | `--elevation-md` | A shadow for your own raised elements, such as dropdowns and popovers. |
142
- | `--elevation-lg` | A shadow for your own full overlays, such as modal dialogs. |
143
- | `--focus-ring` | Focus-visible ring color. |
144
- | `--focus-ring-offset` | Focus-visible outline offset. Unset: the negative of the focused element's line width, so the ring lands on the edge under every aesthetic. |
145
- | `--focus-ring-width` | Focus-visible outline/ring width. |
146
- | `--motion-duration-fast` | Fast transition for a table row's hover (`120ms`). |
147
- | `--motion-duration-normal` | Every component's colour, border, shadow, and press transitions, toggle marks, and the tooltip bubble (`200ms`). |
148
- | `--motion-duration-slow` | Progress value changes, and the base of the skeleton and progress shimmer cycle (`400ms`). |
149
- | `--motion-ease` | Default easing curve. |
150
- | `--z-popover` | Popover/tooltip z-index. |
151
- | `--breakpoint-xs` | Extra-small Tailwind responsive breakpoint. |
152
-
153
- Next: [Customizing](./customizing.md) covers presentation and material tokens — how much of an intent shows, and what a component is made of.
224
+ Foundation tokens set layout, shape, motion, focus, depth, and stacking. Override them on `:root`, or on an element for what is inside it.
225
+
226
+ The four `--elevation-*` shadows are for elements you style yourself. They are separate from the `.flat`, `.raised`, and `.floating` classes, which scale a component's own shadow and draw nothing until an aesthetic or one of those classes asks for depth; see [Composing → Elevation](./composing.md#elevation). Both use the weights none, small, medium, and large, but one is a fixed value and the other scales shadow parts, so they do not always look the same.
227
+
228
+ | Token | Default | Purpose |
229
+ | -------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
230
+ | `--font-default` | `"Segoe UI", system-ui, sans-serif` | Page font. See [Fonts](#fonts). |
231
+ | `--container-narrow` | `48rem` | Width of `.section-content.narrow`. |
232
+ | `--container-max` | `80rem` | Width of `.section-content`. |
233
+ | `--container-wide` | `90rem` | Width of `.section-content.wide`. |
234
+ | `--layout-gutter` | `clamp(1rem, 4vw, 2rem)` | Side padding of `.section`, growing with the window. |
235
+ | `--layout-section-block` | `clamp(4rem, 9vw, 7rem)` | Top and bottom padding of `.section`, growing with the window. |
236
+ | `--layout-gap` | `1rem` | Gap of `.view`, `.stack`, `.cluster`, and `.auto-grid`. |
237
+ | `--layout-grid-min` | `16rem` | Narrowest column of `.auto-grid`. |
238
+ | `--radius-small` | `0.25rem` | Corner of small chips: `.kbd` and `.code`. |
239
+ | `--radius-control` | `0.5rem` | Corner of buttons, fields, and tooltip bubbles. |
240
+ | `--radius-surface` | `0.875rem` | Corner of cards, panels, and alerts. |
241
+ | `--control-height` | `2.5rem` | Minimum height of a default button and a text control. |
242
+ | `--border-width` | `1px` | Border width. |
243
+ | `--elevation-color` | `rgb(15 23 42 / 0.08)` light, `rgb(0 0 0 / 0.55)` dark | The shadow color the four elevations use. Heavier on a dark page, where a light shadow would not show. |
244
+ | `--elevation-none` | `none` | No shadow. |
245
+ | `--elevation-sm` | `0 1px 2px`, `0 1px 3px` | A light shadow for your own surfaces, such as a card. |
246
+ | `--elevation-md` | `0 4px 6px -1px`, `0 2px 4px -2px` | A shadow for your own raised elements, such as a dropdown menu. |
247
+ | `--elevation-lg` | `0 20px 25px -5px`, `0 8px 10px -6px` | A shadow for your own overlays, such as a modal dialog. |
248
+ | `--focus-ring` | Not set | Color of the keyboard focus ring. When unset, `--color-primary` at 64% opacity, on the focused element. |
249
+ | `--focus-ring-offset` | Not set | Distance of the ring from the element. When unset, the ring sits on the element's border under every aesthetic. |
250
+ | `--focus-ring-width` | `3px` | Width of the keyboard focus ring. |
251
+ | `--motion-duration-fast` | `120ms` | A table row's hover. |
252
+ | `--motion-duration-normal` | `200ms` | Color, border, shadow, and press transitions, toggle marks, and the tooltip bubble. |
253
+ | `--motion-duration-slow` | `400ms` | Progress value changes, and the base of the skeleton and progress shimmer cycle. |
254
+ | `--motion-ease` | `cubic-bezier(0.2, 0, 0, 1)` | Easing for every transition. |
255
+ | `--z-popover` | `40` | Stacking order of tooltip bubbles. Use it for your own popovers so they stack the same way. |
256
+ | `--breakpoint-xs` | `30rem` | The `xs:` responsive breakpoint. Only in a `/tw` build; the compiled entrypoints do not declare it. |
257
+
258
+ Each `--elevation-*` shadow is made of the listed parts, each in `--elevation-color`. The four shadows are composed once, on the root, so an `--elevation-color` you set on a container does not reach them: set it on `:root`, or set the `--elevation-*` token itself on the container. The elevation classes, such as `.card.raised`, read `--elevation-color` on the element and follow a container's value, with one exception: every aesthetic class sets its own `--elevation-color` on its region. A value set above a `.glass` or `.pixel` region does not reach inside it. Set it on the aesthetic's element itself, where your utility or unlayered CSS beats the class, or on an element inside the region. Under glass, a surface's shadow is built on the `.glass` element, so only a value set there changes it. Under `.neobrutalism` the shadow is drawn in the intent or the ink, and `.cyber` draws no depth, so neither reads the token. The hover step that deepens a fill under the pointer is a presentation token, `--hover-step`; see [Customizing](./customizing.md#presentation-tokens).
259
+
260
+ ## Fonts
261
+
262
+ The package ships no font files and makes no network requests. Every font token names fonts already installed on the reader's device until you supply your own.
263
+
264
+ | Token | Read by | Without your value |
265
+ | ---------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
266
+ | `--font-default` | The reset, on `html` and `body`, and `.glass` and `.neobrutalism` regions. Controls inherit it. | `"Segoe UI", system-ui, sans-serif` |
267
+ | `--font-mono` | The `kbd`, `code`, and `pre` content helpers, and `<kbd>`, `<code>`, and `<pre>` under `/native` | `ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace`. Under `/tw`, your Tailwind theme's value. |
268
+ | `--font-pixel` | [`.pixel`](./aesthetics.md#pixel) and `.pixel-solo` | Not declared. Falls back to a monospace stack. |
269
+ | `--font-rounded` | [`.chunky-tile`](./aesthetics.md#chunky-tile) and `.chunky-tile-solo` | Not declared. Falls back to `--font-default`, then a sans-serif stack. |
270
+ | `--font-cyber` | [`.cyber`](./aesthetics.md#cyber) and `.cyber-solo` | Not declared. Falls back to a monospace stack. |
271
+ | `--font-sketch` | [`.sketch`](./aesthetics.md#sketch) and `.sketch-solo` | Not declared. Falls back to the platform `cursive` family. |
272
+
273
+ `--font-default` reaches the page only through the reset, which `@codenhub/styles`, `/native`, `/tw`, `/tw/native`, and `/tw/reset` include. With `/theme` or `/components` alone, set `font-family` on `body` yourself, and set `--font-default` to the same font: `.glass` and `.neobrutalism` regions set their own `font-family` from the token, so a font set only on `body` does not reach inside them. Or set only the token, and write `font-family: var(--font-default)` on `body`. The aesthetic tokens are read by the aesthetic's own class, so they work with any entrypoint.
274
+
275
+ To use a web font, load it the way you load any font, then name it in the token. Set the token on `:root` for the whole page, or on any element for that part of it:
276
+
277
+ ```html
278
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
279
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
280
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&display=swap" />
281
+ ```
282
+
283
+ ```css
284
+ :root {
285
+ --font-default: "Inter", system-ui, sans-serif;
286
+ }
287
+ ```
288
+
289
+ A self-hosted font is declared with `@font-face` and named the same way:
290
+
291
+ ```css
292
+ @font-face {
293
+ font-family: "Pixelify Sans";
294
+ src: url("/fonts/pixelify-sans.woff2") format("woff2");
295
+ font-weight: 400 700;
296
+ font-display: swap;
297
+ }
298
+
299
+ :root {
300
+ --font-pixel: "Pixelify Sans", monospace;
301
+ }
302
+ ```
303
+
304
+ Load weights 400 to 700, and 800 for `--font-rounded`: components set `font-weight` from 400 to 700, and `.chunky-tile` sets its labels in 800. A browser that lacks a weight fakes it by thickening the regular one, which smears a pixel or handwriting face. Always end the stack with a generic family, such as `sans-serif` or `monospace`, so text still renders while the font loads or if it fails to.
305
+
306
+ ## Next
307
+
308
+ The screen has your colors in both themes. [Aesthetics](./aesthetics.md) changes what it is made of: thick black outlines, frosted glass, pixels, and more.