@vegastack/design 0.3.2 → 0.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vegastack/design",
3
- "version": "0.3.2",
3
+ "version": "0.4.1",
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": {
@@ -38,6 +38,12 @@
38
38
  "require": "./dist/icons/index.cjs",
39
39
  "default": "./dist/icons/index.js"
40
40
  },
41
+ "./create-animated-icon": {
42
+ "types": "./dist/create-animated-icon.d.ts",
43
+ "import": "./dist/create-animated-icon.js",
44
+ "require": "./dist/create-animated-icon.cjs",
45
+ "default": "./dist/create-animated-icon.js"
46
+ },
41
47
  "./theme-scope": {
42
48
  "types": "./dist/theme-scope.d.ts",
43
49
  "import": "./dist/theme-scope.js",
@@ -58,7 +64,7 @@
58
64
  },
59
65
  "scripts": {
60
66
  "build": "tsup",
61
- "lint": "eslint . && pnpm run verify",
67
+ "lint": "eslint . && node ../../tooling/design-lint.mjs src && pnpm run verify",
62
68
  "verify": "node ../../tooling/verify-preset-source.mjs",
63
69
  "typecheck": "tsc --noEmit",
64
70
  "test": "node test/compare.test.mjs && node test/check-updates.test.mjs && node test/skills-install.test.mjs"
@@ -74,6 +80,7 @@
74
80
  "react": "^19.0.0",
75
81
  "react-dom": "^19.0.0",
76
82
  "lucide-react": "^1.24.0",
83
+ "motion": "^13.2.0",
77
84
  "thesvg": "^3.2.6",
78
85
  "tailwindcss": "catalog:"
79
86
  },
@@ -84,23 +91,27 @@
84
91
  "lucide-react": {
85
92
  "optional": true
86
93
  },
94
+ "motion": {
95
+ "optional": true
96
+ },
87
97
  "thesvg": {
88
98
  "optional": true
89
99
  }
90
100
  },
91
101
  "devDependencies": {
92
- "@tailwindcss/node": "4.3.1",
93
- "@tailwindcss/oxide": "4.3.1",
94
- "@types/react": "^19.2.17",
95
- "@types/react-dom": "^19.2.3",
102
+ "@tailwindcss/node": "4.3.3",
103
+ "@tailwindcss/oxide": "4.3.3",
104
+ "@types/react": "^19.2.18",
105
+ "@types/react-dom": "^19.2.7",
96
106
  "@vegastack/eslint-config": "workspace:*",
97
107
  "@vegastack/typescript-config": "workspace:*",
98
- "eslint": "^10.7.0",
99
- "lucide-react": "^1.24.0",
108
+ "eslint": "^10.10.0",
109
+ "lucide-react": "^1.42.0",
110
+ "motion": "^13.2.0",
100
111
  "react": "catalog:",
101
112
  "react-dom": "catalog:",
102
113
  "tailwindcss": "catalog:",
103
- "thesvg": "^3.2.6",
114
+ "thesvg": "^3.3.2",
104
115
  "tw-animate-css": "^1.4.0",
105
116
  "typescript": "catalog:"
106
117
  },
package/preset.css CHANGED
@@ -13,7 +13,7 @@
13
13
  *
14
14
  * This package's own `dist` ships the icon runtime (`BrandIcon`'s
15
15
  * `inline-flex shrink-0 [&>svg]:size-full`), and `@vegastack/ui` ships a compiled
16
- * provider + `Toaster` whose sonner `classNames` (e.g. `group-[.toaster]:bg-popover`)
16
+ * provider + `Toaster` whose Base UI Toast classes (e.g. `bg-popover shadow-overlay`)
17
17
  * live in its published `dist/*.js`, NOT in the consumer's own source — so without
18
18
  * an explicit `@source` a real npm consumer importing ONLY this preset would get
19
19
  * a partially-unstyled provider/Toaster + icon UI.
