@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,145 +1,230 @@
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
- | `.textarea` | Textarea control styling. |
20
- | `.select` | Select control styling. |
21
- | `input[type="checkbox"].checkbox` | Custom checkbox control styling. |
22
- | `input[type="radio"].radio` | Custom radio control styling. |
23
- | `input[type="checkbox"].switch` | Custom switch control styling. |
24
- | `[aria-invalid="true"]` on controls | Destructive border and focus color. |
25
- | `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` on controls | Disabled styling. |
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.
26
10
 
27
- ## Icons
11
+ ## A labeled field
28
12
 
29
- No icon is shipped for text inputs — that's what this section covers. It used to paint one per input type as a `background-image` — a `data:` URI, which cannot read `currentColor` or a custom property, so every glyph shipped a light and a dark copy and a theme selector chose between them. That put icon artwork, and the package's only theme-by-selector branch, inside a CSS-only design system whose glyphs did not fit every aesthetic. Icons for `.input-group` are the consumer's to choose now.
13
+ A field is a label, a control, and an optional hint, stacked:
30
14
 
31
- This is separate from the toggle marks below, which stay small, fixed, built-in glyphs. `.alert`'s icon followed the same path since — see [Feedback → Icon](./feedback.md#example) — the package no longer paints one there either.
15
+ ```html
16
+ <div class="field">
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>`.
32
27
 
33
- `.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`.
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
+ ## Show a validation error
31
+
32
+ Mark the control invalid with `aria-invalid="true"` and give the error message `.hint.destructive`:
34
33
 
35
34
  ```html
36
- <label class="field">
37
- <span class="label">Email</span>
38
- <div class="input-group">
39
- <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" aria-hidden="true">
40
- <rect width="20" height="16" x="2" y="4" rx="2" />
41
- <path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" />
42
- </svg>
43
- <input class="ipt" type="email" />
35
+ <div class="field">
36
+ <label class="label" for="email">Email</label>
37
+ <input class="ipt" type="email" id="email" name="email" value="ada@" aria-invalid="true" aria-describedby="email-error" />
38
+ <span class="hint destructive" id="email-error">Enter a full email address, such as ada@example.com.</span>
39
+ </div>
40
+ ```
41
+
42
+ 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.
43
+
44
+ ## Put the profile form together
45
+
46
+ ```html
47
+ <form class="card stack" aria-labelledby="profile-title">
48
+ <h2 class="text-title-sm" id="profile-title">Profile</h2>
49
+
50
+ <div class="field">
51
+ <label class="label" for="display-name">Display name</label>
52
+ <input class="ipt" id="display-name" name="display-name" value="Ada Lovelace" aria-describedby="display-name-hint" />
53
+ <span class="hint" id="display-name-hint">Shown on invoices and shared projects.</span>
44
54
  </div>
45
- </label>
46
- <label class="field">
47
- <span class="label">Search</span>
55
+
56
+ <div class="field">
57
+ <label class="label" for="email">Email</label>
58
+ <input class="ipt" type="email" id="email" name="email" value="ada@example.com" />
59
+ </div>
60
+
61
+ <div class="field">
62
+ <label class="label" for="bio">Bio</label>
63
+ <textarea class="textarea" id="bio" name="bio" rows="3"></textarea>
64
+ </div>
65
+
66
+ <div class="field">
67
+ <label class="label" for="language">Language</label>
68
+ <select class="select" id="language" name="language">
69
+ <option>English</option>
70
+ <option>Português</option>
71
+ <option>Español</option>
72
+ </select>
73
+ </div>
74
+
75
+ <div class="cluster">
76
+ <button class="btn primary" type="submit">Save changes</button>
77
+ <button class="btn ghost" type="reset">Cancel</button>
78
+ </div>
79
+ </form>
80
+ ```
81
+
82
+ 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.
83
+
84
+ ## Add an icon or a fixed prefix
85
+
86
+ `.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.
87
+
88
+ The profile address always starts with the same host, so it is a fixed prefix:
89
+
90
+ ```html
91
+ <div class="field">
92
+ <label class="label" for="profile-url">Profile address</label>
93
+ <div class="input-group" style="--input-group-gap: 0">
94
+ <span>app.example.com/u/</span>
95
+ <input class="ipt" id="profile-url" name="profile-url" value="ada" />
96
+ </div>
97
+ </div>
98
+ ```
99
+
100
+ 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).
101
+
102
+ 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)):
103
+
104
+ ```html
105
+ <div class="field">
106
+ <label class="label" for="member-search">Find a member</label>
48
107
  <div class="input-group">
