@codenhub/styles 0.5.0 → 0.7.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 (119) hide show
  1. package/NOTICE +18 -16
  2. package/README.es.md +61 -0
  3. package/README.md +27 -22
  4. package/README.pt-BR.md +61 -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/components.css +2 -2
  13. package/dist/index.css +2 -2
  14. package/dist/native.css +2 -2
  15. package/dist/palette.css +1 -1
  16. package/dist/theme.css +2 -2
  17. package/dist/tw/aesthetics/chunky-tile.css +120 -62
  18. package/dist/tw/aesthetics/cyber.css +102 -40
  19. package/dist/tw/aesthetics/glass.css +371 -19
  20. package/dist/tw/aesthetics/index.css +2 -1
  21. package/dist/tw/aesthetics/neobrutalism.css +121 -33
  22. package/dist/tw/aesthetics/pixel.css +111 -16
  23. package/dist/tw/aesthetics/sketch.css +100 -28
  24. package/dist/tw/box.css +69 -28
  25. package/dist/tw/components/button.css +35 -14
  26. package/dist/tw/components/feedback.css +61 -58
  27. package/dist/tw/components/form.css +255 -142
  28. package/dist/tw/components/index.css +4 -9
  29. package/dist/tw/components/surface.css +80 -68
  30. package/dist/tw/components/tooltip.css +75 -41
  31. package/dist/tw/elevation.css +14 -22
  32. package/dist/tw/index.css +15 -19
  33. package/dist/tw/intent.css +35 -27
  34. package/dist/tw/native.css +22 -12
  35. package/dist/tw/palette.css +14 -11
  36. package/dist/tw/presentation.css +16 -30
  37. package/dist/tw/reset.css +36 -43
  38. package/dist/tw/surface.css +11 -12
  39. package/dist/tw/theme.css +87 -103
  40. package/dist/tw/utilities/content.css +311 -200
  41. package/dist/tw/utilities/layout.css +41 -37
  42. package/docs/accessibility.md +104 -58
  43. package/docs/changelog/0.1.1.md +1 -0
  44. package/docs/changelog/0.2.0.md +1 -0
  45. package/docs/changelog/0.3.0.md +1 -0
  46. package/docs/changelog/0.4.0.md +1 -0
  47. package/docs/changelog/0.5.0.md +8 -3
  48. package/docs/changelog/0.6.0.md +53 -0
  49. package/docs/changelog/0.7.0.md +50 -0
  50. package/docs/changelog/index.md +2 -0
  51. package/docs/concepts.md +188 -46
  52. package/docs/es/accessibility.md +150 -0
  53. package/docs/es/concepts.md +212 -0
  54. package/docs/es/index.md +104 -0
  55. package/docs/es/integrating/astro.md +35 -0
  56. package/docs/es/integrating/index.md +47 -0
  57. package/docs/es/integrating/nextjs.md +67 -0
  58. package/docs/es/integrating/svelte.md +45 -0
  59. package/docs/es/integrating/tailwind.md +78 -0
  60. package/docs/es/integrating/vue.md +31 -0
  61. package/docs/es/integrating/without-a-bundler.md +115 -0
  62. package/docs/es/setup.md +180 -0
  63. package/docs/es/usage/aesthetics.md +276 -0
  64. package/docs/es/usage/buttons.md +131 -0
  65. package/docs/es/usage/composing.md +164 -0
  66. package/docs/es/usage/content-and-layout.md +215 -0
  67. package/docs/es/usage/customizing.md +298 -0
  68. package/docs/es/usage/feedback.md +153 -0
  69. package/docs/es/usage/forms.md +231 -0
  70. package/docs/es/usage/index.md +29 -0
  71. package/docs/es/usage/solo.md +84 -0
  72. package/docs/es/usage/theming.md +296 -0
  73. package/docs/es/usage/tooltips.md +127 -0
  74. package/docs/index.md +89 -21
  75. package/docs/integrating/astro.md +3 -4
  76. package/docs/integrating/index.md +18 -13
  77. package/docs/integrating/nextjs.md +4 -5
  78. package/docs/integrating/svelte.md +5 -4
  79. package/docs/integrating/tailwind.md +29 -8
  80. package/docs/integrating/vue.md +3 -4
  81. package/docs/integrating/without-a-bundler.md +114 -0
  82. package/docs/pt-BR/accessibility.md +150 -0
  83. package/docs/pt-BR/concepts.md +212 -0
  84. package/docs/pt-BR/index.md +104 -0
  85. package/docs/pt-BR/integrating/astro.md +35 -0
  86. package/docs/pt-BR/integrating/index.md +47 -0
  87. package/docs/pt-BR/integrating/nextjs.md +67 -0
  88. package/docs/pt-BR/integrating/svelte.md +45 -0
  89. package/docs/pt-BR/integrating/tailwind.md +78 -0
  90. package/docs/pt-BR/integrating/vue.md +31 -0
  91. package/docs/pt-BR/integrating/without-a-bundler.md +115 -0
  92. package/docs/pt-BR/setup.md +180 -0
  93. package/docs/pt-BR/usage/aesthetics.md +276 -0
  94. package/docs/pt-BR/usage/buttons.md +131 -0
  95. package/docs/pt-BR/usage/composing.md +164 -0
  96. package/docs/pt-BR/usage/content-and-layout.md +215 -0
  97. package/docs/pt-BR/usage/customizing.md +298 -0
  98. package/docs/pt-BR/usage/feedback.md +153 -0
  99. package/docs/pt-BR/usage/forms.md +231 -0
  100. package/docs/pt-BR/usage/index.md +29 -0
  101. package/docs/pt-BR/usage/solo.md +84 -0
  102. package/docs/pt-BR/usage/theming.md +296 -0
  103. package/docs/pt-BR/usage/tooltips.md +127 -0
  104. package/docs/setup.md +134 -91
  105. package/docs/usage/aesthetics.md +102 -65
  106. package/docs/usage/buttons.md +108 -40
  107. package/docs/usage/composing.md +105 -92
  108. package/docs/usage/content-and-layout.md +159 -127
  109. package/docs/usage/customizing.md +112 -43
  110. package/docs/usage/feedback.md +135 -70
  111. package/docs/usage/forms.md +184 -99
  112. package/docs/usage/index.md +18 -14
  113. package/docs/usage/solo.md +83 -0
  114. package/docs/usage/theming.md +260 -119
  115. package/docs/usage/tooltips.md +103 -42
  116. package/llms-full.txt +2465 -1272
  117. package/llms.txt +25 -17
  118. package/package.json +28 -35
  119. package/dist/tw/components/loader.css +0 -117