@@ -48,9 +48,9 @@ Or one import that bundles all of it plus tw-animate:
48
48
  ```
49
49
 
50
50
  **No manual `@source` is required.** The preset already declares `@source` for the classes that live
51
- inside the published builds — the icon runtime, and the provider/Toaster classNames (sonner emits
52
- `group-[.toaster]:bg-popover`-style classes that Tailwind must be told to generate). Your own
53
- application and component source is scanned by Tailwind as usual.
51
+ inside the published builds — the icon runtime, and the compiled Toaster (its surface, stacking
52
+ and status-tint classes live in `@vegastack/ui/dist`, and Tailwind has to be told to generate them).
53
+ Your own application and component source is scanned by Tailwind as usual.
54
54
 
55
55
  ## 3. Wrap the app root in the provider
56
56
 
@@ -58,7 +58,7 @@ application and component source is scanned by Tailwind as usual.
58
58
  npx shadcn@latest add @vegastack/provider
59
59
  ```
60
60
 
61
- This copies `VegaStackProvider` and `useVegaStackTheme` into your project, composing the `sonner`
61
+ This copies `VegaStackProvider` and `useVegaStackTheme` into your project, composing the `toast`
62
62
  Toaster item.
63
63
 
64
64
  ```tsx
@@ -70,7 +70,7 @@ import { VegaStackProvider } from "@/components/ui/provider";
70
70
  </body>;
71
71
  ```
72
72
 
73
- It bundles theme (next-themes, class-based dark), the Sonner toaster, and the Base UI tooltip and
73
+ It bundles theme (next-themes, class-based dark), the Base UI toast stack, and the tooltip and
74
74
  direction providers.
75
75
 
