@sableui/react 0.15.0 → 0.16.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 (134) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/bin/sable-lint.mjs +5 -1
  3. package/dist/{_contract-CkqXDVww.d.ts → _contract-CDa9-NdB.d.ts} +47 -1
  4. package/dist/blocks/wallet-block.js +2 -2
  5. package/dist/components/app-shell/app-shell-frame.js +16 -0
  6. package/dist/components/app-shell/app-shell-main.js +12 -0
  7. package/dist/components/app-shell/app-shell.js +28 -4
  8. package/dist/components/app-shell/app-shell.meta.js +88 -4
  9. package/dist/components/area-chart/area-chart.meta.js +6 -1
  10. package/dist/components/attachment/attachment.meta.js +2 -1
  11. package/dist/components/attachment/attachment.variants.js +1 -1
  12. package/dist/components/badge/badge.meta.js +8 -1
  13. package/dist/components/badge/badge.variants.js +7 -7
  14. package/dist/components/banner/banner.meta.js +6 -1
  15. package/dist/components/banner/banner.variants.js +5 -5
  16. package/dist/components/bar-chart/bar-chart.meta.js +6 -1
  17. package/dist/components/caret-menu/caret-menu.js +5 -6
  18. package/dist/components/chat-composer/chat-composer.meta.js +2 -2
  19. package/dist/components/chat-composer/chat-composer.variants.js +1 -1
  20. package/dist/components/color-field/color-field.js +6 -8
  21. package/dist/components/combobox/combobox.js +27 -5
  22. package/dist/components/combobox/combobox.meta.js +10 -2
  23. package/dist/components/command-menu/command-menu.js +14 -3
  24. package/dist/components/command-menu/command-menu.meta.js +9 -1
  25. package/dist/components/date-picker/date-picker.meta.js +2 -2
  26. package/dist/components/feedback/feedback.js +1 -1
  27. package/dist/components/feedback/feedback.meta.js +2 -1
  28. package/dist/components/gradient-art/gradient-art.js +8 -5
  29. package/dist/components/gradient-art/gradient-art.meta.js +35 -2
  30. package/dist/components/input/input.meta.js +11 -3
  31. package/dist/components/input-group/input-group.js +1 -1
  32. package/dist/components/input-group/input-group.meta.js +1 -1
  33. package/dist/components/line-chart/line-chart.meta.js +6 -1
  34. package/dist/components/menu/menu.js +1 -1
  35. package/dist/components/meter/meter.js +53 -7
  36. package/dist/components/meter/meter.meta.js +51 -4
  37. package/dist/components/meter/meter.variants.js +43 -7
  38. package/dist/components/note/note.meta.js +5 -1
  39. package/dist/components/note/note.variants.js +4 -4
  40. package/dist/components/number-field/number-field.meta.js +2 -2
  41. package/dist/components/otp-field/otp-field.meta.js +2 -2
  42. package/dist/components/panel-group/panel-group.js +5 -8
  43. package/dist/components/pie-chart/pie-chart.meta.js +6 -1
  44. package/dist/components/radar-chart/radar-chart.meta.js +6 -1
  45. package/dist/components/radial-chart/radial-chart.meta.js +6 -1
  46. package/dist/components/search-input/search-input.meta.js +11 -3
  47. package/dist/components/segmented-bar/segmented-bar.meta.js +8 -1
  48. package/dist/components/segmented-bar/segmented-bar.variants.js +7 -7
  49. package/dist/components/select/select.js +1 -1
  50. package/dist/components/select/select.meta.js +2 -2
  51. package/dist/components/sheet/sheet.js +5 -4
  52. package/dist/components/sidebar/sidebar.js +16 -1
  53. package/dist/components/sidebar/sidebar.meta.js +7 -2
  54. package/dist/components/sparkline/sparkline.meta.js +8 -1
  55. package/dist/components/status-dot/status-dot.meta.js +8 -1
  56. package/dist/components/status-dot/status-dot.variants.js +7 -7
  57. package/dist/components/stepper/stepper.meta.js +2 -1
  58. package/dist/components/stepper/stepper.variants.js +1 -1
  59. package/dist/components/table/table.meta.js +4 -4
  60. package/dist/components/textarea/textarea.js +5 -0
  61. package/dist/components/textarea/textarea.meta.js +3 -3
  62. package/dist/file-hashes.json +91 -88
  63. package/dist/foundations/accessibility.js +5 -0
  64. package/dist/index.d.ts +132 -24
  65. package/dist/index.js +9 -1
  66. package/dist/lib/chart/types.js +8 -8
  67. package/dist/lib/chart/use-arrival.js +5 -4
  68. package/dist/lib/field-shell.js +1 -1
  69. package/dist/lib/highlight-glide.js +6 -6
  70. package/dist/lib/scrub-gesture.js +6 -6
  71. package/dist/lib/use-caret-trigger.js +11 -18
  72. package/dist/lib/use-match-highlight.js +69 -0
  73. package/dist/lib/use-smooth-wheel.js +10 -8
  74. package/dist/presets/index.d.ts +13 -2
  75. package/dist/presets/index.js +17 -1
  76. package/dist/styles.css +100 -0
  77. package/dist/{theme-provider-CdSicAad.d.ts → theme-provider-BydfM5Z0.d.ts} +1 -1
  78. package/dist/themes/_contract.js +14 -0
  79. package/dist/themes/atelier/atelier.css +18 -0
  80. package/dist/themes/atelier/index.d.ts +1 -1
  81. package/dist/themes/contrast.js +48 -1
  82. package/dist/themes/ember/ember.css +18 -0
  83. package/dist/themes/ember/index.d.ts +1 -1
  84. package/dist/themes/index.d.ts +2 -2
  85. package/dist/themes/meridian/index.d.ts +1 -1
  86. package/dist/themes/meridian/meridian.css +18 -0
  87. package/dist/themes/poppy/index.d.ts +1 -1
  88. package/dist/themes/poppy/poppy.css +18 -0
  89. package/dist/themes/prism/index.d.ts +1 -1
  90. package/dist/themes/prism/prism.css +18 -0
  91. package/dist/themes/sable/index.d.ts +1 -1
  92. package/dist/themes/sable/sable.css +18 -0
  93. package/dist/themes/verdant/index.d.ts +1 -1
  94. package/dist/themes/verdant/verdant.css +18 -0
  95. package/llms/blocks/wallet-block.md +5 -4
  96. package/llms/components/app-shell.md +72 -3
  97. package/llms/components/area-chart.md +1 -1
  98. package/llms/components/attachment.md +1 -1
  99. package/llms/components/badge.md +1 -1
  100. package/llms/components/banner.md +1 -1
  101. package/llms/components/bar-chart.md +1 -1
  102. package/llms/components/chat-composer.md +1 -1
  103. package/llms/components/combobox.md +2 -1
  104. package/llms/components/command-menu.md +3 -2
  105. package/llms/components/date-picker.md +1 -1
  106. package/llms/components/feedback.md +1 -1
  107. package/llms/components/gradient-art.md +28 -1
  108. package/llms/components/input-group.md +1 -1
  109. package/llms/components/input.md +4 -2
  110. package/llms/components/line-chart.md +1 -1
  111. package/llms/components/meter.md +33 -4
  112. package/llms/components/note.md +1 -1
  113. package/llms/components/number-field.md +1 -1
  114. package/llms/components/otp-field.md +1 -1
  115. package/llms/components/pie-chart.md +1 -1
  116. package/llms/components/radar-chart.md +1 -1
  117. package/llms/components/radial-chart.md +1 -1
  118. package/llms/components/search-input.md +20 -2
  119. package/llms/components/segmented-bar.md +1 -1
  120. package/llms/components/select.md +1 -1
  121. package/llms/components/sidebar.md +3 -2
  122. package/llms/components/sparkline.md +1 -1
  123. package/llms/components/status-dot.md +1 -1
  124. package/llms/components/stepper.md +1 -1
  125. package/llms/components/table.md +2 -2
  126. package/llms/components/textarea.md +2 -2
  127. package/llms/foundations/accessibility.md +1 -0
  128. package/llms/reference/token-intent.md +22 -2
  129. package/llms/reference/utilities.md +5 -2
  130. package/llms-full.txt +222 -49
  131. package/llms.txt +2 -2
  132. package/package.json +1 -1
  133. package/registry.json +396 -57
  134. package/scripts/lint-rules.mjs +6 -3
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Status:** stable
4
4
 
