@codenhub/styles 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/aesthetics/chunky-tile.css +1 -1
- package/dist/aesthetics/index.css +1 -1
- package/dist/components.css +1 -1
- package/dist/index.css +1 -1
- package/dist/native.css +1 -1
- package/dist/palette.css +1 -0
- package/dist/theme.css +1 -1
- package/dist/tw/aesthetics/chunky-tile.css +16 -0
- package/dist/tw/box.css +24 -2
- package/dist/tw/components/button.css +22 -33
- package/dist/tw/components/feedback.css +87 -48
- package/dist/tw/components/form.css +139 -45
- package/dist/tw/components/index.css +1 -1
- package/dist/tw/components/loader.css +2 -16
- package/dist/tw/components/surface.css +93 -0
- package/dist/tw/components/tooltip.css +102 -91
- package/dist/tw/elevation.css +43 -7
- package/dist/tw/index.css +7 -4
- package/dist/tw/intent.css +16 -12
- package/dist/tw/palette.css +451 -0
- package/dist/tw/reset.css +21 -0
- package/dist/tw/theme.css +54 -8
- package/dist/tw/utilities/content.css +90 -16
- package/dist/tw/utilities/index.css +1 -1
- package/dist/tw/utilities/layout.css +33 -0
- package/docs/accessibility.md +34 -23
- package/docs/changelog/0.2.0.md +41 -0
- package/docs/changelog/0.3.0.md +15 -0
- package/docs/changelog/index.md +2 -0
- package/docs/concepts.md +1 -0
- package/docs/setup.md +42 -22
- package/docs/usage/aesthetics.md +4 -3
- package/docs/usage/buttons.md +17 -13
- package/docs/usage/composing.md +28 -21
- package/docs/usage/content-and-layout.md +10 -3
- package/docs/usage/customizing.md +27 -0
- package/docs/usage/feedback.md +18 -10
- package/docs/usage/forms.md +36 -26
- package/docs/usage/tooltips.md +48 -22
- package/llms-full.txt +318 -143
- package/package.json +9 -2
package/llms-full.txt
CHANGED
|
@@ -36,6 +36,7 @@ For the Tailwind CSS v4 entrypoint, theme selection, the intent/presentation axe
|
|
|
36
36
|
- [Usage](./docs/usage/index.md): Composing, theming, customizing, aesthetics, and each component family.
|
|
37
37
|
- [Integrating](./docs/integrating/index.md): Wiring the stylesheet into Next.js, Vue, Svelte, Astro, or a Tailwind CSS v4 build.
|
|
38
38
|
- [Accessibility](./docs/accessibility.md): CSS accessibility hooks and non-goals.
|
|
39
|
+
- [Changelog](./docs/changelog/index.md): Release history, including breaking changes.
|
|
39
40
|
|
|
40
41
|
## Requirements
|
|
41
42
|
|
|
@@ -48,7 +49,7 @@ For the Tailwind CSS v4 entrypoint, theme selection, the intent/presentation axe
|
|
|
48
49
|
|
|
49
50
|
## Notes
|
|
50
51
|
|
|
51
|
-
This package has no JavaScript behavior.
|
|
52
|
+
This package has no JavaScript behavior; no version provides compatibility aliases for a removed class or token. `0.2.0` removes `.btn.loading` (compose a disabled `.btn` with a `.loader` child instead) and `.alert.icon`'s built-in glyph (drop a consumer-supplied icon into `.alert-icon` instead), and `.tooltip` now requires a `.tooltip-bubble` child element rather than a `data-tooltip` attribute. See the [changelog](./docs/changelog/index.md) for the complete history.
|
|
52
53
|
|
|
53
54
|
## License
|
|
54
55
|
|
|
@@ -144,32 +145,52 @@ The root follows the operating-system color-scheme preference. Apply `.light` or
|
|
|
144
145
|
|
|
145
146
|
See [Concepts → Theme selection](docs/concepts.md#theme-selection) for how selectors resolve and [Usage → Theming](docs/usage/theming.md) for the full color token reference.
|
|
146
147
|
|
|
148
|
+
### Dark variant
|
|
149
|
+
|
|
150
|
+
`/tw` source entrypoints define a custom `dark:` variant for your own utilities. It fires under an explicit dark selector (`.dark`, `.theme-dark`, or `[data-theme="dark"]`, on the element or an ancestor), or under the system preference when nothing under it forces a theme explicitly:
|
|
151
|
+
|
|
152
|
+
```html
|
|
153
|
+
<html>
|
|
154
|
+
<body>
|
|
155
|
+
<p class="dark:text-white">Follows the OS preference here</p>
|
|
156
|
+
<div class="light">
|
|
157
|
+
<p class="dark:text-white">Stays light -- this subtree forced it</p>
|
|
158
|
+
</div>
|
|
159
|
+
</body>
|
|
160
|
+
</html>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
An explicit selector at any depth wins over the system preference, in either direction, the same as the token theme it tracks.
|
|
164
|
+
|
|
165
|
+
The one place it does not track the token theme: nesting an explicit selector inside the opposite one. `color-scheme` inherits, so a `.light` nested inside `.dark` themes its own tokens correctly regardless of depth -- the nearest declaration always wins. `dark:` matches by selector instead, and `.dark *` matches every descendant of a `.dark` element with no way to stop at a nearer `.light` in between, so a `dark:` utility inside that nested `.light` still fires even though the tokens around it are light. Apply one theme per subtree rather than nesting opposite ones if a page uses both tokens and `dark:` utilities together.
|
|
166
|
+
|
|
147
167
|
## Import paths
|
|
148
168
|
|
|
149
169
|
Compiled entrypoints are ready-to-import CSS and require only tooling that can resolve package CSS imports. `/tw` entrypoints publish copied, uncompiled source from `dist/tw`; a Tailwind v4 build must still process their `@theme`, `@utility`, `@apply`, and related directives. Focused source component entrypoints include theme tokens so their classes can work independently.
|
|
150
170
|
|
|
151
|
-
| Import path | Composition and effects
|
|
152
|
-
| -------------------------------- |
|
|
153
|
-
| `@codenhub/styles` | Compiled complete surface: Tailwind base/theme output, tokens, typography, utilities, components, and reset. Applies global reset, root theme, and focus rules.
|
|
154
|
-
| `@codenhub/styles/theme` | Compiled Tailwind theme variables, Codenhub tokens, and the presentation classes. Sets root theme properties and system/explicit theme selectors; no reset or component classes.
|
|
155
|
-
| `@codenhub/styles/
|
|
156
|
-
| `@codenhub/styles/
|
|
157
|
-
| `@codenhub/styles/
|
|
158
|
-
| `@codenhub/styles/aesthetics
|
|
159
|
-
| `@codenhub/styles/
|
|
160
|
-
| `@codenhub/styles/tw
|
|
161
|
-
| `@codenhub/styles/tw/
|
|
162
|
-
| `@codenhub/styles/tw/
|
|
163
|
-
| `@codenhub/styles/tw/
|
|
164
|
-
| `@codenhub/styles/tw/
|
|
165
|
-
| `@codenhub/styles/tw/
|
|
166
|
-
| `@codenhub/styles/tw/
|
|
167
|
-
| `@codenhub/styles/tw/
|
|
168
|
-
| `@codenhub/styles/tw/
|
|
169
|
-
| `@codenhub/styles/tw/
|
|
170
|
-
| `@codenhub/styles/tw/
|
|
171
|
-
| `@codenhub/styles/tw/
|
|
172
|
-
| `@codenhub/styles/tw/
|
|
171
|
+
| Import path | Composition and effects |
|
|
172
|
+
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
173
|
+
| `@codenhub/styles` | Compiled complete surface: Tailwind base/theme output, tokens, typography, utilities, components, and reset. Applies global reset, root theme, and focus rules. |
|
|
174
|
+
| `@codenhub/styles/theme` | Compiled Tailwind theme variables, Codenhub tokens, and the presentation classes. Sets root theme properties and system/explicit theme selectors; no reset or component classes. |
|
|
175
|
+
| `@codenhub/styles/palette` | Generated, self-contained `--palette-*` custom properties: every intent/presentation cell's fill, text, and edge color, plus the flat `border`/`surface`/`text` neutral tokens, pre-composed to a flat value. For a consumer that cannot take this package as a build-time dependency (an optional peer, for example) but wants to look consistent with it. Not a replacement for the components themselves when Tailwind is available -- see [Customizing → Generated palette](docs/usage/customizing.md#generated-palette). |
|
|
176
|
+
| `@codenhub/styles/components` | Compiled theme tokens, typography utilities, and every component class. Includes static reduced-motion loader masks; no reset, layout/content utilities, or native mappings. |
|
|
177
|
+
| `@codenhub/styles/native` | Compiled complete surface plus classless mappings for headings, text, content elements, form controls, and buttons. Applies the reset and broad element selectors. |
|
|
178
|
+
| `@codenhub/styles/aesthetics` | Compiled `.neobrutalism`, `.glass`, `.pixel`, and `.chunky-tile` classes. Adds no tokens or components of its own; import after the base stylesheet. |
|
|
179
|
+
| `@codenhub/styles/aesthetics/*` | One compiled aesthetic: `/neobrutalism`, `/glass`, `/pixel`, or `/chunky-tile`. Same rules and ordering as the combined file. |
|
|
180
|
+
| `@codenhub/styles/tw` | Source equivalent of the complete stylesheet. Imports Tailwind itself, theme, typography, utilities, components, and reset; applies the same global rules. |
|
|
181
|
+
| `@codenhub/styles/tw/theme` | Source theme, tokens, and presentation classes. Imports Tailwind's theme layer; emits root/system/explicit theme selectors and defines the package's custom `dark:` variant. |
|
|
182
|
+
| `@codenhub/styles/tw/components` | Published Tailwind source for theme, typography, buttons, feedback, forms, loaders, surfaces, and tooltips, including static reduced-motion loader masks. No reset, layout/content utilities, or native mappings. |
|
|
183
|
+
| `@codenhub/styles/tw/surface` | Source theme plus `.surface`, `.card`, and `.panel`. |
|
|
184
|
+
| `@codenhub/styles/tw/button` | Published Tailwind source for theme and `.btn`. Pair it with `/tw/loader` for a loading state composed from `.loader`. |
|
|
185
|
+
| `@codenhub/styles/tw/form` | Source theme plus field, public `text-control`, input, textarea, select, checkbox, radio, and switch utilities. |
|
|
186
|
+
| `@codenhub/styles/tw/feedback` | Source theme plus alert, badge, skeleton, and progress utilities and their keyframes. |
|
|
187
|
+
| `@codenhub/styles/tw/loader` | Published Tailwind source for activity indicators and loaders, including static reduced-motion masks. It has no theme or reset side effects and uses `currentColor`. |
|
|
188
|
+
| `@codenhub/styles/tw/tooltip` | Source theme plus tooltip utilities. |
|
|
189
|
+
| `@codenhub/styles/tw/reset` | Source theme, typography utilities, and global reset/accessibility rules. It changes root/body/elements and selection, motion, focus, scrollbar, and forced colors. |
|
|
190
|
+
| `@codenhub/styles/tw/native` | Source complete surface plus reset and classless native-element mappings; broadest global effects. |
|
|
191
|
+
| `@codenhub/styles/tw/typography` | Source theme plus typography utilities; sets theme selectors but no reset or classless mappings. |
|
|
192
|
+
| `@codenhub/styles/tw/utilities` | Source theme and typography composition plus layout and content utilities; safelists the package utility names. |
|
|
193
|
+
| `@codenhub/styles/tw/aesthetics` | Source aesthetics, combined. `/tw/aesthetics/neobrutalism`, `/glass`, `/pixel`, and `/chunky-tile` publish one apiece. Plain CSS with no theme or reset side effects. |
|
|
173
194
|
|
|
174
195
|
Avoid importing overlapping entrypoints in the same build unless duplicate generated CSS is acceptable; pick the narrowest entrypoint that covers what a page needs.
|
|
175
196
|
|
|
@@ -240,6 +261,7 @@ The root follows the operating-system `color-scheme` preference until an explici
|
|
|
240
261
|
- A nested explicit selector overrides an inherited theme, so the nearest themed ancestor controls that subtree at any depth.
|
|
241
262
|
- If conflicting light and dark selectors are placed on the same element, the dark declaration wins because it appears later with equal specificity. Do not rely on this conflict behavior; apply one theme per element.
|
|
242
263
|
- Component colors resolve from tokens or `currentColor`, so they follow the theme without separate dark-variant rules, with no exception: every glyph the package draws is a `currentColor` mask. Icon artwork a consumer brings is theirs to theme.
|
|
264
|
+
- The Tailwind source entrypoints' `dark:` variant tracks this rule for the explicit-selector-versus-system-preference choice: it fires under an explicit dark selector, or under the system preference when the subtree carries no explicit selector at all. It does not track the previous rule the same way: that one holds because `color-scheme` inherits, so the nearest declaration always wins regardless of depth, while `dark:` matches by selector -- `.dark *` matches every descendant of a `.dark` element, with no way to stop at a nearer `.light` in between. A `.light` subtree nested inside `.dark` themes its tokens correctly but still matches `dark:`'s explicit arm. See [Setup → Dark variant](docs/setup.md#dark-variant).
|
|
243
265
|
|
|
244
266
|
Setting `color-scheme` yourself on an element also re-themes the tokens below it, because that is the only signal the palette reads. This also themes native UI such as scrollbars and form-control internals to match.
|
|
245
267
|
|
|
@@ -307,6 +329,8 @@ Values each class ships:
|
|
|
307
329
|
| `.edged` | | | `100%` |
|
|
308
330
|
| `.edgeless` | | | `0%` |
|
|
309
331
|
|
|
332
|
+
`.card`, `.panel`, `.alert`, `.kbd`, `.code`, and `.pre` override `.soft`'s fill at rest to `0%` for the no-named-intent case only, and reveal it on `.card`'s own `:hover`/`.interactive`/`.hoverable` -- the one place a component remaps what a presentation class resolves to for part of its intent range, the same way text controls cap it at `6%` cascaded. A named intent on any of the six keeps the ordinary `12%`. `.badge` reads the identical intent/presentation classes but keeps its 12% wash at rest even with no named intent, since it has no border to fall back on and would otherwise disappear rather than read quieter. See [Content and layout → Surfaces](docs/usage/content-and-layout.md#surfaces) and [Feedback](docs/usage/feedback.md).
|
|
333
|
+
|
|
310
334
|
The combinations mean exactly what they spell:
|
|
311
335
|
|
|
312
336
|
```html
|
|
@@ -324,20 +348,21 @@ The edge is the silhouette and nothing else. Rules _inside_ a component that has
|
|
|
324
348
|
|
|
325
349
|
With no presentation class in scope, each component keeps its own default: buttons are filled, alerts and badges are tinted, surfaces and controls are neutral and bordered.
|
|
326
350
|
|
|
327
|
-
| Component | Reads
|
|
328
|
-
| ----------------------------------------------- |
|
|
329
|
-
| `.btn`, `.alert`, `.badge`, `.card`, `.panel` | Fill, text, border, and border width.
|
|
330
|
-
| `.kbd` | Fill, text, border, and border width.
|
|
331
|
-
| `.data-table` | Header fill and text, border, border width.
|
|
332
|
-
| `.ipt`, `.textarea`, `.select`, `.text-control` | Border, border width, and a capped fill.
|
|
333
|
-
| `.input-group` | Border, border width, and a capped fill.
|
|
334
|
-
| `.checkbox`, `.radio` | A capped fill, checked and unchecked.
|
|
335
|
-
| `.switch` | Border, border width, and a capped fill.
|
|
336
|
-
| `.quote` | Fill, text, border, and width.
|
|
337
|
-
| `.code`, `.pre`, `.tooltip`
|
|
338
|
-
| `.
|
|
339
|
-
|
|
340
|
-
|
|
351
|
+
| Component | Reads |
|
|
352
|
+
| ----------------------------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
353
|
+
| `.btn`, `.alert`, `.badge`, `.card`, `.panel` | Fill, text, border, and border width. |
|
|
354
|
+
| `.kbd` | Fill, text, border, and border width. |
|
|
355
|
+
| `.data-table` | Header fill and text, border, border width. |
|
|
356
|
+
| `.ipt`, `.textarea`, `.select`, `.text-control` | Border, border width, and a capped fill. |
|
|
357
|
+
| `.input-group` | Border, border width, and a capped fill. |
|
|
358
|
+
| `.checkbox`, `.radio` | A capped fill, checked and unchecked. |
|
|
359
|
+
| `.switch` | Border, border width, and a capped fill. |
|
|
360
|
+
| `.quote` | Fill, text, border, and width. |
|
|
361
|
+
| `.code`, `.pre`, `.tooltip-bubble` | Fill, text, border, and material. |
|
|
362
|
+
| `.progress` | Track fill, border, and border width; the value fill is always the intent color, full strength. |
|
|
363
|
+
| `.loader`, `.skeleton`, `.divider` | Intent only, not presentation. |
|
|
364
|
+
|
|
365
|
+
A text control caps a **cascaded** fill at `6%`, keeping typed text legible when a `.solid` container reaches a field nobody classed. A fill class written on the control itself names its own cap instead, because that is a consumer describing what they want: `.soft` takes `12%` and `.solid` takes `20%`.
|
|
341
366
|
|
|
342
367
|
| On a text control | fill |
|
|
343
368
|
| ----------------- | ---- |
|
|
@@ -346,13 +371,15 @@ Text controls and toggles cap a **cascaded** fill at `6%`, keeping typed text le
|
|
|
346
371
|
| `.soft` | 12% |
|
|
347
372
|
| `.solid` | 20% |
|
|
348
373
|
|
|
349
|
-
|
|
374
|
+
A toggle's cap does not have that split: it is `40%` whether the presentation class is on the checkbox/radio/switch itself or cascaded from a container, the same value for all three. That is where an unchecked `.ghost` (0%), `.soft`/default (12%), and `.solid` (the cap) read as three distinct plates instead of shades of the same wash. Being checked pins the fill to `100%` instead, whatever the presentation class asks — a checkbox, a radio, and a switch each have one "on" look, and presentation only shapes them unchecked. See [Forms](docs/usage/forms.md) for the full toggle reference.
|
|
350
375
|
|
|
351
|
-
|
|
376
|
+
The same split governs the edge: a cascaded `.edgeless` is floored and the element's own is honoured — except on `.checkbox` and `.radio`, which never drop their line at all, and on `.switch.ghost`, which keeps its line specifically against its own `.edgeless`: `.ghost` already asks for zero fill, so removing the line too would leave nothing marking the track but the knob. `.switch.ghost.edgeless` renders the same as `.switch.ghost.edged` rather than as a distinct, boundary-less look. See [Forms](docs/usage/forms.md) for the full toggle and text control reference.
|
|
377
|
+
|
|
378
|
+
A tooltip bubble is filled, over a tinted ground. An intent fills it with that intent's own color: `.tooltip-bubble.primary` is the primary color, black on a light page and white on a dark one, and `.tooltip-bubble.destructive` is a red bubble.
|
|
352
379
|
|
|
353
380
|
With no intent the bubble is `--color-tooltip`, the one plate in the package chosen per theme rather than derived from an intent. Composed like everything else it was `20%` of the page's ink over the surface tone, which steps _lighter_ than the surface on a dark page and _darker_ on a light one — so the dark bubble read as lifted and the light one as a mid-grey slab two steps darker than every card on the screen. The token states each direction instead: near-white over a light page, a mid grey over a dark one. `--color-tooltip-contrast` is its ink.
|
|
354
381
|
|
|
355
|
-
Near-white is only 1.04:1 against a light page, so the bubble draws a hairline as well, and floors it: a container's `.solid` or `.edgeless` cannot take the boundary away from a bubble nobody classed, while `.tooltip.edgeless` still can. The ground keeps the bubble opaque at every fill, so a cascaded presentation leaves it readable over whatever it floats above. It reads the material tokens too, so an [aesthetic](docs/usage/aesthetics.md) in scope shapes the bubble like any other component. See [Tooltips](docs/usage/tooltips.md) for the full tooltip reference.
|
|
382
|
+
Near-white is only 1.04:1 against a light page, so the bubble draws a hairline as well, and floors it: a container's `.solid` or `.edgeless` cannot take the boundary away from a bubble nobody classed, while `.tooltip-bubble.edgeless` still can. The ground keeps the bubble opaque at every fill, so a cascaded presentation leaves it readable over whatever it floats above. It reads the material tokens too, so an [aesthetic](docs/usage/aesthetics.md) in scope shapes the bubble like any other component. See [Tooltips](docs/usage/tooltips.md) for the full tooltip reference.
|
|
356
383
|
|
|
357
384
|
## Component axis reference
|
|
358
385
|
|
|
@@ -372,16 +399,18 @@ Presentation is two independent axes, so they get a column each. A `Yes` means t
|
|
|
372
399
|
| `.surface` | Yes | Yes | Yes | Shape |
|
|
373
400
|
| `.checkbox`, `.radio` | Yes | Yes | No | Radius and edge |
|
|
374
401
|
| `.switch` | Yes | Yes | Yes | Radius and edge |
|
|
375
|
-
| `.progress` | Yes |
|
|
402
|
+
| `.progress` | Yes | Yes | Yes | Radius and edge |
|
|
376
403
|
| `.code`, `.pre` | Yes | Yes | Yes | Radius and clip |
|
|
377
|
-
| `.tooltip`
|
|
404
|
+
| `.tooltip-bubble` | Yes | Yes | Yes | Shape and overlay |
|
|
378
405
|
| `.table-wrap`, `.quote-inline`, layout and text utilities | No | No | No | No |
|
|
379
406
|
|
|
380
407
|
`.checkbox` and `.radio` are the only components that read one axis and not the other. Their line is the only thing marking an unchecked box, so it is drawn whatever the edge class says, from a container or from the element itself. `.edgeless` on either is unsupported rather than merely discouraged.
|
|
381
408
|
|
|
409
|
+
`.progress` reads both axes on its track; the value fill, the moving bar itself, is always the intent color at full strength and never changes. `.solid` is unsupported on the track: at full fill the track and the value fill compose the same color, and the one thing the component exists to show — how much is filled versus not — disappears. `.ghost.edgeless` renders (no fill, no border) but is not recommended: `.ghost.edged` — an outline capsule around the moving fill — keeps a visible frame at rest, while pairing `.ghost` with `.edgeless` leaves the track with none.
|
|
410
|
+
|
|
382
411
|
Every other text control floors a _cascaded_ `.edgeless` and honours its own: a `.edgeless` toolbar will not erase the line of a field nobody classed, while `.ipt.edgeless` is a consumer describing what they want and gets it.
|
|
383
412
|
|
|
384
|
-
Intent aliases such as `.danger` and `.error` occupy the same intent axis as `.destructive`; they do not add component behavior. State and modifier classes such as `.interactive`, `.hoverable`, `.pressable`, `.
|
|
413
|
+
Intent aliases such as `.danger` and `.error` occupy the same intent axis as `.destructive`; they do not add component behavior. State and modifier classes such as `.interactive`, `.hoverable`, `.pressable`, `.invalid`, `.compact`, and `.vertical` sit above this map and are documented with their component.
|
|
385
414
|
|
|
386
415
|
## Elevation
|
|
387
416
|
|
|
@@ -403,7 +432,7 @@ Each is one unitless multiplier over whatever shadow geometry is in scope, so th
|
|
|
403
432
|
<article class="card elevation-none">Flat, whatever the page or container says</article>
|
|
404
433
|
```
|
|
405
434
|
|
|
406
|
-
The multiplier
|
|
435
|
+
The multiplier does not inherit: an elevation class lifts or flattens the element it is written on only, and an unclassed element inside it takes no depth of its own. Give each element that needs depth its own elevation class. Spread is deliberately left out of the multiplication, because an aesthetic that draws its edge as an inset ring spends spread on it and scaling that would erase the edge.
|
|
407
436
|
|
|
408
437
|
<!-- Source: docs/usage/theming.md -->
|
|
409
438
|
|
|
@@ -691,6 +720,33 @@ Knobs and material tokens reach in opposite directions, and telling them apart m
|
|
|
691
720
|
|
|
692
721
|
Shipped aesthetics set the shared material tokens above. `.neobrutalism` and `.pixel` set `--ui-ink` for their neutral outline; intent classes still override the intent slots on each component.
|
|
693
722
|
|
|
723
|
+
## Generated palette
|
|
724
|
+
|
|
725
|
+
`@codenhub/styles/palette` is a generated, self-contained stylesheet of every `intent x presentation` cell's composed colors, baked to flat `--palette-*` custom properties -- the same values `box.css` composes live, computed once at build time rather than re-derived. It exists for a consumer that cannot take `@codenhub/styles` as a build-time dependency (an optional peer, for example) but still wants to look consistent with it, not as a replacement for the components themselves when Tailwind is available.
|
|
726
|
+
|
|
727
|
+
```css
|
|
728
|
+
@import "@codenhub/styles/palette";
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
```css
|
|
732
|
+
.my-badge {
|
|
733
|
+
background: var(--palette-success-soft-page-bg);
|
|
734
|
+
color: var(--palette-success-soft-fg);
|
|
735
|
+
border-color: var(--palette-success-soft-page-edge);
|
|
736
|
+
}
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
Every value is already the composited color -- never a `var(--color-background)` or `var(--intent-subtle)` reference -- so this file never needs `./theme` loaded alongside it to resolve anything.
|
|
740
|
+
|
|
741
|
+
Naming: `--palette-<intent>-<presentation>-<slot>`, where `<intent>` is one of the seven published intents (`neutral`, `primary`, `secondary`, `success`, `warning`, `destructive`, `info`) and `<presentation>` is `solid`, `soft`, or `ghost`. `<slot>` is `bg`, `fg`, or `edge`. Two qualifiers layer on only where they apply:
|
|
742
|
+
|
|
743
|
+
- A ground qualifier (`page` or `subtle`) inserted before the slot, present only on `.soft`/`.ghost`'s `bg` and `edge` -- `.solid` is ground-independent at full fill, and `fg` never varies by ground. No qualifier means the `transparent` ground (`.badge`/`.btn`'s ground): `--palette-success-soft-bg`. `page` is `.alert`'s ground (`--color-background`): `--palette-success-soft-page-bg`. `subtle` is `.pre`/`.code`/`.kbd`/`.tooltip-bubble`'s ground (that intent's own `--intent-subtle`): `--palette-success-soft-subtle-bg`.
|
|
744
|
+
- A `-hover` suffix on `bg` and `edge` only -- `fg` never changes on hover: `--palette-success-soft-page-bg-hover`.
|
|
745
|
+
|
|
746
|
+
Dark values are scoped the same way `@codenhub/styles`' own explicit theme override is: `.dark`, `.theme-dark`, and `[data-theme="dark"]`, on the element or an ancestor. There is no `prefers-color-scheme` fallback in this file -- a consumer reaching for a standalone palette is already handling its own theme switching, so the OS-preference case is left to `@codenhub/styles`' own `light-dark()`-based tokens when that package is actually in use.
|
|
747
|
+
|
|
748
|
+
Three more tokens sit alongside the intent cells for the same reason, without an intent, presentation, ground, or hover axis of their own: `--palette-border`, `--palette-surface`, and `--palette-text`, flat values for `--color-border`, `--color-surface`, and `--color-text` -- the resting colors anything that is not intent-colored needs (a container's border, a dialog's background, its text), which an optional-peer consumer needs just as much as the intent cells.
|
|
749
|
+
|
|
694
750
|
## Component internals
|
|
695
751
|
|
|
696
752
|
Component classes may define scoped implementation variables such as `--surface-padding`, `--field-gap`, `--input-group-pad`, `--switch-*`, `--table-*`, `--quote-*`, and `--tooltip-*`. These variables are internal wiring for class composition and are not the public token contract.
|
|
@@ -734,7 +790,7 @@ Aesthetics compose with every supported fill and edge pair. `.ghost.edgeless` in
|
|
|
734
790
|
|
|
735
791
|
An explicit presentation on the element still wins over the aesthetic's defaults. The aesthetic supplies edge thickness; presentation only decides whether that edge is drawn.
|
|
736
792
|
|
|
737
|
-
An aesthetic directly on a component likewise wins over an inherited aesthetic. This includes tooltip
|
|
793
|
+
An aesthetic directly on a component likewise wins over an inherited aesthetic. This includes tooltip bubbles: `.tooltip-bubble.glass` gets the complete glass bubble under a pixel ancestor, and `.tooltip-bubble.pixel` gets the complete stepped bubble under a glass ancestor.
|
|
738
794
|
|
|
739
795
|
Each aesthetic that scales from one number publishes it as a knob — see [Customizing → Aesthetic tokens](docs/usage/customizing.md#aesthetic-tokens) for how knobs resolve and where they can be set. This page covers what each aesthetic looks like and where it makes documented exceptions.
|
|
740
796
|
|
|
@@ -787,8 +843,9 @@ The shadow is cast in the component's own intent, so a success button throws a g
|
|
|
787
843
|
|
|
788
844
|
- Corners are one unit or nothing. Chips square instead of stepping — badges, key caps, code, checkboxes, and switches all read `--ui-clip-tight`, which this aesthetic sets to none, because one unit off each corner of a 24px badge is a bite rather than a corner. Tables, progress bars, and skeletons square too, each for its own reason: a table's `overflow: hidden` fights the clip and the corners square off where the two meet, and progress and skeleton never read a clip at all — a squaring aesthetic reaches them through `border-radius` alone. `.pre` is the one exception that steps: it carries no clip override, so it inherits the same polygon a button or a card gets, and with no border by default the cut shows with no ring around it.
|
|
789
845
|
- The outline is an inset ring, because a clip removes a real border — and the focus ring, for the same reason, is a second inset layer rather than an outline. Both are the element's own edge rather than a shadow, so the border answers `.edged` and `.edgeless` the way a border does: a `.edgeless` badge and a `.solid` button draw none, a `.edged` card draws one, and a field keeps one whatever a container asks for.
|
|
790
|
-
- The tooltip trigger and `.radio` are hardcoded past the clip rather than reached by it. `.tooltip-icon` forces `clip-path: none
|
|
846
|
+
- The tooltip trigger and `.radio` are hardcoded past the clip rather than reached by it. `.tooltip-icon` forces `clip-path: none` because the docs call it a circular fixed identity, the same reason `.radio` forces it: the circle is the only thing telling it from a checkbox at a glance, and a stepped polygon would square either one.
|
|
791
847
|
- `--font-pixel` is yours to supply. The package ships no font binary, so the aesthetic has no network side effect and falls back to the monospace stack.
|
|
848
|
+
- Numeral legibility depends on the face you pick. At UI sizes -- pagination, counters, small badges -- some pixel/bitmap faces render lookalike digits such as `2`/`8` or `3`/`9` closely enough to misread at a glance; this is a property of the font, not something the aesthetic's CSS can correct. Prefer a face with clearly distinct digit shapes wherever small numeric text matters.
|
|
792
849
|
|
|
793
850
|
## Chunky tile
|
|
794
851
|
|
|
@@ -815,7 +872,7 @@ The bar is a darker shade of the element's own colour rather than a shadow under
|
|
|
815
872
|
- `--font-rounded` is yours to supply. A heavy rounded grotesque is most of this look and the package ships no font binary, so with nothing supplied the page's own stack is used.
|
|
816
873
|
- Action labels are drawn heavier and slightly tracked. Casing is left alone: how a label is worded is the application's decision, so if you want uppercase buttons, write that rule in your own stylesheet.
|
|
817
874
|
- `.card.interactive` presses like a `.btn`, so a chunky answer tile drops onto its bar on click; `.card.pressable` gets the same press on its own. A plain `.card` stays put.
|
|
818
|
-
- The shipped `.primary` is a near-black monochrome,
|
|
875
|
+
- The shipped `.primary` is a near-black-or-near-white monochrome depending on theme, and the bar mixes toward a fixed black regardless of theme. In light theme this leaves a primary button's bar almost invisible against its own near-black plate (measured `1.04:1`, 10 units of sRGB distance); in dark theme the near-white plate against the same black-anchored bar reads fine (`2.48:1`, 154 units). Give `.primary` a hue of its own if it is the call to action and needs the depth cue in both themes.
|
|
819
876
|
- Depth in this aesthetic is the bar, never a blurred drop shadow. Anything the registry rests flat sits flush on the page.
|
|
820
877
|
|
|
821
878
|
<!-- Source: docs/usage/buttons.md -->
|
|
@@ -844,17 +901,16 @@ Every component that supports intent accepts the same list and reads the same [i
|
|
|
844
901
|
|
|
845
902
|
Size and shape classes:
|
|
846
903
|
|
|
847
|
-
| Class | Purpose
|
|
848
|
-
| ---------------------------------------------------------------------- |
|
|
849
|
-
| `.pill` | Fully rounded button corners (`border-radius: 9999px`).
|
|
850
|
-
| `.sm` | Smaller button.
|
|
851
|
-
| `.lg` | Larger button.
|
|
852
|
-
| `.p-xs`, `.dense` | Tightest padding modifier (`px-2 py-0.5`; icon `p-0.5`). Also drops the `--control-height` minimum so a chip-sized button fits a table cell or a dense toolbar; every other size and padding class keeps that floor
|
|
853
|
-
| `.p-sm`, `.compact` | Compact padding modifier (`px-2.5 py-1`; icon `p-1`).
|
|
854
|
-
| `.p-lg`, `.spacious` | Spacious padding modifier (`px-6 py-3`; icon `p-3`).
|
|
855
|
-
| `.icon` | Square icon button
|
|
856
|
-
| `.
|
|
857
|
-
| `.disabled`, `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` | Disabled styling. |
|
|
904
|
+
| Class | Purpose |
|
|
905
|
+
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
906
|
+
| `.pill` | Fully rounded button corners (`border-radius: 9999px`). |
|
|
907
|
+
| `.sm` | Smaller button. |
|
|
908
|
+
| `.lg` | Larger button. |
|
|
909
|
+
| `.p-xs`, `.dense` | Tightest padding modifier (`px-2 py-0.5`; icon `p-0.5`). Also drops the `--control-height` minimum so a chip-sized button fits a table cell or a dense toolbar; every other size and padding class keeps that floor for a labeled button. |
|
|
910
|
+
| `.p-sm`, `.compact` | Compact padding modifier (`px-2.5 py-1`; icon `p-1`). |
|
|
911
|
+
| `.p-lg`, `.spacious` | Spacious padding modifier (`px-6 py-3`; icon `p-3`). |
|
|
912
|
+
| `.icon` | Square icon button, sized to its own padding rather than to `--control-height`. Use an accessible name in HTML. |
|
|
913
|
+
| `.disabled`, `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` | Disabled styling. |
|
|
858
914
|
|
|
859
915
|
Examples:
|
|
860
916
|
|
|
@@ -864,10 +920,15 @@ Examples:
|
|
|
864
920
|
<button class="btn warning soft">Warning soft</button>
|
|
865
921
|
<button class="btn destructive ghost edgeless">Danger ghost</button>
|
|
866
922
|
<button class="btn icon primary" aria-label="Create">+</button>
|
|
867
|
-
<button class="btn primary
|
|
923
|
+
<button class="btn primary" disabled>
|
|
924
|
+
<span class="loader sm" aria-hidden="true"></span>
|
|
925
|
+
Saving
|
|
926
|
+
</button>
|
|
868
927
|
```
|
|
869
928
|
|
|
870
|
-
|
|
929
|
+
## Loading
|
|
930
|
+
|
|
931
|
+
The package ships no loading state of its own: a loading button is a disabled `.btn` composed with a real [`.loader`](docs/usage/feedback.md) child, the same activity indicator every other component uses. Disable the button so users cannot trigger duplicate work, and drop a `.loader` in beside the label — any of its variants (`.dots-wave`, `.bars-wave`, and so on) work the same way. Whether to also hide the label is your call; the package has no built-in mechanism for that.
|
|
871
932
|
|
|
872
933
|
On the base look a press scales the button down a little (`--ui-active-transform`, `scale(0.97)`). [Aesthetics](docs/usage/aesthetics.md) swap in their own press gesture, and `prefers-reduced-motion` turns it off.
|
|
873
934
|
|
|
@@ -877,29 +938,29 @@ Use `<button>` for actions or an accessible link for navigation; see [Accessibil
|
|
|
877
938
|
|
|
878
939
|
# Forms
|
|
879
940
|
|
|
880
|
-
| Class or Selector | Purpose
|
|
881
|
-
| --------------------------------------------------------------------- |
|
|
882
|
-
| `.field` | Vertical field wrapper.
|
|
883
|
-
| `.label` | Form label text.
|
|
884
|
-
| `.hint` | Secondary helper text.
|
|
885
|
-
| `.hint.error` | Helper text with destructive intent.
|
|
886
|
-
| `.surface` | Shared public container composition utility.
|
|
887
|
-
| `.text-control` | Shared public text-control composition utility.
|
|
888
|
-
| `.ipt` | Input control styling.
|
|
889
|
-
| `.input-group` | Wrapper that owns the field box so a control can carry
|
|
890
|
-
| `.textarea` | Textarea control styling.
|
|
891
|
-
| `.select` | Select control styling.
|
|
892
|
-
| `input[type="checkbox"].checkbox` | Custom checkbox control styling.
|
|
893
|
-
| `input[type="radio"].radio` | Custom radio control styling.
|
|
894
|
-
| `input[type="checkbox"].switch` | Custom switch control styling.
|
|
895
|
-
| `[aria-invalid="true"]` on controls | Destructive border and focus color.
|
|
896
|
-
| `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` on controls | Disabled styling.
|
|
941
|
+
| Class or Selector | Purpose |
|
|
942
|
+
| --------------------------------------------------------------------- | ------------------------------------------------------------------------ |
|
|
943
|
+
| `.field` | Vertical field wrapper. |
|
|
944
|
+
| `.label` | Form label text. |
|
|
945
|
+
| `.hint` | Secondary helper text. |
|
|
946
|
+
| `.hint.error` | Helper text with destructive intent. |
|
|
947
|
+
| `.surface` | Shared public container composition utility. |
|
|
948
|
+
| `.text-control` | Shared public text-control composition utility. |
|
|
949
|
+
| `.ipt` | Input control styling. |
|
|
950
|
+
| `.input-group` | Wrapper that owns the field box so a control can carry an icon or affix. |
|
|
951
|
+
| `.textarea` | Textarea control styling. |
|
|
952
|
+
| `.select` | Select control styling. |
|
|
953
|
+
| `input[type="checkbox"].checkbox` | Custom checkbox control styling. |
|
|
954
|
+
| `input[type="radio"].radio` | Custom radio control styling. |
|
|
955
|
+
| `input[type="checkbox"].switch` | Custom switch control styling. |
|
|
956
|
+
| `[aria-invalid="true"]` on controls | Destructive border and focus color. |
|
|
957
|
+
| `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]` on controls | Disabled styling. |
|
|
897
958
|
|
|
898
959
|
## Icons
|
|
899
960
|
|
|
900
961
|
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.
|
|
901
962
|
|
|
902
|
-
This is separate from the toggle marks below
|
|
963
|
+
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](docs/usage/feedback.md#example) — the package no longer paints one there either.
|
|
903
964
|
|
|
904
965
|
`.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`.
|
|
905
966
|
|
|
@@ -927,6 +988,17 @@ The group reads intent and both [presentation](docs/usage/composing.md#presentat
|
|
|
927
988
|
|
|
928
989
|
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.
|
|
929
990
|
|
|
991
|
+
The slot works the same way for a text affix — a URL host, a currency symbol, a unit — not just an icon:
|
|
992
|
+
|
|
993
|
+
```html
|
|
994
|
+
<div class="input-group">
|
|
995
|
+
<span>example.com/</span>
|
|
996
|
+
<input class="ipt" type="text" />
|
|
997
|
+
</div>
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
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.
|
|
1001
|
+
|
|
930
1002
|
## Toggles
|
|
931
1003
|
|
|
932
1004
|
`.checkbox`, `.radio`, and `.switch` accept the same intent classes as buttons to set the checked color:
|
|
@@ -941,16 +1013,15 @@ Non-icon input types keep the browser's native `date` and `datetime-local` picke
|
|
|
941
1013
|
| `.destructive`, `.danger`, `.error` | Destructive color. |
|
|
942
1014
|
| `.info` | Info color. |
|
|
943
1015
|
|
|
944
|
-
All three toggles rest at `.
|
|
1016
|
+
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:
|
|
945
1017
|
|
|
946
|
-
|
|
1018
|
+
| Toggle | unchecked | checked |
|
|
1019
|
+
| ---------------- | --------- | ---------------------------------------- |
|
|
1020
|
+
| `.ghost` | 0% | 100%, mark in the contrast tone (pinned) |
|
|
1021
|
+
| `.soft`, default | 12% | 100%, mark in the contrast tone (pinned) |
|
|
1022
|
+
| `.solid` | 40% | 100%, mark in the contrast tone (pinned) |
|
|
947
1023
|
|
|
948
|
-
|
|
949
|
-
| ----------------- | --------------- | ------------------------------- |
|
|
950
|
-
| `.solid`, default | 20%, switch 40% | 100%, mark in the contrast tone |
|
|
951
|
-
| `.soft` | 12% | 12%, mark in the strong tone |
|
|
952
|
-
|
|
953
|
-
A checked checkbox and a checked switch cut their mark out of that plate. A checked radio thickens its ring to twice the resting line and takes the intent whole on it, so `.radio.soft` is the classic ring-and-dot radio and `.radio.solid` is a filled circle whose dot stays readable — the mark follows the plate rather than being pinned to the intent color.
|
|
1024
|
+
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.
|
|
954
1025
|
|
|
955
1026
|
## Text controls
|
|
956
1027
|
|
|
@@ -1012,7 +1083,7 @@ Use labels, `type`, validation logic, `aria-describedby`, and error message rela
|
|
|
1012
1083
|
| Class | Purpose |
|
|
1013
1084
|
| ---------------- | ---------------------------------------------------------------------------- |
|
|
1014
1085
|
| `.alert` | Inline feedback surface. |
|
|
1015
|
-
| `.icon`
|
|
1086
|
+
| `.alert-icon` | Sizing and alignment for an icon element dropped in as a child of `.alert`. |
|
|
1016
1087
|
| `.badge` | Compact status pill. |
|
|
1017
1088
|
| `.loader-mask` | Low-level activity indicator composition utility. Applies the spinner mask. |
|
|
1018
1089
|
| `.loader` | Standalone inline loader artwork. |
|
|
@@ -1040,30 +1111,37 @@ These variants retain their embedded SVG animations normally. Under `prefers-red
|
|
|
1040
1111
|
|
|
1041
1112
|
Loader size modifiers:
|
|
1042
1113
|
|
|
1043
|
-
| Class | Size
|
|
1044
|
-
| -------- |
|
|
1045
|
-
| _(none)_ | Default (`
|
|
1046
|
-
| `.sm` | Small (`1.
|
|
1047
|
-
| `.lg` | Large (`2.
|
|
1114
|
+
| Class | Size |
|
|
1115
|
+
| -------- | -------------------- |
|
|
1116
|
+
| _(none)_ | Default (`1.75rem`). |
|
|
1117
|
+
| `.sm` | Small (`1.25rem`). |
|
|
1118
|
+
| `.lg` | Large (`2.25rem`). |
|
|
1048
1119
|
|
|
1049
1120
|
Alerts, badges, progress bars, skeletons, and loaders accept `.primary`, `.secondary`, `.success`, `.warning`, `.destructive`, `.danger`, `.error`, and `.info`. Without an intent, they use the text palette, except `.loader`, which keeps `currentColor` so it matches whatever content surrounds it.
|
|
1050
1121
|
|
|
1051
|
-
Skeletons, loaders,
|
|
1122
|
+
Skeletons, loaders, and dividers are indicators: they read intent but ignore fill and edge [presentation](docs/usage/composing.md#presentation). `.progress` is not one of them — its track reads both, `.soft.edgeless` by default; only the moving value fill stays intent-colored at full strength regardless of presentation. `.solid` is unsupported on the track, and `.ghost.edgeless` renders but is discouraged, since neither leaves the track a visible frame. See [Composing → Component axis reference](docs/usage/composing.md#component-axis-reference) for the full table.
|
|
1052
1123
|
|
|
1053
1124
|
```html
|
|
1054
1125
|
<span class="loader success" aria-hidden="true"></span> <span class="skeleton info"></span>
|
|
1055
1126
|
```
|
|
1056
1127
|
|
|
1057
|
-
|
|
1128
|
+
The package ships no icon of its own: `.alert` is already `flex`, so an icon dropped in as a child is spaced from the message by its `gap-3` with nothing to trigger. `.alert-icon` only sizes and aligns whatever icon element you provide — an inline `<svg>`, an `<img>`, or a class from an icon set such as `@codenhub/icons` — the same contract [`.input-group`](docs/usage/forms.md#icons) documents.
|
|
1058
1129
|
|
|
1059
|
-
Alerts and badges read the shared [presentation](docs/usage/composing.md#presentation) classes. Without one they use a tinted surface, intent-colored text, and a mixed intent border.
|
|
1130
|
+
Alerts and badges read the shared [presentation](docs/usage/composing.md#presentation) classes. Without one they use a tinted surface, intent-colored text, and a mixed intent border -- except a neutral `.alert` (no named intent), which rests untinted instead, the same no-named-intent carve-out `.card.soft` and `.panel` use: a plain `--color-foreground` plate rather than 12% of near-black ink over the page. A named intent (`.alert.success`, `.alert.destructive`, and so on) keeps its ordinary tint. `.badge` is unaffected either way -- it has no border to fall back on at rest, so untinting it would make a neutral badge disappear rather than read quieter.
|
|
1060
1131
|
|
|
1061
1132
|
## Example
|
|
1062
1133
|
|
|
1063
1134
|
```html
|
|
1064
1135
|
<div class="alert success" role="status">Saved successfully.</div>
|
|
1065
1136
|
<div class="alert primary solid" role="status">Deployment started.</div>
|
|
1066
|
-
<div class="alert warning soft
|
|
1137
|
+
<div class="alert warning soft" role="status">
|
|
1138
|
+
<svg class="alert-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
|
1139
|
+
<path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
|
|
1140
|
+
<path d="M12 9v4" />
|
|
1141
|
+
<path d="M12 17h.01" />
|
|
1142
|
+
</svg>
|
|
1143
|
+
Review required.
|
|
1144
|
+
</div>
|
|
1067
1145
|
<div class="alert destructive ghost edged" role="alert">Deployment failed.</div>
|
|
1068
1146
|
<span class="badge warning">Queued</span>
|
|
1069
1147
|
<span class="badge success solid">Live</span>
|
|
@@ -1076,6 +1154,7 @@ Alerts and badges read the shared [presentation](docs/usage/composing.md#present
|
|
|
1076
1154
|
<span class="loader dots-grow lg" aria-hidden="true"></span>
|
|
1077
1155
|
<div class="progress" role="progressbar" aria-label="Upload progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="64" style="--progress-value: 64%"></div>
|
|
1078
1156
|
<div class="progress secondary active" role="progressbar" aria-label="Upload progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="64" style="--progress-value: 64%"></div>
|
|
1157
|
+
<div class="progress primary ghost edged" role="progressbar" aria-label="Upload progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="64" style="--progress-value: 64%"></div>
|
|
1079
1158
|
<div class="progress info indeterminate" role="progressbar" aria-label="Loading" aria-valuemin="0" aria-valuemax="100"></div>
|
|
1080
1159
|
```
|
|
1081
1160
|
|
|
@@ -1085,37 +1164,63 @@ Add `role="status"` or `role="alert"` to alerts based on announcement urgency, m
|
|
|
1085
1164
|
|
|
1086
1165
|
# Tooltips
|
|
1087
1166
|
|
|
1088
|
-
| Class or Attribute
|
|
1089
|
-
|
|
|
1090
|
-
| `.tooltip`
|
|
1091
|
-
| `.tooltip
|
|
1092
|
-
|
|
|
1093
|
-
| `[data-tooltip-position="
|
|
1094
|
-
| `[data-tooltip-position="
|
|
1095
|
-
| `[data-tooltip-position="
|
|
1096
|
-
|
|
|
1097
|
-
| `
|
|
1098
|
-
|
|
|
1167
|
+
| Class or Attribute | Purpose |
|
|
1168
|
+
| -------------------------------------- | ------------------------------------------------------------------------ |
|
|
1169
|
+
| `.tooltip` | Wrapper around a trigger and a bubble. Paints nothing itself. |
|
|
1170
|
+
| `.tooltip-bubble` | The message. A real element, styled like any other surface. |
|
|
1171
|
+
| `.tooltip-icon` | Circular icon-style trigger, for a message with nothing else to hang on. |
|
|
1172
|
+
| `[data-tooltip-position="top"]` | Positions the bubble above the trigger, on `.tooltip`. |
|
|
1173
|
+
| `[data-tooltip-position="bottom"]` | Positions the bubble below the trigger, on `.tooltip`. |
|
|
1174
|
+
| `[data-tooltip-position="left"]` | Positions the bubble left of the trigger, on `.tooltip`. |
|
|
1175
|
+
| `[data-tooltip-position="right"]` | Positions the bubble right of the trigger, on `.tooltip`. |
|
|
1176
|
+
| No `data-tooltip-position` | Defaults to top placement. |
|
|
1177
|
+
| `[data-state="open"]` | Shows the bubble without hover, on `.tooltip`. |
|
|
1178
|
+
| `.tooltip-bubble.raised` / `.floating` | Adds one or two units of depth. The bubble is flat by default. |
|
|
1179
|
+
|
|
1180
|
+
A tooltip is composition, not a single class: `.tooltip` wraps a trigger element and a `.tooltip-bubble`, and shows the bubble on hover or focus within the wrapper. This replaced a `::after` pseudo-element that carried the message as `content: attr(data-tooltip)` — CSS generated content is not reliably exposed to assistive tech, and cannot be the target of an `aria-describedby`, since that needs a real element with a real `id`. A real bubble can be. Wire the association yourself: give the bubble an `id`, give the trigger `aria-describedby` pointing at it, and put `role="tooltip"` on the bubble.
|
|
1099
1181
|
|
|
1100
|
-
A tooltip bubble's fill, ground color, and floor behavior are covered in [Composing → Presentation](docs/usage/composing.md#presentation), and its material follows whichever [aesthetic](docs/usage/aesthetics.md) is in scope,
|
|
1182
|
+
A tooltip bubble's fill, ground color, and floor behavior are covered in [Composing → Presentation](docs/usage/composing.md#presentation), and its material follows whichever [aesthetic](docs/usage/aesthetics.md) is in scope. Intent, presentation, aesthetic, and elevation classes go on `.tooltip-bubble` — the element that actually paints — not on `.tooltip`, the same rule every component in this package follows: put the class on the element that shows it.
|
|
1101
1183
|
|
|
1102
|
-
The bubble rests flat. It draws no shadow of its own on a plain page, and zeroes an aesthetic's part-based depth (such as `.neobrutalism`'s slab) the same way `.flat` does elsewhere. Add [`.raised` or `.floating`](docs/usage/composing.md#elevation) to lift it — `.tooltip.floating` is the pre-0.1.0 look. An aesthetic whose shadow is a complete value, like `.glass`, still reaches the bubble as it reaches any surface.
|
|
1184
|
+
The bubble rests flat. It draws no shadow of its own on a plain page, and zeroes an aesthetic's part-based depth (such as `.neobrutalism`'s slab) the same way `.flat` does elsewhere. Add [`.raised` or `.floating`](docs/usage/composing.md#elevation) to lift it, on the bubble itself — `.tooltip-bubble.floating` is the pre-0.1.0 look. An aesthetic whose shadow is a complete value, like `.glass`, still reaches the bubble as it reaches any surface.
|
|
1103
1185
|
|
|
1104
|
-
Intent works on a tooltip the same way it works on a filled component: `.tooltip.primary` fills the bubble with the primary color, black text on a light page and white on a dark one; `.tooltip.destructive` is a red bubble with the same logic. With no intent, the bubble uses `--color-tooltip`, a plate chosen per theme rather than derived from an intent
|
|
1186
|
+
Intent works on a tooltip bubble the same way it works on a filled component: `.tooltip-bubble.primary` fills the bubble with the primary color, black text on a light page and white on a dark one; `.tooltip-bubble.destructive` is a red bubble with the same logic. With no intent, the bubble uses `--color-tooltip`, a plate chosen per theme rather than derived from an intent; see [Composing → Presentation](docs/usage/composing.md#presentation) for why.
|
|
1105
1187
|
|
|
1106
1188
|
## Example
|
|
1107
1189
|
|
|
1108
1190
|
```html
|
|
1109
|
-
<
|
|
1110
|
-
<button
|
|
1111
|
-
<
|
|
1112
|
-
|
|
1113
|
-
|
|
1191
|
+
<span class="tooltip">
|
|
1192
|
+
<button aria-describedby="status-bubble">Status</button>
|
|
1193
|
+
<span class="tooltip-bubble" role="tooltip" id="status-bubble">Saved 2 minutes ago</span>
|
|
1194
|
+
</span>
|
|
1195
|
+
|
|
1196
|
+
<span class="tooltip" data-tooltip-position="bottom">
|
|
1197
|
+
<button aria-describedby="docs-bubble">Docs</button>
|
|
1198
|
+
<span class="tooltip-bubble" role="tooltip" id="docs-bubble">Opens in a new tab</span>
|
|
1199
|
+
</span>
|
|
1200
|
+
|
|
1201
|
+
<span class="tooltip">
|
|
1202
|
+
<button class="btn primary" aria-describedby="deploy-bubble">Deploy</button>
|
|
1203
|
+
<span class="tooltip-bubble raised primary" role="tooltip" id="deploy-bubble">Primary intent, lifted a step</span>
|
|
1204
|
+
</span>
|
|
1205
|
+
|
|
1206
|
+
<span class="tooltip" data-state="open">
|
|
1207
|
+
<span aria-describedby="pinned-bubble">Pinned</span>
|
|
1208
|
+
<span class="tooltip-bubble" role="tooltip" id="pinned-bubble">Always visible, no hover needed</span>
|
|
1209
|
+
</span>
|
|
1210
|
+
|
|
1211
|
+
<span class="tooltip" data-tooltip-position="right">
|
|
1212
|
+
<span class="tooltip-icon" tabindex="0" aria-label="More details" aria-describedby="icon-bubble">?</span>
|
|
1213
|
+
<span class="tooltip-bubble" role="tooltip" id="icon-bubble">More details</span>
|
|
1214
|
+
</span>
|
|
1114
1215
|
```
|
|
1115
1216
|
|
|
1116
|
-
The first two show plain
|
|
1217
|
+
The first two show plain triggers with default (top) and explicit bottom placement. The third combines an intent with `.raised` on the bubble to show they compose freely — the intent decides the bubble's fill, `.raised` decides whether it casts depth. The fourth uses `data-state="open"` on the wrapper to keep a tooltip visible without a hover or focus interaction, useful for a tooltip that should be demonstrated or tested without simulating pointer input. The fifth uses `.tooltip-icon` as the trigger, for a message with no other element to hang it on.
|
|
1218
|
+
|
|
1219
|
+
`.tooltip` composes with any trigger: wrap an existing `.btn` or `.badge` in it and both keep their own look, gaining a bubble beside them. `.tooltip-icon` is only the circular chip for the case where there is nothing else to be the trigger.
|
|
1220
|
+
|
|
1221
|
+
Showing and hiding the bubble is CSS-only — `:hover` and `:focus-within` on `.tooltip` — but that is presentational. It is not a complete accessible tooltip implementation by itself: provide the `id`/`aria-describedby`/`role="tooltip"` association outside the CSS; see [Accessibility](docs/accessibility.md).
|
|
1117
1222
|
|
|
1118
|
-
|
|
1223
|
+
Next: [Content and layout](docs/usage/content-and-layout.md) covers layout helpers, content chips and tables, surfaces, and typography utilities.
|
|
1119
1224
|
|
|
1120
1225
|
<!-- Source: docs/usage/content-and-layout.md -->
|
|
1121
1226
|
|
|
@@ -1133,6 +1238,7 @@ Layout helpers use the shared `--layout-gap` token. `.tight` sets it to `0.5rem`
|
|
|
1133
1238
|
- `.section` adds responsive block padding and an inline gutter.
|
|
1134
1239
|
- `.section-content` centers content at `--container-max`; `.narrow` and `.wide` select the corresponding container tokens.
|
|
1135
1240
|
- `.divider` is horizontal; `.vertical` makes it self-stretch vertically. It takes intent, but presentation classes do not affect it.
|
|
1241
|
+
- `.visually-hidden` hides content visually while keeping it in the accessibility tree, using the standard clip-based recipe. Not named `.sr-only`: on a `/tw` entry the consumer's own Tailwind build already ships a real `.sr-only`, and a same-named utility of this package's own would silently collide with it in the cascade — the same reason `.data-table` is not named `.table`.
|
|
1136
1242
|
|
|
1137
1243
|
The removed `--layout-stack-gap` and `--layout-cluster-gap` tokens have no compatibility aliases.
|
|
1138
1244
|
|
|
@@ -1220,7 +1326,9 @@ This is the one place intent cascades, because a table's rows are parts of the t
|
|
|
1220
1326
|
|
|
1221
1327
|
On `.data-table` it removes the header plate along with the body tint, so `.data-table.ghost` is boundaries and type alone. The component rests at `soft`, so that is something to ask for rather than the default.
|
|
1222
1328
|
|
|
1223
|
-
|
|
1329
|
+
`.soft` rests untinted throughout -- `--color-foreground`, no intent color mixed in, for every intent, including the resting default, on the head plate and the table's own base fill alike. A table has no hover or interaction state to reveal a tint into the way `.card.soft` does on `.interactive`/`.hoverable`, so unlike a card there is no state where a `.data-table.success.soft` reveals green; only the boundary and rule lines still read the intent.
|
|
1330
|
+
|
|
1331
|
+
On `.kbd`, `.code`, and `.pre` it is **not supported**. All three rest on `--intent-subtle`, and that ground draws the plate whatever the fill says — so `.ghost` took the fill away and changed nothing visible. Worse, the ground alone is a near-page tint: a ghost chip measured the same `#e5e5e5` for neutral and primary on the light page, and `#f5f5f5` at 1.04:1 against it for secondary. A named intent rests at `.soft` instead, which puts a real `12%` of the intent over the ground and separates it from the others; the no-named-intent case reads no differently from `.ghost` either way, so it rests at the ground alone rather than adding an unmotivated wash of its own.
|
|
1224
1332
|
|
|
1225
1333
|
Use `.table-wrap` around `.data-table` when table width may exceed its container:
|
|
1226
1334
|
|
|
@@ -1259,15 +1367,19 @@ Use `.table-wrap` around `.data-table` when table width may exceed its container
|
|
|
1259
1367
|
|
|
1260
1368
|
Both read intent, [presentation](docs/usage/composing.md#presentation), and [material tokens](docs/usage/customizing.md#material-tokens). A plain `.card` is a neutral bordered container; only an explicit presentation tints it. Resting at elevation `1` is not a visible shadow by itself — nothing draws depth until an [aesthetic](docs/usage/aesthetics.md) supplies shadow geometry or `.elevation-sm`/`.elevation-md` asks for it; see [Composing → Elevation](docs/usage/composing.md#elevation).
|
|
1261
1369
|
|
|
1370
|
+
`.card.soft` with no named intent is untinted at rest — a plain `--color-foreground` plate, no ink mixed in, marking a different container without drawing attention to it. The intent tint and a `--color-surface` ground only appear together, on `:hover`, and only on `.card.interactive`/`.card.hoverable`: a plain `.card.soft` with neither has no hover state to reveal into and stays at the quiet rest. A named intent (`.card.soft.success`, `.card.soft.destructive`, and so on) keeps its ordinary tint at rest instead, and deepens further on hover the same way any other filled surface does — only the no-intent case is untinted, since removing an intent's own color would silence it rather than quiet it. `.solid` and `.ghost` are unaffected either way.
|
|
1371
|
+
|
|
1372
|
+
`.panel` with no named intent behaves the same way, permanently rather than on hover: a plain `.panel` rests at the same untinted `--color-foreground` plate a neutral `.card.soft` does, because a panel has no hover state to reveal a tint into. `.panel.primary`, `.panel.destructive`, and every other named intent keep their ordinary tint. `.kbd`, `.code`, and `.pre` follow the same no-named-intent rule for their own fill, without moving off their `--intent-subtle` ground.
|
|
1373
|
+
|
|
1262
1374
|
On the base look, `.card.interactive` scales down slightly on press (`--ui-active-transform`, `scale(0.97)`); an [aesthetic](docs/usage/aesthetics.md) substitutes its own press gesture, and `prefers-reduced-motion` turns it off.
|
|
1263
1375
|
|
|
1264
1376
|
`.interactive` is the pair `.hoverable` (hover response only, no pointer cursor) and `.pressable` (press response and pointer cursor) applied together. Take one half for a card that answers a single gesture: a card that warms on hover but holds its own action in a nested button takes `.hoverable`, so pressing that button does not dip the whole card.
|
|
1265
1377
|
|
|
1266
1378
|
```html
|
|
1267
1379
|
<article class="card">Neutral card</article>
|
|
1268
|
-
<article class="card success
|
|
1380
|
+
<article class="card success solid">Filled success card</article>
|
|
1269
1381
|
<article class="card primary ghost edged">Intent border, no fill</article>
|
|
1270
|
-
<a class="card interactive" href="/package">
|
|
1382
|
+
<a class="card success soft interactive" href="/package">Success-tinted at rest; deepens on hover</a>
|
|
1271
1383
|
<article class="card hoverable">Warms on hover; the action is a nested button</article>
|
|
1272
1384
|
<aside class="panel">Flush panel</aside>
|
|
1273
1385
|
```
|
|
@@ -1553,23 +1665,29 @@ This package provides CSS hooks for accessible states. It does not provide seman
|
|
|
1553
1665
|
|
|
1554
1666
|
### Focus and state
|
|
1555
1667
|
|
|
1556
|
-
| Feature | Behavior
|
|
1557
|
-
| ------------------ |
|
|
1558
|
-
| `:focus-visible` | Global focus-visible outline using `--focus-ring`, `--focus-ring-width`, and `--focus-ring-offset`.
|
|
1559
|
-
| Form control focus | `.ipt`, `.textarea`, and `.select` draw the global focus ring and move their line to the intent color.
|
|
1560
|
-
| Invalid controls | `[aria-invalid="true"]` applies destructive border/focus color on form controls.
|
|
1561
|
-
| Disabled controls | `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]`, and `.disabled` apply disabled cursor/opacity where supported.
|
|
1562
|
-
| Open state | `[data-state="open"]` styles supported surfaces and tooltips.
|
|
1668
|
+
| Feature | Behavior |
|
|
1669
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1670
|
+
| `:focus-visible` | Global focus-visible outline using `--focus-ring`, `--focus-ring-width`, and `--focus-ring-offset`. |
|
|
1671
|
+
| Form control focus | `.ipt`, `.textarea`, and `.select` draw the global focus ring and move their line to the intent color. |
|
|
1672
|
+
| Invalid controls | `[aria-invalid="true"]` applies destructive border/focus color on form controls. |
|
|
1673
|
+
| Disabled controls | `[disabled]`, `[aria-disabled="true"]`, `[data-disabled]`, and `.disabled` apply disabled cursor/opacity where supported. |
|
|
1674
|
+
| Open state | `[data-state="open"]` styles supported surfaces and tooltips. |
|
|
1675
|
+
| Native `<dialog>` | `dialog:not([open])` and a centered `dialog[open]` are restored in author origin, so `.card` composed onto a `<dialog>` (the closest primitive the package ships) does not render open on load or lose its centering to another rule. Focus trapping, escape handling, and inert-background behavior stay outside this package. |
|
|
1676
|
+
|
|
1677
|
+
### Visually hidden content
|
|
1678
|
+
|
|
1679
|
+
`.visually-hidden` hides content from sighted users while keeping it in the accessibility tree, using the standard clip-based recipe. It ships wherever the layout utilities do -- `.`, `./native`, `./tw`, `./tw/native`, and `./tw/utilities` -- including the vanilla entries with no Tailwind utility layer of their own; it is not part of `./components` or the narrower `./tw/components`/`./tw/*` component slices. See [Content and layout](docs/usage/content-and-layout.md).
|
|
1563
1680
|
|
|
1564
1681
|
### Control boundaries
|
|
1565
1682
|
|
|
1566
|
-
| Feature | Behavior
|
|
1567
|
-
| -------------------- |
|
|
1568
|
-
| Control boundaries | Text controls rest at `--_line-rest` of their intent, under 3:1 by choice; hover and focus take the tone whole.
|
|
1569
|
-
| Toggle boundaries | `.checkbox`, `.radio`, and `.switch` draw their line whole, because it is the only thing marking an unchecked box.
|
|
1570
|
-
| Cascaded `.edgeless` | Floored on every text control: a container cannot erase the line of a field or toggle nobody classed.
|
|
1571
|
-
| Own `.edgeless` | On the element, `.ipt`, `.textarea`, `.select`, and `.switch` do drop their line and do not meet 1.4.11 at rest.
|
|
1572
|
-
| `.checkbox`/`.radio` | Never drop their line, on the element or from a container. `.edgeless` on them is unsupported, not merely discouraged.
|
|
1683
|
+
| Feature | Behavior |
|
|
1684
|
+
| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1685
|
+
| Control boundaries | Text controls rest at `--_line-rest` of their intent, under 3:1 by choice; hover and focus take the tone whole. |
|
|
1686
|
+
| Toggle boundaries | `.checkbox`, `.radio`, and `.switch` draw their line whole, because it is the only thing marking an unchecked box. |
|
|
1687
|
+
| Cascaded `.edgeless` | Floored on every text control: a container cannot erase the line of a field or toggle nobody classed. |
|
|
1688
|
+
| Own `.edgeless` | On the element, `.ipt`, `.textarea`, `.select`, and `.switch` do drop their line and do not meet 1.4.11 at rest, except `.switch.ghost`: `.ghost` already asks for zero fill, so dropping the line too would leave no boundary at all, and `.switch.ghost.edgeless` keeps it up instead. |
|
|
1689
|
+
| `.checkbox`/`.radio` | Never drop their line, on the element or from a container. `.edgeless` on them is unsupported, not merely discouraged. |
|
|
1690
|
+
| Progress track | Known gap. `.progress`'s registered default, `.soft.edgeless`, does not meet 1.4.11 at rest -- measured ~1.1-1.3:1 against the page in both themes and across intents, down from the ~3.6-4.3:1 the prior fixed-neutral track cleared. An `.edged` presentation restores a boundary through the line for most intents (`primary.soft.edged` ~15:1), but not every one clears 3:1 there either (`warning.soft.edged` ~2.7:1). A consumer whose track must meet 1.4.11 should choose an `.edged` presentation and verify the intents they use, until this is resolved. |
|
|
1573
1691
|
|
|
1574
1692
|
### Motion and transparency
|
|
1575
1693
|
|
|
@@ -1583,13 +1701,14 @@ The loader fallback is embedded in loader CSS, so focused imports that include l
|
|
|
1583
1701
|
|
|
1584
1702
|
### Color and contrast
|
|
1585
1703
|
|
|
1586
|
-
| Feature | Behavior
|
|
1587
|
-
| ------------------ |
|
|
1588
|
-
| Filled labels | Every intent's `.solid` label meets 1.4.3 (4.5:1) as normal text, in both themes. Lowest is success at 5.14:1.
|
|
1589
|
-
| Warning ink | `--color-warning-contrast` is the near-black tone, not the page tone. Amber dark enough to carry white is not amber.
|
|
1590
|
-
| Partial fills | Contrast ink appears only past a half fill, so a capped plate keeps `--intent-strong` rather than walking toward the page.
|
|
1591
|
-
| Intent hue in dark | `--intent-strong` is the `-300` shade in dark, so a soft or ghost label keeps its own hue instead of reading white.
|
|
1592
|
-
| Toggle marks | A checked mark meets 3:1 as a state indicator on every plate its fill class can paint, in both themes.
|
|
1704
|
+
| Feature | Behavior |
|
|
1705
|
+
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1706
|
+
| Filled labels | Every intent's `.solid` label meets 1.4.3 (4.5:1) as normal text, in both themes. Lowest is success at 5.14:1. |
|
|
1707
|
+
| Warning ink | `--color-warning-contrast` is the near-black tone, not the page tone. Amber dark enough to carry white is not amber. |
|
|
1708
|
+
| Partial fills | Contrast ink appears only past a half fill, so a capped plate keeps `--intent-strong` rather than walking toward the page. |
|
|
1709
|
+
| Intent hue in dark | `--intent-strong` is the `-300` shade in dark, so a soft or ghost label keeps its own hue instead of reading white. |
|
|
1710
|
+
| Toggle marks | A checked mark meets 3:1 as a state indicator on every plate its fill class can paint, in both themes. |
|
|
1711
|
+
| Soft-edged border | `.alert`, `.panel`, `.badge`, `.kbd`, and `.code`/`.pre`'s `.edged` border blends toward the component's own fill; at `.soft`'s 12% fill this misses 3:1 against the fill for some intents, and which ones misses depends on the theme. Known, not fixed -- `--color-border`/`--intent-border` is a shared blend seam every one of them reads, so a targeted fix would need to touch it for one component without moving it for the rest. |
|
|
1593
1712
|
|
|
1594
1713
|
### Forced colors and direction
|
|
1595
1714
|
|
|
@@ -1612,7 +1731,7 @@ Use semantic HTML and behavior appropriate for the component.
|
|
|
1612
1731
|
| Forms | Use labels, `type`, validation logic, `aria-describedby`, and error message relationships. |
|
|
1613
1732
|
| Alerts | Add `role="status"` or `role="alert"` based on announcement urgency. |
|
|
1614
1733
|
| Toasts | Add live-region behavior, dismissal behavior, focus rules, and pause/timeout logic when needed. |
|
|
1615
|
-
| Tooltips |
|
|
1734
|
+
| Tooltips | Give the bubble a real `id` and `role="tooltip"`, and reference it from the trigger's `aria-describedby`. |
|
|
1616
1735
|
| Progress | Use semantic progress elements or ARIA values when numeric progress must be announced. |
|
|
1617
1736
|
| Skeletons/loaders | Mark decorative loading visuals with `aria-hidden="true"` and expose loading state elsewhere when needed. |
|
|
1618
1737
|
| Popovers/modals | Provide focus trapping, escape handling, inert background behavior, labels, and roles outside this CSS package. |
|
|
@@ -1628,11 +1747,67 @@ Prefer native attributes first:
|
|
|
1628
1747
|
Use ARIA or data attributes when native attributes are not available for the element or library:
|
|
1629
1748
|
|
|
1630
1749
|
```html
|
|
1631
|
-
<a class="btn secondary" aria-disabled="true">Unavailable</a>
|
|
1750
|
+
<a class="btn secondary" aria-disabled="true">Unavailable</a>
|
|
1751
|
+
<span class="tooltip" data-state="open">
|
|
1752
|
+
<span class="tooltip-icon" tabindex="0" aria-label="More details" aria-describedby="more-details-bubble">?</span>
|
|
1753
|
+
<span class="tooltip-bubble" role="tooltip" id="more-details-bubble">More details</span>
|
|
1754
|
+
</span>
|
|
1632
1755
|
```
|
|
1633
1756
|
|
|
1634
1757
|
`aria-disabled="true"` communicates disabled state but does not prevent activation. JavaScript or element choice must prevent activation when required.
|
|
1635
1758
|
|
|
1759
|
+
<!-- Source: docs/changelog/0.3.0.md -->
|
|
1760
|
+
|
|
1761
|
+
# 0.3.0
|
|
1762
|
+
|
|
1763
|
+
## Added
|
|
1764
|
+
|
|
1765
|
+
- **`@codenhub/styles/palette`**, a generated, self-contained stylesheet of every `intent x presentation` cell's composed colors -- `--palette-<intent>-<presentation>[-<ground>]-<slot>[-hover]` custom properties, already-composited light and dark values, for a consumer that cannot take this package as a build-time dependency (an optional peer, for example) but wants to look consistent with it. Not a replacement for the components themselves when Tailwind is available. Also includes `--palette-border`/`--palette-surface`/`--palette-text`, flat values for the three neutral surface tokens that sit outside the intent x presentation cross. See [Customizing → Generated palette](docs/usage/customizing.md#generated-palette).
|
|
1766
|
+
|
|
1767
|
+
## Changed
|
|
1768
|
+
|
|
1769
|
+
- **`.progress`'s track reads presentation.** It used to be intent-only, like `.loader`, `.skeleton`, and `.divider`; now it composes a fill and an edge like `.quote` does, while the moving value fill stays `--intent-color` at full strength, unaffected by presentation. The registered default is `.soft.edgeless`, chosen to stay closest to the previous fixed-neutral look, but it renders differently than before: it now tints by intent instead of a fixed grey, and every existing `.progress` gets that new look with no markup change. `.solid` is unsupported on the track (a fully-filled track and the value fill it carries would become the same color); `.ghost.edgeless` renders but is discouraged, since it leaves the track with neither fill nor line.
|
|
1770
|
+
- **Known issue: the new `.progress.soft.edgeless` default does not meet WCAG 1.4.11's 3:1 non-text contrast floor.** Measured at roughly 1.1-1.3:1 against the page, in both themes and across intents -- down from the ~3.6-4.3:1 the prior fixed-neutral track cleared. An `.edged` presentation restores a boundary through the line for most intents, but not all of them clear 3:1 there either (`warning.soft.edged` measured ~2.7:1). A consumer whose track must meet 1.4.11 should choose an `.edged` presentation and verify the intents they use. See [Accessibility](docs/accessibility.md#control-boundaries).
|
|
1771
|
+
|
|
1772
|
+
<!-- Source: docs/changelog/0.2.0.md -->
|
|
1773
|
+
|
|
1774
|
+
# 0.2.0
|
|
1775
|
+
|
|
1776
|
+
Fixes from a stress-test pass composing real screens (`app-shell`, `form`, `settings`) against the published `0.1.x` model. Several change default token values rather than adding new surface, so this lands as one minor rather than a run of patches -- see [Changed](#changed) for what an upgrader needs to check.
|
|
1777
|
+
|
|
1778
|
+
## Added
|
|
1779
|
+
|
|
1780
|
+
- `.visually-hidden`, a utility that hides content from sighted users while keeping it in the accessibility tree. Ships wherever the layout utilities do (`.`, `/native`, `/tw`, `/tw/native`, `/tw/utilities`), including the vanilla entries that have no Tailwind utility layer of their own; not part of `/components` or the narrower `/tw/*` component slices.
|
|
1781
|
+
- `.tooltip-bubble`, a real element for a tooltip's message that can carry its own `id` and be referenced by `aria-describedby`.
|
|
1782
|
+
- `.alert-icon`, sizing and alignment for a consumer-supplied icon element dropped into `.alert` -- any inline SVG, `<img>`, or icon-set class works, not just a built-in glyph.
|
|
1783
|
+
- `dialog:not([open])`/`dialog[open]` restated in author origin, so `.card` composed onto a `<dialog>` hides until opened and centers when shown, without a dialog primitive of its own.
|
|
1784
|
+
- `.icon` added to `registry.json` under `modifiers.shape`. The button shape modifier was already shipped and documented; it was missing only from the machine-readable contract.
|
|
1785
|
+
- `.switch.ghost.edgeless` now renders a track instead of disappearing.
|
|
1786
|
+
|
|
1787
|
+
## Changed
|
|
1788
|
+
|
|
1789
|
+
- **Tooltips are composition, not a single class.** `.tooltip` now wraps a trigger and a `.tooltip-bubble`, one more DOM node than before. Intent, presentation, aesthetic, and elevation classes move to `.tooltip-bubble`, the element that actually paints.
|
|
1790
|
+
- **`.checkbox`, `.radio`, and `.switch` default to `.soft` instead of `.solid`, and their checked state now composes one plate regardless of presentation.** Unchecked, `.ghost` (0% fill), `.soft` (12%), and `.solid` now read as three distinct plates instead of three shades of one wash; `.ghost` is a supported look on every toggle for the first time.
|
|
1791
|
+
- **A neutral (no named intent) `.card.soft`, `data-table`, `.panel`, or `.alert` rests untinted instead of mixing a 12% ink wash.** A named intent (`.card.soft.destructive`, `.alert.success`, and so on) is unaffected and keeps its ordinary tint; `.badge` is unaffected either way, since it has no border to fall back on if untinted.
|
|
1792
|
+
- **Elevation no longer cascades onto descendants.** `--ui-elevation`, `--elevation-y`, and `--elevation-blur` are registered non-inheriting, so an elevation class lifts only the element it is written on. A container that used to lift a whole unclassed subtree in one class now needs an elevation class on each element that wants depth.
|
|
1793
|
+
- `--input-group-gap` replaces a hardcoded gap in `.input-group`; unchanged by default for icon compositions, and settable to `0` on a text affix for a flush look.
|
|
1794
|
+
- `.loader`'s sizes step down from 24/32/40px to 20/28/36px for `.sm`/default/`.lg`.
|
|
1795
|
+
- A `/tw` consumer's own `dark:` utilities now also fire under `prefers-color-scheme: dark` when no explicit theme selector is present, matching how the token palette already follows the OS default.
|
|
1796
|
+
|
|
1797
|
+
## Removed
|
|
1798
|
+
|
|
1799
|
+
- **`.btn.loading`.** A loading button is now a disabled `.btn` composed with a real `.loader` child instead of a hardcoded `::after` spinner -- any loader variant works, not just one circular spinner.
|
|
1800
|
+
- **`.alert.icon`'s hardcoded per-intent SVG mask.** Replaced by `.alert-icon` (above), sized for a consumer-supplied icon element.
|
|
1801
|
+
|
|
1802
|
+
## Fixed
|
|
1803
|
+
|
|
1804
|
+
- `.icon` buttons kept the wrong `min-width` floor under `.compact`/`.spacious`; all three padding tiers now size `.icon` to its own padding.
|
|
1805
|
+
- `.chunky-tile`'s offset shadow read as a solid fill behind a small `.icon.ghost` button; halved for that combination.
|
|
1806
|
+
- Table row and boundary rules stayed a 1px hairline under `.chunky-tile`'s 2px frame; both now track the same width token the frame uses.
|
|
1807
|
+
- `.progress`'s track failed WCAG 1.4.11 in both themes; `--progress-surface` now mixes enough ink to clear 3:1.
|
|
1808
|
+
- `.card.soft.edged`'s border was nearly invisible against its own fill in light theme; `--color-border`'s light value moved one step darker.
|
|
1809
|
+
- A badge nested inside an untinted `data-table` lost its own fill; box now restates its fill cap on every element that uses it, so a nested badge, button, or chip no longer inherits its container's cap.
|
|
1810
|
+
|
|
1636
1811
|
<!-- Source: docs/changelog/0.1.1.md -->
|
|
1637
1812
|
|
|
1638
1813
|
# 0.1.1
|