@sableui/react 0.15.0 → 0.17.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 (145) hide show
  1. package/CHANGELOG.md +99 -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/approval-block.js +30 -48
  5. package/dist/blocks/approval-block.meta.js +7 -8
  6. package/dist/blocks/index.d.ts +1 -1
  7. package/dist/blocks/wallet-block.js +2 -2
  8. package/dist/components/app-shell/app-shell-frame.js +16 -0
  9. package/dist/components/app-shell/app-shell-main.js +12 -0
  10. package/dist/components/app-shell/app-shell.js +28 -4
  11. package/dist/components/app-shell/app-shell.meta.js +88 -4
  12. package/dist/components/area-chart/area-chart.meta.js +6 -1
  13. package/dist/components/attachment/attachment.meta.js +2 -1
  14. package/dist/components/attachment/attachment.variants.js +1 -1
  15. package/dist/components/badge/badge.meta.js +8 -1
  16. package/dist/components/badge/badge.variants.js +7 -7
  17. package/dist/components/banner/banner.meta.js +6 -1
  18. package/dist/components/banner/banner.variants.js +5 -5
  19. package/dist/components/bar-chart/bar-chart.meta.js +6 -1
  20. package/dist/components/caret-menu/caret-menu.js +5 -6
  21. package/dist/components/chat-composer/chat-composer.meta.js +2 -2
  22. package/dist/components/chat-composer/chat-composer.variants.js +1 -1
  23. package/dist/components/color-field/color-field.js +6 -8
  24. package/dist/components/combobox/combobox.js +27 -5
  25. package/dist/components/combobox/combobox.meta.js +10 -2
  26. package/dist/components/command-menu/command-menu.js +14 -3
  27. package/dist/components/command-menu/command-menu.meta.js +9 -1
  28. package/dist/components/date-picker/date-picker.meta.js +2 -2
  29. package/dist/components/feedback/feedback.js +1 -1
  30. package/dist/components/feedback/feedback.meta.js +2 -1
  31. package/dist/components/gradient-art/gradient-art.js +8 -5
  32. package/dist/components/gradient-art/gradient-art.meta.js +35 -2
  33. package/dist/components/input/input.meta.js +11 -3
  34. package/dist/components/input-group/input-group.js +1 -1
  35. package/dist/components/input-group/input-group.meta.js +1 -1
  36. package/dist/components/line-chart/line-chart.meta.js +6 -1
  37. package/dist/components/menu/menu.js +1 -1
  38. package/dist/components/meter/meter.js +53 -7
  39. package/dist/components/meter/meter.meta.js +51 -4
  40. package/dist/components/meter/meter.variants.js +43 -7
  41. package/dist/components/note/note.meta.js +5 -1
  42. package/dist/components/note/note.variants.js +4 -4
  43. package/dist/components/number-field/number-field.meta.js +2 -2
  44. package/dist/components/otp-field/otp-field.meta.js +2 -2
  45. package/dist/components/panel-group/panel-group.js +5 -8
  46. package/dist/components/pie-chart/pie-chart.meta.js +6 -1
  47. package/dist/components/radar-chart/radar-chart.meta.js +6 -1
  48. package/dist/components/radial-chart/radial-chart.meta.js +6 -1
  49. package/dist/components/search-input/search-input.meta.js +11 -3
  50. package/dist/components/section-rail/index.js +7 -0
  51. package/dist/components/section-rail/section-rail.js +153 -0
  52. package/dist/components/section-rail/section-rail.meta.js +189 -0
  53. package/dist/components/segmented-bar/segmented-bar.meta.js +8 -1
  54. package/dist/components/segmented-bar/segmented-bar.variants.js +7 -7
  55. package/dist/components/select/select.js +1 -1
  56. package/dist/components/select/select.meta.js +2 -2
  57. package/dist/components/sheet/sheet.js +5 -4
  58. package/dist/components/sidebar/sidebar.js +16 -1
  59. package/dist/components/sidebar/sidebar.meta.js +7 -2
  60. package/dist/components/sparkline/sparkline.meta.js +8 -1
  61. package/dist/components/status-dot/status-dot.meta.js +8 -1
  62. package/dist/components/status-dot/status-dot.variants.js +7 -7
  63. package/dist/components/stepper/stepper.meta.js +2 -1
  64. package/dist/components/stepper/stepper.variants.js +1 -1
  65. package/dist/components/table/table.meta.js +4 -4
  66. package/dist/components/textarea/textarea.js +5 -0
  67. package/dist/components/textarea/textarea.meta.js +3 -3
  68. package/dist/file-hashes.json +99 -92
  69. package/dist/foundations/accessibility.js +5 -0
  70. package/dist/index.d.ts +227 -25
  71. package/dist/index.js +12 -1
  72. package/dist/lib/chart/types.js +8 -8
  73. package/dist/lib/chart/use-arrival.js +5 -4
  74. package/dist/lib/field-shell.js +1 -1
  75. package/dist/lib/highlight-glide.js +6 -6
  76. package/dist/lib/scrub-gesture.js +6 -6
  77. package/dist/lib/taxonomy.js +1 -0
  78. package/dist/lib/use-active-section.js +59 -0
  79. package/dist/lib/use-caret-trigger.js +11 -18
  80. package/dist/lib/use-match-highlight.js +69 -0
  81. package/dist/lib/use-smooth-wheel.js +10 -8
  82. package/dist/presets/index.d.ts +13 -2
  83. package/dist/presets/index.js +17 -1
  84. package/dist/styles.css +100 -0
  85. package/dist/{theme-provider-CdSicAad.d.ts → theme-provider-BydfM5Z0.d.ts} +1 -1
  86. package/dist/themes/_contract.js +14 -0
  87. package/dist/themes/atelier/atelier.css +18 -0
  88. package/dist/themes/atelier/index.d.ts +1 -1
  89. package/dist/themes/contrast.js +48 -1
  90. package/dist/themes/ember/ember.css +18 -0
  91. package/dist/themes/ember/index.d.ts +1 -1
  92. package/dist/themes/index.d.ts +2 -2
  93. package/dist/themes/meridian/index.d.ts +1 -1
  94. package/dist/themes/meridian/meridian.css +18 -0
  95. package/dist/themes/poppy/index.d.ts +1 -1
  96. package/dist/themes/poppy/poppy.css +18 -0
  97. package/dist/themes/prism/index.d.ts +1 -1
  98. package/dist/themes/prism/prism.css +18 -0
  99. package/dist/themes/sable/index.d.ts +1 -1
  100. package/dist/themes/sable/sable.css +18 -0
  101. package/dist/themes/verdant/index.d.ts +1 -1
  102. package/dist/themes/verdant/verdant.css +18 -0
  103. package/llms/blocks/approval-block.md +40 -55
  104. package/llms/blocks/wallet-block.md +5 -4
  105. package/llms/components/app-shell.md +72 -3
  106. package/llms/components/area-chart.md +1 -1
  107. package/llms/components/attachment.md +1 -1
  108. package/llms/components/badge.md +1 -1
  109. package/llms/components/banner.md +1 -1
  110. package/llms/components/bar-chart.md +1 -1
  111. package/llms/components/chat-composer.md +1 -1
  112. package/llms/components/combobox.md +2 -1
  113. package/llms/components/command-menu.md +3 -2
  114. package/llms/components/date-picker.md +1 -1
  115. package/llms/components/feedback.md +1 -1
  116. package/llms/components/gradient-art.md +28 -1
  117. package/llms/components/input-group.md +1 -1
  118. package/llms/components/input.md +4 -2
  119. package/llms/components/line-chart.md +1 -1
  120. package/llms/components/meter.md +33 -4
  121. package/llms/components/note.md +1 -1
  122. package/llms/components/number-field.md +1 -1
  123. package/llms/components/otp-field.md +1 -1
  124. package/llms/components/pie-chart.md +1 -1
  125. package/llms/components/radar-chart.md +1 -1
  126. package/llms/components/radial-chart.md +1 -1
  127. package/llms/components/search-input.md +20 -2
  128. package/llms/components/section-rail.md +101 -0
  129. package/llms/components/segmented-bar.md +1 -1
  130. package/llms/components/select.md +1 -1
  131. package/llms/components/sidebar.md +3 -2
  132. package/llms/components/sparkline.md +1 -1
  133. package/llms/components/status-dot.md +1 -1
  134. package/llms/components/stepper.md +1 -1
  135. package/llms/components/table.md +2 -2
  136. package/llms/components/textarea.md +2 -2
  137. package/llms/foundations/accessibility.md +1 -0
  138. package/llms/reference/token-intent.md +22 -2
  139. package/llms/reference/utilities.md +5 -2
  140. package/llms-full.txt +367 -105
  141. package/llms.txt +6 -5
  142. package/package.json +1 -1
  143. package/registry.json +609 -67
  144. package/scripts/lint-rules.mjs +6 -3
  145. package/targets/ios.json +9 -0
