@vegastack/design 0.4.0 → 0.5.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/dist/index.d.cts CHANGED
@@ -25,8 +25,8 @@ import * as React from 'react';
25
25
  * coexist on one tree; the root form is the one that works for both consumers.
26
26
  *
27
27
  * Every value is a literal so Tailwind's scanner sees it in this file — and in the shipped `dist`,
28
- * which `preset.css` scans through `@source "./dist"` (the same contract `surfaceInteractive` and
29
- * `fillInteractive` rely on).
28
+ * which `preset.css` scans through `@source "./dist"` (the same contract `"hover:bg-accent"` and
29
+ * the family's own hover wash rely on).
30
30
  *
31
31
  * Logical properties throughout (`ms`/`ps`/`border-s`/`text-start`): prose is the surface most
32
32
  * likely to carry translated content, and the contract lane asserts RTL containment.
@@ -41,15 +41,15 @@ import * as React from 'react';
41
41
  */
42
42
  declare const prose: {
43
43
  /** The root's own ink and size — every rule below is relative to this. */
44
- readonly root: "text-base text-foreground";
45
- readonly h1: "[&_h1]:mt-6 [&_h1]:mb-3 [&_h1]:scroll-m-20 [&_h1]:text-h1 [&_h1]:text-foreground [&_h1]:first:mt-0";
46
- readonly h2: "[&_h2]:mt-6 [&_h2]:mb-3 [&_h2]:scroll-m-20 [&_h2]:text-h2 [&_h2]:text-foreground [&_h2]:first:mt-0";
47
- readonly h3: "[&_h3]:mt-5 [&_h3]:mb-2 [&_h3]:scroll-m-20 [&_h3]:text-h3 [&_h3]:text-foreground [&_h3]:first:mt-0";
48
- readonly h4: "[&_h4]:mt-4 [&_h4]:mb-2 [&_h4]:scroll-m-20 [&_h4]:text-h4 [&_h4]:text-foreground [&_h4]:first:mt-0";
49
- readonly h5: "[&_h5]:mt-4 [&_h5]:mb-2 [&_h5]:text-label [&_h5]:text-foreground [&_h5]:first:mt-0";
50
- readonly h6: "[&_h6]:mt-4 [&_h6]:mb-2 [&_h6]:text-label [&_h6]:text-muted-foreground [&_h6]:first:mt-0";
44
+ readonly root: "text-sm text-foreground";
45
+ readonly h1: "[&_h1]:mt-6 [&_h1]:mb-3 [&_h1]:scroll-m-20 [&_h1]:text-3xl [&_h1]:font-semibold [&_h1]:text-foreground [&_h1]:first:mt-0";
46
+ readonly h2: "[&_h2]:mt-6 [&_h2]:mb-3 [&_h2]:scroll-m-20 [&_h2]:text-2xl [&_h2]:font-semibold [&_h2]:text-foreground [&_h2]:first:mt-0";
47
+ readonly h3: "[&_h3]:mt-5 [&_h3]:mb-2 [&_h3]:scroll-m-20 [&_h3]:text-xl [&_h3]:font-semibold [&_h3]:text-foreground [&_h3]:first:mt-0";
48
+ readonly h4: "[&_h4]:mt-4 [&_h4]:mb-2 [&_h4]:scroll-m-20 [&_h4]:text-base [&_h4]:font-medium [&_h4]:text-foreground [&_h4]:first:mt-0";
49
+ readonly h5: "[&_h5]:mt-4 [&_h5]:mb-2 [&_h5]:text-sm [&_h5]:font-medium [&_h5]:text-foreground [&_h5]:first:mt-0";
50
+ readonly h6: "[&_h6]:mt-4 [&_h6]:mb-2 [&_h6]:text-sm [&_h6]:font-medium [&_h6]:text-muted-foreground [&_h6]:first:mt-0";
51
51
  readonly p: "[&_p]:my-3 [&_p]:leading-relaxed [&_p]:text-foreground [&_p]:first:mt-0 [&_p]:last:mb-0";
52
- readonly a: "[&_a]:font-medium [&_a]:text-info-text [&_a]:underline [&_a]:underline-offset-4 [&_a]:hover:text-info-text/(--alpha-link-hover)";
52
+ readonly a: "[&_a]:font-medium [&_a]:text-info-text [&_a]:underline [&_a]:underline-offset-4 [&_a]:hover:text-info-text/88";
53
53
  readonly strong: "[&_strong]:font-medium [&_strong]:text-foreground";
54
54
  readonly em: "[&_em]:italic";
55
55
  readonly del: "[&_del]:text-muted-foreground [&_del]:line-through [&_s]:text-muted-foreground [&_s]:line-through";
@@ -57,11 +57,11 @@ declare const prose: {
57
57
  readonly ol: "[&_ol]:my-3 [&_ol]:ms-6 [&_ol]:list-decimal [&_ol]:text-foreground [&_ol]:marker:text-muted-foreground";
58
58
  readonly li: "[&_li]:mt-1.5 [&_li]:leading-relaxed";
59
59
  readonly blockquote: "[&_blockquote]:my-3 [&_blockquote]:border-s-2 [&_blockquote]:border-border [&_blockquote]:ps-4 [&_blockquote]:text-muted-foreground [&_blockquote]:italic";
60
- readonly code: "[&_code]:rounded-sm [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-base [&_code]:text-foreground";
60
+ readonly code: "[&_code]:rounded-sm [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-sm [&_code]:text-foreground";
61
61
  readonly pre: "[&_pre:not([data-slot='code-block-pre'])]:my-3 [&_pre:not([data-slot='code-block-pre'])]:overflow-x-auto [&_pre:not([data-slot='code-block-pre'])]:rounded-lg [&_pre:not([data-slot='code-block-pre'])]:border [&_pre:not([data-slot='code-block-pre'])]:border-border [&_pre:not([data-slot='code-block-pre'])]:bg-muted [&_pre:not([data-slot='code-block-pre'])]:p-4 [&_pre:not([data-slot='code-block-pre'])]:text-foreground";
62
- readonly preCode: "[&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:font-mono [&_pre_code]:text-code [&_pre_code]:text-foreground";
62
+ readonly preCode: "[&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:font-mono [&_pre_code]:text-sm [&_pre_code]:text-foreground";
63
63
  readonly hr: "[&_hr]:my-6 [&_hr]:border-border";
64
- readonly table: "[&_table]:w-full [&_table]:border-collapse [&_table]:text-base [&_table]:text-foreground [&_thead]:border-b [&_thead]:border-border [&_tr]:border-b [&_tr]:border-border [&_tr]:last:border-0 [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-medium [&_th]:text-foreground [&_td]:px-3 [&_td]:py-2 [&_td]:text-muted-foreground";
64
+ readonly table: "[&_table]:w-full [&_table]:border-collapse [&_table]:text-sm [&_table]:text-foreground [&_thead]:border-b [&_thead]:border-border [&_tr]:border-b [&_tr]:border-border [&_tr]:last:border-0 [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-medium [&_th]:text-foreground [&_td]:px-3 [&_td]:py-2 [&_td]:text-muted-foreground";
65
65
  readonly img: "[&_img]:my-3 [&_img]:max-w-full [&_img]:rounded-lg [&_img]:border [&_img]:border-border";
66
66
  };
67
67
  /** The element roles the prose recipe covers. */
@@ -86,170 +86,27 @@ declare const proseClassName: string;
86
86
  *
87
87
  * @example
88
88
  * cn('px-2 py-1', 'px-4') // 'py-1 px-4'
89
- * cn('text-foreground', isError && 'text-destructive')
89
+ * cn('text-foreground', isError && 'text-destructive-text')
90
90
  */
91
91
  declare function cn(...inputs: ClassValue[]): string;
92
92
 
93
93
  /**
94
- * THE hover/pressed recipe for a control that sits on a KNOWN surface (page, card, popover, well):
95
- * hover climbs one rung of the surface ladder, pressing climbs one more. Rows, menu items, ghost and
96
- * outline buttons, sidebar buttons, toggles, tabs, table rows, pagination — every transparent
97
- * control — spread this string instead of writing a `hover:bg-*` literal.
98
- *
99
- * `hover:` compiles under `@media (hover: hover)` in Tailwind v4, so touch devices keep the rest
100
- * fill and still get the pressed rung through `active:`. A selected/current state is the SAME rung
101
- * as pressed (`data-selected:bg-surface-3`), which is why the two are never far apart.
102
- *
103
- * @example
104
- * <button className={cn("rounded-md px-2", surfaceInteractive)} />
105
- */
106
- declare const surfaceInteractive = "hover:bg-surface-2 active:bg-surface-3";
107
- /**
108
- * The GROUP-SCOPED twin of {@link surfaceInteractive}, for the one geometry where the two rungs
109
- * cannot live on the interactive element itself: a wash painted by an INNER chip inset from a
110
- * container hairline (`design.md` §Hover geometry — "a wash is inset ≥4px from a container hairline
111
- * and inherits its inner radius"). NumberField's steppers are the case: the button is full-height and
112
- * flush to the field's border, so its own background would run into that hairline; a `size-full` chip
113
- * inside the button's `p-1` paints the inset wash instead, and it must react to the BUTTON's hover.
114
- *
115
- * The group is named `wash` rather than left unnamed so a consumer's own `group` on an ancestor of a
116
- * copied-in component cannot fire it. Put `group/wash` on the interactive element, this string on the
117
- * chip. Everything else spreads {@link surfaceInteractive} directly — a group indirection where the
118
- * element can carry the rungs itself is noise.
119
- *
120
- * @example
121
- * <button className="group/wash p-1">
122
- * <span className={cn("size-full rounded-sm", surfaceInteractiveGroup)} />
123
- * </button>
124
- */
125
- declare const surfaceInteractiveGroup = "group-hover/wash:bg-surface-2 group-active/wash:bg-surface-3";
126
- /**
127
- * The inks a translucent hover/pressed wash can be composited from — the neutral ink and the five
128
- * chromatic families the Button matrix and its outline/soft variants use.
94
+ * DELETED BY THE SHADCN RESET (Batch 1, 2026-09-18), with no replacement and no alias
95
+ * (mandate § 1, non-negotiable 2): `"hover:bg-accent"`, `"group-hover/wash:bg-accent"`,
96
+ * the family's own hover wash, `FillTone`, `"rounded-lg border border-input bg-transparent transition-colors outline-none placeholder:text-muted-foreground focus:border-ring/70 not-focus:aria-invalid:border-destructive not-focus:data-invalid:border-destructive disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:bg-input/50 data-disabled:opacity-50 dark:bg-input/30 dark:disabled:bg-input/80"`, `"rounded-lg border border-input bg-transparent transition-colors focus-within:border-ring/70 data-focused:border-ring/70 not-focus-within:aria-invalid:border-destructive not-focus-within:has-aria-invalid:border-destructive not-focus-within:data-invalid:border-destructive has-disabled:cursor-not-allowed has-disabled:bg-input/50 has-disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:bg-input/50 data-disabled:opacity-50 dark:bg-input/30"` and the selected-chip recipe.
97
+ *
98
+ * Every one of them was a shared class-string recipe over the surface ladder, the alpha ladder and
99
+ * the `<family>-subtle` tokens — the vocabulary this reset removes. COL-4/COL-5/COL-6/COL-8/COL-9
100
+ * and FRM-1 are all decided as **shadcn**, which means the component, not a shared constant, owns
101
+ * its own hover and pressed chrome, exactly as upstream writes it. The call sites were migrated to
102
+ * upstream's own strings in this batch; Batches 2-7 replace those components with upstream's files.
129
103
  */
130
- type FillTone = "foreground" | "primary" | "destructive" | "success" | "warning" | "info" | "brand";
131
- /**
132
- * The ALPHA twin of {@link surfaceInteractive}: the same two rungs composited from an ink at
133
- * `--alpha-hover` / `--alpha-pressed`, for a control whose backdrop is not a ladder surface (a kbd
134
- * inside a hovered row, a chip on a well, chrome over media) or one that hovers in its OWN hue (the
135
- * outline/soft status buttons). `foreground` is the neutral twin, and it is anchored to a DIFFERENT
136
- * host per theme: in light it lands within 0.003 L of `surface-2`/`surface-3` over the page
137
- * (measured L 0.9430 vs 0.945 and 0.9210 vs 0.922), but the dark ladder is CARD-anchored — over the
138
- * dark `card` it is within 0.003 (0.2665 vs 0.269, 0.2918 vs 0.290), while over the dark
139
- * `background` it is Δ0.028 / Δ0.023, a full rung off (measured 2026-09-09, LOW-7). Read that as
140
- * the constraint it is: the alpha twin substitutes for the opaque rung on the surface a control of
141
- * that theme actually sits on. It is AA-gated over page, card and popover in both themes.
142
- *
143
- * Solid fills do NOT use this: a solid already owns its darker `<tone>-hover` / `<tone>-active`
144
- * steps (`bg-primary hover:bg-primary-hover active:bg-primary-active`) — an alpha over a solid
145
- * would only thin it.
146
- *
147
- * Every value is a literal so Tailwind's scanner sees it in this file (and in the shipped `dist`,
148
- * which `preset.css` scans).
149
- *
150
- * @example
151
- * <button className={cn("bg-destructive-subtle text-destructive-text", fillInteractive.destructive)} />
152
- */
153
- declare const fillInteractive: Record<FillTone, string>;
154
- /**
155
- * THE field chrome — the one border/fill/hover/focus/invalid/disabled grammar every text-entry
156
- * control wears (audit B1-11, 2026-09-07). Input, Textarea, NumberField's input, OTP slots, the
157
- * Select trigger and the Combobox input all spread this string; before it existed the same nine
158
- * declarations were copy-pasted in four files and restated a fifth time as slot overrides, so
159
- * retuning the field meant finding every copy.
160
- *
161
- * It is CHROME only — no width, padding, height or type. Those differ per control (a square OTP
162
- * slot is not `w-full`; a Textarea sizes by min-height, not `--size-*`), so each component adds its
163
- * own layout and size classes after this string.
164
- *
165
- * The three border rungs, in ascending weight:
166
- * - rest `border-input` (the derived `foreground` alpha hairline),
167
- * - hover `foreground` at `--alpha-border-subtle` — neutral ink, one step darker, guarded by
168
- * `not-disabled:not-data-disabled:` because D7 keeps pointer events ON a disabled control so a
169
- * Tooltip can explain it, which would otherwise let a dead field light up under the cursor,
170
- * - focus `ring` at `--alpha-tint-border`, on plain `focus` (not `focus-visible`) — a raw text field
171
- * cannot tell mouse from keyboard, so the tint is the one cue for both. Forced colours erase a
172
- * border tint outright, so the outline fallback for that case is written ONCE, unlayered, in
173
- * `@vegastack/design-tokens`' `base.css` — never per component.
174
- *
175
- * **FOCUS OUTRANKS INVALID, and it has to be said in the selector** (#100, 2026-09-09). The invalid
176
- * tint and the focus tint are the same property at the same specificity, and Tailwind v4 emits
177
- * `aria-invalid:`/`data-invalid:` AFTER `focus:`, so an invalid field simply kept its destructive
178
- * border when focused. Text entry carries `outline-hidden`, so that border IS the whole affordance:
179
- * a focused invalid field had NO focus indicator at all, which is a WCAG 2.2 §2.4.7 failure the
180
- * geometry lane's focus assertion found on its first run. `not-focus:` makes the invalid tint stand
181
- * down while the field is focused rather than fighting the cascade — the error is still carried by
182
- * `aria-invalid`, by Field's message and icon, and by the tint returning on blur, whereas focus has
183
- * exactly one channel. design.md § Accessibility ("focus is the neutral `ring`, never a colour")
184
- * settles which one owns the border when both want it.
185
- *
186
- * @example
187
- * <input className={cn(fieldControl, "h-(--size-md) w-full min-w-0 px-3 text-base")} />
188
- */
189
- declare const fieldControl: string;
190
- /**
191
- * The WRAPPER twin of {@link fieldControl}: the identical chrome on a bordered group whose state
192
- * comes from a descendant — Input's prefix/suffix group, NumberField's stepper group, ChipInput and
193
- * the Combobox input-group. Same three border rungs, read through `focus-within` / `has-*` /
194
- * Base UI's `data-focused` instead of the control's own pseudo-classes.
195
- *
196
- * Every element carrying this string must also carry `data-field-group` (a bare attribute). That is
197
- * the hook `base.css` uses to paint the forced-colours focus outline on the GROUP: the inner input's
198
- * own outline would be clipped by the group's `overflow-hidden`, which is exactly how a High
199
- * Contrast user lost the caret location on an addon field.
200
- *
201
- * @example
202
- * <div data-field-group className={cn(fieldControlGroup, "flex h-(--size-md) items-center")} />
203
- */
204
- declare const fieldControlGroup: string;
205
- /**
206
- * THE selected-chip recipe — one formula for every "raised chip on a muted track" control:
207
- * Tabs `pill` and `chip`, `Segmented`, `Toggle` pressed and `ToggleGroup` pressed. Before this
208
- * existed the four wrote four different selected looks (`bg-background`, `bg-secondary` + hairline,
209
- * `bg-foreground/10`); audit 2026-09-07 B6-02.
210
- *
211
- * The track is the ladder's well rung (`surface-1`); the chip is the ladder's alpha form of the
212
- * pressed/selected step (§Surfaces) — `bg-foreground/(--alpha-ink-tint)`, which doctrine reaches
213
- * for exactly here ("a chip on a well"). Over the `surface-1` track it composites to L 0.899 light
214
- * / 0.318 dark, which is Δ0.023 / Δ0.028 PAST `surface-3` — a full extra rung, since the ladder's
215
- * own step is 0.021–0.033 (measured 2026-09-09, LOW-6; the doc used to claim "within a hair of
216
- * `surface-3`", which is only true of the twin composited over the PAGE, not over the track).
217
- * That extra rung is deliberate and is what makes a selected chip read as raised off its own
218
- * track rather than level with it. Being an alpha is also what lets the SELECTED chip keep stepping: a hovered
219
- * selected chip strengthens to `--alpha-ink-tint-strong` and a pressed one drops back to the resting
220
- * tint (previewing the release), so no state ever reads as dead — an opaque `surface-3` chip would
221
- * have nowhere left to climb.
222
- *
223
- * Base UI spells "selected" differently per primitive, so the state rules ship as two literal
224
- * strings rather than a selector parameter (Tailwind v4's scanner only sees literals):
225
- * {@link selectedChipVariants.pressed} for Toggle/ToggleGroup/Segmented (`data-pressed`) and
226
- * {@link selectedChipVariants.active} for Tabs (`data-active`). The unselected steps are guarded by
227
- * the matching `not-*` variant so the two sets are mutually exclusive and never race on specificity.
228
- *
229
- * @example
230
- * <div className={cn("rounded-md p-0.5", selectedChipVariants.track)}>
231
- * <Toggle className={cn("rounded-sm", selectedChipVariants.item, selectedChipVariants.pressed)} />
232
- * </div>
233
- */
234
- declare const selectedChipVariants: {
235
- /** The muted track the chips sit in — the ladder's well rung. */
236
- readonly track: "bg-surface-1";
237
- /**
238
- * Chrome shared by every chip: a transparent hairline reserved at rest (so selecting adds no
239
- * layout shift) and the muted→ink text step.
240
- */
241
- readonly item: "border border-transparent hover:text-foreground";
242
- /** Selected keyed on Base UI's `data-pressed` — Toggle, ToggleGroup, Segmented. */
243
- readonly pressed: "not-data-pressed:hover:bg-foreground/(--alpha-hover) not-data-pressed:active:bg-foreground/(--alpha-pressed) data-pressed:border-border data-pressed:bg-foreground/(--alpha-ink-tint) data-pressed:text-foreground data-pressed:hover:bg-foreground/(--alpha-ink-tint-strong) data-pressed:active:bg-foreground/(--alpha-ink-tint)";
244
- /** Selected keyed on Base UI's `data-active` — Tabs. */
245
- readonly active: "not-data-[active]:hover:bg-foreground/(--alpha-hover) not-data-[active]:active:bg-foreground/(--alpha-pressed) data-[active]:border-border data-[active]:bg-foreground/(--alpha-ink-tint) data-[active]:text-foreground data-[active]:hover:bg-foreground/(--alpha-ink-tint-strong) data-[active]:active:bg-foreground/(--alpha-ink-tint)";
246
- };
247
104
  /**
248
105
  * @internal Registry theme-scope plumbing lives at `@vegastack/design/theme-scope`, NOT here.
249
106
  * It calls `React.createContext()` at module scope, which is `undefined` under the `react-server`
250
107
  * condition — re-exporting it from this entry would make every Server Component that imports
251
108
  * `cn` crash on import. This entry stays server-safe by contract (see tsup.config.ts).
252
- * Product code should use `MarketingSurface` rather than either symbol.
109
+ * Product code never reaches for either symbol; the canonical overlays apply the scope.
253
110
  */
254
111
  /**
255
112
  * Interaction timing constants (ms) — design decisions, not magic numbers (register P2-14).
@@ -325,4 +182,4 @@ declare const FLOATING: {
325
182
  */
326
183
  declare function mergeRefs<T>(...refs: Array<React.Ref<T> | null | undefined>): React.RefCallback<T>;
327
184
 
328
- export { FLOATING, type FillTone, type ProseElement, TIMINGS, cn, fieldControl, fieldControlGroup, fillInteractive, mergeRefs, prose, proseClassName, selectedChipVariants, surfaceInteractive, surfaceInteractiveGroup };
185
+ export { FLOATING, type ProseElement, TIMINGS, cn, mergeRefs, prose, proseClassName };
package/dist/index.d.ts CHANGED
@@ -25,8 +25,8 @@ import * as React from 'react';
25
25
  * coexist on one tree; the root form is the one that works for both consumers.
26
26
  *
27
27
  * Every value is a literal so Tailwind's scanner sees it in this file — and in the shipped `dist`,
28
- * which `preset.css` scans through `@source "./dist"` (the same contract `surfaceInteractive` and
29
- * `fillInteractive` rely on).
28
+ * which `preset.css` scans through `@source "./dist"` (the same contract `"hover:bg-accent"` and
29
+ * the family's own hover wash rely on).
30
30
  *
31
31
  * Logical properties throughout (`ms`/`ps`/`border-s`/`text-start`): prose is the surface most
32
32
  * likely to carry translated content, and the contract lane asserts RTL containment.
@@ -41,15 +41,15 @@ import * as React from 'react';
41
41
  */
42
42
  declare const prose: {
43
43
  /** The root's own ink and size — every rule below is relative to this. */
44
- readonly root: "text-base text-foreground";
45
- readonly h1: "[&_h1]:mt-6 [&_h1]:mb-3 [&_h1]:scroll-m-20 [&_h1]:text-h1 [&_h1]:text-foreground [&_h1]:first:mt-0";
46
- readonly h2: "[&_h2]:mt-6 [&_h2]:mb-3 [&_h2]:scroll-m-20 [&_h2]:text-h2 [&_h2]:text-foreground [&_h2]:first:mt-0";
47
- readonly h3: "[&_h3]:mt-5 [&_h3]:mb-2 [&_h3]:scroll-m-20 [&_h3]:text-h3 [&_h3]:text-foreground [&_h3]:first:mt-0";
48
- readonly h4: "[&_h4]:mt-4 [&_h4]:mb-2 [&_h4]:scroll-m-20 [&_h4]:text-h4 [&_h4]:text-foreground [&_h4]:first:mt-0";
49
- readonly h5: "[&_h5]:mt-4 [&_h5]:mb-2 [&_h5]:text-label [&_h5]:text-foreground [&_h5]:first:mt-0";
50
- readonly h6: "[&_h6]:mt-4 [&_h6]:mb-2 [&_h6]:text-label [&_h6]:text-muted-foreground [&_h6]:first:mt-0";
44
+ readonly root: "text-sm text-foreground";
45
+ readonly h1: "[&_h1]:mt-6 [&_h1]:mb-3 [&_h1]:scroll-m-20 [&_h1]:text-3xl [&_h1]:font-semibold [&_h1]:text-foreground [&_h1]:first:mt-0";
46
+ readonly h2: "[&_h2]:mt-6 [&_h2]:mb-3 [&_h2]:scroll-m-20 [&_h2]:text-2xl [&_h2]:font-semibold [&_h2]:text-foreground [&_h2]:first:mt-0";
47
+ readonly h3: "[&_h3]:mt-5 [&_h3]:mb-2 [&_h3]:scroll-m-20 [&_h3]:text-xl [&_h3]:font-semibold [&_h3]:text-foreground [&_h3]:first:mt-0";
48
+ readonly h4: "[&_h4]:mt-4 [&_h4]:mb-2 [&_h4]:scroll-m-20 [&_h4]:text-base [&_h4]:font-medium [&_h4]:text-foreground [&_h4]:first:mt-0";
49
+ readonly h5: "[&_h5]:mt-4 [&_h5]:mb-2 [&_h5]:text-sm [&_h5]:font-medium [&_h5]:text-foreground [&_h5]:first:mt-0";
50
+ readonly h6: "[&_h6]:mt-4 [&_h6]:mb-2 [&_h6]:text-sm [&_h6]:font-medium [&_h6]:text-muted-foreground [&_h6]:first:mt-0";
51
51
  readonly p: "[&_p]:my-3 [&_p]:leading-relaxed [&_p]:text-foreground [&_p]:first:mt-0 [&_p]:last:mb-0";
52
- readonly a: "[&_a]:font-medium [&_a]:text-info-text [&_a]:underline [&_a]:underline-offset-4 [&_a]:hover:text-info-text/(--alpha-link-hover)";
52
+ readonly a: "[&_a]:font-medium [&_a]:text-info-text [&_a]:underline [&_a]:underline-offset-4 [&_a]:hover:text-info-text/88";
53
53
  readonly strong: "[&_strong]:font-medium [&_strong]:text-foreground";
54
54
  readonly em: "[&_em]:italic";
55
55
  readonly del: "[&_del]:text-muted-foreground [&_del]:line-through [&_s]:text-muted-foreground [&_s]:line-through";
@@ -57,11 +57,11 @@ declare const prose: {
57
57
  readonly ol: "[&_ol]:my-3 [&_ol]:ms-6 [&_ol]:list-decimal [&_ol]:text-foreground [&_ol]:marker:text-muted-foreground";
58
58
  readonly li: "[&_li]:mt-1.5 [&_li]:leading-relaxed";
59
59
  readonly blockquote: "[&_blockquote]:my-3 [&_blockquote]:border-s-2 [&_blockquote]:border-border [&_blockquote]:ps-4 [&_blockquote]:text-muted-foreground [&_blockquote]:italic";
60
- readonly code: "[&_code]:rounded-sm [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-base [&_code]:text-foreground";
60
+ readonly code: "[&_code]:rounded-sm [&_code]:bg-muted [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-sm [&_code]:text-foreground";
61
61
  readonly pre: "[&_pre:not([data-slot='code-block-pre'])]:my-3 [&_pre:not([data-slot='code-block-pre'])]:overflow-x-auto [&_pre:not([data-slot='code-block-pre'])]:rounded-lg [&_pre:not([data-slot='code-block-pre'])]:border [&_pre:not([data-slot='code-block-pre'])]:border-border [&_pre:not([data-slot='code-block-pre'])]:bg-muted [&_pre:not([data-slot='code-block-pre'])]:p-4 [&_pre:not([data-slot='code-block-pre'])]:text-foreground";
62
- readonly preCode: "[&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:font-mono [&_pre_code]:text-code [&_pre_code]:text-foreground";
62
+ readonly preCode: "[&_pre_code]:bg-transparent [&_pre_code]:p-0 [&_pre_code]:font-mono [&_pre_code]:text-sm [&_pre_code]:text-foreground";
63
63
  readonly hr: "[&_hr]:my-6 [&_hr]:border-border";
64
- readonly table: "[&_table]:w-full [&_table]:border-collapse [&_table]:text-base [&_table]:text-foreground [&_thead]:border-b [&_thead]:border-border [&_tr]:border-b [&_tr]:border-border [&_tr]:last:border-0 [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-medium [&_th]:text-foreground [&_td]:px-3 [&_td]:py-2 [&_td]:text-muted-foreground";
64
+ readonly table: "[&_table]:w-full [&_table]:border-collapse [&_table]:text-sm [&_table]:text-foreground [&_thead]:border-b [&_thead]:border-border [&_tr]:border-b [&_tr]:border-border [&_tr]:last:border-0 [&_th]:px-3 [&_th]:py-2 [&_th]:text-start [&_th]:font-medium [&_th]:text-foreground [&_td]:px-3 [&_td]:py-2 [&_td]:text-muted-foreground";
65
65
  readonly img: "[&_img]:my-3 [&_img]:max-w-full [&_img]:rounded-lg [&_img]:border [&_img]:border-border";
66
66
  };
67
67
  /** The element roles the prose recipe covers. */
@@ -86,170 +86,27 @@ declare const proseClassName: string;
86
86
  *
87
87
  * @example
88
88
  * cn('px-2 py-1', 'px-4') // 'py-1 px-4'
89
- * cn('text-foreground', isError && 'text-destructive')
89
+ * cn('text-foreground', isError && 'text-destructive-text')
90
90
  */
91
91
  declare function cn(...inputs: ClassValue[]): string;
92
92
 
93
93
  /**
94
- * THE hover/pressed recipe for a control that sits on a KNOWN surface (page, card, popover, well):
95
- * hover climbs one rung of the surface ladder, pressing climbs one more. Rows, menu items, ghost and
96
- * outline buttons, sidebar buttons, toggles, tabs, table rows, pagination — every transparent
97
- * control — spread this string instead of writing a `hover:bg-*` literal.
98
- *
99
- * `hover:` compiles under `@media (hover: hover)` in Tailwind v4, so touch devices keep the rest
100
- * fill and still get the pressed rung through `active:`. A selected/current state is the SAME rung
101
- * as pressed (`data-selected:bg-surface-3`), which is why the two are never far apart.
102
- *
103
- * @example
104
- * <button className={cn("rounded-md px-2", surfaceInteractive)} />
105
- */
106
- declare const surfaceInteractive = "hover:bg-surface-2 active:bg-surface-3";
107
- /**
108
- * The GROUP-SCOPED twin of {@link surfaceInteractive}, for the one geometry where the two rungs
109
- * cannot live on the interactive element itself: a wash painted by an INNER chip inset from a
110
- * container hairline (`design.md` §Hover geometry — "a wash is inset ≥4px from a container hairline
111
- * and inherits its inner radius"). NumberField's steppers are the case: the button is full-height and
112
- * flush to the field's border, so its own background would run into that hairline; a `size-full` chip
113
- * inside the button's `p-1` paints the inset wash instead, and it must react to the BUTTON's hover.
114
- *
115
- * The group is named `wash` rather than left unnamed so a consumer's own `group` on an ancestor of a
116
- * copied-in component cannot fire it. Put `group/wash` on the interactive element, this string on the
117
- * chip. Everything else spreads {@link surfaceInteractive} directly — a group indirection where the
118
- * element can carry the rungs itself is noise.
119
- *
120
- * @example
121
- * <button className="group/wash p-1">
122
- * <span className={cn("size-full rounded-sm", surfaceInteractiveGroup)} />
123
- * </button>
124
- */
125
- declare const surfaceInteractiveGroup = "group-hover/wash:bg-surface-2 group-active/wash:bg-surface-3";
126
- /**
127
- * The inks a translucent hover/pressed wash can be composited from — the neutral ink and the five
128
- * chromatic families the Button matrix and its outline/soft variants use.
94
+ * DELETED BY THE SHADCN RESET (Batch 1, 2026-09-18), with no replacement and no alias
95
+ * (mandate § 1, non-negotiable 2): `"hover:bg-accent"`, `"group-hover/wash:bg-accent"`,
96
+ * the family's own hover wash, `FillTone`, `"rounded-lg border border-input bg-transparent transition-colors outline-none placeholder:text-muted-foreground focus:border-ring/70 not-focus:aria-invalid:border-destructive not-focus:data-invalid:border-destructive disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:bg-input/50 data-disabled:opacity-50 dark:bg-input/30 dark:disabled:bg-input/80"`, `"rounded-lg border border-input bg-transparent transition-colors focus-within:border-ring/70 data-focused:border-ring/70 not-focus-within:aria-invalid:border-destructive not-focus-within:has-aria-invalid:border-destructive not-focus-within:data-invalid:border-destructive has-disabled:cursor-not-allowed has-disabled:bg-input/50 has-disabled:opacity-50 data-disabled:cursor-not-allowed data-disabled:bg-input/50 data-disabled:opacity-50 dark:bg-input/30"` and the selected-chip recipe.
97
+ *
98
+ * Every one of them was a shared class-string recipe over the surface ladder, the alpha ladder and
99
+ * the `<family>-subtle` tokens — the vocabulary this reset removes. COL-4/COL-5/COL-6/COL-8/COL-9
100
+ * and FRM-1 are all decided as **shadcn**, which means the component, not a shared constant, owns
101
+ * its own hover and pressed chrome, exactly as upstream writes it. The call sites were migrated to
102
+ * upstream's own strings in this batch; Batches 2-7 replace those components with upstream's files.
129
103
  */
130
- type FillTone = "foreground" | "primary" | "destructive" | "success" | "warning" | "info" | "brand";
131
- /**
132
- * The ALPHA twin of {@link surfaceInteractive}: the same two rungs composited from an ink at
133
- * `--alpha-hover` / `--alpha-pressed`, for a control whose backdrop is not a ladder surface (a kbd
134
- * inside a hovered row, a chip on a well, chrome over media) or one that hovers in its OWN hue (the
135
- * outline/soft status buttons). `foreground` is the neutral twin, and it is anchored to a DIFFERENT
136
- * host per theme: in light it lands within 0.003 L of `surface-2`/`surface-3` over the page
137
- * (measured L 0.9430 vs 0.945 and 0.9210 vs 0.922), but the dark ladder is CARD-anchored — over the
138
- * dark `card` it is within 0.003 (0.2665 vs 0.269, 0.2918 vs 0.290), while over the dark
139
- * `background` it is Δ0.028 / Δ0.023, a full rung off (measured 2026-09-09, LOW-7). Read that as
140
- * the constraint it is: the alpha twin substitutes for the opaque rung on the surface a control of
141
- * that theme actually sits on. It is AA-gated over page, card and popover in both themes.
142
- *
143
- * Solid fills do NOT use this: a solid already owns its darker `<tone>-hover` / `<tone>-active`
144
- * steps (`bg-primary hover:bg-primary-hover active:bg-primary-active`) — an alpha over a solid
145
- * would only thin it.
146
- *
147
- * Every value is a literal so Tailwind's scanner sees it in this file (and in the shipped `dist`,
148
- * which `preset.css` scans).
149
- *
150
- * @example
151
- * <button className={cn("bg-destructive-subtle text-destructive-text", fillInteractive.destructive)} />
152
- */
153
- declare const fillInteractive: Record<FillTone, string>;
154
- /**
155
- * THE field chrome — the one border/fill/hover/focus/invalid/disabled grammar every text-entry
156
- * control wears (audit B1-11, 2026-09-07). Input, Textarea, NumberField's input, OTP slots, the
157
- * Select trigger and the Combobox input all spread this string; before it existed the same nine
158
- * declarations were copy-pasted in four files and restated a fifth time as slot overrides, so
159
- * retuning the field meant finding every copy.
160
- *
161
- * It is CHROME only — no width, padding, height or type. Those differ per control (a square OTP
162
- * slot is not `w-full`; a Textarea sizes by min-height, not `--size-*`), so each component adds its
163
- * own layout and size classes after this string.
164
- *
165
- * The three border rungs, in ascending weight:
166
- * - rest `border-input` (the derived `foreground` alpha hairline),
167
- * - hover `foreground` at `--alpha-border-subtle` — neutral ink, one step darker, guarded by
168
- * `not-disabled:not-data-disabled:` because D7 keeps pointer events ON a disabled control so a
169
- * Tooltip can explain it, which would otherwise let a dead field light up under the cursor,
170
- * - focus `ring` at `--alpha-tint-border`, on plain `focus` (not `focus-visible`) — a raw text field
171
- * cannot tell mouse from keyboard, so the tint is the one cue for both. Forced colours erase a
172
- * border tint outright, so the outline fallback for that case is written ONCE, unlayered, in
173
- * `@vegastack/design-tokens`' `base.css` — never per component.
174
- *
175
- * **FOCUS OUTRANKS INVALID, and it has to be said in the selector** (#100, 2026-09-09). The invalid
176
- * tint and the focus tint are the same property at the same specificity, and Tailwind v4 emits
177
- * `aria-invalid:`/`data-invalid:` AFTER `focus:`, so an invalid field simply kept its destructive
178
- * border when focused. Text entry carries `outline-hidden`, so that border IS the whole affordance:
179
- * a focused invalid field had NO focus indicator at all, which is a WCAG 2.2 §2.4.7 failure the
180
- * geometry lane's focus assertion found on its first run. `not-focus:` makes the invalid tint stand
181
- * down while the field is focused rather than fighting the cascade — the error is still carried by
182
- * `aria-invalid`, by Field's message and icon, and by the tint returning on blur, whereas focus has
183
- * exactly one channel. design.md § Accessibility ("focus is the neutral `ring`, never a colour")
184
- * settles which one owns the border when both want it.
185
- *
186
- * @example
187
- * <input className={cn(fieldControl, "h-(--size-md) w-full min-w-0 px-3 text-base")} />
188
- */
189
- declare const fieldControl: string;
190
- /**
191
- * The WRAPPER twin of {@link fieldControl}: the identical chrome on a bordered group whose state
192
- * comes from a descendant — Input's prefix/suffix group, NumberField's stepper group, ChipInput and
193
- * the Combobox input-group. Same three border rungs, read through `focus-within` / `has-*` /
194
- * Base UI's `data-focused` instead of the control's own pseudo-classes.
195
- *
196
- * Every element carrying this string must also carry `data-field-group` (a bare attribute). That is
197
- * the hook `base.css` uses to paint the forced-colours focus outline on the GROUP: the inner input's
198
- * own outline would be clipped by the group's `overflow-hidden`, which is exactly how a High
199
- * Contrast user lost the caret location on an addon field.
200
- *
201
- * @example
202
- * <div data-field-group className={cn(fieldControlGroup, "flex h-(--size-md) items-center")} />
203
- */
204
- declare const fieldControlGroup: string;
205
- /**
206
- * THE selected-chip recipe — one formula for every "raised chip on a muted track" control:
207
- * Tabs `pill` and `chip`, `Segmented`, `Toggle` pressed and `ToggleGroup` pressed. Before this
208
- * existed the four wrote four different selected looks (`bg-background`, `bg-secondary` + hairline,
209
- * `bg-foreground/10`); audit 2026-09-07 B6-02.
210
- *
211
- * The track is the ladder's well rung (`surface-1`); the chip is the ladder's alpha form of the
212
- * pressed/selected step (§Surfaces) — `bg-foreground/(--alpha-ink-tint)`, which doctrine reaches
213
- * for exactly here ("a chip on a well"). Over the `surface-1` track it composites to L 0.899 light
214
- * / 0.318 dark, which is Δ0.023 / Δ0.028 PAST `surface-3` — a full extra rung, since the ladder's
215
- * own step is 0.021–0.033 (measured 2026-09-09, LOW-6; the doc used to claim "within a hair of
216
- * `surface-3`", which is only true of the twin composited over the PAGE, not over the track).
217
- * That extra rung is deliberate and is what makes a selected chip read as raised off its own
218
- * track rather than level with it. Being an alpha is also what lets the SELECTED chip keep stepping: a hovered
219
- * selected chip strengthens to `--alpha-ink-tint-strong` and a pressed one drops back to the resting
220
- * tint (previewing the release), so no state ever reads as dead — an opaque `surface-3` chip would
221
- * have nowhere left to climb.
222
- *
223
- * Base UI spells "selected" differently per primitive, so the state rules ship as two literal
224
- * strings rather than a selector parameter (Tailwind v4's scanner only sees literals):
225
- * {@link selectedChipVariants.pressed} for Toggle/ToggleGroup/Segmented (`data-pressed`) and
226
- * {@link selectedChipVariants.active} for Tabs (`data-active`). The unselected steps are guarded by
227
- * the matching `not-*` variant so the two sets are mutually exclusive and never race on specificity.
228
- *
229
- * @example
230
- * <div className={cn("rounded-md p-0.5", selectedChipVariants.track)}>
231
- * <Toggle className={cn("rounded-sm", selectedChipVariants.item, selectedChipVariants.pressed)} />
232
- * </div>
233
- */
234
- declare const selectedChipVariants: {
235
- /** The muted track the chips sit in — the ladder's well rung. */
236
- readonly track: "bg-surface-1";
237
- /**
238
- * Chrome shared by every chip: a transparent hairline reserved at rest (so selecting adds no
239
- * layout shift) and the muted→ink text step.
240
- */
241
- readonly item: "border border-transparent hover:text-foreground";
242
- /** Selected keyed on Base UI's `data-pressed` — Toggle, ToggleGroup, Segmented. */
243
- readonly pressed: "not-data-pressed:hover:bg-foreground/(--alpha-hover) not-data-pressed:active:bg-foreground/(--alpha-pressed) data-pressed:border-border data-pressed:bg-foreground/(--alpha-ink-tint) data-pressed:text-foreground data-pressed:hover:bg-foreground/(--alpha-ink-tint-strong) data-pressed:active:bg-foreground/(--alpha-ink-tint)";
244
- /** Selected keyed on Base UI's `data-active` — Tabs. */
245
- readonly active: "not-data-[active]:hover:bg-foreground/(--alpha-hover) not-data-[active]:active:bg-foreground/(--alpha-pressed) data-[active]:border-border data-[active]:bg-foreground/(--alpha-ink-tint) data-[active]:text-foreground data-[active]:hover:bg-foreground/(--alpha-ink-tint-strong) data-[active]:active:bg-foreground/(--alpha-ink-tint)";
246
- };
247
104
  /**
248
105
  * @internal Registry theme-scope plumbing lives at `@vegastack/design/theme-scope`, NOT here.
249
106
  * It calls `React.createContext()` at module scope, which is `undefined` under the `react-server`
250
107
  * condition — re-exporting it from this entry would make every Server Component that imports
251
108
  * `cn` crash on import. This entry stays server-safe by contract (see tsup.config.ts).
252
- * Product code should use `MarketingSurface` rather than either symbol.
109
+ * Product code never reaches for either symbol; the canonical overlays apply the scope.
253
110
  */
254
111
  /**
255
112
  * Interaction timing constants (ms) — design decisions, not magic numbers (register P2-14).
@@ -325,4 +182,4 @@ declare const FLOATING: {
325
182
  */
326
183
  declare function mergeRefs<T>(...refs: Array<React.Ref<T> | null | undefined>): React.RefCallback<T>;
327
184
 
328
- export { FLOATING, type FillTone, type ProseElement, TIMINGS, cn, fieldControl, fieldControlGroup, fillInteractive, mergeRefs, prose, proseClassName, selectedChipVariants, surfaceInteractive, surfaceInteractiveGroup };
185
+ export { FLOATING, type ProseElement, TIMINGS, cn, mergeRefs, prose, proseClassName };
package/dist/index.js CHANGED
@@ -2,27 +2,15 @@ import {
2
2
  FLOATING,
3
3
  TIMINGS,
4
4
  cn,
5
- fieldControl,
6
- fieldControlGroup,
7
- fillInteractive,
8
5
  mergeRefs,
9
6
  prose,
10
- proseClassName,
11
- selectedChipVariants,
12
- surfaceInteractive,
13
- surfaceInteractiveGroup
14
- } from "./chunk-LRQSVCP6.js";
7
+ proseClassName
8
+ } from "./chunk-42HJ4OYZ.js";
15
9
  export {
16
10
  FLOATING,
17
11
  TIMINGS,
18
12
  cn,
19
- fieldControl,
20
- fieldControlGroup,
21
- fillInteractive,
22
13
  mergeRefs,
23
14
  prose,
24
- proseClassName,
25
- selectedChipVariants,
26
- surfaceInteractive,
27
- surfaceInteractiveGroup
15
+ proseClassName
28
16
  };
@@ -5,7 +5,7 @@ import * as React from 'react';
5
5
  * portals whose DOM nodes are mounted outside the scoped source subtree.
6
6
  *
7
7
  * @internal Registry infrastructure only. This is not a consumer-facing
8
- * theming API; use `MarketingSurface` to establish the supported scope.
8
+ * theming API.
9
9
  */
10
10
  declare function InternalThemeScopeProvider({ children, scope, }: {
11
11
  children: React.ReactNode;
@@ -5,7 +5,7 @@ import * as React from 'react';
5
5
  * portals whose DOM nodes are mounted outside the scoped source subtree.
6
6
  *
7
7
  * @internal Registry infrastructure only. This is not a consumer-facing
8
- * theming API; use `MarketingSurface` to establish the supported scope.
8
+ * theming API.
9
9
  */
10
10
  declare function InternalThemeScopeProvider({ children, scope, }: {
11
11
  children: React.ReactNode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vegastack/design",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "VegaStack design system — cn utility, icon runtime, Tailwind v4 preset, and the vegastack-design CLI (tokens ship separately as @vegastack/design-tokens)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -79,7 +79,7 @@
79
79
  "peerDependencies": {
80
80
  "react": "^19.0.0",
81
81
  "react-dom": "^19.0.0",
82
- "lucide-react": "^1.24.0",
82
+ "lucide-react": "^1.47.0",
83
83
  "motion": "^13.2.0",
84
84
  "thesvg": "^3.2.6",
85
85
  "tailwindcss": "catalog:"
@@ -106,7 +106,7 @@
106
106
  "@vegastack/eslint-config": "workspace:*",
107
107
  "@vegastack/typescript-config": "workspace:*",
108
108
  "eslint": "^10.10.0",
109
- "lucide-react": "^1.42.0",
109
+ "lucide-react": "^1.47.0",
110
110
  "motion": "^13.2.0",
111
111
  "react": "catalog:",
112
112
  "react-dom": "catalog:",
@@ -25,5 +25,6 @@ Brand marks for external and marketing surfaces are rendered through `BrandIcon`
25
25
  - Logo usage: variants, clear-space, minimum sizes, prohibited treatments.
26
26
  - Brand colour and marketing typography — a separate layer from the product tokens, never a
27
27
  replacement for them.
28
- - Marketing surface patterns, which in the component library are scoped to `.vs-marketing` and must
29
- never appear in product UI.
28
+ - Marketing surface patterns. The component library ships none: the marketing layer — its
29
+ components, its `.vs-marketing` scope and its tokens — was deleted on 2026-09-18, so a marketing
30
+ surface is a brand-layer question with no product-side answer yet.