5
- Styled search field with a leading magnifier and a clearable X. Soft-square (rounded-sm), card surface, 2px focus ring; the clear button appears only when there is a value.
5
+ Styled search field with a leading magnifier and a clearable X. Soft-square (rounded-sm), the theme's field surface, 2px focus ring; the clear button appears only when there is a value.
6
6
 
7
7
  **Keywords:** search, input, filter, query
8
8
 
@@ -15,6 +15,7 @@ Use SearchInput for text queries that filter or search a known content set.
15
15
  - Do: Use a clear placeholder that names the content being searched.
16
16
  - Do: Provide a visible clear affordance when query state persists.
17
17
  - Do: When a button stands in for search (a header trigger that opens CommandMenu with ⌘K), use CommandMenuTrigger. It wears this field's shell from the same source, so the two cannot drift; a hand-styled button already did once, into a borderless chip that matched neither the field nor a button.
18
+ - Do: Light the query in the results it filters with `useMatchHighlight(query)` — a callback ref for the results container, with `selector` to keep it to titles. It paints on the `highlight` pair without wrapping matches in `<mark>`, the same paint Combobox and CommandMenu use for their rows.
18
19
  - Don't: Do not use SearchInput as a command palette; use CommandMenu for global commands, opened from the header by CommandMenuTrigger. A SearchInput that opens a dialog on focus is a text box you cannot type in.