@@ -0,0 +1,189 @@
1
+ // src/components/section-rail/section-rail.meta.ts
2
+ var sectionRailMeta = {
3
+ name: "SectionRail",
4
+ summary: "A slim vertical rail of dots, one per section of a long page, that shows where the reader is and jumps to any section. The current section's bar glides as the page scrolls; a shared tooltip names each dot.",
5
+ keywords: [
6
+ "section rail",
7
+ "scroll spy",
8
+ "scrollspy",
9
+ "dot navigation",
10
+ "page navigation",
11
+ "table of contents",
12
+ "on this page",
13
+ "chapter nav",
14
+ "scroll progress",
15
+ "case study nav"
16
+ ],
17
+ usage: {
18
+ description: "Use SectionRail beside a long, single-column read \u2014 a case study, an essay, a long landing page \u2014 so the reader can see which section they are in and jump to another without leaving the text. It follows the scroll by itself (useActiveSection); pass value when the parent already knows the current section.",
19
+ bestPractices: [
20
+ {
21
+ guidance: "do",
22
+ description: "Give every section an id that matches its entry, and keep the list to the page's top-level sections \u2014 about ten at most, so the rail stays shorter than the viewport."
23
+ },
24
+ {
25
+ guidance: "do",
26
+ description: "Place it with layout classes \u2014 usually `fixed top-1/2 left-6 z-(--z-nav) -translate-y-1/2` \u2014 and hide it below the width where it would overlap the text (`max-lg:hidden`). Offer the same links another way on narrow screens, such as a Collapsible list at the top of the page."
27
+ },
28
+ {
29
+ guidance: "do",
30
+ description: "Pass `root` when the content scrolls inside an element (a Scroller, an inset AppShell main) rather than the window, and give sections `scroll-mt-*` when a sticky header would cover their tops after a jump."
31
+ },
32
+ {
33
+ guidance: "do",
34
+ description: 'Set `side="right"` when the rail sits on the right edge, so labels open toward the content.'
35
+ },
36
+ {
37
+ guidance: "dont",
38
+ description: "Use it for app navigation between pages; that is SideNav or Sidebar. A SectionRail only moves within one page.",
39
+ instead: ["SideNav", "Sidebar"]
40
+ },
41
+ {
42
+ guidance: "dont",
43
+ description: "Use it to show the steps of a task or a flow; that is Stepper, which shows progress through steps rather than position in a read.",
44
+ instead: ["Stepper"]
45
+ },
46
+ {
47
+ guidance: "dont",
48
+ description: "Use it for a handful of switchable views in one place; that is Tabs.",
49
+ instead: ["Tabs"]
50
+ }
51
+ ],
52
+ anatomy: [
53
+ {
54
+ name: "Rail",
55
+ required: true,
56
+ description: "The `<nav>` landmark: a rounded muted track holding one link per section."
57
+ },
58
+ {
59
+ name: "Dot",
60
+ required: true,
61
+ description: "A link to `#id`, drawn as a small dot that grows under the pointer or focus."
62
+ },
63
+ {
64
+ name: "Marker",
65
+ required: true,
66
+ description: "One primary bar that sits on the current section's dot and glides to the next."
67
+ },
68
+ {
69
+ name: "Label",
70
+ required: true,
71
+ description: "A shared tooltip that names the hovered or focused dot and travels between dots."
72
+ }
73
+ ]
74
+ },
75
+ related: {
76
+ components: ["SideNav", "Tooltip", "Stepper", "Tabs", "GlidingHighlight"]
77
+ },
78
+ status: "stable",
79
+ props: [
80
+ {
81
+ name: "sections",
82
+ type: "readonly { id: string; label: string }[]",
83
+ description: "The sections, in document order. `id` is the section element's id (the link goes to `#id`); `label` is the tooltip and the link's accessible name."
84
+ },
85
+ {
86
+ name: "value",
87
+ type: "string",
88
+ description: "The current section's id, when the parent decides it. Leave it unset and the rail follows the scroll itself."
89
+ },
90
+ {
91
+ name: "onValueChange",
92
+ type: "(id: string) => void",
93
+ description: "Called with the new id when the reader scrolls into a section or picks one."
94
+ },
95
+ {
96
+ name: "offset",
97
+ type: "number",
98
+ default: "0.35",
99
+ description: "Where the reading line sits, as a fraction of the viewport from its top. A section becomes current once its top crosses it."
100
+ },
101
+ {
102
+ name: "root",
103
+ type: "RefObject<HTMLElement | null>",
104
+ description: "The element that scrolls, when it is not the window."
105
+ },
106
+ {
107
+ name: "side",
108
+ type: '"left" | "right"',
109
+ default: '"left"',
110
+ description: "Which edge of the page the rail sits on. The labels open on the other side, toward the content."
111
+ },
112
+ {
113
+ name: "motion",
114
+ type: '"full" | "subtle" | "off"',
115
+ default: '"full"',
116
+ description: "How much motion this component uses: `full` inherits the app's policy, `subtle` keeps feedback and drops the marker's glide, `off` stills it. It can only turn motion down \u2014 it never overrides a reader's reduce-motion setting."
117
+ },
118
+ {
119
+ name: "aria-label",
120
+ type: "string",
121
+ default: '"Sections"',
122
+ description: `The nav landmark's name. Name it for the page ("Case study sections") when it has other navs.`
123
+ }
124
+ ],
125
+ motion: [
126
+ {
127
+ slot: "arrival",
128
+ recipe: "none",
129
+ part: "Root",
130
+ why: "Page chrome arrives with the page; the marker lands on the current section with no transition, because a bar sliding in on load narrates a move nobody made."
131
+ },
132
+ {
133
+ slot: "response",
134
+ recipe: "motion-interactive",
135
+ part: "Dot",
136
+ why: "The dot darkens and grows under the pointer or focus, and the label travels between dots as one tooltip (TooltipGroup) rather than closing and reopening at each."
137
+ },
138
+ {
139
+ slot: "transition",
140
+ recipe: "motion-glide",
141
+ part: "Marker",
142
+ why: "One bar travels to the new section as the page scrolls, so the change reads as movement down the page. A picked section holds the bar until the reader scrolls by hand, so it goes straight there instead of stepping through every section in between, and stays on the pick even when the page ends before that section can reach the reading line. Expressive: `subtle` moves it at once."
143
+ },
144
+ {
145
+ slot: "activity",
146
+ recipe: "none",
147
+ part: "Root",
148
+ why: "Navigation chrome has no working state."
149
+ }
150
+ ],
151
+ a11y: [
152
+ 'Renders a <nav> landmark (default name "Sections") of real links to `#id`, so it works with no script, middle-click and copy-link.',
153
+ `Each link is named by its section's label; the current one carries aria-current="location".`,
154
+ "Activating a link from the keyboard moves focus into the section, as following an anchor does, so the next Tab continues from there.",
155
+ "The tooltip opens on focus as well as hover; the dots are 44\xD732 targets.",
156
+ "Under reduced motion the jump is instant and the marker moves without travel."
157
+ ],
158
+ tokensUsed: ["muted", "muted-foreground", "foreground", "primary", "ring", "popover", "popover-foreground", "border-floating"],
159
+ examples: [
160
+ {
161
+ label: "Fixed beside a long read",
162
+ code: `<SectionRail
163
+ aria-label="Case study sections"
164
+ sections={[
165
+ { id: "overview", label: "Overview" },
166
+ { id: "problem", label: "The problem" },
167
+ { id: "process", label: "Process" },
168
+ { id: "outcome", label: "Outcome" },
169
+ ]}
170
+ className="fixed top-1/2 left-6 z-(--z-nav) -translate-y-1/2 max-lg:hidden"
171
+ />`
172
+ },
173
+ {
174
+ label: "On the right edge",
175
+ code: `<SectionRail
176
+ side="right"
177
+ sections={[
178
+ { id: "intro", label: "Introduction" },
179
+ { id: "usage", label: "Usage" },
180
+ { id: "api", label: "API" },
181
+ ]}
182
+ className="fixed top-1/2 right-6 z-(--z-nav) -translate-y-1/2 max-lg:hidden"
183
+ />`
184
+ }
185
+ ]
186
+ };
187
+ export {
188
+ sectionRailMeta
189
+ };
@@ -71,7 +71,14 @@ var segmentedBarMeta = {
71
71
  "muted-foreground",
72
72
  "success",
73
73
  "tone-warning",
74
- "destructive"
74
+ "destructive",
75
+ "destructive-mark",
76
+ "success-mark",
77
+ "tone-warning-mark",
78
+ "tone-info-mark",
79
+ "tone-1-mark",
80
+ "tone-2-mark",
81
+ "tone-3-mark"
75
82
  ],
