@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
@@ -1,161 +1,255 @@
1
1
  ---
2
2
  title: Forms
3
- description: Field, text control, and toggle class reference.
4
- order: 6
3
+ description: Labeled fields, hints and errors, icons and fixed prefixes, selects, and checkbox, radio, and switch toggles.
4
+ order: 2
5
5
  ---
6
6
 
7
7
  # Forms
8
8
 
9
- | Class or Selector | Purpose |
10
- | --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
11
- | `.field` | Vertical field wrapper. |
12
- | `.label` | Form label text. |
13
- | `.hint` | Secondary helper text. |
14
- | `.hint.destructive` | Helper text with destructive intent. |
15
- | `.surface` | Shared public container composition utility. |
16
- | `.text-control` | Shared public text-control composition utility. |
17
- | `.ipt` | Input control styling. |
18
- | `.input-group` | Wrapper that owns the field box so a control can carry an icon or affix. |
19
- | `.unadorned` | Removes the browser's own field decorations, on the control or on its `.input-group`. |
20
- | `.textarea` | Textarea control styling. |
21
- | `.select` | Select control styling. With `multiple` or a `size` above 1 it is a list box: no chevron, as tall as its rows. |
22
- | `input[type="checkbox"].checkbox` | Custom checkbox control styling. |
23
- | `input[type="radio"].radio` | Custom radio control styling. |
24
- | `input[type="checkbox"].switch` | Custom switch control styling. |
25
- | `[aria-invalid="true"]` on controls | Destructive border and focus color. |
26
- | `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` on controls | Disabled styling. |
27
-
28
- ## Icons
29
-
30
- The package ships no icon for text inputs. Icons for `.input-group` are the consumer's to choose: artwork painted as a `data:` URI could not read `currentColor` or a custom property, so it could not follow the theme or fit every aesthetic. The toggle marks below are the exception: small, fixed, built-in glyphs. `.alert` likewise takes a consumer-supplied icon; see [Feedback](./feedback.md#example).
31
-
32
- `.input-group` is the wrapper for a control that carries one. It owns the field box — border, radius, fill, focus ring, invalid and disabled state — and the control inside it goes flush, so the boundary is drawn once. Any icon element works, placed before the control for a leading icon or after it for a trailing one: an inline `<svg>`, an `<img>`, or a class from an icon set such as `@codenhub/icons`.
9
+ The profile card has a Save button but nothing to save. This page fills it in: a display name with a hint, an email address that shows a validation error, a profile address with a fixed prefix, a language select, and the toggles for notifications, billing, and profile visibility. The package styles each control; your HTML provides the labels, the validation, and the error messages, and this page shows how to connect them.
10
+
11
+ ## A labeled field
12
+
13
+ A field is a label, a control, and an optional hint, stacked:
33
14
 