@@ -1,154 +1,295 @@
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 nothing else. 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">`.
34
+ - The nearest selector wins, at any depth. A `.light` card inside a `.dark` page is light.
35
+ - With no selector, the root has `color-scheme: light dark`, which follows the system.
36
+ - 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.
37
+ - Setting `color-scheme` yourself on an element works 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.
38
+
39
+ 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.
40
+
41
+ `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).
42
+
43
+ ### The `dark:` variant
44
+
45
+ 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:
72
46
 
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.
47
+ ```html
48
+ <html>
49
+ <body>
50
+ <p class="dark:text-white">Follows the system preference</p>
51
+ <div class="light">
52
+ <p class="dark:text-white">Stays light: this part of the page chose a theme</p>
53
+ </div>
54
+ </body>
55
+ </html>
56
+ ```
57
+
58
+ 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.
59
+
60
+ The compiled entrypoints do not define a `dark:` variant; they have no Tailwind build to define it in.
61
+
62
+ ## Change the brand color
63
+
64
+ 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:
65
+
66
+ ```css
67
+ :root {
68
+ --color-primary: light-dark(#0f766e, #2dd4bf);
69
+ --color-primary-contrast: light-dark(#ffffff, #042f2e);
70
+ --color-primary-hover: light-dark(#115e59, #5eead4);
71
+ --color-primary-subtle: light-dark(#ccfbf1, #134e4a);
72
+ --color-primary-strong: light-dark(#115e59, #5eead4);
73
+ }
74
+ ```
74
75
 
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. Resolves to `--color-border` when no intent is set. |
83
- | `--intent-fill-max` | How far a fill of this intent is allowed to go. `100%` for every intent but neutral. |
76
+ What each one is for:
84
77
 
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.
78
+ | Token | Role | Check |
79
+ | -------------------------- | ---------------------------------------------------------------------- | ---------------------------------------------- |
80
+ | `--color-primary` | The fill of a solid primary button, a checked toggle, a progress fill. | Text in `-contrast` on it reaches 4.5:1. |
81
+ | `--color-primary-contrast` | Text and icons on a full primary fill. | See above. |
82
+ | `--color-primary-hover` | The fill under the pointer. | Text in `-contrast` on it still reaches 4.5:1. |
83
+ | `--color-primary-subtle` | The light tint behind soft components. | Text in `-strong` on it reaches 4.5:1. |
84
+ | `--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`. |
86
85
 
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.
86
+ 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.
88
87
 
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.
88
+ 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.
90
89
 
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.
90
+ 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.
92
91
 
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.
92
+ 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.
94
93
 
95
- That means a capped neutral takes the strong tone whole rather than a fraction of the way toward the page. Tying it to the fill instead cost a neutral `.solid` button four points of contrast and a neutral tooltip nearly ten. 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.
94
+ ## Your own intent
96
95
 
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%.
96
+ 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:
98
97
 
99
98
  ```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);
99
+ .beta {
100
+ --intent-color: #7c3aed;
101
+ --intent-contrast: #ffffff;
102
+ --intent-hover: #6d28d9;
103
+ --intent-strong: light-dark(#5b21b6, #c4b5fd);
104
+ --intent-subtle: light-dark(#ede9fe, #4c1d95);
105
+ --intent-border: light-dark(#7c3aed, #a78bfa);
107
106
  --intent-fill-max: 100%;
108
107
  }
109
108
  ```
110
109
 
111
110
  ```html
112
- <button class="btn brand">Custom intent</button> <span class="badge brand soft">Works everywhere</span>
111
+ <span class="badge beta">Beta</span> <button class="btn beta" type="button">Try the new editor</button>
113
112
  ```
114
113
 
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.
114
+ 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.
115
+
116
+ **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).
117
+
118
+ 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:
119
+
120
+ - **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.
121
+ - **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.
122
+ - **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.
123
+
124
+ If you need those, there are two ways:
125
+
126
+ - **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.
127
+ - **Paint it yourself.** Write your own CSS for that one component, such as a `background-color` on `.panel.beta`.
128
+
129
+ > **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.
130
+
131
+ ## Color tokens
132
+
133
+ 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.
134
+
135
+ 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`.
136
+
137
+ | Token | Light, dark | Purpose |
138
+ | ------------------------------ | ---------------------------- | ---------------------------------------------------------------------------------------------- |
139
+ | `--color-primary` | `neutral-950`, `neutral-50` | Fill of `.primary`. |
140
+ | `--color-primary-contrast` | `neutral-50`, `neutral-950` | Text and icons on a full primary fill. |
141
+ | `--color-primary-hover` | `neutral-700`, `neutral-200` | Primary fill under the pointer. |
142
+ | `--color-primary-subtle` | `neutral-200`, `neutral-900` | Primary tint behind soft components. |
143
+ | `--color-primary-strong` | `neutral-900`, `neutral-100` | Primary text, icons, and borders where the fill is not full. |
144
+ | `--color-accent` | `neutral-600`, `neutral-400` | Fill of `.secondary`. |
145
+ | `--color-accent-contrast` | `neutral-50`, `neutral-950` | Text and icons on a full secondary fill. |
146
+ | `--color-accent-hover` | `neutral-700`, `neutral-300` | Secondary fill under the pointer. |
147
+ | `--color-accent-subtle` | `neutral-100`, `neutral-800` | Secondary tint behind soft components. |
148
+ | `--color-accent-strong` | `neutral-800`, `neutral-200` | Secondary text, icons, and borders where the fill is not full. |
149
+ | `--color-border` | `neutral-400`, `neutral-700` | Border of neutral surfaces, tables, dividers, and key caps. |
150
+ | `--color-border-hover` | `neutral-600`, `neutral-500` | Border under the pointer. |
151
+ | `--color-control-border` | `neutral-500` | Resting border of neutral text controls and toggles, drawn at a fraction on text controls. |
152
+ | `--color-background` | `neutral-50`, `neutral-950` | The page. |
153
+ | `--color-foreground` | `neutral-100`, `neutral-900` | The quiet background tone one step off the page: neutral soft cards, panels, and alerts. |
154
+ | `--color-surface` | `neutral-200`, `neutral-800` | A further step: nested and muted surfaces, and the neutral intent's tint. |
155
+ | `--color-text` | `neutral-950`, `neutral-50` | Body text, and the neutral intent's color. |
156
+ | `--color-text-secondary` | `neutral-700`, `neutral-400` | Secondary text, such as `.hint`. |
157
+ | `--color-text-contrast` | `neutral-50`, `neutral-950` | Text on a full fill of `--color-text`, such as the mark of a checked neutral toggle. |
158
+ | `--color-text-hover` | `neutral-700`, `neutral-200` | Neutral color under the pointer. |
159
+ | `--color-text-strong` | `neutral-900`, `neutral-100` | Neutral text, icons, and borders where the fill is not full. |
160
+ | `--color-tooltip` | `white`, `neutral-700` | Background of a tooltip bubble with no intent. Chosen per theme so it reads as lifted in both. |
161
+ | `--color-tooltip-contrast` | `neutral-900`, `neutral-100` | Text on `--color-tooltip`. |
162
+ | `--color-success` | `emerald-700` | Fill of `.success`. |
163
+ | `--color-success-contrast` | `neutral-50` | Text on a full success fill: 5.14:1. |
164
+ | `--color-success-hover` | `#007552`, `#008e66` | Success fill under the pointer. |
165
+ | `--color-success-subtle` | `emerald-100`, `emerald-900` | Success tint behind soft components. |
166
+ | `--color-success-strong` | `emerald-800`, `emerald-300` | Success text, icons, and borders where the fill is not full. |
167
+ | `--color-warning` | `amber-600` | Fill of `.warning`. |
168
+ | `--color-warning-contrast` | `neutral-950` | Text on a full warning fill: near-black, not the page color. 6.19:1. |
169
+ | `--color-warning-hover` | `#d46700`, `#e78300` | Warning fill under the pointer. |
170
+ | `--color-warning-subtle` | `amber-100`, `amber-900` | Warning tint behind soft components. |
171
+ | `--color-warning-strong` | `amber-800`, `amber-300` | Warning text, icons, and borders where the fill is not full. |
172
+ | `--color-destructive` | `rose-700` | Fill of `.destructive`. |
173
+ | `--color-destructive-contrast` | `neutral-50` | Text on a full destructive fill: 5.78:1. |
174
+ | `--color-destructive-hover` | `#c00036`, `#d1054b` | Destructive fill under the pointer. |
175
+ | `--color-destructive-subtle` | `rose-100`, `rose-900` | Destructive tint behind soft components. |
176
+ | `--color-destructive-strong` | `rose-800`, `rose-300` | Destructive text, icons, and borders where the fill is not full. |
177
+ | `--color-info` | `indigo-600` | Fill of `.info`. |
178
+ | `--color-info-contrast` | `neutral-50` | Text on a full info fill: 6.19:1. |
179
+ | `--color-info-hover` | `#4b36e9`, `#5e4ff9` | Info fill under the pointer. |
180
+ | `--color-info-subtle` | `indigo-100`, `indigo-900` | Info tint behind soft components. |
181
+ | `--color-info-strong` | `indigo-800`, `indigo-300` | Info text, icons, and borders where the fill is not full. |
182
+
183
+ Theme changes belong in these values, not in component rules that check the theme.
184
+
185
+ ## Intent tokens
186
+
187
+ 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.
188
+
189
+ | Token | Purpose |
190
+ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
191
+ | `--intent-color` | The intent's base color, what a fill is made of. |
192
+ | `--intent-contrast` | Text on a full fill of `--intent-color`. |
193
+ | `--intent-hover` | The base color under the pointer. |
194
+ | `--intent-strong` | The intent as text, icon, or border wherever the fill is not full. |
195
+ | `--intent-subtle` | The light tint behind soft components and progress tracks. Neutral uses `--color-surface`. |
196
+ | `--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. |
197
+ | `--intent-fill-max` | How far a fill of this intent may go. `100%` for every intent except neutral. |
198
+
199
+ `--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.
200
+
201
+ 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.
202
+
203
+ `--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.
204
+
205
+ `--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.
206
+
207
+ 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
208
 
117
209
  ## Foundation tokens
118
210
 
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. Negative, so the ring lands on the edge. |
145
- | `--focus-ring-width` | Focus-visible outline/ring width. |
146
- | `--motion-duration-fast` | Fast transition for buttons and input interactions (`120ms`). |
147
- | `--motion-duration-normal` | Normal transition for layout and modals (`200ms`). |
148
- | `--motion-duration-slow` | Slow animation for skeleton loaders and progress bars (`400ms`). |
149
- | `--motion-ease` | Default easing curve. |
150
- | `--z-popover` | Popover/tooltip z-index. |
151
- | `--breakpoint-xs` | Extra-small Tailwind responsive breakpoint. |
152
- | `--breakpoint-2xl` | Extended large Tailwind responsive breakpoint. |
153
-
154
- Next: [Customizing](./customizing.md) covers presentation and material tokens — how much of an intent shows, and what a component is made of.
211
+ Foundation tokens set layout, shape, motion, focus, depth, and stacking. Override them on `:root`, or on an element for what is inside it.
212
+
213
+ 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.
214
+
215
+ | Token | Default | Purpose |
216
+ | -------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
217
+ | `--font-default` | `"Segoe UI", system-ui, sans-serif` | Page font. See [Fonts](#fonts). |
218
+ | `--container-narrow` | `48rem` | Width of `.section-content.narrow`. |
219
+ | `--container-max` | `80rem` | Width of `.section-content`. |
220
+ | `--container-wide` | `90rem` | Width of `.section-content.wide`. |
221
+ | `--layout-gutter` | `clamp(1rem, 4vw, 2rem)` | Side padding of `.section`, growing with the window. |
222
+ | `--layout-section-block` | `clamp(4rem, 9vw, 7rem)` | Top and bottom padding of `.section`, growing with the window. |
223
+ | `--layout-gap` | `1rem` | Gap of `.view`, `.stack`, `.cluster`, and `.auto-grid`. |
224
+ | `--layout-grid-min` | `16rem` | Narrowest column of `.auto-grid`. |
225
+ | `--radius-small` | `0.25rem` | Corner of small chips: `.kbd` and `.code`. |
226
+ | `--radius-control` | `0.5rem` | Corner of buttons, fields, and tooltip bubbles. |
227
+ | `--radius-surface` | `0.875rem` | Corner of cards, panels, and alerts. |
228
+ | `--control-height` | `2.5rem` | Minimum height of a default button and a text control. |
229
+ | `--border-width` | `1px` | Border width. |
230
+ | `--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. |
231
+ | `--elevation-none` | `none` | No shadow. |
232
+ | `--elevation-sm` | `0 1px 2px`, `0 1px 3px` | A light shadow for your own surfaces, such as a card. |
233
+ | `--elevation-md` | `0 4px 6px -1px`, `0 2px 4px -2px` | A shadow for your own raised elements, such as a dropdown menu. |
234
+ | `--elevation-lg` | `0 20px 25px -5px`, `0 8px 10px -6px` | A shadow for your own overlays, such as a modal dialog. |
235
+ | `--focus-ring` | `--color-primary` at 64% opacity | Color of the keyboard focus ring. |
236
+ | `--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. |
237
+ | `--focus-ring-width` | `3px` | Width of the keyboard focus ring. |
238
+ | `--motion-duration-fast` | `120ms` | A table row's hover. |
239
+ | `--motion-duration-normal` | `200ms` | Color, border, shadow, and press transitions, toggle marks, and the tooltip bubble. |
240
+ | `--motion-duration-slow` | `400ms` | Progress value changes, and the base of the skeleton and progress shimmer cycle. |
241
+ | `--motion-ease` | `cubic-bezier(0.2, 0, 0, 1)` | Easing for every transition. |
242
+ | `--z-popover` | `40` | Stacking order of tooltip bubbles. Use it for your own popovers so they stack the same way. |
243
+ | `--breakpoint-xs` | `30rem` | The `xs:` responsive breakpoint. Only in a `/tw` build; the compiled entrypoints do not declare it. |
244
+
245
+ Each `--elevation-*` shadow is made of the listed parts, each in `--elevation-color`. The hover step that deepens a fill under the pointer is a presentation token, `--hover-step`; see [Customizing](./customizing.md#presentation-tokens).
246
+
247
+ ## Fonts
248
+
249
+ 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.
250
+
251
+ | Token | Read by | Without your value |
252
+ | ---------------- | ------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
253
+ | `--font-default` | The reset, on `html` and `body`. Buttons and form controls inherit it. | `"Segoe UI", system-ui, sans-serif` |
254
+ | `--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. |
255
+ | `--font-pixel` | [`.pixel`](./aesthetics.md#pixel) and `.pixel-solo` | Not declared. Falls back to a monospace stack. |
256
+ | `--font-rounded` | [`.chunky-tile`](./aesthetics.md#chunky-tile) and `.chunky-tile-solo` | Not declared. Falls back to `--font-default`, then a sans-serif stack. |
257
+ | `--font-cyber` | [`.cyber`](./aesthetics.md#cyber) and `.cyber-solo` | Not declared. Falls back to a monospace stack. |
258
+ | `--font-sketch` | [`.sketch`](./aesthetics.md#sketch) and `.sketch-solo` | Not declared. Falls back to the platform `cursive` family. |
259
+
260
+ `--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. The aesthetic tokens are read by the aesthetic's own class, so they work with any entrypoint.
261
+
262
+ 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:
263
+
264
+ ```html
265
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
266
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
267
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400..700&display=swap" />
268
+ ```
269
+
270
+ ```css
271
+ :root {
272
+ --font-default: "Inter", system-ui, sans-serif;
273
+ }
274
+ ```
275
+
276
+ A self-hosted font is declared with `@font-face` and named the same way:
277
+
278
+ ```css
279
+ @font-face {
280
+ font-family: "Pixelify Sans";
281
+ src: url("/fonts/pixelify-sans.woff2") format("woff2");
282
+ font-weight: 400 700;
283
+ font-display: swap;
284
+ }
285
+
286
+ :root {
287
+ --font-pixel: "Pixelify Sans", monospace;
288
+ }
289
+ ```
290
+
291
+ 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.
292
+
293
+ ## Next
294
+
295
+ 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.