49
- <input class="ipt" type="search" />
50
- <i class="ic-search" aria-hidden="true"></i>
108
+ <i class="ic-lucide-search" aria-hidden="true"></i>
109
+ <input class="ipt" type="search" id="member-search" name="q" />
51
110
  </div>
52
- </label>
111
+ </div>
53
112
  ```
54
113
 
55
- 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.
114
+ 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.
115
+
116
+ 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.
56
117
 
57
- Non-icon input types keep the browser's native `date` and `datetime-local` pickers. WebKit's native `search` decorations are still suppressed on `.text-control` to keep them from overlapping the value.
118
+ The group reads intent and presentation the way a lone `.ipt` does: `.input-group.primary` colors the border, and `.input-group.soft.edgeless` is a field sunk into the page. `aria-invalid="true"` or `disabled` on the control inside applies to the whole group, and so does either on the group itself. The focus ring appears when anything inside the group has focus, so unlike a lone field it also shows after a mouse click.
58
119
 
59
- The slot works the same way for a text affix — a URL host, a currency symbol, a unit — not just an icon:
120
+ ## Browser decorations
121
+
122
+ 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.
123
+
124
+ `.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.
60
125
 
61
126
  ```html
62
- <div class="input-group">
63
- <span>example.com/</span>
64
- <input class="ipt" type="text" />
127
+ <div class="field">
128
+ <label class="label" for="member-filter">Find a member</label>
129
+ <div class="input-group unadorned">
130
+ <i class="ic-lucide-search" aria-hidden="true"></i>
131
+ <input class="ipt" type="search" id="member-filter" name="q" />
132
+ <button class="btn icon ghost p-xs" type="button" aria-label="Clear search">
133
+ <i class="ic-lucide-x" aria-hidden="true"></i>
134
+ </button>
135
+ </div>
65
136
  </div>
66
137
  ```
67
138
 
68
- 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.
139
+ 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.
69
140
 
70
141
  ## Toggles
71
142
 
72
- `.checkbox`, `.radio`, and `.switch` accept the same intent classes as buttons to set the checked color:
143
+ 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:
144
+
145
+ ```html
146
+ <fieldset class="stack tight">
147
+ <legend class="label">Notifications</legend>
148
+ <label class="cluster tight">
149
+ <input class="checkbox" type="checkbox" name="invoice-email" checked />
150
+ Email me when an invoice is ready
151
+ </label>
152
+ </fieldset>
153
+
154
+ <fieldset class="stack tight">
155
+ <legend class="label">Billing period</legend>
156
+ <label class="cluster tight">
157
+ <input class="radio" type="radio" name="period" value="monthly" />
158
+ Monthly
159
+ </label>
160
+ <label class="cluster tight">
161
+ <input class="radio" type="radio" name="period" value="yearly" checked />
162
+ Yearly, two months free
163
+ </label>
164
+ </fieldset>
165
+
166
+ <label class="cluster tight">
167
+ <input class="switch primary" type="checkbox" role="switch" name="public-profile" />
168
+ Public profile
169
+ </label>
170
+ ```
171
+
172
+ - `.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.
173
+ - `.cluster.tight` on the `<label>` lines the control up with its text and leaves a 0.5rem gap.
174
+ - 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.
73
175
 
74
- | Class | Meaning |
75
- | ---------------------- | ----------------------- |
76
- | `.neutral` _(default)_ | Text color, capped. |
77
- | `.primary` | Primary color. |
78
- | `.secondary` | Secondary/accent color. |
79
- | `.success` | Success color. |
80
- | `.warning` | Warning color. |
81
- | `.destructive` | Destructive color. |
82
- | `.info` | Info color. |
176
+ ### Checked and unchecked colors
83
177
 
84
- 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:
178
+ 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.
85
179
 
86
- | Toggle | unchecked | checked |
87
- | ---------------- | --------- | ---------------------------------------- |
88
- | `.ghost` | 0% | 100%, mark in the contrast tone (pinned) |
89
- | `.soft`, default | 12% | 100%, mark in the contrast tone (pinned) |
90
- | `.solid` | 40% | 100%, mark in the contrast tone (pinned) |
180
+ Presentation changes only the unchecked look. Every toggle rests at `.soft`; once checked, each has one look whatever its presentation:
91
181
 
92
- 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.
182
+ | Toggle | Unchecked fill | Checked |
183
+ | ---------------- | -------------- | ---------------------------------------------- |
184
+ | `.ghost` | 0% | 100% fill, with the mark in the contrast color |
185
+ | `.soft`, default | 12% | 100% fill, with the mark in the contrast color |
186
+ | `.solid` | 40% | 100% fill, with the mark in the contrast color |
93
187
 
94
- ## Text controls
188
+ 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.
95
189
 
96
- 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.
190
+ `.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).
97
191
 