19
20
  - Don't: Do not search on every keystroke against an expensive backend without debouncing. The user sees results flicker and reorder under them, and you send a request per character.
20
21
 
@@ -34,7 +35,7 @@ Use SearchInput for text queries that filter or search a known content set.
34
35
 
35
36
  ## Tokens used
36
37
 
37
- `input`, `card`, `foreground`, `muted-foreground`, `ring`, `destructive`
38
+ `field-border`, `field`, `foreground`, `muted-foreground`, `ring`, `destructive`
38
39
 
39
40
  ## Motion
40
41
 
@@ -82,6 +83,23 @@ import { Field, SearchInput } from "@sableui/react"
82
83
  <Field label="Search posts"><SearchInput defaultValue="roadmap" className="max-w-80" /></Field>
83
84
  ```
84
85
 
86
+ ### Highlighting matches in the results
87
+
88
+ ```tsx
89
+ import { SearchInput, useMatchHighlight } from "@sableui/react"
90
+
91
+ const [q, setQ] = useState("")
92
+ const results = useMatchHighlight<HTMLUListElement>(q, { selector: "[data-title]" })
93
+ const shown = posts.filter((post) => post.title.toLowerCase().includes(q.trim().toLowerCase()))
94
+
95
+ <SearchInput value={q} onValueChange={setQ} placeholder="Search posts" />
96
+ <ul ref={results}>
97
+ {shown.map((post) => (
98
+ <li key={post.id}><span data-title>{post.title}</span></li>
99
+ ))}
100
+ </ul>
101
+ ```
102
+
85
103
  ### Disabled
86
104
 
87
105
  ```tsx
@@ -40,7 +40,7 @@ Use SegmentedBar when a stat card or row needs to show how a total divides into
40
40
 
41
41
  ## Tokens used
42
42
 
43
- `tone-1`, `tone-2`, `tone-3`, `tone-info`, `primary`, `muted-foreground`, `success`, `tone-warning`, `destructive`
43
+ `tone-1`, `tone-2`, `tone-3`, `tone-info`, `primary`, `muted-foreground`, `success`, `tone-warning`, `destructive`, `destructive-mark`, `success-mark`, `tone-warning-mark`, `tone-info-mark`, `tone-1-mark`, `tone-2-mark`, `tone-3-mark`
44
44
 
45
45
  ## Motion
46
46
 
@@ -50,7 +50,7 @@ Use Select when users choose one value from a known, finite set. Use Combobox fo
50
50
 
51
51
  ## Tokens used
52
52
 
53
- `input`, `card`, `foreground`, `ring`, `border`, `popover`, `popover-foreground`, `accent`, `accent-foreground`, `muted-foreground`
53
+ `field-border`, `field`, `foreground`, `ring`, `border`, `popover`, `popover-foreground`, `accent`, `accent-foreground`, `muted-foreground`
54
54
 
55
55
  ## Motion
56
56
 
@@ -15,6 +15,7 @@ Use Sidebar for persistent desktop navigation or application chrome outside the
15
15
  - Do: Use it for stable app destinations that should remain visible while content changes.
16
16
  - Do: Keep item labels predictable and active state obvious.
17
17
  - Do: Set surface="page" when a full-width top bar sits above the rail. A theme's tinted or dark rail is for a full-height rail that frames the app.
18
+ - Do: Keep surface="rail" inside AppShell frame="inset": the rail's tint becomes the ground the main panel floats on. Do not add a border-r class back — the gap is the divider.
18
19
  - Don't: Do not use Sidebar for page-local settings, filters, or temporary action groups.
19
20
  - Don't: Do not put page content in it. It is fixed-width chrome OUTSIDE the content grid, so anything in it is exempt from the page's measure and rhythm.
20
21
 