34
15
  ```html
35
16
  <div class="field">
36
- <label class="label" for="contact-email">Email</label>
37
- <div class="input-group">
38
- <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" aria-hidden="true">
39
- <rect width="20" height="16" x="2" y="4" rx="2" />
40
- <path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" />
41
- </svg>
42
- <input class="ipt" type="email" id="contact-email" />
17
+ <label class="label" for="display-name">Display name</label>
18
+ <input class="ipt" id="display-name" name="display-name" aria-describedby="display-name-hint" />
19
+ <span class="hint" id="display-name-hint">Shown on invoices and shared projects.</span>
20
+ </div>
21
+ ```
22
+
23
+ - `.field` stacks its children with a small gap (0.35rem) and takes the full width.
24
+ - `.label` sets label text: 1rem, medium weight.
25
+ - `.hint` is smaller, secondary-colored helper text. Keep it outside the `<label>` and point `aria-describedby` at it, so a screen reader announces it as a description instead of reading it as part of the field's name.
26
+ - `.ipt` styles an `<input>`. Use `.textarea` for a `<textarea>` and `.select` for a `<select>`.
27
+
28
+ A text control is at least 2.5rem tall (`--control-height`), the same height as a default button, so a field and a button line up in a row. Its default look is no fill and a border. A `.textarea` is at least 7rem tall and can be resized vertically.
29
+
30
+ ### Sizes
31
+
32
+ `.ipt`, `.select`, `.textarea`, and `.input-group` take the button's size classes, so a small field and a small button line up in a row:
33
+
34
+ | Class | Height | Side padding | Text |
35
+ | ------- | ------ | ------------ | --------- |
36
+ | `.sm` | 2rem | 0.625rem | 0.875rem |
37
+ | default | 2.5rem | 0.75rem | inherited |
38
+ | `.lg` | 3rem | 1rem | 1.0625rem |
39
+
40
+ The corners scale with the size, as a button's do. A `.textarea` keeps its 7rem minimum height at every size; the class changes its text, padding, and corners. Size an `.input-group` on the group, not on the control inside it: the control and its adornments take the group's text size.
41
+
42
+ ```html
43
+ <div class="cluster tight">
44
+ <input class="ipt sm" aria-label="Coupon code" />
45
+ <button class="btn sm" type="button">Apply</button>
46
+ </div>
47
+ ```
48
+
49
+ ## Show a validation error
50
+
51
+ Mark the control invalid with `aria-invalid="true"` and give the error message `.hint.destructive`:
52
+
53
+ ```html
54
+ <div class="field">
55
+ <label class="label" for="email">Email</label>
56
+ <input class="ipt" type="email" id="email" name="email" value="ada@" aria-invalid="true" aria-describedby="email-error" />
57
+ <span class="hint destructive" id="email-error">Enter a full email address, such as ada@example.com.</span>
58
+ </div>
59
+ ```
60
+
61
+ An invalid control draws its border, focus ring, and typed text in the destructive color, whatever intent class it has. The text uses the readable tone of that color, `--intent-strong`, so it stays legible on the page. The package only paints the state: deciding that the value is invalid, setting and clearing `aria-invalid`, and writing the message are your code. Remove `aria-invalid` once the value is valid again; leaving it set keeps the field red. On a checkbox, radio, or switch, `aria-invalid="true"` draws the toggle exactly as `.destructive` does, checked mark included.
62
+
63
+ A hint takes any of the six built-in intents, not only `.destructive`: `.hint.success` confirms a value was accepted, and `.hint.warning` flags one that is allowed but unusual. Each prints in its intent's readable tone, `--intent-strong`, which holds 4.5:1 against the page in both themes. A hint with no intent, with `.neutral`, or with an [intent you defined yourself](./theming.md#your-own-intent), stays secondary-colored. Only a class on the hint itself colors it; an intent on the field or a container around it does not.
64
+
65
+ ```html
66
+ <input class="ipt success" id="username" value="ada" aria-describedby="username-hint" /> <span class="hint success" id="username-hint">That username is available.</span>
67
+ ```
68
+
69
+ ## Put the profile form together
70
+
71
+ ```html
72
+ <form class="card stack" aria-labelledby="profile-title">
73
+ <h2 class="text-title-sm" id="profile-title">Profile</h2>
74
+
75
+ <div class="field">
76
+ <label class="label" for="display-name">Display name</label>
77
+ <input class="ipt" id="display-name" name="display-name" value="Ada Lovelace" aria-describedby="display-name-hint" />
78
+ <span class="hint" id="display-name-hint">Shown on invoices and shared projects.</span>
79
+ </div>
80
+
81
+ <div class="field">
82
+ <label class="label" for="email">Email</label>
83
+ <input class="ipt" type="email" id="email" name="email" value="ada@example.com" />
84
+ </div>
85
+
86
+ <div class="field">
87
+ <label class="label" for="bio">Bio</label>
88
+ <textarea class="textarea" id="bio" name="bio" rows="3"></textarea>
89
+ </div>
90
+
91
+ <div class="field">
92
+ <label class="label" for="language">Language</label>
93
+ <select class="select" id="language" name="language">
94
+ <option>English</option>
95
+ <option>Português</option>
96
+ <option>Español</option>
97
+ </select>
98
+ </div>
99
+
100
+ <div class="cluster">
101
+ <button class="btn primary" type="submit">Save changes</button>
102
+ <button class="btn ghost" type="reset">Cancel</button>
103
+ </div>
104
+ </form>
105
+ ```
106
+
107
+ A `.select` draws its own arrow. With `multiple` or a `size` above 1 it becomes a list box instead: no arrow, as tall as the rows it shows. The open list of options is drawn by the browser and the operating system, so it does not take the package's look.
108
+
109
+ ## Add an icon or a fixed prefix
110
+
111
+ `.input-group` puts an icon or a piece of text inside a field's border. The group draws the border, background, focus ring, and invalid and disabled states; the control inside it gives up its own, so the border is drawn once. Place the adornment before the control to put it at the start, after it to put it at the end. A group is the height of a lone field, so it lines up with the fields and buttons beside it.
112
+
113
+ The profile address always starts with the same host, so it is a fixed prefix:
114
+
115
+ ```html
116
+ <div class="field">
117
+ <label class="label" for="profile-url">Profile address</label>
118
+ <div class="input-group" style="--input-group-gap: 0">
119
+ <span>app.example.com/u/</span>
120
+ <input class="ipt" id="profile-url" name="profile-url" value="ada" />
43
121
  </div>
44
122
  </div>
123
+ ```
124
+
125
+ The gap between the control and its adornment is `--input-group-gap`, 0.5rem by default. That suits an icon, but text meant to read as part of the value looks broken with a space after it, so the example sets it to `0`. Set it on the `.input-group` itself: the group declares its own default, so a value set on a parent element has no effect. It is one of six [public component variables](./customizing.md#component-variables).
126
+
127
+ A members search, with an icon at the start (the `ic-lucide-*` classes need `@codenhub/icons` loaded; see [Buttons → Loading icons](./buttons.md#loading-icons)):
128
+
129
+ ```html
45
130
  <div class="field">
46
- <label class="label" for="site-search">Search</label>
131
+ <label class="label" for="member-search">Find a member</label>
47
132
  <div class="input-group">
48
- <input class="ipt" type="search" id="site-search" />
49
- <i class="ic-search" aria-hidden="true"></i>
133
+ <i class="ic-lucide-search" aria-hidden="true"></i>
134
+ <input class="ipt" type="search" id="member-search" name="q" />
50
135
  </div>
51
136
  </div>
52
137
  ```
53
138
 
54
- The group reads intent and both [presentation](./composing.md#presentation) axes the same way a lone `.ipt` does — `.input-group.soft.edgeless` is the sunk variant, `.input-group.primary` colors the boundary — and follows whichever [aesthetic](./aesthetics.md) is in scope. `aria-invalid` or `disabled` on the control inside propagates to the group; so does either on the group itself. Focus is shown with `:focus-within`, so unlike a lone field the ring also appears on a mouse click.
139
+ A `.select` works in a group too. The group removes the control's side padding, except at the end of a single-row select, which keeps 1.75rem so the chosen option's text does not run under the arrow.
55
140
 
56
- Text controls keep the browser's own decorations: the date and time picker button, a number field's spin buttons, and a search field's clear button and magnifier. Each one is also behaviour -- the picker button is the only pointer route to the picker, and the clear button empties the field.
141
+ Any element works as the adornment: an icon class, an inline `<svg>`, an `<img>`, or text. The package ships no icons for fields, because a built-in image could not follow your text color, theme, or aesthetic.
57
142
 
58
- `.unadorned` removes all of them, for a field whose adornments you draw yourself. Put it on the control, or on its `.input-group` to reach the control inside. Removing a decoration removes what it did, so wire the behaviour back to your own adornment: `input.showPicker()` for a picker, `stepUp()`/`stepDown()` for spin buttons, and setting `value` to `""` and dispatching an `input` event for a clear button. Keyboard entry keeps working either way. Firefox exposes no way to remove its date picker button, so it stays there.
143
+ The group reads intent and presentation the way a lone `.ipt` does: `.input-group.primary` colors the border and the typed text, and `.input-group.soft.edgeless` is a field sunk into the page. Put the intent on the group, not on the control inside: the group draws the field, so the control's text takes the group's intent and an intent class on the control has no effect. `aria-invalid="true"` or `disabled` on the control inside applies to the whole group, and so does either on the group itself. The group's focus ring appears when its field has focus, so unlike a lone field it also shows after a mouse click. A button inside the group, such as a clear button, draws its own ring when it has focus, and the group draws none: one ring always marks the one control that has focus.
144
+
145
+ ## Browser decorations
146
+
147
+ Some input types come with built-in buttons: a date or time field's picker button, a number field's up and down arrows, and a search field's clear button. The package keeps them, because each one also does something: the picker button is the only way to open the picker with a mouse, and the clear button empties the field.
148
+
149
+ `.unadorned` removes them, for a field where you draw your own. Put it on the control, or on its `.input-group`. Removing a decoration removes what it did, so connect your replacement yourself: `input.showPicker()` opens a picker, `stepUp()` and `stepDown()` replace the arrows, and setting `value` to `""` and dispatching an `input` event replaces a clear button. Typing keeps working either way. Firefox gives no way to remove its date picker button, so it stays there.
59
150
 
60
151
  ```html
61
152
  <div class="field">
62
- <label class="label" for="query">Search</label>
153
+ <label class="label" for="member-filter">Find a member</label>
63
154
  <div class="input-group unadorned">
64
- <i class="ic-search" aria-hidden="true"></i>
65
- <input class="ipt" type="search" id="query" />
66
- <button type="button" class="btn icon ghost p-xs" aria-label="Clear search">
67
- <i class="ic-x" aria-hidden="true"></i>
155
+ <i class="ic-lucide-search" aria-hidden="true"></i>
156
+ <input class="ipt" type="search" id="member-filter" name="q" />
157
+ <button class="btn icon ghost p-xs" type="button" aria-label="Clear search">
158
+ <i class="ic-lucide-x" aria-hidden="true"></i>
68
159
  </button>
69
160
  </div>
70
161
  </div>
71
162
  ```
72
163
 
73
- The slot works the same way for a text affix — a URL host, a currency symbol, a unit — not just an icon:
164
+ The clear button above is a real `<button>` with an accessible name. A button inside a group keeps its own classes: a `.btn.ghost` has no fill and, like every button by default, no border, so it sits inside the field's border without drawing one of its own.
165
+
166
+ ## Toggles
167
+
168
+ The settings screen has three kinds of toggle: a checkbox to opt into invoice emails, radio buttons to choose a billing period, and a switch to make the profile public. Each class goes on a native `<input>` of the matching type:
74
169
 
75
170
  ```html
76
- <div class="input-group">
77
- <span>example.com/</span>
78
- <input class="ipt" type="text" />
79
- </div>
171
+ <fieldset class="stack tight">
172
+ <legend class="label">Notifications</legend>
173
+ <label class="cluster tight">
174
+ <input class="checkbox" type="checkbox" name="invoice-email" checked />
175
+ Email me when an invoice is ready
176
+ </label>
177
+ </fieldset>
178
+
179
+ <fieldset class="stack tight">
180
+ <legend class="label">Billing period</legend>
181
+ <label class="cluster tight">
182
+ <input class="radio" type="radio" name="period" value="monthly" />
183
+ Monthly
184
+ </label>
185
+ <label class="cluster tight">
186
+ <input class="radio" type="radio" name="period" value="yearly" checked />
187
+ Yearly, two months free
188
+ </label>
189
+ </fieldset>
190
+
191
+ <label class="cluster tight">
192
+ <input class="switch primary" type="checkbox" role="switch" name="public-profile" />
193
+ Public profile
194
+ </label>
80
195
  ```
81
196
 
82
- The gap between the control and its adornment is `--input-group-gap`, `0.5rem` by default: the right amount of breathing room for a distinct icon glyph, and too much for text meant to read as glued to the value — `example.com/` followed by a gap reads as a stray space breaking the path apart. Set `--input-group-gap: 0` on the group for a flush affix; icon compositions keep the default unchanged.
197
+ - `.checkbox` goes on `type="checkbox"`, `.radio` on `type="radio"`, and `.switch` on `type="checkbox"`. Give a switch `role="switch"` yourself, so it is announced as on or off rather than checked.
198
+ - `.cluster.tight` on the `<label>` lines the control up with its text and leaves a 0.5rem gap.
199
+ - A checkbox whose `indeterminate` property is set, for a group where only some items are checked, shows a dash, and keeps the dash while also `checked`, as the browser's own checkbox does. Set it from script (`input.indeterminate = true`); there is no attribute for it.
83
200
 
84
- ## Toggles
201
+ ### Checked and unchecked colors
85
202
 
86
- `.checkbox`, `.radio`, and `.switch` accept the same intent classes as buttons to set the checked color:
203
+ An intent class sets the checked color. With no intent, a checked toggle is filled with the text color, and the mark is drawn in the page color: a black box with a white check on a light page. Unlike a neutral button, a neutral toggle fills all the way when checked.
87
204
 
88
- | Class | Meaning |
89
- | ---------------------- | ----------------------- |
90
- | `.neutral` _(default)_ | Text color, capped. |
91
- | `.primary` | Primary color. |
92
- | `.secondary` | Secondary/accent color. |
93
- | `.success` | Success color. |
94
- | `.warning` | Warning color. |
95
- | `.destructive` | Destructive color. |
96
- | `.info` | Info color. |
205
+ Presentation changes only the unchecked look. Every toggle rests at `.soft`; once checked, each has one look whatever its presentation:
97
206
 
98
- All three toggles rest at `.soft`. Presentation decides the _unchecked_ plate; checked is pinned to one look regardless of which class is on the element, so a checkbox, a radio, and a switch each have a single "on" identity rather than one per presentation:
207
+ | Toggle | Unchecked fill | Checked |
208
+ | ---------------- | -------------- | ---------------------------------------------- |
209
+ | `.ghost` | 0% | 100% fill, with the mark in the contrast color |
210
+ | `.soft`, default | 12% | 100% fill, with the mark in the contrast color |
211
+ | `.solid` | 40% | 100% fill, with the mark in the contrast color |
99
212
 
100
- | Toggle | unchecked | checked |
101
- | ---------------- | --------- | ---------------------------------------- |
102
- | `.ghost` | 0% | 100%, mark in the contrast tone (pinned) |
103
- | `.soft`, default | 12% | 100%, mark in the contrast tone (pinned) |
104
- | `.solid` | 40% | 100%, mark in the contrast tone (pinned) |
213
+ A checked checkbox and a checked switch cut their mark out of the filled plate. A checked radio thickens its ring to twice the resting line width, up to 3px, takes the full intent color on it, and shows a dot. The 3px limit keeps the ring from swallowing the dot under an aesthetic with a thick line.
105
214
 
106
- A checked checkbox and a checked switch cut their mark out of that pinned plate. A checked radio thickens its ring to twice the resting line and takes the intent whole on it, and the dot follows the same pinned plate, so `.radio.soft` and `.radio.solid` render the same filled circle once checked — presentation only changes what the radio looks like before it's picked.
215
+ `.checkbox` and `.radio` always draw their border, even with `.edgeless`, because the border is the only thing that shows an unchecked box. `.switch.ghost` keeps its border too, since with no fill and no border nothing would mark the track. These are listed in [Composing](./composing.md#component-axis-reference).
107
216
 
108
- A checkbox whose `indeterminate` property is set -- a group some of whose items are checked -- takes the same pinned plate with a dash for its mark, and keeps the dash while it is also `checked`, as the browser's own checkbox does. The property is set from script (`input.indeterminate = true`); there is no attribute for it.
217
+ An aesthetic that draws its border as an inner shadow, such as `.pixel`, cannot make that shadow wider than the toggle's border width. A 16px checkbox or a 20px switch has no room for a thicker inner ring without covering the mark. This only matters if you set `--ui-shadow-spread` yourself; see [Customizing → Material tokens](./customizing.md#material-tokens).
109
218
 
110
- ## Text controls
219
+ ## Fill and border on text controls
111
220
 
112
- Text controls also take intent, which colors the resting border and the focus-visible border, and both [presentation](./composing.md#presentation) axes. `.ghost` fills nothing, `.soft` takes `12%`, and `.solid` takes `20%` — quiet enough that typed text still reads on it, and ordered so the louder name draws the stronger tint. None of them touches the line.
221
+ Text controls take intent, which colors the border, the focus ring, and the typed text, the text in the intent's readable tone (`--intent-strong`). They also take both presentation axes. Their fill is capped low, so typed text stays readable:
113
222
 
114
- The boundary is the edge axis's, and it splits the same way. A container's `.edgeless` is floored, so a toolbar cannot leave a field with no mark of where typing goes. `.edgeless` on the control itself is honoured, which is how the borderless field is spelled — it does not meet WCAG 1.4.11 at rest, so it is opt-in and never a default, and the focus ring still shows.
223
+ | On a text control | Fill |
224
+ | ----------------------------------------------- | ---- |
225
+ | `.ghost`, default | 0% |
226
+ | Any fill from a container (`.soft` or `.solid`) | 6% |
227
+ | `.soft` on the control | 12% |
228
+ | `.solid` on the control | 20% |
229
+
230
+ A container's fill is meant for the surfaces in it, so a field nobody classed takes only a faint 6% of it. Put the class on the field itself for more.
231
+
232
+ The border works the same way. An `.edgeless` inherited from a container is ignored, so a toolbar cannot leave a field with no visible edge. `.edgeless` written on the control itself is honored: that is how you ask for a borderless field. A borderless field does not meet the WCAG 1.4.11 contrast requirement for a control's boundary, so it is never a default; its focus ring still shows.
115
233
 
116
234
  ```html
117
- <input class="ipt success" placeholder="Valid" />
118
- <input class="ipt ghost edged" placeholder="Outlined" />
119
- <input class="ipt soft edged" placeholder="Tinted with a border" />
235
+ <input class="ipt success" placeholder="Matches your current password" />
236
+ <input class="ipt soft edged" placeholder="Tinted, with a border" />
120
237
  <input class="ipt soft edgeless" placeholder="Sunk into the page" />
121
238
  <div class="soft">
122
- <input class="ipt" placeholder="Tinted from the container, capped at 6%" />
123
- </div>
124
- <div class="edgeless">
125
- <input class="ipt" placeholder="Keeps its line; the floor holds" />
239
+ <input class="ipt" placeholder="A container's .soft gives a 6% tint" />
126
240
  </div>
127
241
  ```
128
242
 
129
- `.surface` is a public low-level utility. It is the box every container in the package composes: ghost, edged, surface radius, and the two slots only a surface resolves — the backdrop filter and the complete surface shadow. Use it to paint a container the package does not ship, under whichever aesthetic is in scope. Prefer `.card` or `.panel` when they fit.
243
+ At rest, a field's border is deliberately lighter than 3:1 against the page, so hover and focus have a visible change to make. [Accessibility → Control boundaries](../accessibility.md#control-boundaries) explains the trade-off and how to get a stronger border.
130
244
 
131
- ```html
132
- <div class="surface glass">Blurred, translucent, glass corners</div>
133
- <div class="surface pixel">Cut corners and an inset ring</div>
134
- <div class="surface primary solid">Any intent, any presentation</div>
135
- ```
245
+ ## Disabled controls
136
246
 
137
- `.text-control` is a public low-level utility from the form entrypoint. It provides the shared control dimensions, border, placeholder, focus-visible, invalid, and disabled styles composed by `ipt`, `textarea`, and `select`. Use it for custom text-like controls; prefer those higher-level utilities when they fit.
247
+ `disabled`, `aria-disabled="true"`, `data-disabled`, or `.disabled` on a control draws it at reduced opacity with a not-allowed cursor. As with buttons, only the `disabled` attribute stops input; the others change the look only.
138
248
 
139
- ## Example
249
+ ## Building your own control
140
250
 
141
- ```html
142
- <div class="field">
143
- <label class="label" for="email">Email</label>
144
- <input class="ipt" type="email" id="email" aria-invalid="true" aria-describedby="email-error" />
145
- <span class="hint destructive" id="email-error">Enter a valid email.</span>
146
- </div>
147
- <label style="display: flex; gap: 0.5rem; align-items: center">
148
- <input type="checkbox" class="checkbox success" />
149
- <span>Accept terms</span>
150
- </label>
151
- <label style="display: flex; gap: 0.5rem; align-items: center">
152
- <input type="radio" class="radio secondary" name="plan" />
153
- <span>Standard plan</span>
154
- </label>
155
- <label style="display: flex; gap: 0.5rem; align-items: center">
156
- <input type="checkbox" class="switch destructive" role="switch" />
157
- <span>Enable</span>
158
- </label>
159
- ```
251
+ For a text-like control the package does not ship, `.text-control` gives an element the same border, placeholder, hover, focus, invalid, and disabled styles as `.ipt`, without its height or padding. [Customizing → Composition helpers](./customizing.md#composition-helpers) explains it with the other building blocks.
252
+
253
+ ## Next
160
254
 
161
- Keep a hint or error message outside the `<label>` and point `aria-describedby` at it, so it is announced as a description rather than read as part of the field's name. Give a `.switch` `role="switch"`. Use labels, `type`, validation logic, `aria-describedby`, and error message relationships; see [Accessibility](../accessibility.md).
255
+ The form can be filled in and submitted. [Feedback](./feedback.md) shows what the screen does next: a Save button that says it is saving, a success message, a plan badge, and a storage usage bar.
@@ -1,24 +1,32 @@
1
1
  ---
2
2
  title: Overview
3
- description: Guides for composing axes, theming, customizing, aesthetics, and each component family.
3
+ description: The guides that finish the settings screen, then the lookup pages for every class, token, and default.
4
4
  group: Usage
5
- order: 3
5
+ order: 4
6
6
  ---
7
7
 
8
8
  # Usage
9
9
 
10
- These guides assume the [Concepts](../concepts.md) mental model: intent, presentation, and aesthetic as three independent axes, resolved through tokens a component reads.
10
+ These guides finish the settings screen started in [Building a screen](../concepts.md). Read the first seven in order: each one adds a part of the screen and assumes the ones before it. The next three are lookup pages; you will be sent to them when a guide needs the full table, and you can come back whenever you need a default or a rule. The last page, Presets, is for when the screen works and you are tired of typing the same classes.
11
11
 
12
- - [Composing](./composing.md): Combining the three axes on a component, the axis-support matrix, and elevation modifiers.
13
- - [Theming](./theming.md): The color, intent, and foundation token reference.
14
- - [Customizing](./customizing.md): The presentation and material token reference, aesthetic knobs, and defining a custom intent.
15
- - [Aesthetics](./aesthetics.md): Each shipped aesthetic in depth, including its documented exceptions.
16
- - [Buttons](./buttons.md)
17
- - [Forms](./forms.md)
18
- - [Feedback](./feedback.md)
19
- - [Tooltips](./tooltips.md)
20
- - [Content and layout](./content-and-layout.md): Layout helpers, tables, quotes, code, surfaces, and typography utilities.
12
+ Guides, in order:
21
13
 
22
- ## Supported surface
14
+ 1. [Buttons](./buttons.md): the danger zone's destructive action, icon buttons, sizes, link buttons, and disabled states, plus how to load icons.
15
+ 2. [Forms](./forms.md): the profile form, with labels, hints, validation errors, an input with an icon or a fixed prefix, a select, and checkbox, radio, and switch toggles.
16
+ 3. [Feedback](./feedback.md): what the screen shows while saving and after: a loading button, a success alert, a plan badge, a loading placeholder, and a storage usage bar.
17
+ 4. [Tooltips](./tooltips.md): help text beside a form field, how to wire it for keyboard and screen reader users, and where a tooltip can be cut off.
18
+ 5. [Theming](./theming.md): light and dark mode, replacing the palette with brand colors, and defining an intent of your own, with the full color and foundation token tables.
19
+ 6. [Aesthetics](./aesthetics.md): the six optional looks, what each one changes, and its limits.
20
+ 7. [Solo classes](./solo.md): painting an aesthetic onto a component from another library.
23
21
 
24
- A class, component, and axis combination is supported when this documentation describes it and the package playground demonstrates it. Undocumented and undemonstrated combinations may still produce CSS, but are not maintained behavior. Playground variant matrices are the exhaustive demonstrations; small examples elsewhere illustrate usage without widening that contract.
22
+ Lookup pages:
23
+
24
+ - [Composing](./composing.md): the complete rules for combining intent, presentation, and elevation, every component's defaults, and which axes each component reads.
25
+ - [Customizing](./customizing.md): presentation and material tokens, aesthetic knobs, the six public component variables, the generated palette, and the composition helpers for building your own components.
26
+ - [Content and layout](./content-and-layout.md): every layout helper, the type scale, cards and panels, tables, quotes, code, and keyboard keys.
27
+
28
+ Once the screen works:
29
+
30
+ - [Presets](./presets.md): set a look once, with axis classes on a container, theme and material tokens, or your own component classes built with `@apply`.
31
+
32
+ When you finish, [Accessibility](../accessibility.md) collects what the CSS does for you and what your application still owes its users.
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: Presets
3
+ description: Three ways to set a look once instead of on every element, and when to reach for each.
4
+ order: 11
5
+ ---
6
+
7
+ # Presets
8
+
9
+ By now the settings screen says `btn primary soft sm` in a dozen places, and changing your mind means changing a dozen places. This page covers the three ways to set a look once: axis classes on a container, theme and material tokens, and your own component classes built with `@apply`. Each fits a different kind of "once".
10
+
11
+ ## Which one to use
12
+
13
+ | You want | Reach for |
14
+ | --------------------------------------------------------------------- | ----------------------------------------------------------- |
15
+ | Every component in one region to share a fill, an edge, or a material | [Axis classes on a container](#axis-classes-on-a-container) |
16
+ | Different colors, corners, borders, or depth for a page or a region | [Theme and material tokens](#theme-and-material-tokens) |
17
+ | One name for a recipe you repeat, such as "the save button" | [Your own component classes](#your-own-component-classes) |
18
+
19
+ They combine. A region can set a material token, sit under an aesthetic, and hold your own button class, and each element can still override what it inherits.
20
+
21
+ ## Axis classes on a container
22
+
23
+ Some classes work on a container as well as on a component, and everything inside takes them:
24
+
25
+ - The presentation classes: `.solid`, `.soft`, `.ghost`, `.edged`, and `.edgeless`.
26
+ - The corner patterns: `.cut-diagonal` and `.cut-diagonal-reverse`.
27
+ - The [aesthetics](./aesthetics.md), such as `.glass` or `.pixel`.
28
+
29
+ ```html
30
+ <div class="cluster soft edged">
31
+ <button class="btn primary" type="button">Bold</button>
32
+ <button class="btn" type="button">Italic</button>
33
+ <button class="btn destructive solid" type="button">Clear formatting</button>
34
+ </div>
35
+ ```
36
+
37
+ The toolbar's buttons are soft and edged, and the last one asks for its own fill. A class on the element always beats one from a container.
38
+
39
+ The other axes do not pass down. Intent, elevation (`.flat`, `.raised`, `.floating`), and size (`.sm`, `.lg`) change only the element they are on, so a `.sm` toolbar does not shrink its buttons; [Composing → Combining classes](./composing.md#combining-classes) explains why. An aesthetic class also sets its own corner pattern on its region, so a `.cut-diagonal` above a `.pixel` region does not reach inside it.
40
+
41
+ Use a container class when the look belongs to the region, such as a quiet sidebar, rather than to the components in it.
42
+
43
+ ## Theme and material tokens
44
+
45
+ Tokens are CSS custom properties. Set one on `:root` and the whole page follows; set it on an element and only what is inside follows. They reach further than classes, because components read them while they draw:
46
+
47
+ ```css
48
+ .admin {
49
+ --radius-control: 0.25rem;
50
+ --radius-surface: 0.5rem;
51
+ --border-width: 2px;
52
+ }
53
+ ```
54
+
55
+ Every button, field, tooltip bubble, and surface inside `.admin` takes the tighter corners and the heavier border, with no class on any of them. Checkboxes, `.kbd`, and `.code` keep their own smaller corner, `--radius-small`.
56
+
57
+ The tables live where the tokens are explained, so this page does not repeat them:
58
+
59
+ - [Theming → Color tokens](./theming.md#color-tokens): brand and status colors, in both themes.
60
+ - [Theming → Foundation tokens](./theming.md#foundation-tokens): corners, border width, control height, focus ring, depth, and motion.
61
+ - [Customizing → Presentation tokens](./customizing.md#presentation-tokens): how much a fill, an edge, or a hover gives.
62
+ - [Customizing → Material tokens](./customizing.md#material-tokens): what an aesthetic sets, which you can set yourself, such as `--ui-radius`.
63
+ - [Customizing → Aesthetic tokens](./customizing.md#aesthetic-tokens): the knobs of each aesthetic, such as `--neo-offset`.
64
+
65
+ Use tokens when the change is a value, such as a color or a corner, rather than a choice between classes.
66
+
67
+ ## Your own component classes
68
+
69
+ With the Tailwind source entrypoints, every component, helper, and axis class is a Tailwind utility, so one class of yours can carry the whole recipe:
70
+
71
+ ```css
72
+ @import "@codenhub/styles/tw";
73
+
74
+ @utility btn-save {
75
+ @apply btn primary soft sm;
76
+ }
77
+ ```
78
+
79
+ ```html
80
+ <button class="btn-save" type="submit">Save changes</button>
81
+ ```
82
+
83
+ `btn-save` behaves like `btn primary soft sm` written out, aesthetics and hover included, and changing the recipe changes every save button. Modifiers that belong to one component, such as `.tight` on `.stack` or `.icon` on `.btn`, are utilities too, so `@apply btn icon` gives you an icon button of your own. [Tailwind CSS v4 → Build your own components with `@apply`](../integrating/tailwind.md#build-your-own-components-with-apply) has the rules, including why `@utility` beats a plain rule.
84
+
85
+ With the compiled CSS there is no `@apply`. Keep the recipe in one place on the other side instead: a template partial, a component in your framework, or a constant such as `const saveButton = "btn primary soft sm"`.
86
+
87
+ Use your own class when the same combination names one thing in your design, such as the save button or the plan card.
88
+
89
+ ## Next
90
+
91
+ [Accessibility](../accessibility.md) lists what the CSS does for your users and what your application still owes them.