98
- 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.
192
+ 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).
193
+
194
+ ## Fill and border on text controls
195
+
196
+ 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:
197
+
198
+ | On a text control | Fill |
199
+ | ----------------------------------------------- | ---- |
200
+ | `.ghost`, default | 0% |
201
+ | Any fill from a container (`.soft` or `.solid`) | 6% |
202
+ | `.soft` on the control | 12% |
203
+ | `.solid` on the control | 20% |
204
+
205
+ 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.
206
+
207
+ 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.
99
208
 
100
209
  ```html
101
- <input class="ipt success" placeholder="Valid" />
102
- <input class="ipt ghost edged" placeholder="Outlined" />
103
- <input class="ipt soft edged" placeholder="Tinted with a border" />
210
+ <input class="ipt success" placeholder="Matches your current password" />
211
+ <input class="ipt soft edged" placeholder="Tinted, with a border" />
104
212
  <input class="ipt soft edgeless" placeholder="Sunk into the page" />
105
213
  <div class="soft">
106
- <input class="ipt" placeholder="Tinted from the container, capped at 6%" />
107
- </div>
108
- <div class="edgeless">
109
- <input class="ipt" placeholder="Keeps its line; the floor holds" />
214
+ <input class="ipt" placeholder="A container's .soft gives a 6% tint" />
110
215
  </div>
111
216
  ```
112
217
 
113
- `.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.
218
+ 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.
114
219
 
115
- ```html
116
- <div class="surface glass">Blurred, translucent, glass corners</div>
117
- <div class="surface pixel">Cut corners and an inset ring</div>
118
- <div class="surface primary solid">Any intent, any presentation</div>
119
- ```
220
+ ## Disabled controls
120
221
 
121
- `.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.
222
+ `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.
122
223
 
123
- ## Example
224
+ ## Building your own control
124
225
 
125
- ```html
126
- <label class="field">
127
- <span class="label">Email</span>
128
- <input class="ipt" type="email" aria-invalid="true" aria-describedby="email-error" />
129
- <span class="hint destructive" id="email-error">Enter a valid email.</span>
130
- </label>
131
- <label style="display: flex; gap: 0.5rem; align-items: center">
132
- <input type="checkbox" class="checkbox success" />
133
- <span>Accept terms</span>
134
- </label>
135
- <label style="display: flex; gap: 0.5rem; align-items: center">
136
- <input type="radio" class="radio secondary" name="plan" />
137
- <span>Standard plan</span>
138
- </label>
139
- <label style="display: flex; gap: 0.5rem; align-items: center">
140
- <input type="checkbox" class="switch destructive" />
141
- <span>Enable</span>
142
- </label>
143
- ```
226
+ 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.
227
+
228
+ ## Next
144
229
 
145
- Use labels, `type`, validation logic, `aria-describedby`, and error message relationships; see [Accessibility](../accessibility.md).
230
+ 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,28 @@
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 last 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.
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
+ When you finish, [Accessibility](../accessibility.md) collects what the CSS does for you and what your application still owes its users.
@@ -0,0 +1,83 @@
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. Reduced motion removes the movement. Chunky tile's heavier label follows the same rule.
80
+
81
+ ## Next
82
+
83
+ 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.