@@ -40,7 +41,7 @@ Use Sidebar for persistent desktop navigation or application chrome outside the
40
41
  | collapsed | boolean | false | Collapse the rail to an icon-only width (--sidebar-width-collapsed). Items hide their label + badge and reveal the label in a hover tooltip; the content area reflows. Controlled — wire a toggle from app state. Read the state in custom header/footer content via useSidebar(). |
41
42
  | icons | boolean | true | Render the leading icon on each SidebarItem. Set false for a text-only rail. Ignored while collapsed (icon-only by definition). |
42
43
  | variant | "app" \| "inline" | "app" | Where the rail lives. "app" is the page's chrome column — sticky, full viewport height, hidden below lg where AppHeader + BottomNav take over. "inline" is always visible and fills its parent's height, for a rail inside a bounded frame (a nested shell, an embedded preview). Use it instead of overriding the layout with !important classes. |
43
- | surface | "rail" \| "page" | "rail" | Which surface the rail is painted on. "rail" is its own --sidebar-* surface with a border — right for a full-height rail that frames the app, which a theme may tint or paint dark. "page" uses the page's surface and ink with no border — use it when a full-width header sits above the rail, where a tinted column starting under a page-coloured bar reads as pasted on. Use it instead of overriding bg-/text- classes, which the rail's re-pointed tokens defeat. |
44
+ | surface | "rail" \| "page" | "rail" | Which surface the rail is painted on. "rail" is its own --sidebar-* surface with a border — right for a full-height rail that frames the app, which a theme may tint or paint dark. "page" uses the page's surface and ink with no border — use it when a full-width header sits above the rail, where a tinted column starting under a page-coloured bar reads as pasted on. Use it instead of overriding bg-/text- classes, which the rail's re-pointed tokens defeat. In AppShell's sidebar slot with frame="inset", the rail's surface is also the shell's ground: from lg a "rail" rail drops its border (the gap around the main panel divides them) and either surface stretches to the shell's body row instead of sizing to the viewport. AppShell signals this to its sidebar slot only, so a Sidebar elsewhere in the page is unaffected. |
44
45
  | motion | "full" \| "subtle" \| "off" | "full" | How much motion this component uses: `full` inherits the app's policy, `subtle` keeps feedback and drops the collapse ease and the current-page glide, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. |
45
46
 
46
47
  ## Variants
@@ -49,7 +50,7 @@ Use Sidebar for persistent desktop navigation or application chrome outside the
49
50
 
50
51
  ## Tokens used
51
52
 
52
- `sidebar`, `sidebar-foreground`, `sidebar-border`, `sidebar-accent`, `sidebar-accent-foreground`, `sidebar-ring`
53
+ `sidebar`, `sidebar-foreground`, `sidebar-border`, `sidebar-accent`, `sidebar-accent-foreground`, `sidebar-ring`, `shell-ground`
53
54
 
54
55
  ## Motion
55
56
 
@@ -36,7 +36,7 @@ Use Sparkline next to a number to show where it has been — a KPI's last 30 day
36
36
 
37
37
  ## Tokens used
38
38
 