76
76
  **The `isolate` is required, not cosmetic.** Overlay components (Dialog, Sheet, Popover, Tooltip,
@@ -70,7 +70,9 @@ rg -n '\b(rounded-xl|rounded-2xl|rounded-3xl|text-4xl|text-5xl|text-6xl|font-bol
70
70
  - `font-bold`/`font-semibold` — the weight ladder is 400/500, owned by the type roles. **error**
71
71
  - `transition-all` / `transition-colors` — colour changes are immediate; enumerate the causal
72
72
  opacity, transform, or geometry properties. **error**
73
- - a raw `z-N` — two bands only: `z-(--z-raised)`, `z-(--z-overlay)`. **error**
73
+ - a raw `z-N` — three token bands only: `z-(--z-raised)` (local raises), `z-(--z-overlay)` (portaled
74
+ surfaces), `z-(--z-toast)` (the toast stack alone). DOM order resolves nesting within a band.
75
+ **error**
74
76
  - a raw `opacity-NN` — use an `--opacity-*` role (`opacity-0`/`opacity-100` are exempt). **warning**
75
77
  - raw `tracking-*`, `shadow-*`, `blur-*` — owned by the type and effect roles. **warning**
76
78
  - a raw `/NN` colour-alpha step — use an `--alpha-*` role. Alpha and opacity are different roles and
@@ -25,10 +25,31 @@ pnpm dlx shadcn@latest list @vegastack
25
25
 
26
26
  Rules that decide most component questions:
27
27
 
28
+ - **`Button` is two axes** — `variant` is the shape (`solid · soft · outline · ghost · link · cta`),
29
+ `tone` is the hue (`neutral · destructive · success · warning · info`). A destructive action is
30
+ `variant="soft" tone="destructive"`; a solid red button does not type-check. Icon-only actions are
31
+ **`IconButton`** (`shape="square" | "round"`) — `Button` has no icon size, and an icon in a bare
32
+ `<button>` is off-system. An icon-only LINK stays an `<a>`, styled with
33
+ `buttonVariants(...) + iconButtonGeometry(size)` — never an `IconButton`, which would put
34
+ `role="button"` on navigation.
35
+ - **One size vocabulary everywhere** — `xs · sm · md · lg`, with `md` the default. No component has a
36
+ size called `default`.
28
37
  - **Compose `app-shell`** for a sidebar + header + main layout — never hand-roll the landmark trio.
38
+ - **`select`** for a short fixed option set; **`searchable-select`** when the list is long enough to
39
+ need a search field (it is the preset `country-select` and `region-select` are built from — reach
40
+ for it before composing `combobox` by hand); **`combobox`** directly only for free text,
41
+ suggestions or multi-select chips.
29
42
  - **`segmented`** for 2–5 exclusive options inline; **`tabs`** when the choice switches page regions.
30
43
  - **`alert` variant=strip** for in-content notices and plan/trial rows; **`announcement-banner`** only
31
44
  for the full-width inverse strip at the very top of the page.
45
+ - **`chip` is the ONE pill** — `hue` × `size` (`sm` inline · `md` control-scale) × `active`, with
46
+ `onRemove` giving a real 24×24 remove control. `Tag`, `FilterChip` and `ComboboxChip` are that
47
+ primitive composed through `render`; never hand-roll a pill with its own height, radius, or a
48
+ sub-24px `×`. A **`badge`** is the different job: status, never removable, never a selection.
49
+ - **`useAnnouncer` is the one live region** — destructure `announce` and `Announcer` from it and
50
+ render ONE `Announcer` element per component, mounted for its life. It keeps the region observed from first paint
51
+ and re-keys it per call, so repeating an identical string still announces. Do not hand-roll a
52
+ `role="status"` node with a `{text, seq}` counter.
32
53
  - **`code-block`** for static syntax-highlighted source; **`terminal`** for command sessions.
33
54
  - **`navigation-menu`** is top-level site navigation with panels, not a menu inside a page.
34
55
  - **Marketing components** (`marketing-surface`, `section-header`, `figure-frame`, `terminal`,
@@ -42,7 +63,8 @@ Always use the utility, never a raw value.
42
63
 
43
64
  | Role | Utilities |
44
65
  | -------- | ------------------------------------------------------------------------------------------------ |
45
- | Surface | `bg-background` `bg-card` `bg-popover` `bg-muted` `bg-accent` `bg-sidebar-*` |
66
+ | Surface | `bg-background` (page) · `bg-card` (every surface; `popover`/`sidebar` ARE `card`) |
67
+ | Ladder | `bg-surface-1` (rest fill / well) · `bg-surface-2` (hover) · `bg-surface-3` (pressed / selected) |
46
68
  | Text | `text-foreground` `text-muted-foreground` `text-{primary,accent,popover}-foreground` |
47
69
  | Status | `bg-{destructive,success,warning,info}` + `-subtle` / `-hover` / `-text` / `-foreground` |
48
70
  | Border | `border-border` `border-input` — there are no rings; focus is the native outline |
@@ -50,7 +72,72 @@ Always use the utility, never a raw value.
50
72
  | Type | `text-{xs…3xl}` · `text-h1…h4` · `text-label` · `text-mono-label` · `text-display-{sm,md,lg,xl}` |
51
73
  | Font | `font-sans` `font-mono` `font-serif` |
52
74
  | Motion | `duration-{fast,base,slow}` paired with `ease-{standard,emphasized,exit,spring}` |
53
- | Entrance | `motion-pop-in` `motion-enter-up` `motion-shake` |
75
+ | Entrance | `motion-pop-in` `motion-enter-up` `motion-shake` `motion-flash` |
76
+ | Docked | `motion-dock-in` / `motion-dock-out` — a control parked at a viewport edge, 150ms in / 100ms out |
77
+ | Prose | `proseClassName` from `@vegastack/design` — the whole rendered-rich-text recipe, one class |
78
+
79
+ **Hover and pressed come from a recipe, never a literal.** Import the two class strings rather than
80
+ writing `hover:bg-*` by hand — that is how a control gets both steps and stays on the ladder:
81
+
82
+ ```tsx
83
+ import { cn, surfaceInteractive, fillInteractive } from "@vegastack/design";
84
+
85
+ // A transparent control on a known surface.
86
+ <button className={cn("rounded-md px-2", surfaceInteractive)} />;
87
+ // hover:bg-surface-2 active:bg-surface-3
88
+
89
+ // A control on an unknown backdrop, or one that hovers in its own hue.
90
+ <button className={cn("bg-destructive-subtle", fillInteractive.destructive)} />;
91
+ // hover:bg-destructive/(--alpha-hover) active:bg-destructive/(--alpha-pressed)
92
+ ```
93
+
94
+ A **solid** fill uses neither — it steps through its own darker `-hover` / `-active` tokens.
95
+
96
+ **Rendered rich text comes from a recipe too.** Anything the system did not author element by element
97
+ — markdown, a contenteditable, CMS copy — wears one class on its root:
98
+
99
+ ```tsx
100
+ import { cn, proseClassName } from "@vegastack/design";
101
+
102
+ <div
103
+ className={cn(proseClassName, className)}
104
+ dangerouslySetInnerHTML={html}
105
+ />;
106
+ ```
107
+
108
+ `MarkdownView` and `TextEdit` both wear it, so they render identical typography. It is expressed as
109
+ descendant rules (`[&_h1]:…`), which means an element-level class on a child **loses** to it
110
+ (specificity (0,1,0) against (0,1,1)) — restyle by composing `prose` (the per-element record), never
111
+ by setting a class on the rendered element.
112
+
113
+ **A selected chip on a muted track has a third recipe.** If you are building a view switcher, a
114
+ segmented control or chip-shaped tabs of your own, take `selectedChipVariants` rather than inventing
115
+ a selected look — it is the same formula `Tabs`, `Segmented` and `Toggle` use:
116
+
117
+ ```tsx
118
+ import { cn, selectedChipVariants } from "@vegastack/design";
119
+
120
+ <div className={cn("rounded-md p-0.5", selectedChipVariants.track)}>
121
+ <Toggle
122
+ className={cn(
123
+ "rounded-sm",
124
+ selectedChipVariants.item,
125
+ selectedChipVariants.pressed,
126
+ )}
127
+ />
128
+ </div>;
129
+ ```
130
+
131
+ Use `.pressed` for a control whose selected state is Base UI's `data-pressed` and `.active` for one
132
+ using `data-active`. The selected chip keeps its own hover and pressed steps — never guard them off.
133
+
134
+ `secondary`, `muted`, `accent` and the `sidebar-*` family are **aliases** of ladder rungs
135
+ (`secondary` = `muted` = `surface-1`, `accent` = `sidebar-accent` = `surface-2`, `sidebar` = `card`).
136
+ They still compile; name the rung in new code.
137
+
138
+ `border` is one translucent hairline — `foreground` at `--alpha-border` — so it reads on the page, on
139
+ a card and inside a well alike. `info` is **links and informational UI only**: promotion and
140
+ selection take a ladder rung or `primary`.
54
141
 
55
142
  Alpha and opacity are **different roles**: colour compositing takes an `--alpha-*` token
56
143
  (`bg-foreground/(--alpha-ink-tint)`), whole-element opacity takes an `--opacity-*` token
@@ -73,7 +160,17 @@ contract.
73
160
  ## Composition patterns
74
161
 
75
162
  - **Forms** — Base UI `Field` + react-hook-form `Controller` + Zod 4 (`z.email()`). `Field.Control`
76
- emits `onValueChange`, not a DOM `onChange` event.
163
+ emits `onValueChange`, not a DOM `onChange` event. **`Field` owns the feedback layer**: helper text
164
+ renders below the control, the error below that as a polite `role="status"`, and the invalid shake
165
+ belongs to the field — wrap a control in a `Field` to get it, and pass `shakeSignal` (a
166
+ submit-attempt counter) there to re-shake a field that never stopped being invalid. A bare
167
+ `<Input aria-invalid>` outside a `Field` tints its border and does not move.
168
+ - **A set of related checkboxes is a `CheckboxGroup`** — pass `allValues` and mark one child
169
+ `parent` to get select-all with the mixed state, rather than computing checked/indeterminate in
170
+ your own state. Name the group with a `FieldSet`/`FieldLegend` or `aria-labelledby`.
171
+ - **Click-to-edit is `useInlineEdit`** — draft, commit, cancel, focus restoration and the
172
+ double-commit guard, with no opinion about the editor or the display. `FieldInline` and
173
+ `EditableCell` are built on it.
77
174
  - **Overlays** — enter/exit is driven by `data-starting-style`/`data-ending-style` on the popup root,
78
175
  inside a portal + positioner. Theme, toast, tooltip, and direction providers all come from
79
176
  `<VegaStackProvider>`; your app root needs `isolation: isolate` or portaled popups can render under
@@ -95,6 +192,8 @@ contract.
95
192
  chrome.
96
193
  - Implement every applicable state: default, hover, focus, loading, empty, error, success, disabled.
97
194
  - Put `truncate` on an inner span, with `min-w-0` on the flex container.
195
+ - Let the parent decide a form control's width — every control is `w-full` and takes its height from
196
+ the `--size-*` scale.
98
197
 
99
198
  **Don't**
100
199
 
@@ -104,6 +203,9 @@ contract.
104
203
  - Set `outline-none` without providing another focus affordance.
105
204
  - Pull in a second icon library or hand-write an inline `<svg>` as an icon.
106
205
  - Put `uppercase` on non-mono type, or on anything above 14px.
206
+ - Hand-roll a removable pill, or a `role="status"` live region with its own sequence counter.
207
+ - Give a form control a fixed width (`w-56`, `w-64`) — it reads fine on the page it was tuned for
208
+ and overflows at 320px. Constrain the parent instead.
107
209
 
108
210
  ## Reference
109
211
 
@@ -3,7 +3,7 @@
3
3
  <!-- GENERATED — do not hand-edit. Regenerated from the design system's component contract,
4
4
  which is the authority for membership and counts. -->
5
5
 
6
- **110 components**, plus 439 animated-icon items, 6 hooks (`use-animation-replay`, `use-drag-reorder`, `use-file-drop`, `use-list-nav`, `use-mobile`, `use-platform`), and 1 starter block (`dashboard-01`) — 556 registry items in total.
6
+ **116 components**, plus 467 animated-icon items, 11 hooks (`use-animation-replay`, `use-announcer`, `use-drag-reorder`, `use-file-drop`, `use-inline-edit`, `use-list-nav`, `use-media-query`, `use-mobile`, `use-modal-inert`, `use-overflow`, `use-platform`), 1 starter block (`dashboard-01`), and 2 data libs (`geo-data`, `drag-item`) — 597 registry items in total.
7
7
 
8
8
  Install any of them with `shadcn add @vegastack/<name>`. Animated icons install as
9
9
  `@vegastack/icon-<name>`; the bare name is reserved for components, so `icon-button` is the
@@ -11,9 +11,9 @@ component and never an icon.
11
11
 
12
12
  ## Actions
13
13
 
14
- - **`button`** — Trigger an action. 15 variants × 8 sizes, with loading + Base UI Button semantics.
14
+ - **`button`** — Trigger an action. Six variants × five tones × four sizes, with loading + Base UI Button semantics.
15
15
  - **`copy-button`** — Copy a value to the clipboard with transient check feedback — a ghost icon button that swaps Copy → Check and fires onCopied.
16
- - **`icon-button`** — A square, icon-only action button — a thin Button wrapper that requires an accessible label.
16
+ - **`icon-button`** — A square or round icon-only action button — a thin Button wrapper that requires an accessible label.
17
17
  - **`segmented`** — Segmented control — a single-select, always-one-selected view/mode switcher on a muted track with a raised active chip.
18
18
  - **`split-button`** — A primary action joined to a dropdown of related secondary actions — one default click, plus a chevron menu.
19
19
  - **`toggle`** — A two-state button that can be pressed on or off — bold/italic, mute, pin.
@@ -23,30 +23,33 @@ component and never an icon.
23
23
 
24
24
  - **`auto-save-input`** — An input that debounces edits and persists them via an async onSave, with an inline idle/saving/saved/error status.
25
25
  - **`checkbox`** — A binary (or tri-state) toggle — checked, unchecked, indeterminate, disabled, built on Base UI Checkbox.
26
+ - **`checkbox-group`** — Shared state for a set of checkboxes, with first-class “select all” — parent, mixed, and the whole-set toggle, built on Base UI Checkbox Group.
26
27
  - **`chip-input`** — Free-token entry field — Enter/comma/paste commits chips, Backspace removes, per-chip validation marks invalid entries instead of dropping them. Combobox field chrome + real Tag chips.
27
28
  - **`color-picker`** — A swatch-triggered popover presenting a grid of preset colors — pick one, fire onValueChange, mark the selection.
28
29
  - **`combobox`** — A filterable, keyboard-navigable listbox behind a text input — type-to-filter, grouped items, async status, and a multi-select chip mode.
29
- - **`country-select`** — A searchable country combobox returning the ISO 3166-1 alpha-2 code, with flag + name. Built on Combobox.
30
+ - **`country-select`** — A searchable country picker returning the ISO 3166-1 alpha-2 code, with flag + name. A thin wrapper over SearchableSelect fed by the geo-data item.
30
31
  - **`date-picker`** — Pick a single date or a date range from a calendar popover — token-styled, keyboard-navigable, with optional quick presets.
31
32
  - **`dropzone`** — File acquisition surface — drop, click-to-browse, and paste — as a thin shell over use-file-drop; the surface is the named focusable control over a hidden picker-bridge input; data-dragging/data-drag-invalid styling flags.
32
33
  - **`editable-cell`** — Inline-editable value with an async commit lifecycle — optimistic display, saving/saved/error status, revert on a rejected write, and a typed text/select/custom editor registry.
33
34
  - **`emoji-picker`** — A popover with a searchable, category-grouped grid of emoji that returns the selected character via onSelect (curated set, not full Unicode).
34
35
  - **`field`** — A form-field wrapper — label, inline label action, description, and error/success message, built on Base UI Field.
35
36
  - **`field-inline`** — Click-to-edit text — displays a value, swaps to a focused input on click, commits on Enter or blur, cancels on Escape.
36
- - **`input`** — A styled Base UI input — all input types, Field state data attributes, error and disabled states, focus-visible ring, and optional prefix/suffix addons.
37
+ - **`input`** — A styled Base UI input — all input types, Field state data attributes, error and disabled states, a focus border tint, and optional prefix/suffix addons.
37
38
  - **`label`** — A styled native label for form controls — htmlFor association, disabled dimming, optional required indicator.
38
39
  - **`number-field`** — Locale-aware numeric input on Base UI's NumberField in Input's field chrome — Intl formatting (money is a format prop), min/max/step, keyboard stepping, wheel scrub, full-height steppers.
39
40
  - **`otp-input`** — A multi-slot one-time-passcode input — keyboard navigation, paste distribution, masking, disabled, built on Base UI OTP Field.
40
41
  - **`password-input`** — A password field with a show/hide eye toggle and an optional live requirements checklist.
41
42
  - **`radio-group`** — A set of mutually-exclusive options — single selection, arrow-key navigation, disabled, built on Base UI Radio Group.
42
- - **`region-select`** — A searchable combobox of states/provinces for a country, with a free-text fallback for countries with no subdivisions.
43
+ - **`region-select`** — A searchable picker of states/provinces for a country, with a free-text fallback for countries with no subdivisions. A thin wrapper over SearchableSelect fed by the geo-data item.
44
+ - **`searchable-select`** — The Select-shaped Combobox preset: a full-width trigger, an in-panel search field, a check on the selected row and an optional clear control. Single-select, controlled through value/onValueChange.
43
45
  - **`select`** — A dropdown for choosing one option — trigger with value and chevron, grouped scrollable popup, full keyboard navigation, animated enter/exit.
44
46
  - **`slider`** — Pick a number or a [from, to] range from a continuous track — keyboard accessible, with optional steps. Built on Base UI Slider.
45
47
  - **`switch`** — An on/off toggle for instant, self-saving binary settings — built on Base UI Switch.
46
- - **`textarea`** — A styled native textarea for multi-line text — error/disabled states, a focus-visible ring, and an optional auto-grow mode.
48
+ - **`textarea`** — A styled native textarea for multi-line text — error/disabled states, a focus border tint, and an optional auto-grow mode.
47
49
 
48
50
  ## Display
49
51
 
52
+ - **`chip`** — The one labelled pill primitive — 10 decorative hues, two tiers, an optional selection rung, and a real 24x24 remove control. Behind Tag, FilterChip and Combobox chips.
50
53
  - **`code-block`** — A code panel with a language header and copy affordance — the shared code surface for chat transcripts, docs, and examples.
51
54
  - **`onboarding-checklist`** — A getting-started card — segmented progress + step rows, collapsible to a progress pill.
52
55
  - **`stat`** — A labelled value block — muted label over a value, honest faint empty state, optional delta line. Two scales.
@@ -57,24 +60,25 @@ component and never an icon.
57
60
  - **`accordion`** — A stack of collapsible sections — single or multiple open, animated height, a rotating chevron, full keyboard support.
58
61
  - **`animated-number`** — A number display that tweens from its previous value to a new one on every change — Intl.NumberFormat-aware (currency/percent/compact), instant under reduced motion, the dashboard stat-card counter.
59
62
  - **`avatar`** — A circular user/entity image with an initials fallback, five sizes, and an overlapping AvatarGroup stack.
60
- - **`badge`** — A compact status or label chip. 3 variants × semantic colors × 4 sizes, with dot, loading, and icon support.
63
+ - **`badge`** — A compact status or label chip — solid / soft / outline / minimal × 5 semantic colors × 3 real size tiers, with a dot, a leading icon, and loading.
61
64
  - **`card`** — A borders-only content surface — no shadows, with composable header, content, and footer parts.
62
65
  - **`chart`** — A themed Recharts wrapper — token-only series colors (--chart-1…--chart-8), a bordered tooltip/legend, and Recharts' own built-in keyboard + screen-reader layer.
63
66
  - **`collapsible`** — A single toggleable open/close region with an animated height, built on Base UI Collapsible.
64
- - **`empty`** — A zero-data placeholder — icon, title, description, actions, with intent tints and an optional dashed border.
67
+ - **`empty`** — A zero-data placeholder — icon, title, description, actions, with intent tints and a plain, card, or dashed container.
65
68
  - **`item`** — A compact anatomy row for list/feed content — media, title, description, actions, groupable with dividers.
66
- - **`kbd`** — A styled keyboard-key indicator — OS-aware modifier glyphs, a keys array, and small sizes.
69
+ - **`kbd`** — The one keyboard-key chip — modifier labels the caller resolves per platform, a keys array, and three inline sizes.
67
70
  - **`markdown-view`** — Render a markdown string to safe, token-styled HTML — headings, lists, code, blockquotes, links, GFM tables — XSS-safe, no raw HTML.
68
71
  - **`relative-time`** — Render a date as a human-relative string ("2 hours ago", "yesterday") with native Intl.RelativeTimeFormat — self-updating, with an absolute-date tooltip.
69
72
  - **`status-icon`** — A small status indicator icon — todo, in progress, blocked, done — each mapping to a lucide icon and semantic color.
70
73
  - **`table`** — Styled semantic table primitives — a scrollable container plus header, body, footer, row, head, cell, caption.
71
74
  - **`timeline`** — Rail geometry for chronological records — a continuous connector with a node per entry. Rows compose Item parts; separators render through Marker; entries carry content-visibility render skipping.
72
- - **`truncated-text`** — Truncate text to one line or N lines with an ellipsis, revealing the full text in a tooltip only when it overflows.
75
+ - **`truncated-text`** — Truncate text to one line or N lines with an ellipsis, revealing the full text in a tooltip only when it overflows — with per-region control over the tab stop.
73
76
 
74
77
  ## Data
75
78
 
76
79
  - **`data-grid`** — The full-parity grid — TanStack-sorted multi-key sort, column picker with responsive revelation, collapsible grouping, keyboard-continuous load-more, opt-in virtualization, and an APG grid keyboard layer with inline cell editing.
77
80
  - **`data-list`** — A generic, typed data table — configurable columns, row selection, sortable headers, plus loading and empty states.
81
+ - **`data-table-parts`** — The chrome DataList and DataGrid share — sort header, selection cells, skeleton rows, the empty row, column class rules, and the selection/sort/controlled-state hooks.
78
82
  - **`filter-bar`** — A row of removable filter chips, an "Add filter" dropdown, and an optional search input — for list and table filter toolbars.
79
83
  - **`filter-bar-managed`** — The stateful nested and/or filter builder — host-injected field grammar (vocabulary + per-type value editors), depth and condition caps, focus-managed removal, and a removable FilterChip summary.
80
84
  - **`property-list`** — Record-facts rows: an icon+label column beside a value column, as an accessible definition list.
@@ -86,6 +90,7 @@ component and never an icon.
86
90
  - **`context-menu`** — A menu of actions revealed by right-click (or long-press) — items, submenus, separators, labels, shortcuts, checkbox/radio.
87
91
  - **`dialog`** — A modal overlay — five sizes, a header/footer layout, focus trapping, and animated enter/exit.
88
92
  - **`dropdown-menu`** — A menu of actions triggered by a button — items, submenus, separators, labels, shortcuts, and checkbox/radio selections.
93
+ - **`floating-surface`** — The shared floating-overlay module: one Portal/Positioner/Popup composer, the popup surface recipes, the list-item recipe, and the in-panel search row.
89
94
  - **`hover-card`** — A rich preview panel that opens on hover or focus — interactive content, four directions, forgiving delays.
90
95
  - **`popover`** — A click-triggered floating panel for arbitrary content — positioning, an optional arrow, and built-in dismiss.
91
96
  - **`sheet`** — A dialog that slides in from a screen edge — four sides, header/footer layout, focus trapping, animated slide.
@@ -107,12 +112,12 @@ component and never an icon.
107
112
 
108
113
  - **`action-bar`** — Floating contextual bar — status region + action children, CSS-only enter/exit, raised band. Bulk selection, unsaved changes, and batch progress are recipes over it.
109
114
  - **`alert`** — A status banner — five semantic variants, an optional icon, and an optional dismiss button.
110
- - **`progress`** — A determinate horizontal progress bar for measurable, ongoing tasks — built on Base UI Progress.
115
+ - **`progress`** — A horizontal progress bar for measurable, ongoing tasks — determinate or a sweeping indeterminate segment, built on Base UI Progress.
111
116
  - **`progress-indicator`** — A compact circular pie-fill progress indicator (0–100%) with optional visible percentage variants.
112
- - **`provider`** — The single app-root wrapper — theme (next-themes), Sonner toasts, tooltip coordination, and text direction in one mount-once component.
113
- - **`skeleton`** — A token-driven loading placeholder — line, circle, rect, card shapes, configurable count, reduced-motion-aware pulse.
114
- - **`sonner`** — Brief, non-blocking notifications — a token-styled Sonner toaster with success/error/warning/info variants that follows the theme.
117
+ - **`provider`** — The single app-root wrapper — theme (next-themes), Base UI toasts, tooltip delays, and text direction in one mount-once component.
118
+ - **`skeleton`** — A token-driven loading placeholder — line, circle, rect, card shapes, a configurable count, and a pulse that stops under reduced motion.
115
119
  - **`spinner`** — An indeterminate loading indicator — a spinning icon inheriting currentColor, four sizes, role=status by default.
120
+ - **`toast`** — Brief, non-blocking notifications — a stacking Base UI Toast surface with success, error, warning, info and loading types, promise toasts and swipe-to-dismiss.
116
121
 
117
122
  ## Layout
118
123
 
@@ -127,6 +132,7 @@ component and never an icon.
127
132
 
128
133
  - **`audio-player`** — A custom audio transport with play/pause, skip, seek, a tappable speed control, and keyboard shortcuts (mute on the M key); a single line on a wide player, two lines with an optional transcript control on a narrow, mobile-width player.
129
134
  - **`image`** — A presentational framed image with aspect-ratio, rounding, a loading skeleton, and an error fallback.
135
+ - **`media-player-controls`** — The shared media transport — play/pause, skip, seek, elapsed/duration, mute + volume, playback speed, and one keyboard shortcut map (useMediaShortcuts) — composed by Audio Player and Video Player.
130
136
  - **`notification-bell`** — A bell icon button with an unread-count badge overlay. Presentational — the app supplies the count.
131
137
  - **`video-player`** — A framed video player with the same grouped custom transport controls as Audio Player.
132
138
 
@@ -1,52 +0,0 @@
1
- // src/index.ts
2
- import { clsx } from "clsx";
3
- import { extendTailwindMerge } from "tailwind-merge";
4
- var twMerge = extendTailwindMerge({
5
- extend: {
6
- classGroups: {
7
- "font-size": [
8
- {
9
- text: [
10
- "h1",
11
- "h2",
12
- "h3",
13
- "h4",
14
- "label",
15
- "label-sm",
16
- "code",
17
- "code-sm",
18
- "mono-label",
19
- "display-sm",
20
- "display-md",
21
- "display-lg",
22
- "display-xl"
23
- ]
24
- }
25
- ]
26
- }
27
- }
28
- });
29
- function cn(...inputs) {
30
- return twMerge(clsx(inputs));
31
- }
32
- var TIMINGS = {
33
- /** How long transient success feedback holds before reverting (CopyButton "Copied ✓"). */
34
- feedbackRevertMs: 1500,
35
- /** Debounce before auto-persisting a text field (AutoSaveInput). */
36
- autoSaveDebounceMs: 800,
37
- /** Hover delay before a rich preview (HoverCard) opens — guards accidental opens. */
38
- hoverOpenDelayMs: 700,
39
- /** Hover delay before a rich preview closes — lets the pointer travel into the card. */
40
- hoverCloseDelayMs: 300
41
- };
42
- var FLOATING = {
43
- sideOffsetAttached: 4,
44
- sideOffsetDetached: 8,
45
- collisionPadding: 8
46
- };
47
-
48
- export {
49
- cn,
50
- TIMINGS,
51
- FLOATING
52
- };