76
83
  motion: [
77
84
  {
@@ -5,15 +5,15 @@ var segmentedBarSegmentVariants = cva(
5
5
  {
6
6
  variants: {
7
7
  tone: {
8
- "1": "bg-tone-1",
9
- "2": "bg-tone-2",
10
- "3": "bg-tone-3",
11
- info: "bg-tone-info",
8
+ "1": "bg-tone-1-mark",
9
+ "2": "bg-tone-2-mark",
10
+ "3": "bg-tone-3-mark",
11
+ info: "bg-tone-info-mark",
12
12
  primary: "bg-primary",
13
13
  neutral: "bg-muted-foreground",
14
- success: "bg-success",
15
- warning: "bg-tone-warning",
16
- destructive: "bg-destructive"
14
+ success: "bg-success-mark",
15
+ warning: "bg-tone-warning-mark",
16
+ destructive: "bg-destructive-mark"
17
17
  }
18
18
  },
19
19
  defaultVariants: {
@@ -72,7 +72,7 @@ function SelectTrigger({
72
72
  className: cn(
73
73
  `flex w-full items-center justify-between ${tier.body} text-foreground motion-interactive`,
74
74
  mobileTier,
75
- size === "xs" ? cn("h-(--control-xs) gap-1 px-1.5", FIELD_SHELL_XS) : "h-(--control-md) gap-2 rounded-sm border border-input bg-card px-3",
75
+ size === "xs" ? cn("h-(--control-xs) gap-1 px-1.5", FIELD_SHELL_XS) : "h-(--control-md) gap-2 rounded-sm border border-field-border bg-field px-3",
76
76
  "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
77
77
  "data-[popup-open]:ring-2 data-[popup-open]:ring-ring",
78
78
  // Invalid like every other field: Base UI marks the trigger
@@ -124,8 +124,8 @@ var selectMeta = {
124
124
  }
125
125
  ],
126
126
  tokensUsed: [
127
- "input",
128
- "card",
127
+ "field-border",
128
+ "field",
129
129
  "foreground",
130
130
  "ring",
131
131
  "border",
@@ -61,11 +61,12 @@ function useKeyboardInset(enabled) {
61
61
  Math.abs(viewport.scale - 1) > 0.01 ? 0 : Math.max(0, Math.round(window.innerHeight - viewport.height - viewport.offsetTop))
62
62
  );
63
63
  measure();
64
- viewport.addEventListener("resize", measure);
65
- viewport.addEventListener("scroll", measure);
64
+ const listeners = new AbortController();
65
+ const { signal } = listeners;
66
+ viewport.addEventListener("resize", measure, { signal });
67
+ viewport.addEventListener("scroll", measure, { signal });
66
68
  return () => {
67
- viewport.removeEventListener("resize", measure);
68
- viewport.removeEventListener("scroll", measure);
69
+ listeners.abort();
69
70
  setInset(0);
70
71
  };
71
72
  }, [enabled]);
@@ -10,6 +10,7 @@ import {
10
10
  import { cn } from "../../lib/cn.js";
11
11
  import { HighlightGlide } from "../../lib/highlight-glide.js";
12
12
  import { motionAttribute } from "../../motion/MotionProvider.js";
13
+ import { useAppShellFrame } from "../app-shell/app-shell-frame.js";
13
14
  import { RAIL_ROW_INLINE, sideNavItemIconVariants, sideNavItemVariants } from "../side-nav/side-nav.variants.js";
14
15
  import { SimpleTooltip } from "../tooltip/index.js";
15
16
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -27,6 +28,8 @@ var SIDEBAR_VARIANT = {
27
28
  app: "hidden lg:flex lg:sticky lg:top-0 lg:h-[calc(100vh-var(--dock-height,0px))] lg:self-start",
28
29
  inline: "flex h-full"
29
30
  };
31
+ var INSET_RAIL = "lg:h-auto lg:self-stretch";
32
+ var INSET_RAIL_SURFACE = "lg:border-r-0 lg:[--sidebar:var(--shell-ground)]";
30
33
  var SIDEBAR_INK = [
31
34
  "[--background:var(--sidebar)] [--sable-surface-bg:var(--sidebar)]",
32
35
  "[--foreground:var(--sidebar-foreground)]",
@@ -54,6 +57,7 @@ function Sidebar({
54
57
  }) {
55
58
  const [hasCollapsed, setHasCollapsed] = useState(collapsed);
56
59
  if (collapsed && !hasCollapsed) setHasCollapsed(true);
60
+ const inset = useAppShellFrame() === "inset";
57
61
  return /* @__PURE__ */ jsx(SidebarContext.Provider, { value: { collapsed, icons, reopened: hasCollapsed && !collapsed }, children: /* @__PURE__ */ jsx(
58
62
  "aside",
59
63
  {
@@ -61,6 +65,7 @@ function Sidebar({
61
65
  "data-collapsed": collapsed || void 0,
62
66
  "data-variant": variant,
63
67
  "data-surface": surface,
68
+ "data-frame": inset ? "inset" : void 0,
64
69
  className: cn(
65
70
  "shrink-0 flex-col bg-sidebar text-sidebar-foreground",
66
71
  // The rail is its own surface. Anything placed in it that inks with
@@ -71,8 +76,11 @@ function Sidebar({
71
76
  // a theme names --sidebar-muted-foreground.
72
77
  // On the page surface the rail is separated by space and its
73
78
  // content, not a rule and a tonal step.
74
- surface === "rail" ? cn("border-r border-sidebar-border", SIDEBAR_INK) : PAGE_INK,
79
+ // In an inset AppShell the rail is the ground itself, so from lg its border
80
+ // goes too: the gap around the main panel is the divider.
81
+ surface === "rail" ? cn("border-r border-sidebar-border", inset && INSET_RAIL_SURFACE, SIDEBAR_INK) : PAGE_INK,
75
82
  SIDEBAR_VARIANT[variant],
83
+ inset && INSET_RAIL,
76
84
  "w-[var(--sidebar-width,16rem)] data-[collapsed]:w-[var(--sidebar-width-collapsed,4rem)]",
77
85
  // Collapsing eases the width (`motion-size`, expressive) rather than
78
86
  // jumping 192px in one frame. It reflows the page beside the rail for
@@ -164,11 +172,18 @@ function SidebarFooter({
164
172
  className,
165
173
  ...props
166
174
  }) {
175
+ const inset = useAppShellFrame() === "inset";
167
176
  return /* @__PURE__ */ jsx(
168
177
  "div",
169
178
  {
170
179
  className: cn(
171
180
  "flex flex-col gap-1 border-t border-sidebar-border px-3 py-4",
181
+ // In an inset shell the rail has no edge of its own (the gap is the
182
+ // divider), so a full-width rule ran out into that gap and stopped on
183
+ // nothing. Inset to the content column it spans what the rows span:
184
+ // the padding becomes margin, so the rule shortens and nothing inside
185
+ // moves.
186
+ inset && "lg:mx-3 lg:px-0",
172
187
  className
173
188
  ),
174
189
  ...props
@@ -24,6 +24,10 @@ var sidebarMeta = {
24
24
  guidance: "do",
25
25
  description: `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.`
26
26
  },
27
+ {
28
+ guidance: "do",
29
+ description: `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 \u2014 the gap is the divider.`
30
+ },
27
31
  {
28
32
  guidance: "dont",
29
33
  description: "Do not use Sidebar for page-local settings, filters, or temporary action groups."
@@ -76,7 +80,7 @@ var sidebarMeta = {
76
80
  name: "surface",
77
81
  type: '"rail" | "page"',
78
82
  default: '"rail"',
79
- description: `Which surface the rail is painted on. "rail" is its own --sidebar-* surface with a border \u2014 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 \u2014 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.`
83
+ description: `Which surface the rail is painted on. "rail" is its own --sidebar-* surface with a border \u2014 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 \u2014 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.`
80
84
  },
81
85
  {
82
86
  name: "motion",
@@ -146,7 +150,8 @@ var sidebarMeta = {
146
150
  "sidebar-border",
147
151
  "sidebar-accent",
148
152
  "sidebar-accent-foreground",
149
- "sidebar-ring"
153
+ "sidebar-ring",
154
+ "shell-ground"
150
155
  ],
151
156
  variants: {
152
157
  variant: ["app", "inline"]
@@ -106,7 +106,14 @@ var sparklineMeta = {
106
106
  "tone-warning",
107
107
  "muted-foreground",
108
108
  "success",
109
- "destructive"
109
+ "destructive",
110
+ "destructive-mark",
111
+ "success-mark",
112
+ "tone-warning-mark",
113
+ "tone-info-mark",
114
+ "tone-1-mark",
115
+ "tone-2-mark",
116
+ "tone-3-mark"
110
117
  ],
111
118
  motion: [
112
119
  {
@@ -83,7 +83,14 @@ var statusDotMeta = {
83
83
  "tone-info",
84
84
  "tone-1",
85
85
  "tone-2",
86
- "destructive"
86
+ "destructive",
87
+ "destructive-mark",
88
+ "success-mark",
89
+ "tone-warning-mark",
90
+ "tone-info-mark",
91
+ "tone-1-mark",
92
+ "tone-2-mark",
93
+ "tone-3-mark"
87
94
  ],
88
95
  a11y: [
89
96
  "Rendered aria-hidden \u2014 purely decorative. Convey status in adjacent text, not color alone.",
@@ -4,13 +4,13 @@ var statusDotVariants = cva("inline-block shrink-0 rounded-full", {
4
4
  variants: {
5
5
  tone: {
6
6
  neutral: "bg-muted-foreground",
7
- "3": "bg-tone-3",
8
- success: "bg-success",
9
- warning: "bg-tone-warning",
10
- info: "bg-tone-info",
11
- "1": "bg-tone-1",
12
- "2": "bg-tone-2",
13
- destructive: "bg-destructive",
7
+ "3": "bg-tone-3-mark",
8
+ success: "bg-success-mark",
9
+ warning: "bg-tone-warning-mark",
10
+ info: "bg-tone-info-mark",
11
+ "1": "bg-tone-1-mark",
12
+ "2": "bg-tone-2-mark",
13
+ destructive: "bg-destructive-mark",
14
14
  // A palette family paints from the slot its data-palette scope fills.
15
15
  red: "bg-palette-dot",
16
16
  orange: "bg-palette-dot",
@@ -115,7 +115,8 @@ var stepperMeta = {
115
115
  "border-strong",
116
116
  "destructive",
117
117
  "destructive-foreground",
118
- "ring"
118
+ "ring",
119
+ "destructive-mark"
119
120
  ],
120
121
  motion: [
121
122
  {
@@ -24,7 +24,7 @@ var stepperDotVariants = cva(
24
24
  complete: "bg-primary",
25
25
  current: "bg-[var(--sable-surface-bg,var(--background))] ring-[0.1875em] ring-primary ring-inset",
26
26
  upcoming: "bg-border-strong",
27
- error: "bg-destructive"
27
+ error: "bg-destructive-mark"
28
28
  }
29
29
  },
30
30
  defaultVariants: {
@@ -100,13 +100,13 @@ var tableMeta = {
100
100
  },
101
101
  {
102
102
  name: "TableHead",
103
- type: 'ComponentPropsWithRef<"th">',
104
- description: "A column header cell (th) in muted label styling."
103
+ type: 'ComponentPropsWithRef<"th"> & { numeric?: boolean }',
104
+ description: "A column header cell (th) in muted label styling. `numeric` marks a number column: end-aligned, tabular figures \u2014 set it on the column's cells too."
105
105
  },
106
106
  {
107
107
  name: "TableCell",
108
- type: 'ComponentPropsWithRef<"td">',
109
- description: "A data cell (td)."
108
+ type: 'ComponentPropsWithRef<"td"> & { numeric?: boolean }',
109
+ description: "A data cell (td). `numeric` end-aligns it with tabular figures, matching its column's header."
110
110
  },
111
111
  {
112
112
  name: "TableCaption",
@@ -23,6 +23,11 @@ function Textarea({
23
23
  // already lost the invalid ring Input shows on focus.
24
24
  FIELD_SHELL,
25
25
  FIELD_INVALID,
26
+ // Text being edited must not rewrap above the caret. `text-wrap-style`
27
+ // inherits, so a field inside a `text-pretty` or `text-balance` block
28
+ // would re-break every earlier line on each keystroke; `stable` is
29
+ // what an editing surface asks for. Longhand only: the wrap mode stays.
30
+ "[text-wrap-style:stable]",
26
31
  autoResize && "field-sizing-content resize-none",
27
32
  tier.body,
28
33
  density === "comfortable" && MOBILE_FIELD_TIER,
@@ -1,7 +1,7 @@
1
1
  // src/components/textarea/textarea.meta.ts
2
2
  var textareaMeta = {
3
3
  name: "Textarea",
4
- summary: "Styled native multi-line text control. Soft-square, card surface, --ring focus, and an aria-invalid destructive error state.",
4
+ summary: "Styled native multi-line text control. Soft-square, the theme's field surface, --ring focus, and an aria-invalid destructive error state.",
5
5
  status: "stable",
6
6
  keywords: [
7
7
  "textarea",
@@ -75,8 +75,8 @@ var textareaMeta = {
75
75
  }
76
76
  ],
77
77
  tokensUsed: [
78
- "input",
79
- "card",
78
+ "field-border",
79
+ "field",
80
80
  "foreground",
81
81
  "muted-foreground",
82
82
  "ring",