@vegastack/design 0.3.2 → 0.4.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/bin/verify-registry-item.mjs +1 -0
- package/dist/chunk-LRQSVCP6.js +182 -0
- package/dist/create-animated-icon.cjs +437 -0
- package/dist/create-animated-icon.d.cts +210 -0
- package/dist/create-animated-icon.d.ts +210 -0
- package/dist/create-animated-icon.js +350 -0
- package/dist/icons/index.cjs +26 -0
- package/dist/icons/index.d.cts +15 -8
- package/dist/icons/index.d.ts +15 -8
- package/dist/icons/index.js +1 -1
- package/dist/index.cjs +142 -3
- package/dist/index.d.cts +273 -1
- package/dist/index.d.ts +273 -1
- package/dist/index.js +21 -3
- package/package.json +20 -9
- package/preset.css +1 -1
- package/skills/vegastack-consume/SKILL.md +5 -5
- package/skills/vegastack-design-audit/SKILL.md +3 -1
- package/skills/vegastack-design-system/SKILL.md +105 -3
- package/skills/vegastack-design-system/references/components.md +21 -15
- package/dist/chunk-FBU37ITM.js +0 -52
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,79 @@
|
|
|
1
1
|
import { ClassValue } from 'clsx';
|
|
2
2
|
export { ClassValue } from 'clsx';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* THE prose recipe — one token vocabulary for rendered rich text (audit B4-09, 2026-09-07).
|
|
7
|
+
*
|
|
8
|
+
* Two surfaces render prose the system did not author element-by-element: `MarkdownView`
|
|
9
|
+
* (react-markdown turns a markdown string into plain HTML elements) and `TextEdit` (ProseMirror
|
|
10
|
+
* owns the contenteditable DOM). Before this file each restated the same heading/paragraph/list/
|
|
11
|
+
* code/quote recipe in its own grammar — a `Components` map in one, 69 `[&_h1]:` rules in the
|
|
12
|
+
* other — and they had already drifted (`h4`–`h6`, `table`, `img` existed on one side only).
|
|
13
|
+
*
|
|
14
|
+
* ## Why descendant variants and not per-element classes
|
|
15
|
+
*
|
|
16
|
+
* A prose root cannot put a class on every element: ProseMirror generates the editor's DOM, and
|
|
17
|
+
* react-markdown's output is only reachable through an override map. Descendant variants
|
|
18
|
+
* (`[&_h1]:mt-6` → `.recipe h1 { margin-top: … }`) style both from ONE string on the root, which
|
|
19
|
+
* is what makes "MarkdownView and TextEdit render the same computed styles" true by construction
|
|
20
|
+
* rather than by review.
|
|
21
|
+
*
|
|
22
|
+
* It also settles a cascade trap the two-grammar version would have re-created: `[&_h1]:mt-6`
|
|
23
|
+
* compiles at specificity (0,1,1) and a plain `.mt-6` on the element at (0,1,0), so an
|
|
24
|
+
* element-level class silently LOSES to a root-level descendant rule. The two forms cannot
|
|
25
|
+
* coexist on one tree; the root form is the one that works for both consumers.
|
|
26
|
+
*
|
|
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).
|
|
30
|
+
*
|
|
31
|
+
* Logical properties throughout (`ms`/`ps`/`border-s`/`text-start`): prose is the surface most
|
|
32
|
+
* likely to carry translated content, and the contract lane asserts RTL containment.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // A prose root — the whole recipe:
|
|
36
|
+
* <div className={cn(proseClassName, className)} />
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // One element's rules, composed into a narrower surface:
|
|
40
|
+
* <div className={cn(prose.root, prose.p, prose.code)} />
|
|
41
|
+
*/
|
|
42
|
+
declare const prose: {
|
|
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";
|
|
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)";
|
|
53
|
+
readonly strong: "[&_strong]:font-medium [&_strong]:text-foreground";
|
|
54
|
+
readonly em: "[&_em]:italic";
|
|
55
|
+
readonly del: "[&_del]:text-muted-foreground [&_del]:line-through [&_s]:text-muted-foreground [&_s]:line-through";
|
|
56
|
+
readonly ul: "[&_ul]:my-3 [&_ul]:ms-6 [&_ul]:list-disc [&_ul]:text-foreground [&_ul]:marker:text-muted-foreground [&_ul.contains-task-list]:list-none";
|
|
57
|
+
readonly ol: "[&_ol]:my-3 [&_ol]:ms-6 [&_ol]:list-decimal [&_ol]:text-foreground [&_ol]:marker:text-muted-foreground";
|
|
58
|
+
readonly li: "[&_li]:mt-1.5 [&_li]:leading-relaxed";
|
|
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";
|
|
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";
|
|
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";
|
|
65
|
+
readonly img: "[&_img]:my-3 [&_img]:max-w-full [&_img]:rounded-lg [&_img]:border [&_img]:border-border";
|
|
66
|
+
};
|
|
67
|
+
/** The element roles the prose recipe covers. */
|
|
68
|
+
type ProseElement = keyof typeof prose;
|
|
69
|
+
/**
|
|
70
|
+
* The whole recipe as one class string — what a prose root wears.
|
|
71
|
+
*
|
|
72
|
+
* Joined rather than merged: every entry scopes a different selector, so there is no conflict for
|
|
73
|
+
* `cn` to resolve, and a plain join keeps the composition free of a `tailwind-merge` pass on a
|
|
74
|
+
* string that never changes.
|
|
75
|
+
*/
|
|
76
|
+
declare const proseClassName: string;
|
|
3
77
|
|
|
4
78
|
/**
|
|
5
79
|
* Merges Tailwind CSS class names with intelligent conflict resolution.
|
|
@@ -16,6 +90,160 @@ export { ClassValue } from 'clsx';
|
|
|
16
90
|
*/
|
|
17
91
|
declare function cn(...inputs: ClassValue[]): string;
|
|
18
92
|
|
|
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.
|
|
129
|
+
*/
|
|
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
|
+
};
|
|
19
247
|
/**
|
|
20
248
|
* @internal Registry theme-scope plumbing lives at `@vegastack/design/theme-scope`, NOT here.
|
|
21
249
|
* It calls `React.createContext()` at module scope, which is `undefined` under the `react-server`
|
|
@@ -37,6 +265,20 @@ declare const TIMINGS: {
|
|
|
37
265
|
readonly hoverOpenDelayMs: 700;
|
|
38
266
|
/** Hover delay before a rich preview closes — lets the pointer travel into the card. */
|
|
39
267
|
readonly hoverCloseDelayMs: 300;
|
|
268
|
+
/**
|
|
269
|
+
* Hover delay before a tooltip opens. A tooltip is a cheap label, not a preview, so it is
|
|
270
|
+
* deliberately much faster than {@link TIMINGS.hoverOpenDelayMs} — long enough that sweeping
|
|
271
|
+
* across a toolbar does not flash five tips, short enough that a deliberate hover feels
|
|
272
|
+
* instant. Base UI's `Tooltip.Provider` grouping then opens adjacent tips with no delay at
|
|
273
|
+
* all. Set once, in the app-root provider.
|
|
274
|
+
*/
|
|
275
|
+
readonly tooltipOpenDelayMs: 300;
|
|
276
|
+
/**
|
|
277
|
+
* Delay before a tooltip closes. Zero: a tooltip has nothing to travel into (it is not
|
|
278
|
+
* hoverable content, unlike a HoverCard), so holding it after the pointer leaves only
|
|
279
|
+
* obscures what the user moved on to.
|
|
280
|
+
*/
|
|
281
|
+
readonly tooltipCloseDelayMs: 0;
|
|
40
282
|
};
|
|
41
283
|
/**
|
|
42
284
|
* Floating-surface positioning constants (px) — the unified sideOffset/collisionPadding
|
|
@@ -53,4 +295,34 @@ declare const FLOATING: {
|
|
|
53
295
|
readonly collisionPadding: 8;
|
|
54
296
|
};
|
|
55
297
|
|
|
56
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Fan one DOM node out to several refs — a forwarded `ref` prop plus one or more internal
|
|
300
|
+
* refs — as a single ref callback. Skips `null`/`undefined` entries, so an optional
|
|
301
|
+
* forwarded ref needs no guard at the call site. Handles both ref shapes React 19 accepts:
|
|
302
|
+
* a callback ref is invoked, an object ref has its `.current` assigned.
|
|
303
|
+
*
|
|
304
|
+
* This lives in `@vegastack/design` rather than in any one component because ref-as-prop
|
|
305
|
+
* (React 19, no `forwardRef`) makes "the component needs the node AND has to forward it"
|
|
306
|
+
* the normal case, not a special one — it was hand-inlined in nine registry files and
|
|
307
|
+
* exported from `use-animation-replay` before this. It touches no React runtime value
|
|
308
|
+
* (only ref objects the caller already holds), so it is server-safe like `cn`.
|
|
309
|
+
*
|
|
310
|
+
* **Not memoized.** Calling it produces a NEW function every time, and React detaches a
|
|
311
|
+
* changed ref callback (calls it with `null`) and reattaches it on every render. Wrap the
|
|
312
|
+
* CALL at the call site when the inputs are stable:
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* const mergedRef = React.useMemo(() => mergeRefs(ref, internalRef), [ref]);
|
|
316
|
+
* return <input ref={mergedRef} />;
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* // A ref callback that also does work — merge it with the forwarded ref
|
|
320
|
+
* const setRef = React.useCallback(
|
|
321
|
+
* (node: HTMLDivElement | null) => { setContainer(node); },
|
|
322
|
+
* [],
|
|
323
|
+
* );
|
|
324
|
+
* const mergedRef = React.useMemo(() => mergeRefs(ref, setRef), [ref, setRef]);
|
|
325
|
+
*/
|
|
326
|
+
declare function mergeRefs<T>(...refs: Array<React.Ref<T> | null | undefined>): React.RefCallback<T>;
|
|
327
|
+
|
|
328
|
+
export { FLOATING, type FillTone, type ProseElement, TIMINGS, cn, fieldControl, fieldControlGroup, fillInteractive, mergeRefs, prose, proseClassName, selectedChipVariants, surfaceInteractive, surfaceInteractiveGroup };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,79 @@
|
|
|
1
1
|
import { ClassValue } from 'clsx';
|
|
2
2
|
export { ClassValue } from 'clsx';
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* THE prose recipe — one token vocabulary for rendered rich text (audit B4-09, 2026-09-07).
|
|
7
|
+
*
|
|
8
|
+
* Two surfaces render prose the system did not author element-by-element: `MarkdownView`
|
|
9
|
+
* (react-markdown turns a markdown string into plain HTML elements) and `TextEdit` (ProseMirror
|
|
10
|
+
* owns the contenteditable DOM). Before this file each restated the same heading/paragraph/list/
|
|
11
|
+
* code/quote recipe in its own grammar — a `Components` map in one, 69 `[&_h1]:` rules in the
|
|
12
|
+
* other — and they had already drifted (`h4`–`h6`, `table`, `img` existed on one side only).
|
|
13
|
+
*
|
|
14
|
+
* ## Why descendant variants and not per-element classes
|
|
15
|
+
*
|
|
16
|
+
* A prose root cannot put a class on every element: ProseMirror generates the editor's DOM, and
|
|
17
|
+
* react-markdown's output is only reachable through an override map. Descendant variants
|
|
18
|
+
* (`[&_h1]:mt-6` → `.recipe h1 { margin-top: … }`) style both from ONE string on the root, which
|
|
19
|
+
* is what makes "MarkdownView and TextEdit render the same computed styles" true by construction
|
|
20
|
+
* rather than by review.
|
|
21
|
+
*
|
|
22
|
+
* It also settles a cascade trap the two-grammar version would have re-created: `[&_h1]:mt-6`
|
|
23
|
+
* compiles at specificity (0,1,1) and a plain `.mt-6` on the element at (0,1,0), so an
|
|
24
|
+
* element-level class silently LOSES to a root-level descendant rule. The two forms cannot
|
|
25
|
+
* coexist on one tree; the root form is the one that works for both consumers.
|
|
26
|
+
*
|
|
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).
|
|
30
|
+
*
|
|
31
|
+
* Logical properties throughout (`ms`/`ps`/`border-s`/`text-start`): prose is the surface most
|
|
32
|
+
* likely to carry translated content, and the contract lane asserts RTL containment.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* // A prose root — the whole recipe:
|
|
36
|
+
* <div className={cn(proseClassName, className)} />
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* // One element's rules, composed into a narrower surface:
|
|
40
|
+
* <div className={cn(prose.root, prose.p, prose.code)} />
|
|
41
|
+
*/
|
|
42
|
+
declare const prose: {
|
|
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";
|
|
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)";
|
|
53
|
+
readonly strong: "[&_strong]:font-medium [&_strong]:text-foreground";
|
|
54
|
+
readonly em: "[&_em]:italic";
|
|
55
|
+
readonly del: "[&_del]:text-muted-foreground [&_del]:line-through [&_s]:text-muted-foreground [&_s]:line-through";
|
|
56
|
+
readonly ul: "[&_ul]:my-3 [&_ul]:ms-6 [&_ul]:list-disc [&_ul]:text-foreground [&_ul]:marker:text-muted-foreground [&_ul.contains-task-list]:list-none";
|
|
57
|
+
readonly ol: "[&_ol]:my-3 [&_ol]:ms-6 [&_ol]:list-decimal [&_ol]:text-foreground [&_ol]:marker:text-muted-foreground";
|
|
58
|
+
readonly li: "[&_li]:mt-1.5 [&_li]:leading-relaxed";
|
|
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";
|
|
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";
|
|
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";
|
|
65
|
+
readonly img: "[&_img]:my-3 [&_img]:max-w-full [&_img]:rounded-lg [&_img]:border [&_img]:border-border";
|
|
66
|
+
};
|
|
67
|
+
/** The element roles the prose recipe covers. */
|
|
68
|
+
type ProseElement = keyof typeof prose;
|
|
69
|
+
/**
|
|
70
|
+
* The whole recipe as one class string — what a prose root wears.
|
|
71
|
+
*
|
|
72
|
+
* Joined rather than merged: every entry scopes a different selector, so there is no conflict for
|
|
73
|
+
* `cn` to resolve, and a plain join keeps the composition free of a `tailwind-merge` pass on a
|
|
74
|
+
* string that never changes.
|
|
75
|
+
*/
|
|
76
|
+
declare const proseClassName: string;
|
|
3
77
|
|
|
4
78
|
/**
|
|
5
79
|
* Merges Tailwind CSS class names with intelligent conflict resolution.
|
|
@@ -16,6 +90,160 @@ export { ClassValue } from 'clsx';
|
|
|
16
90
|
*/
|
|
17
91
|
declare function cn(...inputs: ClassValue[]): string;
|
|
18
92
|
|
|
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.
|
|
129
|
+
*/
|
|
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
|
+
};
|
|
19
247
|
/**
|
|
20
248
|
* @internal Registry theme-scope plumbing lives at `@vegastack/design/theme-scope`, NOT here.
|
|
21
249
|
* It calls `React.createContext()` at module scope, which is `undefined` under the `react-server`
|
|
@@ -37,6 +265,20 @@ declare const TIMINGS: {
|
|
|
37
265
|
readonly hoverOpenDelayMs: 700;
|
|
38
266
|
/** Hover delay before a rich preview closes — lets the pointer travel into the card. */
|
|
39
267
|
readonly hoverCloseDelayMs: 300;
|
|
268
|
+
/**
|
|
269
|
+
* Hover delay before a tooltip opens. A tooltip is a cheap label, not a preview, so it is
|
|
270
|
+
* deliberately much faster than {@link TIMINGS.hoverOpenDelayMs} — long enough that sweeping
|
|
271
|
+
* across a toolbar does not flash five tips, short enough that a deliberate hover feels
|
|
272
|
+
* instant. Base UI's `Tooltip.Provider` grouping then opens adjacent tips with no delay at
|
|
273
|
+
* all. Set once, in the app-root provider.
|
|
274
|
+
*/
|
|
275
|
+
readonly tooltipOpenDelayMs: 300;
|
|
276
|
+
/**
|
|
277
|
+
* Delay before a tooltip closes. Zero: a tooltip has nothing to travel into (it is not
|
|
278
|
+
* hoverable content, unlike a HoverCard), so holding it after the pointer leaves only
|
|
279
|
+
* obscures what the user moved on to.
|
|
280
|
+
*/
|
|
281
|
+
readonly tooltipCloseDelayMs: 0;
|
|
40
282
|
};
|
|
41
283
|
/**
|
|
42
284
|
* Floating-surface positioning constants (px) — the unified sideOffset/collisionPadding
|
|
@@ -53,4 +295,34 @@ declare const FLOATING: {
|
|
|
53
295
|
readonly collisionPadding: 8;
|
|
54
296
|
};
|
|
55
297
|
|
|
56
|
-
|
|
298
|
+
/**
|
|
299
|
+
* Fan one DOM node out to several refs — a forwarded `ref` prop plus one or more internal
|
|
300
|
+
* refs — as a single ref callback. Skips `null`/`undefined` entries, so an optional
|
|
301
|
+
* forwarded ref needs no guard at the call site. Handles both ref shapes React 19 accepts:
|
|
302
|
+
* a callback ref is invoked, an object ref has its `.current` assigned.
|
|
303
|
+
*
|
|
304
|
+
* This lives in `@vegastack/design` rather than in any one component because ref-as-prop
|
|
305
|
+
* (React 19, no `forwardRef`) makes "the component needs the node AND has to forward it"
|
|
306
|
+
* the normal case, not a special one — it was hand-inlined in nine registry files and
|
|
307
|
+
* exported from `use-animation-replay` before this. It touches no React runtime value
|
|
308
|
+
* (only ref objects the caller already holds), so it is server-safe like `cn`.
|
|
309
|
+
*
|
|
310
|
+
* **Not memoized.** Calling it produces a NEW function every time, and React detaches a
|
|
311
|
+
* changed ref callback (calls it with `null`) and reattaches it on every render. Wrap the
|
|
312
|
+
* CALL at the call site when the inputs are stable:
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* const mergedRef = React.useMemo(() => mergeRefs(ref, internalRef), [ref]);
|
|
316
|
+
* return <input ref={mergedRef} />;
|
|
317
|
+
*
|
|
318
|
+
* @example
|
|
319
|
+
* // A ref callback that also does work — merge it with the forwarded ref
|
|
320
|
+
* const setRef = React.useCallback(
|
|
321
|
+
* (node: HTMLDivElement | null) => { setContainer(node); },
|
|
322
|
+
* [],
|
|
323
|
+
* );
|
|
324
|
+
* const mergedRef = React.useMemo(() => mergeRefs(ref, setRef), [ref, setRef]);
|
|
325
|
+
*/
|
|
326
|
+
declare function mergeRefs<T>(...refs: Array<React.Ref<T> | null | undefined>): React.RefCallback<T>;
|
|
327
|
+
|
|
328
|
+
export { FLOATING, type FillTone, type ProseElement, TIMINGS, cn, fieldControl, fieldControlGroup, fillInteractive, mergeRefs, prose, proseClassName, selectedChipVariants, surfaceInteractive, surfaceInteractiveGroup };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FLOATING,
|
|
3
3
|
TIMINGS,
|
|
4
|
-
cn
|
|
5
|
-
|
|
4
|
+
cn,
|
|
5
|
+
fieldControl,
|
|
6
|
+
fieldControlGroup,
|
|
7
|
+
fillInteractive,
|
|
8
|
+
mergeRefs,
|
|
9
|
+
prose,
|
|
10
|
+
proseClassName,
|
|
11
|
+
selectedChipVariants,
|
|
12
|
+
surfaceInteractive,
|
|
13
|
+
surfaceInteractiveGroup
|
|
14
|
+
} from "./chunk-LRQSVCP6.js";
|
|
6
15
|
export {
|
|
7
16
|
FLOATING,
|
|
8
17
|
TIMINGS,
|
|
9
|
-
cn
|
|
18
|
+
cn,
|
|
19
|
+
fieldControl,
|
|
20
|
+
fieldControlGroup,
|
|
21
|
+
fillInteractive,
|
|
22
|
+
mergeRefs,
|
|
23
|
+
prose,
|
|
24
|
+
proseClassName,
|
|
25
|
+
selectedChipVariants,
|
|
26
|
+
surfaceInteractive,
|
|
27
|
+
surfaceInteractiveGroup
|
|
10
28
|
};
|