39
- `primary`, `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `muted-foreground`, `success`, `destructive`
39
+ `primary`, `tone-1`, `tone-2`, `tone-3`, `tone-info`, `tone-warning`, `muted-foreground`, `success`, `destructive`, `destructive-mark`, `success-mark`, `tone-warning-mark`, `tone-info-mark`, `tone-1-mark`, `tone-2-mark`, `tone-3-mark`
40
40
 
41
41
  ## Motion
42
42
 
@@ -39,7 +39,7 @@ Use StatusDot as a compact visual state cue next to text, avatars, rows, or labe
39
39
 
40
40
  ## Tokens used
41
41
 
42
- `palette-dot`, `muted-foreground`, `tone-3`, `success`, `tone-warning`, `tone-info`, `tone-1`, `tone-2`, `destructive`
42
+ `palette-dot`, `muted-foreground`, `tone-3`, `success`, `tone-warning`, `tone-info`, `tone-1`, `tone-2`, `destructive`, `destructive-mark`, `success-mark`, `tone-warning-mark`, `tone-info-mark`, `tone-1-mark`, `tone-2-mark`, `tone-3-mark`
43
43
 
44
44
  ## Accessibility
45
45
 
@@ -51,7 +51,7 @@ Use Stepper above or beside a flow that is split into a known, small number of n
51
51
 
52
52
  ## Tokens used
53
53
 
54
- `primary`, `primary-foreground`, `background`, `foreground`, `muted-foreground`, `border-strong`, `destructive`, `destructive-foreground`, `ring`
54
+ `primary`, `primary-foreground`, `background`, `foreground`, `muted-foreground`, `border-strong`, `destructive`, `destructive-foreground`, `ring`, `destructive-mark`
55
55
 
56
56
  ## Motion
57
57
 
@@ -36,8 +36,8 @@ Use Table for structured data with consistent columns and rows. Use DataTable wh
36
36
  | TableBody | ComponentPropsWithRef<"tbody"> | The data row group (tbody). |
37
37
  | TableFooter | ComponentPropsWithRef<"tfoot"> | Summary row group (tfoot) with a muted fill. |
38
38
  | TableRow | ComponentPropsWithRef<"tr"> | A row (tr) — hairline bottom border, hover lift, and a selected state via the data-selected attribute. |
39
- | TableHead | ComponentPropsWithRef<"th"> | A column header cell (th) in muted label styling. |
40
- | TableCell | ComponentPropsWithRef<"td"> | A data cell (td). |
39
+ | TableHead | ComponentPropsWithRef<"th"> & { numeric?: boolean } | A column header cell (th) in muted label styling. `numeric` marks a number column: end-aligned, tabular figures — set it on the column's cells too. |
40
+ | TableCell | ComponentPropsWithRef<"td"> & { numeric?: boolean } | A data cell (td). `numeric` end-aligns it with tabular figures, matching its column's header. |
41
41
  | TableCaption | ComponentPropsWithRef<"caption"> | Accessible description of the table (caption), muted. |
42
42
 
43
43
  ## Props
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Status:** stable
4
4
 
5
- Styled native multi-line text control. Soft-square, card surface, --ring focus, and an aria-invalid destructive error state.
5
+ Styled native multi-line text control. Soft-square, the theme's field surface, --ring focus, and an aria-invalid destructive error state.
6
6
 
7
7
  **Keywords:** textarea, multi line input, form, text
8
8
 
@@ -31,7 +31,7 @@ Use Textarea for multi-line freeform text entry such as notes, descriptions, or
31
31
 
32
32
  ## Tokens used
33
33
 
34
- `input`, `card`, `foreground`, `muted-foreground`, `ring`, `destructive`
34
+ `field-border`, `field`, `foreground`, `muted-foreground`, `ring`, `destructive`
35
35
 
36
36
  ## Accessibility
37
37
 
@@ -30,6 +30,7 @@ Sable's components ship accessible behavior from Base UI. These rules cover what
30
30
  - **Never:** Color as the only signal (a red border with no message). **Instead:** Color plus text or an icon. Roughly 1 in 12 men cannot tell red from green.
31
31
  - **Never:** Opacity on the focus ring. **Instead:** ring-ring at full opacity; tune the token in the theme. A faded ring loses the contrast the theme tested. *Enforced by sable-lint: faded-focus-ring.*
32
32
  - **Never:** A brand or status fill retuned under its old label — dark ink on a bright red that passes 4.5:1 at APCA Lc 42. **Instead:** A label that clears 4.5:1 and APCA Lc 60 in both modes. Move the fill's lightness, not its hue, keeping the ink its colour takes. A status colour on a surface is its own token (`--destructive-text`), so the fill can move freely. The ratio alone passes labels that read muddy. A deliberate brand pair is allowed with a recorded reason (lint disable or preset exceptions). *Enforced by sable-lint: label-contrast.*
33
+ - **Never:** A label fill painted on a mark that carries no text — a meter, a segmented bar, a status dot, a chart series, a soft tint. **Instead:** The colour's `-mark` token (`bg-success-mark`, `bg-tone-warning-mark/15`): the same hue lifted and saturated, held to 2:1 on the page. Text contrast is for text; a graphic owes visibility, and 3:1 (WCAG 1.4.11) only when it is the sole carrier of its meaning. A fill is pinned dark enough for a white label. On a bar or dot with nothing to label, that darkness only reads as mud: a brown warning, a forest-green success.
33
34
  - **Never:** Blocking paste, or type="number" for codes and card numbers. **Instead:** type="text" inputMode="numeric" with the right autocomplete. Blocking paste breaks password managers; number inputs scroll-change identifiers.
34
35
  - **Never:** forced-color-adjust: none to 'fix' high-contrast mode. **Instead:** Let the system colors apply; verify the result. It removes the user's chosen colors exactly where they need them.
35
36
  - **Never:** outline: none without a replacement. **Instead:** focus-visible:ring-2 ring-ring. Keyboard users lose their place.
@@ -24,7 +24,7 @@ Which token to reach for, and what it is most often confused with. Generated fro
24
24
  - **Not** `border-strong`. Reach for `border-strong` when the line carries *structure* (a grid rule, a table divider, a section separator) and for `border` when it merely bounds a control.
25
25
  - **`border-strong`** — A structural divider. Visibly stronger than `border`. *Use for:* Grid lines, table rules, separators between major sections.
26
26
  - **Not** `border`. If removing the line would make the layout ambiguous, it is structural and belongs here.
27
- - **`input`** — The outline of a form control specifically. *Use for:* Text inputs, textareas and selects. Not the edge of a checkbox, radio or switch — that is `control-border`.
27
+ - **`input`** — The outline of a form control specifically. *Use for:* Text inputs, textareas and selects (through `field-border`, which derives from it). Not the edge of a checkbox, radio or switch — that is `control-border`.
28
28
  - **Not** `border`. They are often the same value, but a theme is entitled to make form controls read differently from other chrome, and only this token gives it the hook.
29
29
  - **`control-border`** — The resting edge of a control whose outline is the whole control. Held to 3:1 (WCAG 1.4.11) on every surface. *Use for:* An unchecked checkbox, an unselected radio, an off switch track, a selection card's check or radio mark — anywhere removing the edge would make the control disappear.
30
30
  - **Not** `input`. A text field is also found by its fill, caret and placeholder, so its outline may stay quiet; a checkbox has nothing else, and at `input`'s strength it measures about 1.4:1.
@@ -314,7 +314,7 @@ Which token to reach for, and what it is most often confused with. Generated fro
314
314
  - **`type-mono-sm-tracking`** — The monospace family: mono-xl through mono-sm, plus mono-caption. *Use for:* Code, identifiers, hashes, keyboard keys, and any figure where digits must align in a column.
315
315
  - **Not** the sans tiers for numeric data. Proportional digits make a column of numbers ragged; that is what `text-ui-num` solves in chrome and mono solves on a page.
316
316
 
317
- ### derived (10)
317
+ ### derived (20)
318
318
 
319
319
  - **`canvas`** — The WORK surface — the artboard, the document being edited. *Use for:* Behind the thing the tool operates on. `WorkspaceCanvas` uses it.
320
320
  - **Not** `background`. The thing being edited is not the page behind the UI, and collapsing the two makes the document indistinguishable from its chrome.
@@ -336,6 +336,26 @@ Which token to reach for, and what it is most often confused with. Generated fro
336
336
  - **Not** `hairline`, when the line would otherwise disappear against what it bounds.
337
337
  - **`border-floating`** — The edge of a floating surface. Fainter than `border`: the shadow carries the elevation. *Use for:* Every floating surface — popover, menu, select, tooltip, dialog, sheet, toast, a floating panel — as `border-border-floating` or `ring-border-floating`, together with `bg-popover` and a `shadow-md`+ shadow. Derived for every theme (a faint alpha of the foreground); a theme may tune it with `--theme-border-floating`.
338
338
  - **Not** `border`. A full hairline plus a shadow stacks into a heavy outline around anything that floats. `border` is for resting edges: inputs, cards, badges.
339
+ - **`field`** — The fill of a text field: Input, Textarea, Select, Combobox, SearchInput, InputGroup, ChatComposer. *Use for:* Read by every field through its shell, so you rarely name it. Derived for every theme (`card`, an outlined field on the card); a theme fills its fields instead with `--theme-field: var(--muted)` and `--theme-field-border: transparent`, and every field changes at once. Name it only for a field-shaped surface of your own that must match.
340
+ - **Not** `card` or `muted` on a hand-built field. A field that paints `card` keeps its outline when the theme fills the rest, and one that paints `muted` stays filled when the theme outlines them.
341
+ - **`field-border`** — The outline of a text field. Derived from `input`; `transparent` in a theme with filled fields. *Use for:* With `field`, on the same field-shaped surfaces. Tune it with `--theme-field-border`.
342
+ - **Not** `input` on a text field. `input` stays the theme's outline colour for form chrome; this is the field's outline, which a filled theme turns off.
343
+ - **`shell-ground`** — The ground of an inset app frame: the rail and the gap around the raised main panel. *Use for:* Painted by `<AppShell frame="inset">` and the Sidebar in its slot; you rarely name it. Derived for every theme as one step off `sidebar` toward its ink, so the panel reads as lifted even where the rail is the page colour. Tune it with `--theme-shell-ground`.
344
+ - **Not** `sidebar` or `canvas` behind an inset panel. `sidebar` is the page colour in most themes, so the panel would sit on itself; `canvas` is a workspace's artboard, not app chrome.
345
+ - **`destructive-mark`** — `destructive` at graphic strength: the same hue lifted and saturated, for a mark that carries no text. *Use for:* A meter's or progress fill, a segmented bar, a status dot, a chart series, and the soft tint behind a badge or note (`bg-destructive-mark/15`). Held to 2:1 on the page — visibility, not a text ratio — because each of these sits beside text that says the same thing. Every bundled theme tunes its own; a preset sets `--theme-destructive-mark` or gets one derived from `destructive`.
346
+ - **Not** `bg-destructive` on a mark. The fill is pinned dark enough to carry a white label, so a meter or dot painted with it reads brown, forest or plum. And not for text — that is `text-destructive` — nor for a graphic that is the only carrier of its meaning, which owes 3:1 (WCAG 1.4.11): use the fill or add the text.
347
+ - **`success-mark`** — `success` at graphic strength: the same hue lifted and saturated, for a mark that carries no text. *Use for:* A meter's or progress fill, a segmented bar, a status dot, a chart series, and the soft tint behind a badge or note (`bg-success-mark/15`). Held to 2:1 on the page — visibility, not a text ratio — because each of these sits beside text that says the same thing. Every bundled theme tunes its own; a preset sets `--theme-success-mark` or gets one derived from `success`.
348
+ - **Not** `bg-success` on a mark. The fill is pinned dark enough to carry a white label, so a meter or dot painted with it reads brown, forest or plum. And not for text — that is `text-success` — nor for a graphic that is the only carrier of its meaning, which owes 3:1 (WCAG 1.4.11): use the fill or add the text.
349
+ - **`tone-warning-mark`** — `tone-warning` at graphic strength: the same hue lifted and saturated, for a mark that carries no text. *Use for:* A meter's or progress fill, a segmented bar, a status dot, a chart series, and the soft tint behind a badge or note (`bg-tone-warning-mark/15`). Held to 2:1 on the page — visibility, not a text ratio — because each of these sits beside text that says the same thing. Every bundled theme tunes its own; a preset sets `--theme-tone-warning-mark` or gets one derived from `tone-warning`.
350
+ - **Not** `bg-tone-warning` on a mark. The fill is pinned dark enough to carry a white label, so a meter or dot painted with it reads brown, forest or plum. And not for text — that is `text-tone-warning` — nor for a graphic that is the only carrier of its meaning, which owes 3:1 (WCAG 1.4.11): use the fill or add the text.
351
+ - **`tone-info-mark`** — `tone-info` at graphic strength: the same hue lifted and saturated, for a mark that carries no text. *Use for:* A meter's or progress fill, a segmented bar, a status dot, a chart series, and the soft tint behind a badge or note (`bg-tone-info-mark/15`). Held to 2:1 on the page — visibility, not a text ratio — because each of these sits beside text that says the same thing. Every bundled theme tunes its own; a preset sets `--theme-tone-info-mark` or gets one derived from `tone-info`.
352
+ - **Not** `bg-tone-info` on a mark. The fill is pinned dark enough to carry a white label, so a meter or dot painted with it reads brown, forest or plum. And not for text — that is `text-tone-info` — nor for a graphic that is the only carrier of its meaning, which owes 3:1 (WCAG 1.4.11): use the fill or add the text.
353
+ - **`tone-1-mark`** — `tone-1` at graphic strength: the same hue lifted and saturated, for a mark that carries no text. *Use for:* A meter's or progress fill, a segmented bar, a status dot, a chart series, and the soft tint behind a badge or note (`bg-tone-1-mark/15`). Held to 2:1 on the page — visibility, not a text ratio — because each of these sits beside text that says the same thing. Every bundled theme tunes its own; a preset sets `--theme-tone-1-mark` or gets one derived from `tone-1`.
354
+ - **Not** `bg-tone-1` on a mark. The fill is pinned dark enough to carry a white label, so a meter or dot painted with it reads brown, forest or plum. And not for text — that is `text-tone-1` — nor for a graphic that is the only carrier of its meaning, which owes 3:1 (WCAG 1.4.11): use the fill or add the text.
355
+ - **`tone-2-mark`** — `tone-2` at graphic strength: the same hue lifted and saturated, for a mark that carries no text. *Use for:* A meter's or progress fill, a segmented bar, a status dot, a chart series, and the soft tint behind a badge or note (`bg-tone-2-mark/15`). Held to 2:1 on the page — visibility, not a text ratio — because each of these sits beside text that says the same thing. Every bundled theme tunes its own; a preset sets `--theme-tone-2-mark` or gets one derived from `tone-2`.
356
+ - **Not** `bg-tone-2` on a mark. The fill is pinned dark enough to carry a white label, so a meter or dot painted with it reads brown, forest or plum. And not for text — that is `text-tone-2` — nor for a graphic that is the only carrier of its meaning, which owes 3:1 (WCAG 1.4.11): use the fill or add the text.
357
+ - **`tone-3-mark`** — `tone-3` at graphic strength: the same hue lifted and saturated, for a mark that carries no text. *Use for:* A meter's or progress fill, a segmented bar, a status dot, a chart series, and the soft tint behind a badge or note (`bg-tone-3-mark/15`). Held to 2:1 on the page — visibility, not a text ratio — because each of these sits beside text that says the same thing. Every bundled theme tunes its own; a preset sets `--theme-tone-3-mark` or gets one derived from `tone-3`.
358
+ - **Not** `bg-tone-3` on a mark. The fill is pinned dark enough to carry a white label, so a meter or dot painted with it reads brown, forest or plum. And not for text — that is `text-tone-3` — nor for a graphic that is the only carrier of its meaning, which owes 3:1 (WCAG 1.4.11): use the fill or add the text.
339
359
 
340
360
  ### Generated palette
341
361
 
@@ -230,7 +230,7 @@ The `@utility` and `@custom-variant` layer in `styles.css`: what each one is, wh
230
230
  - **Instead of** A hand-written dash-offset animation with literal timing, which keeps animating, or keeps its delay, under reduced motion.
231
231
  - `<Reveal><svg viewBox="0 0 100 100"><path className="motion-draw" pathLength="1" d="M10 50 L90 50" /></svg></Reveal>`
232
232
 
233
- ### helper (5)
233
+ ### helper (6)
234
234
 
235
235
  - **`hit-target`** — Grows an element's tap target to 44px with a transparent `::after`, without moving its layout by a pixel. `--hit-target-size` overrides the size. *Use for:* A deliberately small mark — a tag's remove ×, a tiny disclosure, a toast close — that must still meet the WCAG touch floor. Shrink it (`[--hit-target-size:32px]`) where targets sit side by side, or the later one steals the taps.
236
236
  - **Instead of** Padding the glyph up (moves it off the row's grid and pushes siblings) or a bigger icon. Never on an `<input>` or other replaced element — pseudo-elements silently do nothing there; wrap it in a label.
@@ -246,4 +246,7 @@ The `@utility` and `@custom-variant` layer in `styles.css`: what each one is, wh
246
246
  - `<a href="/terms" className="link-underline motion-interactive text-primary">terms</a>`
247
247
  - **`checkerboard`** — The transparency checkerboard, painted from `--panel` and a derived darker step so it reads in both modes. `--checkerboard-size` (default 16px) and `--checkerboard-alt` tune it. *Use for:* Behind anything that can be translucent: a colour swatch with alpha, an image or artboard preview, an alpha slider track.
248
248
  - **Instead of** A hand-rolled `repeating-conic-gradient` with two hardcoded greys. Deliberately not named `bg-checkerboard`, so `cn()` never merges it away against a real `bg-*` colour — you can layer a colour over it.
249
- - `<div className="checkerboard [--checkerboard-size:8px] size-6 rounded-sm" />`
249
+ - `<div className="checkerboard [--checkerboard-size:8px] size-6 rounded-sm" />`
250
+ - **`texture-dither`** — A faint pixel / halftone dither field that dissolves: a small pseudo-random tile of squares painted in `--border-strong` and faded out by a gradient mask. Static, pure CSS, follows every theme and mode. Hooks: `--dither-fade` (direction, any linear-gradient side or angle, default `to bottom`), `--dither-size` (tile, default 48px = 2px squares) and `--dither-color` (default `var(--border-strong)`). *Use for:* Decoration at the top of a content area, a panel or a card, or behind an empty-state heading. Put it on its own `aria-hidden` element, `absolute -z-10` as the first child of a `relative isolate` parent, so the content after it paints on top with no z-index of its own. It sets `pointer-events: none` and no position, so it never takes layout or catches clicks.
251
+ - **Instead of** A hand-written `radial-gradient` dot grid (reads as graph paper, not dither), a PNG noise texture, a canvas, or hardcoded greys. Deliberately not named `bg-dither`, so `cn()` never merges it away against a real `bg-*` colour class.
252
+ - `<div className="relative isolate"><div aria-hidden className="texture-dither absolute inset-x-0 top-0 -z-10 h-40" /></div>`