wunderui-mcp 1.2.0 → 1.2.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/README.md CHANGED
@@ -48,7 +48,7 @@ claude mcp add wunderui -- npx -y wunderui-mcp
48
48
 
49
49
  ## Installing WunderUI itself
50
50
 
51
- The server describes the library; it does not ship it. `@wunderui/react` is not on the public npm registry: Core and Pro licence holders install it from the wunderui-core download (the `wunderui-setup` agent skill does it for you), and the server tells agents exactly that. The Free plan covers the Figma preview and the documentation at [wunderui.com](https://wunderui.com).
51
+ The server describes the library; it does not ship it. `@wunderui/react` on npm is WunderUI Free (the free components only). Core and Pro licence holders install the full library from the wunderui-core download (the `wunderui-setup` agent skill does it for you), and the server tells agents exactly that.
52
52
 
53
53
  ## Where the data comes from
54
54
 
package/data/DESIGN.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Design context for AI coding agents. Paste this into your prompt, or let your
4
4
  > agent pull it from https://wunderui.com/DESIGN.md.
5
- > Generated from the WunderUI source on 2026-10-22 · @wunderui/react@0.1.0
5
+ > Generated from the WunderUI source on 2026-10-23 · @wunderui/react@0.3.0
6
6
 
7
7
  WunderUI is a React component library and design system: 222
8
8
  documented components in 6 groups, built on Base UI
@@ -764,7 +764,7 @@ Agent skills that build and check with these rules (https://wunderui.com/skills)
764
764
 
765
765
  | Component | Plan | What it does | Related exports |
766
766
  | --- | --- | --- | --- |
767
- | `LineChart` (`@wunderui/react/charts`) | Pro | Line chart for trends over time: straight, smooth and stepped lines, comparisons, bands, forecasts and thresholds, with tooltip, keyboard navigation and a data table. | LineChartBand, LineChartProps, LineChartVariant |
767
+ | `LineChart` (`@wunderui/react/charts`) | Pro | Line chart for trends over time: smooth lines (straight on request) and stepped lines, comparisons, bands, forecasts and thresholds, with tooltip, keyboard navigation and a data table. | LineChartBand, LineChartProps, LineChartVariant |
768
768
  | `BarChart` (`@wunderui/react/charts`) | Pro | Bar chart for comparing values: grouped, horizontal, diverging, top-k and highlighted bars, with tooltip, keyboard navigation and a data table. | BarChartProps, BarChartVariant |
769
769
  | `AreaChart` (`@wunderui/react/charts`) | Pro | Area chart for volumes over time: gradients, steps, ranges, a horizon graph, differences and target lines. | AreaChartProps, AreaChartVariant |
770
770
  | `ComposedChart` (`@wunderui/react/charts`) | Pro | Bars, lines and areas in one plot, with an optional second value axis. | ComposedChartProps, ComposedChartVariant |
@@ -988,7 +988,7 @@ Exports of `@wunderui/react` that are not components of their own: hooks, provid
988
988
  ### Hooks
989
989
 
990
990
  - `useAppLayout` (Pro) — Opens the navigation drawer and the side panel of the surrounding AppLayout. `useAppLayout(): AppLayoutState | null` · https://wunderui.com/hooks/use-app-layout
991
- - `useChartAnimation` (Pro) — The draw-in animation props every WunderUI chart passes to its Recharts series. `useChartAnimation(animate?: boolean, appear?: ChartAppearState | undefined): { isAnimationActive: boolean; animationDuration: number; animationEasing: "ease-out"; animationBegin: number; }` · https://wunderui.com/hooks/use-chart-animation
991
+ - `useChartAnimation` (Pro) — Legacy: the draw-in animation props for Recharts series, used by the legacy Recharts charts the templates still use. `useChartAnimation(animate?: boolean, appear?: ChartAppearState | undefined): { isAnimationActive: boolean; animationDuration: number; animationEasing: "ease-out"; animationBegin: number; }` · https://wunderui.com/hooks/use-chart-animation
992
992
  - `useDataTableUrlState` (Pro) — Keeps the page, sorting, filters and search of a DataTable in the address bar. `useDataTableUrlState(options?: DataTableUrlStateOptions & { search?: string; }): { initialState: DataTableQuery; onQueryChange: (query: DataTableQuery) => void; }` · https://wunderui.com/hooks/use-data-table-url-state
993
993
  - `useDensity` (Free) — Reads and changes the density of the nearest DensityProvider. `useDensity(): DensityContextValue` · https://wunderui.com/hooks/use-density
994
994
  - `useInViewAppear` (Pro) — Plays a chart's appear sequence once, when the chart scrolls into view. `useInViewAppear<T>(animate?: boolean, ready?: boolean, threshold?: number): { ref: React.Dispatch<React.SetStateAction<T | null>>; state: ChartAppearState; }` · https://wunderui.com/hooks/use-in-view-appear
@@ -996,12 +996,14 @@ Exports of `@wunderui/react` that are not components of their own: hooks, provid
996
996
  - `useRouteFocus` (Pro) — Moves focus to the new page heading after a client-side route change. `useRouteFocus<T>(routeKey: unknown): React.RefObject<T | null>` · https://wunderui.com/hooks/use-route-focus
997
997
  - `useScrollSpy` (Free) — Tells which section of a page or a scroll container is being read. `useScrollSpy(ids: string[], root?: React.RefObject<HTMLElement | null> | undefined, enabled?: boolean): readonly [string | undefined, React.Dispatch<React.SetStateAction<string | undefined>>]` · https://wunderui.com/hooks/use-scroll-spy
998
998
  - `useToast` (Free) — Shows, updates and closes toasts from anywhere below a Toaster. `useToast<Data>(): UseToastManagerReturnValue<Data>` · https://wunderui.com/hooks/use-toast
999
- - `useAnimatedNumber` from `@wunderui/react/charts` (Pro) — The number on screen while it counts from the previous value to `value` (duration token, default --duration-slow, --ease-entrance).
1000
- - `useChartInteraction` from `@wunderui/react/charts` (Pro) — Pointer and keyboard behaviour shared by the cartesian charts: - pointer move → active point (tooltip), leave → none; - ← → (↑ ↓ for horizontal charts), Home, End walk the points and announce them in the live region; Esc clears point and r…
1001
- - `useChartLegend` from `@wunderui/react/charts` (Pro) — State for a toggleable legend: `hidden` goes to the chart's `hiddenSeries` and the legend's `hidden`, `toggle` to the legend's `onToggle`.
1002
- - `useChartReload` from `@wunderui/react/charts` (Pro) — Reload behaviour for a chart card's refresh button: loading skeleton while the task runs (at least two --duration-deliberate, so the skeleton is readable), then the chart enters again.
999
+ - `useAnimatedNumber` from `@wunderui/react/charts` (Pro) — A number that counts from its previous value to the new one, for chart headlines and KPI values. `useAnimatedNumber(value: number | null | undefined, token?: string): number | null` · https://wunderui.com/hooks/use-animated-number
1000
+ - `useChartInteraction` from `@wunderui/react/charts` (Pro) — Pointer and keyboard behaviour of a chart: the active point, arrow keys, range selection and the live announcement. `useChartInteraction(o: ChartInteractionOptions): { active: number | null; range: ChartRange | null; announcement: string; handlers: { onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void; onPointerMove: (e: React.PointerEvent<HTMLDivElement>) => void; onPointerDown: (e: React.Poin…` · https://wunderui.com/hooks/use-chart-interaction
1001
+ - `useChartLegend` from `@wunderui/react/charts` (Pro) — Keeps track of which series a chart legend has switched off. `useChartLegend(series: readonly ChartSeries[], initialHidden?: readonly string[]): { hidden: string[]; toggle: (key: string) => void; items: ChartLegendItem[]; }` · https://wunderui.com/hooks/use-chart-legend
1002
+ - `useChartReload` from `@wunderui/react/charts` (Pro) — Drives the reload button of a chart card: loading skeleton, your task, then the chart draws in again. `useChartReload(): ChartReload` · https://wunderui.com/hooks/use-chart-reload
1003
1003
  - `useChartWidth` from `@wunderui/react/charts` (Pro) — Width of a box, measured before the first paint and kept current with a ResizeObserver.
1004
- - `useGlide` from `@wunderui/react/charts` (Pro) — Keeps the last active index while the hover marks fade out, and flags the frame they reappear.
1004
+ - `useGlide` from `@wunderui/react/charts` (Pro) — Lets hover marks glide from point to point and fade out in place, instead of jumping or vanishing. `useGlide(active: number | null): Glide` · https://wunderui.com/hooks/use-glide
1005
+ - `useLoadPhase` from `@wunderui/react/charts` (Pro) — The phase of the surrounding load sequence (null when there is none).
1006
+ - `useLoadSequence` from `@wunderui/react/charts` (Pro) — Opens every dashboard page the same way: the cards build up top to bottom, then numbers roll, charts draw in and bars fill — with skeletons first only when the data takes longer. `useLoadSequence(ref: React.RefObject<HTMLElement | null>, options?: LoadSequenceOptions): LoadPhase` · https://wunderui.com/hooks/use-load-sequence
1005
1007
  - `usePrefersReducedMotion` from `@wunderui/react/charts` (Pro) — True when the visitor asked for reduced motion (read once on mount).
1006
1008
 
1007
1009
  ### Providers
@@ -1052,6 +1054,8 @@ Exports of `@wunderui/react` that are not components of their own: hooks, provid
1052
1054
  - `ChartSkeleton` from `@wunderui/react/charts` (Pro) — Loading placeholder in the shape of the chart (Figma State – Loading): a muted area for line charts, muted columns or rows for bar charts.
1053
1055
  - `ChartTooltip` from `@wunderui/react/charts` (Pro) — The tooltip card of the Figma states (Hover): title with an optional delta chip, then one row per series — dot, label, value.
1054
1056
  - `GlideGroup` from `@wunderui/react/charts` (Pro) — An SVG group placed by a CSS transform that glides to its new position (duration-slow, ease-entrance) and fades in and out.
1057
+ - `LoadSequenceContext` from `@wunderui/react/charts` (Pro) — The phase of the load sequence around this element; null outside one.
1058
+ - `NumberRoll` from `@wunderui/react/charts` (Pro) — A number that rolls in like an odometer: each digit column rolls up and settles on its value, left to right, a short blur while moving.
1055
1059
  - `PolarShell` from `@wunderui/react/charts` (Pro) — Shell of the radial charts — the polar twin of CartesianShell: size, states (skeleton ring/pie, empty, error), 2-D hover with the gliding tooltip, keyboard walk over the slices, live region, data table and the enter sequence (marks remount…
1056
1060
 
1057
1061
  ### Utilities
@@ -1113,10 +1117,13 @@ Exports of `@wunderui/react` that are not components of their own: hooks, provid
1113
1117
  - `COMPOSED_CHART_VARIANTS` from `@wunderui/react/charts` (Pro) — The 4 Composed Chart variants of the Figma catalogue (names in kebab-case).
1114
1118
  - `DISTRIBUTION_CHART_VARIANTS` from `@wunderui/react/charts` (Pro) — The 9 Distribution Chart variants of the Figma catalogue (names in kebab-case).
1115
1119
  - `FUNNEL_CHART_VARIANTS` from `@wunderui/react/charts` (Pro) — The 19 Funnel Chart variants of the Figma catalogue (names in kebab-case).
1120
+ - `FUNNEL_HEADROOM` from `@wunderui/react/charts` (Pro) — Space between the highest point of a funnel area and the top of its plot (under the KPI header).
1116
1121
  - `GAUGE_CHART_VARIANTS` from `@wunderui/react/charts` (Pro) — The 4 Gauge variants of the Figma catalogue (names in kebab-case).
1117
1122
  - `HEATMAP_CHART_VARIANTS` from `@wunderui/react/charts` (Pro) — The 5 Heatmap variants of the Figma catalogue (names in kebab-case).
1118
1123
  - `HOVER_TEXT` from `@wunderui/react/charts` (Pro) — Text on a hovered (two steps lighter) fill: the near-black token reads on every step in both themes.
1119
1124
  - `LINE_CHART_VARIANTS` from `@wunderui/react/charts` (Pro) — The 21 Line Chart variants of the Figma catalogue (names in kebab-case).
1125
+ - `LOAD_SKELETON_DELAY` from `@wunderui/react/charts` (Pro) — How long the data may take before the skeletons show (ms).
1126
+ - `LOAD_SKELETON_MIN` from `@wunderui/react/charts` (Pro) — Once the skeletons show, they stay at least this long (ms), so they never flash.
1120
1127
  - `NETWORK_CHART_VARIANTS` from `@wunderui/react/charts` (Pro) — The 4 Network Chart variants of the Figma catalogue (names in kebab-case).
1121
1128
  - `PIE_CHART_VARIANTS` from `@wunderui/react/charts` (Pro) — The 15 Pie Chart variants of the Figma catalogue (names in kebab-case).
1122
1129
  - `PLOT` from `@wunderui/react/charts` (Pro) — Plot frame: Figma large card (1416 × 434), tightened after the owner review (Oct 21 2026) — grid 12 px from the top, x labels 22 px below it.
@@ -1150,13 +1157,16 @@ Exports of `@wunderui/react` that are not components of their own: hooks, provid
1150
1157
  - `labelOn` from `@wunderui/react/charts` (Pro) — Text colour for a label drawn on a chart fill: white or near-black, whichever has the higher contrast (≥ 4.5:1) on that fill at that opacity over the card, in light and dark (variables `--chart-on-<token>-<opacity>` in styles.css).
1151
1158
  - `labelStride` from `@wunderui/react/charts` (Pro) — Every k-th label so neighbouring labels keep at least `gap` px between them.
1152
1159
  - `lineColor` from `@wunderui/react/charts` (Pro) — The colour for a line, a point or a thin stroke of a series (see LINE_COLORS); fills keep `resolveChartColor`.
1160
+ - `loadDelays` from `@wunderui/react/charts` (Pro) — When each card appears, in ms after the page starts building, for the slots of `readingOrder`: strictly top to bottom — a row starts 40 ms after the last card of the row above, the cards of a row follow each other 25 ms apart — capped at 4…
1153
1161
  - `logDomain` from `@wunderui/react/charts` (Pro) — Log axis: whole decades from the smallest positive value to the largest.
1154
1162
  - `niceDomain` from `@wunderui/react/charts` (Pro) — The value axis of the Figma cards: a fixed number of equal intervals (default 6 → 7 grid lines), always including 0, so the grid starts at the lowest value and every bar or area stands on the bottom grid line (owner rules, Sep 25 2026).
1155
1163
  - `prefersReducedMotion` from `@wunderui/react/charts` (Pro)
1156
1164
  - `readDuration` from `@wunderui/react/charts` (Pro) — Reads a duration token (e.g.
1165
+ - `readingOrder` from `@wunderui/react/charts` (Pro) — Reading order of a set of boxes (their top-left corners in px): rows top to bottom — a box whose top lies within `tolerance` of the row's first box joins that row — and left to right inside a row.
1157
1166
  - `resolveChartColor` from `@wunderui/react/charts` (Pro)
1158
1167
  - `seriesColor` from `@wunderui/react/charts` (Pro)
1159
1168
  - `seriesLabel` from `@wunderui/react/charts` (Pro)
1169
+ - `splitNumberRoll` from `@wunderui/react/charts` (Pro) — Splits a formatted value ("$48,200", "3.4h", "+12.5%", "1.2M") into the parts NumberRoll draws: digits roll (one full turn plus their value, so every column moves), everything else — signs, currency, separators, units — stays put.
1160
1170
  - `spreadLabels` from `@wunderui/react/charts` (Pro) — Moves label positions apart so neighbours keep `gap` px (order kept, nulls skipped, clamped to [min, max]).
1161
1171
  - `svgId` from `@wunderui/react/charts` (Pro) — SVG-safe id fragment from React.useId().
1162
1172
  - `toNumber` from `@wunderui/react/charts` (Pro) — A finite number or null (null = gap: missing, NaN, Infinity, non-numeric).
@@ -1591,7 +1601,7 @@ A single choice that is on or off, with an indeterminate state for “some selec
1591
1601
  ```
1592
1602
 
1593
1603
  ```ts
1594
- function Checkbox({ className, ...props }: Checkbox.Root.Props): React.JSX.Element;
1604
+ function Checkbox({ className, inputRef, ...props }: Checkbox.Root.Props): React.JSX.Element;
1595
1605
  ```
1596
1606
 
1597
1607
  | Prop | Type | Default |
@@ -1642,7 +1652,7 @@ A toggle control for a binary setting.
1642
1652
  ```
1643
1653
 
1644
1654
  ```ts
1645
- function Switch({ className, size, ...props }: Switch.Root.Props & {
1655
+ function Switch({ className, size, inputRef, ...props }: Switch.Root.Props & {
1646
1656
  size?: "sm" | "default";
1647
1657
  }): React.JSX.Element;
1648
1658
  ```
@@ -2699,7 +2709,7 @@ Theming: reads `--border`, `--card`, `--foreground`, `--muted`, `--muted-foregro
2699
2709
 
2700
2710
  #### LineChart (@wunderui/react/charts)
2701
2711
 
2702
- Line chart for trends over time: straight, smooth and stepped lines, comparisons, bands, forecasts and thresholds, with tooltip, keyboard navigation and a data table.
2712
+ Line chart for trends over time: smooth lines (straight on request) and stepped lines, comparisons, bands, forecasts and thresholds, with tooltip, keyboard navigation and a data table.
2703
2713
 
2704
2714
  `import { LineChart } from "@wunderui/react/charts"`
2705
2715
 
@@ -9154,12 +9164,18 @@ A small pill showing a change, up or down, in seven icon styles.
9154
9164
  ```
9155
9165
 
9156
9166
  ```ts
9157
- function TrendChip({ value, direction, icon, className, }: {
9167
+ function TrendChip({ value, direction, icon, sentiment, className, }: {
9158
9168
  value: string;
9159
9169
  /** Figma Trend Chip · Trend; `flat` is the old name for `neutral`. */
9160
9170
  direction?: "up" | "down" | "neutral" | "flat";
9161
9171
  /** Figma Trend Chip · Icon; the same glyph turns for `down`. */
9162
9172
  icon?: TrendChipIcon;
9173
+ /**
9174
+ * Figma Trend Chip · Sentiment: whether the change is good or bad. `auto` colours by direction
9175
+ * (up green, down red); use `positive` for a fall that is good (churn, cost) or `negative` for
9176
+ * a rise that is bad. The arrow always follows `direction`.
9177
+ */
9178
+ sentiment?: "auto" | "positive" | "negative" | "neutral";
9163
9179
  className?: string;
9164
9180
  }): React.JSX.Element;
9165
9181
  type TrendChipIcon = "trending" | "arrow" | "arrow-diagonal" | "triangle" | "circle-trending" | "circle-diagonal" | "circle-arrow";
@@ -9171,6 +9187,7 @@ type TrendChipIcon = "trending" | "arrow" | "arrow-diagonal" | "triangle" | "cir
9171
9187
  | `className` | `string` | — |
9172
9188
  | `direction` | `"up" \| "down" \| "neutral" \| "flat"` | `"up"` |
9173
9189
  | `icon` | `"triangle" \| "trending" \| "arrow" \| "arrow-diagonal" \| "circle-trending" \| "circle-diagonal" \| "circle-arrow"` | `"trending"` |
9190
+ | `sentiment` | `"auto" \| "positive" \| "negative" \| "neutral"` | `"auto"` |
9174
9191
 
9175
9192
  Anatomy:
9176
9193
 
package/data/design.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "$schema": "https://wunderui.com/design.schema.json",
3
3
  "name": "@wunderui/react",
4
- "version": "0.1.0",
4
+ "version": "0.3.0",
5
5
  "description": "WunderUI — a design system and component library for React.",
6
6
  "site": "https://wunderui.com",
7
- "generatedAt": "2026-10-22",
7
+ "generatedAt": "2026-10-23",
8
8
  "install": {
9
9
  "available": "Core and Pro licence holders: the full library from the private GitHub repository wunder-ui/wunderui-core (Polar invites your GitHub account after the purchase). The npm package of the same name is WunderUI Free (free components only).",
10
10
  "package": "Accept the GitHub invitation, `git clone https://github.com/wunder-ui/wunderui-core`, run `npm install && npm run build` in it once, then in your project: `mkdir .wunderui`, `npm pack <wunderui-core>/packages/react --pack-destination .wunderui` and `npm install ./.wunderui/<the .tgz>`. The wunderui-setup agent skill does all of it.",
@@ -21,19 +21,19 @@
21
21
  "typeExports": 216,
22
22
  "allExportNames": 729,
23
23
  "catalogExports": 558,
24
- "chartsEntry": {"exports":116,"typeExports":101,"catalogExports":86},
25
- "otherExports": 165,
24
+ "chartsEntry": {"exports":126,"typeExports":108,"catalogExports":86},
25
+ "otherExports": 175,
26
26
  "exportsMethod": "TypeScript compiler API: checker.getExportsOfModule(packages/react/src/index.ts). valueExports = exported values (components, hooks, functions, constants); typeExports = type-only exports; allExportNames = both, unique. catalogExports = names listed in components[].exports (values and types); otherExports = value exports outside the catalogue (hooks, providers, utilities, parts).",
27
- "props": 3317,
28
- "propsWithValues": 314,
29
- "propsWithDefault": 666,
27
+ "props": 3318,
28
+ "propsWithValues": 315,
29
+ "propsWithDefault": 667,
30
30
  "templates": 5,
31
31
  "free": 83,
32
32
  "pro": 139,
33
33
  "guidesAnatomyAuthored": 195,
34
34
  "guidesBestPracticesAuthored": 195,
35
35
  "guidesWithThemingVariables": 211,
36
- "hooks": 16
36
+ "hooks": 18
37
37
  },
38
38
  "plans": {
39
39
  "free": "WunderUI Free: the components with tier \"free\" are open source under the MIT License (https://github.com/wunder-ui/wunderui) — any project, commercial included. Every \"pro\" component needs WunderUI Core or Pro.",
@@ -2114,7 +2114,7 @@
2114
2114
  "group": "Core Elements",
2115
2115
  "tier": "free",
2116
2116
  "exports": ["Checkbox"],
2117
- "props": "function Checkbox({ className, ...props }: Checkbox.Root.Props): React.JSX.Element;",
2117
+ "props": "function Checkbox({ className, inputRef, ...props }: Checkbox.Root.Props): React.JSX.Element;",
2118
2118
  "api": [
2119
2119
  {
2120
2120
  "name": "Checkbox",
@@ -2296,7 +2296,7 @@
2296
2296
  "group": "Core Elements",
2297
2297
  "tier": "free",
2298
2298
  "exports": ["Switch","SwitchItemCell"],
2299
- "props": "function Switch({ className, size, ...props }: Switch.Root.Props & {\n size?: \"sm\" | \"default\";\n}): React.JSX.Element;",
2299
+ "props": "function Switch({ className, size, inputRef, ...props }: Switch.Root.Props & {\n size?: \"sm\" | \"default\";\n}): React.JSX.Element;",
2300
2300
  "api": [
2301
2301
  {
2302
2302
  "name": "Switch",
@@ -8338,7 +8338,7 @@
8338
8338
  "slug": "line-chart",
8339
8339
  "title": "Line Chart",
8340
8340
  "category": "Charts",
8341
- "description": "Line chart for trends over time: straight, smooth and stepped lines, comparisons, bands, forecasts and thresholds, with tooltip, keyboard navigation and a data table.",
8341
+ "description": "Line chart for trends over time: smooth lines (straight on request) and stepped lines, comparisons, bands, forecasts and thresholds, with tooltip, keyboard navigation and a data table.",
8342
8342
  "group": "Data & Charts",
8343
8343
  "tier": "pro",
8344
8344
  "import": "@wunderui/react/charts",
@@ -26052,7 +26052,7 @@
26052
26052
  "group": "Feedback & Overlays",
26053
26053
  "tier": "free",
26054
26054
  "exports": ["TrendChip","TrendCaret","TrendChipIcon"],
26055
- "props": "function TrendChip({ value, direction, icon, className, }: {\n value: string;\n /** Figma Trend Chip · Trend; `flat` is the old name for `neutral`. */\n direction?: \"up\" | \"down\" | \"neutral\" | \"flat\";\n /** Figma Trend Chip · Icon; the same glyph turns for `down`. */\n icon?: TrendChipIcon;\n className?: string;\n}): React.JSX.Element;\ntype TrendChipIcon = \"trending\" | \"arrow\" | \"arrow-diagonal\" | \"triangle\" | \"circle-trending\" | \"circle-diagonal\" | \"circle-arrow\";",
26055
+ "props": "function TrendChip({ value, direction, icon, sentiment, className, }: {\n value: string;\n /** Figma Trend Chip · Trend; `flat` is the old name for `neutral`. */\n direction?: \"up\" | \"down\" | \"neutral\" | \"flat\";\n /** Figma Trend Chip · Icon; the same glyph turns for `down`. */\n icon?: TrendChipIcon;\n /**\n * Figma Trend Chip · Sentiment: whether the change is good or bad. `auto` colours by direction\n * (up green, down red); use `positive` for a fall that is good (churn, cost) or `negative` for\n * a rise that is bad. The arrow always follows `direction`.\n */\n sentiment?: \"auto\" | \"positive\" | \"negative\" | \"neutral\";\n className?: string;\n}): React.JSX.Element;\ntype TrendChipIcon = \"trending\" | \"arrow\" | \"arrow-diagonal\" | \"triangle\" | \"circle-trending\" | \"circle-diagonal\" | \"circle-arrow\";",
26056
26056
  "api": [
26057
26057
  {
26058
26058
  "name": "TrendChip",
@@ -26072,6 +26072,13 @@
26072
26072
  "values": ["triangle","trending","arrow","arrow-diagonal","circle-trending","circle-diagonal","circle-arrow"],
26073
26073
  "default": "\"trending\"",
26074
26074
  "description": "Figma Trend Chip · Icon; the same glyph turns for `down`."
26075
+ },
26076
+ {
26077
+ "name": "sentiment",
26078
+ "type": "\"auto\" | \"positive\" | \"negative\" | \"neutral\"",
26079
+ "values": ["positive","negative","auto","neutral"],
26080
+ "default": "\"auto\"",
26081
+ "description": "Figma Trend Chip · Sentiment: whether the change is good or bad. `auto` colours by direction (up green, down red); use `positive` for a fall that is good (churn, cost) or `negative` for a rise that is bad. The arrow always follows `directi…"
26075
26082
  }
26076
26083
  ]
26077
26084
  },
@@ -32081,7 +32088,7 @@
32081
32088
  "file": "components/ui/chart.tsx",
32082
32089
  "slug": "use-chart-animation",
32083
32090
  "docs": "https://wunderui.com/hooks/use-chart-animation",
32084
- "summary": "The draw-in animation props every WunderUI chart passes to its Recharts series.",
32091
+ "summary": "Legacy: the draw-in animation props for Recharts series, used by the legacy Recharts charts the templates still use.",
32085
32092
  "guide": "Returns `isAnimationActive`, `animationDuration`, `animationEasing` and `animationBegin` for a Recharts series. The series draw in once on mount and then stop animating, so a resize or new data does not replay the draw-in. Under reduced motion they never animate.",
32086
32093
  "signature": "useChartAnimation(animate?: boolean, appear?: ChartAppearState | undefined): { isAnimationActive: boolean; animationDuration: number; animationEasing: \"ease-out\"; animationBegin: number; }",
32087
32094
  "params": [
@@ -32098,6 +32105,7 @@
32098
32105
  ]
32099
32106
  },
32100
32107
  "notes": [
32108
+ "Legacy. The chart system in `@wunderui/react/charts` draws in on its own; for a reload use `useChartReload`, for hover marks `useGlide`, for counting values `useAnimatedNumber`.",
32101
32109
  "Use it only in a custom chart built from Recharts parts; the WunderUI charts already call it.",
32102
32110
  "Pass `animate={false}` to switch the draw-in off, for example in a table of many small charts.",
32103
32111
  "With `useInViewAppear`, pass its `state` as the second argument: the series then animate exactly while the appear sequence runs."
@@ -32108,7 +32116,7 @@
32108
32116
  "code": "import { Line, LineChart } from \"recharts\"\nimport { useChartAnimation } from \"@wunderui/react\"\n\nexport function TrendLine({ data }: { data: { day: string; value: number }[] }) {\n const animation = useChartAnimation()\n return (\n <LineChart width={320} height={120} data={data}>\n <Line dataKey=\"value\" stroke=\"var(--chart-1)\" dot={false} {...animation} />\n </LineChart>\n )\n}"
32109
32117
  }
32110
32118
  ],
32111
- "related": [{"slug":"line-chart","label":"Line Chart"}],
32119
+ "legacy": "Legacy: used by the legacy Recharts charts that the templates still use. The charts of @wunderui/react/charts animate on their own; for your own chart card see",
32112
32120
  "guideSource": "authored"
32113
32121
  },
32114
32122
  {
@@ -32196,13 +32204,15 @@
32196
32204
  "properties": [{"name":"ref","type":"React.Dispatch<React.SetStateAction<T | null>>"},{"name":"state","type":"ChartAppearState"}]
32197
32205
  },
32198
32206
  "notes": [
32207
+ "The new charts of `@wunderui/react/charts` use this hook internally (in their frame, the sparklines and the status tracker), so a chart from that entry needs no extra wiring; use it yourself only for a custom chart root.",
32199
32208
  "`static` means no motion: `animate={false}`, reduced motion, a background tab or a browser without IntersectionObserver.",
32200
32209
  "`ready={false}` (the chart shows its loading skeleton) resets it, so the series arrive with the motion once the data does.",
32201
- "The styles for `data-chart-appear` ship in the WunderUI stylesheet; the hook only sets the state."
32210
+ "The styles for `data-chart-appear` ship in the WunderUI stylesheet; the hook only sets the state.",
32211
+ "Inside a dashboard load sequence (`LoadSequenceContext` is `pending` or `loading`, see useLoadSequence) it waits for the sequence to play, so the chart starts when its card lands — at the card's `--load-content-delay` — not at mount."
32202
32212
  ],
32203
32213
  "examples": [
32204
32214
  {
32205
- "title": "A custom chart root",
32215
+ "title": "A custom chart root (legacy Recharts series)",
32206
32216
  "code": "import { useChartAnimation, useInViewAppear } from \"@wunderui/react\"\n\nexport function RevenueChart({ data, loading }: { data: Point[]; loading: boolean }) {\n const { ref, state } = useInViewAppear<HTMLDivElement>(true, !loading)\n const animation = useChartAnimation(true, state)\n return (\n <div ref={ref} data-chart-appear={state} className=\"h-48\">\n {/* Recharts series receive {...animation} */}\n </div>\n )\n}"
32207
32217
  }
32208
32218
  ],
@@ -32969,6 +32979,14 @@
32969
32979
  "file": "components/ui/charts/funnel-chart.tsx",
32970
32980
  "description": "The 19 Funnel Chart variants of the Figma catalogue (names in kebab-case)."
32971
32981
  },
32982
+ {
32983
+ "name": "FUNNEL_HEADROOM",
32984
+ "import": "@wunderui/react/charts",
32985
+ "kind": "utility",
32986
+ "tier": "pro",
32987
+ "file": "components/ui/charts/funnel-chart.tsx",
32988
+ "description": "Space between the highest point of a funnel area and the top of its plot (under the KPI header)."
32989
+ },
32972
32990
  {
32973
32991
  "name": "GAUGE_CHART_VARIANTS",
32974
32992
  "import": "@wunderui/react/charts",
@@ -33018,6 +33036,32 @@
33018
33036
  "file": "components/ui/charts/line-chart.tsx",
33019
33037
  "description": "The 21 Line Chart variants of the Figma catalogue (names in kebab-case)."
33020
33038
  },
33039
+ {
33040
+ "name": "LOAD_SKELETON_DELAY",
33041
+ "import": "@wunderui/react/charts",
33042
+ "kind": "utility",
33043
+ "tier": "pro",
33044
+ "file": "components/ui/load-sequence.tsx",
33045
+ "description": "How long the data may take before the skeletons show (ms)."
33046
+ },
33047
+ {
33048
+ "name": "LOAD_SKELETON_MIN",
33049
+ "import": "@wunderui/react/charts",
33050
+ "kind": "utility",
33051
+ "tier": "pro",
33052
+ "file": "components/ui/load-sequence.tsx",
33053
+ "description": "Once the skeletons show, they stay at least this long (ms), so they never flash."
33054
+ },
33055
+ {
33056
+ "name": "LoadSequenceContext",
33057
+ "import": "@wunderui/react/charts",
33058
+ "kind": "part",
33059
+ "tier": "pro",
33060
+ "file": "components/ui/load-sequence.tsx",
33061
+ "description": "The phase of the load sequence around this element; null outside one.",
33062
+ "props": [],
33063
+ "inherits": {"from":["React.ProviderProps"],"count":2}
33064
+ },
33021
33065
  {
33022
33066
  "name": "NETWORK_CHART_VARIANTS",
33023
33067
  "import": "@wunderui/react/charts",
@@ -33026,6 +33070,30 @@
33026
33070
  "file": "components/ui/charts/network-chart.tsx",
33027
33071
  "description": "The 4 Network Chart variants of the Figma catalogue (names in kebab-case)."
33028
33072
  },
33073
+ {
33074
+ "name": "NumberRoll",
33075
+ "import": "@wunderui/react/charts",
33076
+ "kind": "part",
33077
+ "tier": "pro",
33078
+ "file": "components/ui/load-sequence.tsx",
33079
+ "description": "A number that rolls in like an odometer: each digit column rolls up and settles on its value, left to right, a short blur while moving. The value renders as plain text on the server, before and after the roll and under reduced motion, so i…",
33080
+ "props": [
33081
+ {"name":"value","type":"string | number","required":true,"description":"The formatted value, exactly as it should read at the end."},
33082
+ {
33083
+ "name": "delay",
33084
+ "type": "number",
33085
+ "default": "0",
33086
+ "description": "Extra wait before the first digit moves (ms), on top of the card's --load-delay."
33087
+ },
33088
+ {"name":"onDone","type":"() => void","description":"Called when the last digit has settled."},
33089
+ {
33090
+ "name": "play",
33091
+ "type": "boolean",
33092
+ "description": "Roll now. Defaults to the surrounding load sequence (rolls while it plays); outside one the value is shown as plain text."
33093
+ }
33094
+ ],
33095
+ "inherits": {"from":["React.HTMLAttributes"],"count":279}
33096
+ },
33029
33097
  {
33030
33098
  "name": "PIE_CHART_VARIANTS",
33031
33099
  "import": "@wunderui/react/charts",
@@ -33341,6 +33409,14 @@
33341
33409
  "file": "components/ui/charts/chart-utils.ts",
33342
33410
  "description": "The colour for a line, a point or a thin stroke of a series (see LINE_COLORS); fills keep `resolveChartColor`."
33343
33411
  },
33412
+ {
33413
+ "name": "loadDelays",
33414
+ "import": "@wunderui/react/charts",
33415
+ "kind": "utility",
33416
+ "tier": "pro",
33417
+ "file": "components/ui/load-sequence.tsx",
33418
+ "description": "When each card appears, in ms after the page starts building, for the slots of `readingOrder`: strictly top to bottom — a row starts 40 ms after the last card of the row above, the cards of a row follow each other 25 ms apart — capped at 4…"
33419
+ },
33344
33420
  {
33345
33421
  "name": "logDomain",
33346
33422
  "import": "@wunderui/react/charts",
@@ -33372,9 +33448,25 @@
33372
33448
  "file": "components/ui/charts/chart-motion.tsx",
33373
33449
  "description": "Reads a duration token (e.g. \"--duration-base\") in ms; falls back when unavailable (SSR, tests)."
33374
33450
  },
33451
+ {
33452
+ "name": "readingOrder",
33453
+ "import": "@wunderui/react/charts",
33454
+ "kind": "utility",
33455
+ "tier": "pro",
33456
+ "file": "components/ui/load-sequence.tsx",
33457
+ "description": "Reading order of a set of boxes (their top-left corners in px): rows top to bottom — a box whose top lies within `tolerance` of the row's first box joins that row — and left to right inside a row. Returns the row and column of every box, i…"
33458
+ },
33375
33459
  {"name":"resolveChartColor","import":"@wunderui/react/charts","kind":"utility","tier":"pro","file":"components/ui/charts/chart-utils.ts"},
33376
33460
  {"name":"seriesColor","import":"@wunderui/react/charts","kind":"utility","tier":"pro","file":"components/ui/charts/chart-utils.ts"},
33377
33461
  {"name":"seriesLabel","import":"@wunderui/react/charts","kind":"utility","tier":"pro","file":"components/ui/charts/chart-utils.ts"},
33462
+ {
33463
+ "name": "splitNumberRoll",
33464
+ "import": "@wunderui/react/charts",
33465
+ "kind": "utility",
33466
+ "tier": "pro",
33467
+ "file": "components/ui/load-sequence.tsx",
33468
+ "description": "Splits a formatted value (\"$48,200\", \"3.4h\", \"+12.5%\", \"1.2M\") into the parts NumberRoll draws: digits roll (one full turn plus their value, so every column moves), everything else — signs, currency, separators, units — stays put. `index`…"
33469
+ },
33378
33470
  {
33379
33471
  "name": "spreadLabels",
33380
33472
  "import": "@wunderui/react/charts",
@@ -33405,7 +33497,31 @@
33405
33497
  "kind": "hook",
33406
33498
  "tier": "pro",
33407
33499
  "file": "components/ui/charts/chart-motion.tsx",
33408
- "description": "The number on screen while it counts from the previous value to `value` (duration token, default --duration-slow, --ease-entrance). Retargets mid-way when the value changes again (sliding across a chart). Null and non-finite values are pas…"
33500
+ "description": "The number on screen while it counts from the previous value to `value` (duration token, default --duration-slow, --ease-entrance). Retargets mid-way when the value changes again (sliding across a chart). Null and non-finite values are pas…",
33501
+ "slug": "use-animated-number",
33502
+ "docs": "https://wunderui.com/hooks/use-animated-number",
33503
+ "summary": "A number that counts from its previous value to the new one, for chart headlines and KPI values.",
33504
+ "guide": "Returns the value to show while it counts. The count uses the `--duration-slow` token and the entrance curve by default; pass another duration token as the second argument. When the value changes mid-count (a pointer sliding across a chart), it turns towards the new target instead of starting over. The `AnimatedNumber` component wraps the hook with a formatter.",
33505
+ "signature": "useAnimatedNumber(value: number | null | undefined, token?: string): number | null",
33506
+ "params": [{"name":"value","type":"number | null | undefined"},{"name":"token","type":"string","optional":true,"default":"\"--duration-slow\""}],
33507
+ "returns": {"type":"number | null"},
33508
+ "notes": [
33509
+ "Under reduced motion the number changes at once, without counting.",
33510
+ "Screen readers should get the final value only: `AnimatedNumber` hides the counting digits from them and adds the final value as visually hidden text. Do the same when you use the hook directly.",
33511
+ "`null` and values that are not finite are passed through, so a missing value shows as your placeholder."
33512
+ ],
33513
+ "examples": [
33514
+ {
33515
+ "title": "The value of the hovered point",
33516
+ "code": "import { ChartCard, LineChart } from \"@wunderui/react/charts\"\n\nexport function Revenue({ data }: { data: { month: string; revenue: number }[] }) {\n const [active, setActive] = React.useState<number | null>(null)\n const value = data[active ?? data.length - 1].revenue\n return (\n <ChartCard title=\"Revenue\" value={value} valueFormatter={(v) => `$${Math.round(v).toLocaleString(\"en-US\")}`}>\n <LineChart data={data} index=\"month\" series={[{ key: \"revenue\" }]} onActiveIndexChange={setActive} label=\"Revenue by month\" />\n </ChartCard>\n )\n}"
33517
+ },
33518
+ {
33519
+ "title": "The hook on its own",
33520
+ "code": "import { useAnimatedNumber } from \"@wunderui/react/charts\"\n\nfunction Total({ value }: { value: number }) {\n const shown = useAnimatedNumber(value, \"--duration-base\")\n return (\n <p className=\"text-3xl font-semibold tabular-nums\">\n <span aria-hidden=\"true\">{Math.round(shown ?? 0).toLocaleString(\"en-US\")}</span>\n <span className=\"sr-only\">{value.toLocaleString(\"en-US\")}</span>\n </p>\n )\n}"
33521
+ }
33522
+ ],
33523
+ "related": [{"slug":"chart-card","label":"Chart Card"},{"slug":"metric-card","label":"Metric Card"}],
33524
+ "guideSource": "authored"
33409
33525
  },
33410
33526
  {
33411
33527
  "name": "useChartInteraction",
@@ -33413,7 +33529,88 @@
33413
33529
  "kind": "hook",
33414
33530
  "tier": "pro",
33415
33531
  "file": "components/ui/charts/use-chart-interaction.ts",
33416
- "description": "Pointer and keyboard behaviour shared by the cartesian charts: - pointer move → active point (tooltip), leave → none; - ← → (↑ ↓ for horizontal charts), Home, End walk the points and announce them in the live region; Esc clears point and r…"
33532
+ "description": "Pointer and keyboard behaviour shared by the cartesian charts: - pointer move → active point (tooltip), leave → none; - ← → (↑ ↓ for horizontal charts), Home, End walk the points and announce them in the live region; Esc clears point and r…",
33533
+ "slug": "use-chart-interaction",
33534
+ "docs": "https://wunderui.com/hooks/use-chart-interaction",
33535
+ "summary": "Pointer and keyboard behaviour of a chart: the active point, arrow keys, range selection and the live announcement.",
33536
+ "guide": "The behaviour the WunderUI cartesian charts share, for a chart you draw yourself. Pointer movement sets the active point and leaving clears it; the arrow keys (up and down on horizontal charts), Home and End walk the points and announce each one; Escape clears point and range. With `rangeSelectable`, dragging across the plot or Shift with the arrow keys selects a range. Spread `handlers` on the focusable plot element and render `announcement` in a polite live region.",
33537
+ "signature": "useChartInteraction(o: ChartInteractionOptions): { active: number | null; range: ChartRange | null; announcement: string; handlers: { onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void; onPointerMove: (e: React.PointerEvent<HTMLDivElement>) => void; onPointerDown: (e: React.Poin…",
33538
+ "params": [
33539
+ {
33540
+ "name": "o",
33541
+ "type": "ChartInteractionOptions",
33542
+ "properties": [
33543
+ {"name":"count","type":"number","description":"Number of categories / points."},
33544
+ {
33545
+ "name": "activeIndex",
33546
+ "type": "number | null | undefined",
33547
+ "optional": true,
33548
+ "description": "Controlled active point (Hover state); `null` = none."
33549
+ },
33550
+ {"name":"defaultActiveIndex","type":"number | null | undefined","optional":true},
33551
+ {"name":"onActiveIndexChange","type":"((index: number | null) => void) | undefined","optional":true},
33552
+ {
33553
+ "name": "selectedRange",
33554
+ "type": "ChartRange | null | undefined",
33555
+ "optional": true,
33556
+ "description": "Controlled selected range (Selected Range state), inclusive indices."
33557
+ },
33558
+ {"name":"defaultSelectedRange","type":"ChartRange | null | undefined","optional":true},
33559
+ {"name":"onSelectedRangeChange","type":"((range: ChartRange | null) => void) | undefined","optional":true},
33560
+ {
33561
+ "name": "rangeSelectable",
33562
+ "type": "boolean | undefined",
33563
+ "optional": true,
33564
+ "description": "Drag (pointer) and Shift + arrow keys (keyboard) select a range."
33565
+ },
33566
+ {
33567
+ "name": "vertical",
33568
+ "type": "boolean | undefined",
33569
+ "optional": true,
33570
+ "description": "Categories run top to bottom (horizontal bar charts): up/down keys, y pointer."
33571
+ },
33572
+ {"name":"invert","type":"(px: number) => number","description":"Pixel position inside the plot → category index."},
33573
+ {
33574
+ "name": "invertPoint",
33575
+ "type": "((x: number, y: number) => number | null) | undefined",
33576
+ "optional": true,
33577
+ "description": "2-D hit test for radial charts (pie, radar …): plot coordinates → index, or null outside every mark."
33578
+ },
33579
+ {"name":"describe","type":"(index: number) => string","description":"Sentence for the live region when the keyboard lands on a point."},
33580
+ {"name":"describeRange","type":"((range: ChartRange) => string) | undefined","optional":true}
33581
+ ]
33582
+ }
33583
+ ],
33584
+ "returns": {
33585
+ "type": "{ active: number | null; range: ChartRange | null; announcement: string; handlers: { onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void; onPointerMove: (e: React.PointerEvent<HTMLDivElement>) => void; onPointerDown: (e: React.Poin…",
33586
+ "properties": [
33587
+ {"name":"active","type":"number | null"},
33588
+ {"name":"range","type":"ChartRange | null"},
33589
+ {"name":"announcement","type":"string"},
33590
+ {
33591
+ "name": "handlers",
33592
+ "type": "{ onKeyDown: (e: React.KeyboardEvent<HTMLDivElement>) => void; onPointerMove: (e: React.PointerEvent<HTMLDivElement>) => void; onPointerDown: (e: React.PointerEvent<HTMLDivElement>) => void; onPointerUp: (e: React.PointerEvent<HTMLDivEleme…"
33593
+ }
33594
+ ]
33595
+ },
33596
+ "notes": [
33597
+ "The active point and the range can be controlled (`activeIndex`, `selectedRange`) or left to the hook (`defaultActiveIndex`, `defaultSelectedRange`).",
33598
+ "`invert` maps a pointer position inside the plot to a category index; radial charts pass `invertPoint` for a 2-D hit test instead.",
33599
+ "`describe` writes the sentence the live region reads for a point, for example \"March: $84,250\".",
33600
+ "Give the plot element `tabIndex={0}`, a role and a label, and a visible focus ring."
33601
+ ],
33602
+ "examples": [
33603
+ {
33604
+ "title": "A focusable custom plot",
33605
+ "code": "import { useChartInteraction } from \"@wunderui/react/charts\"\n\nexport function Dots({ values, width }: { values: number[]; width: number }) {\n const step = width / values.length\n const ui = useChartInteraction({\n count: values.length,\n invert: (px) => Math.max(0, Math.min(values.length - 1, Math.floor(px / step))),\n describe: (i) => `Day ${i + 1}: ${values[i]}`,\n })\n return (\n <div tabIndex={0} role=\"group\" aria-label=\"Daily values\" className=\"rounded-md outline-none focus-visible:ring-2 focus-visible:ring-ring\" {...ui.handlers}>\n <svg width={width} height={80} aria-hidden=\"true\">\n {values.map((v, i) => (\n <circle key={i} cx={i * step + step / 2} cy={70 - v} r={ui.active === i ? 6 : 4} fill=\"var(--chart-1)\" />\n ))}\n </svg>\n <p aria-live=\"polite\" className=\"sr-only\">{ui.announcement}</p>\n </div>\n )\n}"
33606
+ },
33607
+ {
33608
+ "title": "Range selection",
33609
+ "code": "const ui = useChartInteraction({\n count: data.length,\n rangeSelectable: true,\n onSelectedRangeChange: (range) => setRange(range),\n invert,\n describe,\n describeRange: ([a, b]) => `${data[a].month} to ${data[b].month}`,\n})"
33610
+ }
33611
+ ],
33612
+ "related": [{"slug":"line-chart","label":"Line Chart"},{"slug":"candlestick-chart","label":"Candlestick Chart"}],
33613
+ "guideSource": "authored"
33417
33614
  },
33418
33615
  {
33419
33616
  "name": "useChartLegend",
@@ -33421,7 +33618,35 @@
33421
33618
  "kind": "hook",
33422
33619
  "tier": "pro",
33423
33620
  "file": "components/ui/charts/chart-legend.tsx",
33424
- "description": "State for a toggleable legend: `hidden` goes to the chart's `hiddenSeries` and the legend's `hidden`, `toggle` to the legend's `onToggle`. At least one series always stays on."
33621
+ "description": "State for a toggleable legend: `hidden` goes to the chart's and the legend's `hiddenSeries`, `toggle` to the legend's `onToggle` (or the ChartCard's `onLegendToggle`). At least one series always stays on.",
33622
+ "slug": "use-chart-legend",
33623
+ "docs": "https://wunderui.com/hooks/use-chart-legend",
33624
+ "summary": "Keeps track of which series a chart legend has switched off.",
33625
+ "guide": "Give it the chart's series and pass `hidden` to the chart's `hiddenSeries`, `toggle` to the card's `onLegendToggle` and `items` to its `legend`. A ChartCard with two or more series already does this on its own; reach for the hook when the legend and the chart live apart, or when other parts of the page need to know which series are visible.",
33626
+ "signature": "useChartLegend(series: readonly ChartSeries[], initialHidden?: readonly string[]): { hidden: string[]; toggle: (key: string) => void; items: ChartLegendItem[]; }",
33627
+ "params": [{"name":"series","type":"readonly ChartSeries[]"},{"name":"initialHidden","type":"readonly string[]","optional":true,"default":"[]"}],
33628
+ "returns": {
33629
+ "type": "{ hidden: string[]; toggle: (key: string) => void; items: ChartLegendItem[]; }",
33630
+ "properties": [{"name":"hidden","type":"string[]"},{"name":"toggle","type":"(key: string) => void"},{"name":"items","type":"ChartLegendItem[]"}]
33631
+ },
33632
+ "notes": [
33633
+ "The last visible series cannot be switched off, so the chart never ends up empty.",
33634
+ "Keys match either the series `key` or its `label`, so a legend built from labels works too.",
33635
+ "Each badge is a toggle button with `aria-pressed`; a switched-off badge shows its label one step lighter and its dot dimmed.",
33636
+ "One series needs no legend toggle: ChartCard shows a plain badge then."
33637
+ ],
33638
+ "examples": [
33639
+ {
33640
+ "title": "A legend outside the card",
33641
+ "code": "import { ChartLegend, LineChart, useChartLegend } from \"@wunderui/react/charts\"\n\nconst series = [\n { key: \"revenue\", label: \"Revenue\", color: \"indigo\" },\n { key: \"costs\", label: \"Costs\", color: \"orange\" },\n]\n\nexport function RevenueVsCosts({ data }: { data: { month: string; revenue: number; costs: number }[] }) {\n const legend = useChartLegend(series)\n return (\n <div className=\"flex flex-col gap-4\">\n <ChartLegend items={legend.items} hiddenSeries={legend.hidden} onToggle={legend.toggle} />\n <LineChart data={data} index=\"month\" series={series} hiddenSeries={legend.hidden} label=\"Revenue and costs\" />\n </div>\n )\n}"
33642
+ },
33643
+ {
33644
+ "title": "Start with a series switched off",
33645
+ "code": "const legend = useChartLegend(series, [\"costs\"])\n\n<ChartCard title=\"Revenue\" legend={legend.items} hiddenSeries={legend.hidden} onLegendToggle={legend.toggle}>\n <LineChart data={data} index=\"month\" series={series} hiddenSeries={legend.hidden} label=\"Revenue and costs\" />\n</ChartCard>"
33646
+ }
33647
+ ],
33648
+ "related": [{"slug":"chart-card","label":"Chart Card"},{"slug":"line-chart","label":"Line Chart"}],
33649
+ "guideSource": "authored"
33425
33650
  },
33426
33651
  {
33427
33652
  "name": "useChartReload",
@@ -33429,7 +33654,42 @@
33429
33654
  "kind": "hook",
33430
33655
  "tier": "pro",
33431
33656
  "file": "components/ui/charts/chart-motion.tsx",
33432
- "description": "Reload behaviour for a chart card's refresh button: loading skeleton while the task runs (at least two --duration-deliberate, so the skeleton is readable), then the chart enters again."
33657
+ "description": "Reload behaviour for a chart card's refresh button: loading skeleton while the task runs (at least two --duration-deliberate, so the skeleton is readable), then the chart enters again.",
33658
+ "slug": "use-chart-reload",
33659
+ "docs": "https://wunderui.com/hooks/use-chart-reload",
33660
+ "summary": "Drives the reload button of a chart card: loading skeleton, your task, then the chart draws in again.",
33661
+ "guide": "`reload()` switches `state` to `loading`, runs the task you pass (fetching new data, for example) and waits at least two `--duration-deliberate` steps, so the skeleton is readable rather than a flash. Then `state` returns to `ready` and `version` goes up; pass it as `animationKey` so the chart plays its enter sequence again.",
33662
+ "signature": "useChartReload(): ChartReload",
33663
+ "params": [],
33664
+ "returns": {
33665
+ "type": "ChartReload",
33666
+ "properties": [
33667
+ {"name":"state","type":"\"ready\" | \"loading\""},
33668
+ {"name":"version","type":"number","description":"Bumped after every reload; pass it as `animationKey` to replay the enter sequence."},
33669
+ {
33670
+ "name": "reload",
33671
+ "type": "(task?: () => unknown | Promise<unknown>) => Promise<void>",
33672
+ "description": "Shows the loading state, runs `task` (or waits briefly), then draws the chart in again."
33673
+ }
33674
+ ]
33675
+ },
33676
+ "notes": [
33677
+ "Pass `state` to the chart and to the ChartCard: both show their loading skeletons from it.",
33678
+ "Under reduced motion the chart comes back without the draw-in; the loading state still shows.",
33679
+ "Errors thrown by the task still end the loading state; handle them in the task."
33680
+ ],
33681
+ "examples": [
33682
+ {
33683
+ "title": "Reload button on a chart card",
33684
+ "code": "import { BarChart, ChartCard, useChartReload } from \"@wunderui/react/charts\"\n\nexport function Signups({ data }: { data: { week: string; signups: number }[] }) {\n const loader = useChartReload()\n return (\n <ChartCard title=\"Sign-ups\" onReload={() => void loader.reload()} state={loader.state}>\n <BarChart data={data} index=\"week\" series={[{ key: \"signups\", label: \"Sign-ups\" }]} state={loader.state} animationKey={loader.version} label=\"Sign-ups per week\" />\n </ChartCard>\n )\n}"
33685
+ },
33686
+ {
33687
+ "title": "Reload with a fetch",
33688
+ "code": "const loader = useChartReload()\nconst [rows, setRows] = React.useState(initialRows)\n\nconst refresh = () => loader.reload(async () => {\n const res = await fetch(\"/api/signups\")\n setRows(await res.json())\n})"
33689
+ }
33690
+ ],
33691
+ "related": [{"slug":"chart-card","label":"Chart Card"},{"slug":"bar-chart","label":"Bar Chart"}],
33692
+ "guideSource": "authored"
33433
33693
  },
33434
33694
  {
33435
33695
  "name": "useChartWidth",
@@ -33445,7 +33705,129 @@
33445
33705
  "kind": "hook",
33446
33706
  "tier": "pro",
33447
33707
  "file": "components/ui/charts/chart-motion.tsx",
33448
- "description": "Keeps the last active index while the hover marks fade out, and flags the frame they reappear."
33708
+ "description": "Keeps the last active index while the hover marks fade out, and flags the frame they reappear.",
33709
+ "slug": "use-glide",
33710
+ "docs": "https://wunderui.com/hooks/use-glide",
33711
+ "summary": "Lets hover marks glide from point to point and fade out in place, instead of jumping or vanishing.",
33712
+ "guide": "Give it the active index (or `null`). `index` keeps the last active point while the marks fade out, `visible` says whether they show, and `snap` is true for the one frame they appear, so they jump into place the first time and glide from then on. Pair it with `GlideGroup`, an SVG group that moves by a CSS transform on the `duration-slow` token and the entrance curve.",
33713
+ "signature": "useGlide(active: number | null): Glide",
33714
+ "params": [{"name":"active","type":"number | null"}],
33715
+ "returns": {
33716
+ "type": "Glide",
33717
+ "properties": [
33718
+ {"name":"index","type":"number | null","description":"Index to draw the hover marks at (the active one, or the last one while fading out)."},
33719
+ {"name":"visible","type":"boolean"},
33720
+ {
33721
+ "name": "snap",
33722
+ "type": "boolean",
33723
+ "description": "True for the frame the marks appear: they jump into place instead of gliding from the old spot."
33724
+ }
33725
+ ]
33726
+ },
33727
+ "notes": [
33728
+ "Under reduced motion `GlideGroup` only fades; it does not move.",
33729
+ "Only `transform` and `opacity` animate, so a gliding mark never causes layout work.",
33730
+ "Give the marks `aria-hidden`; the chart's live region announces the active point."
33731
+ ],
33732
+ "examples": [
33733
+ {
33734
+ "title": "A gliding hover dot in a custom SVG chart",
33735
+ "code": "import { GlideGroup, useGlide } from \"@wunderui/react/charts\"\n\nfunction HoverDot({ active, points }: { active: number | null; points: { x: number; y: number }[] }) {\n const glide = useGlide(active)\n const p = points[glide.index ?? 0]\n return (\n <GlideGroup x={p.x} y={p.y} visible={glide.visible} snap={glide.snap}>\n <circle r={5} fill=\"var(--chart-1)\" className=\"stroke-card\" strokeWidth={2} />\n </GlideGroup>\n )\n}"
33736
+ }
33737
+ ],
33738
+ "related": [{"slug":"line-chart","label":"Line Chart"},{"slug":"area-chart","label":"Area Chart"}],
33739
+ "guideSource": "authored"
33740
+ },
33741
+ {
33742
+ "name": "useLoadPhase",
33743
+ "import": "@wunderui/react/charts",
33744
+ "kind": "hook",
33745
+ "tier": "pro",
33746
+ "file": "components/ui/load-sequence.tsx",
33747
+ "description": "The phase of the surrounding load sequence (null when there is none)."
33748
+ },
33749
+ {
33750
+ "name": "useLoadSequence",
33751
+ "import": "@wunderui/react/charts",
33752
+ "kind": "hook",
33753
+ "tier": "pro",
33754
+ "file": "components/ui/load-sequence.tsx",
33755
+ "description": "Runs a dashboard page's load sequence: \"pending\" (server render, until the root is on screen and the tab is visible) → \"loading\" (only when the data is still not ready after LOAD_SKELETON_DELAY; shown for at least LOAD_SKELETON_MIN) → \"pla…",
33756
+ "slug": "use-load-sequence",
33757
+ "docs": "https://wunderui.com/hooks/use-load-sequence",
33758
+ "summary": "Opens every dashboard page the same way: the cards build up top to bottom, then numbers roll, charts draw in and bars fill — with skeletons first only when the data takes longer.",
33759
+ "guide": "Attach the hook to the root of your app shell, spread `data-load={phase}` on it and `data-load-main` on the page area, and pass the page's path as `replayKey` so every page opens with the sequence. The shell stays visible; nothing flies in. In `prepare`, mark each card with `data-load-item` and give it `--load-delay` (when it appears) and `--load-content-delay` (when its content starts); `readingOrder` and `loadDelays` turn card positions into rows, columns and delays (strictly top to bottom: a row 40 ms after the last card above it, +25 ms per card, the page built within about half a second). A card appears in place — opacity only, `--duration-fast`. Provide the phase through `LoadSequenceContext`: the charts of `@wunderui/react/charts` hold their enter sequence until `play` and start at `--load-content-delay`, and `NumberRoll` rolls its digits while it is `play`. Bars marked `data-load-fill` grow from their start edge. Pass `ready={false}` while the data loads: if it is still not there after `LOAD_SKELETON_DELAY` (400 ms) the phase turns `loading` — show skeletons in the cards' places — and stays there at least `LOAD_SKELETON_MIN` (500 ms) so it never flashes; data that arrives within the delay shows no skeleton.",
33760
+ "signature": "useLoadSequence(ref: React.RefObject<HTMLElement | null>, options?: LoadSequenceOptions): LoadPhase",
33761
+ "params": [
33762
+ {
33763
+ "name": "ref",
33764
+ "type": "React.RefObject<HTMLElement | null>",
33765
+ "properties": [{"name":"current","type":"HTMLElement | null","description":"The current value of the ref."}]
33766
+ },
33767
+ {
33768
+ "name": "options",
33769
+ "type": "LoadSequenceOptions",
33770
+ "optional": true,
33771
+ "default": "{}",
33772
+ "properties": [
33773
+ {
33774
+ "name": "enabled",
33775
+ "type": "boolean | undefined",
33776
+ "optional": true,
33777
+ "description": "Off: the phase is \"done\" from the start (nothing hidden, nothing moves)."
33778
+ },
33779
+ {
33780
+ "name": "replayKey",
33781
+ "type": "string | number | undefined",
33782
+ "optional": true,
33783
+ "description": "Change it to play the sequence again: the page's path (every page opens with it) or a replay button."
33784
+ },
33785
+ {
33786
+ "name": "ready",
33787
+ "type": "boolean | undefined",
33788
+ "optional": true,
33789
+ "description": "Whether the page's data is there. While it is not, the sequence waits after LOAD_SKELETON_DELAY the phase turns \"loading\" (show skeletons)."
33790
+ },
33791
+ {
33792
+ "name": "prepare",
33793
+ "type": "((root: HTMLElement, reduced: boolean) => LoadSequencePlan | null) | undefined",
33794
+ "optional": true,
33795
+ "description": "Called once the shell is on screen and the data is ready, right before the phase turns \"play\": mark the cards, set their delays, return how long the sequence runs. Return null while the content is not laid out yet (it is asked again on the…"
33796
+ }
33797
+ ]
33798
+ }
33799
+ ],
33800
+ "returns": {"type":"LoadPhase"},
33801
+ "notes": [
33802
+ "Every page opens with it: pass the route's path as `replayKey`. Re-renders on the same page never replay it.",
33803
+ "Under reduced motion nothing animates: the page is shown as it is (nothing is held back), numbers and charts show their final state, and a skeleton for a longer load has no shimmer.",
33804
+ "`NumberRoll` renders the formatted value as plain text on the server, before and after the roll, so it hydrates without a mismatch and always ends exact. Use tabular figures so the digit columns keep their width.",
33805
+ "While `pending` and `loading` the page area's children are held back by the stylesheet (the shell is not). Add `<noscript><style>[data-load-main]>*{opacity:1!important}</style></noscript>` so pages without JavaScript still show their content."
33806
+ ],
33807
+ "examples": [
33808
+ {
33809
+ "title": "A dashboard shell with a load sequence",
33810
+ "code": "import * as React from \"react\"\nimport { LoadSequenceContext, loadDelays, readingOrder, useLoadSequence } from \"@wunderui/react/charts\"\n\nexport function DashboardLayout({ children, path, ready }: { children: React.ReactNode; path: string; ready: boolean }) {\n const ref = React.useRef<HTMLDivElement>(null)\n const phase = useLoadSequence(ref, {\n replayKey: path, // every page opens with the sequence\n ready, // false while the data loads: skeletons after 400 ms\n prepare: (root, reduced) => {\n const cards = Array.from(root.querySelectorAll<HTMLElement>(\"[data-card]\"))\n if (!cards.length) return null // not rendered yet: asked again next frame\n const delays = loadDelays(readingOrder(cards.map((c) => c.getBoundingClientRect()).map((r) => ({ top: r.top, left: r.left }))))\n let last = 0\n cards.forEach((card, i) => {\n const delay = reduced ? 0 : delays[i]\n card.setAttribute(\"data-load-item\", \"\")\n card.style.setProperty(\"--load-delay\", `${delay}ms`)\n card.style.setProperty(\"--load-content-delay\", `${delay + 150}ms`)\n last = Math.max(last, delay)\n })\n return { duration: last + 2000, finish: () => cards.forEach((c) => c.removeAttribute(\"data-load-item\")) }\n },\n })\n return (\n <div ref={ref} data-load={phase}>\n <main data-load-main=\"\">\n <LoadSequenceContext.Provider value={phase}>{children}</LoadSequenceContext.Provider>\n </main>\n </div>\n )\n}"
33811
+ },
33812
+ {
33813
+ "title": "Skeletons only for longer loads",
33814
+ "code": "const { data, isLoading } = useQuery(...)\nconst phase = useLoadSequence(ref, { replayKey: path, ready: !isLoading, prepare })\n\n// \"loading\" only when the data takes longer than LOAD_SKELETON_DELAY (400 ms), then at least LOAD_SKELETON_MIN (500 ms)\n{phase === \"loading\" ? <DashboardSkeleton /> : <Dashboard data={data} />}"
33815
+ },
33816
+ {
33817
+ "title": "A KPI value that rolls in",
33818
+ "code": "import { NumberRoll } from \"@wunderui/react/charts\"\n\n// rolls while the surrounding load sequence plays; plain text otherwise\n<NumberRoll value=\"$48,200\" className=\"text-3xl font-bold tabular-nums\" />"
33819
+ },
33820
+ {
33821
+ "title": "Replay button",
33822
+ "code": "const [replay, setReplay] = React.useState(0)\nconst phase = useLoadSequence(ref, { replayKey: replay, prepare })\n\n<Button onClick={() => setReplay((n) => n + 1)}>Replay</Button>"
33823
+ }
33824
+ ],
33825
+ "related": [
33826
+ {"slug":"use-animated-number","label":"useAnimatedNumber"},
33827
+ {"slug":"use-in-view-appear","label":"useInViewAppear"},
33828
+ {"slug":"metric-card","label":"Metric Card"}
33829
+ ],
33830
+ "guideSource": "authored"
33449
33831
  },
33450
33832
  {
33451
33833
  "name": "usePrefersReducedMotion",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wunderui-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "mcpName": "io.github.Kl-webmedia/wunderui-mcp",
5
5
  "description": "MCP server that gives AI coding agents the WunderUI design system: tokens, component props, templates and starters. Works with Claude Code, Cursor, Windsurf, Cline and Codex.",
6
6
  "keywords": [
@@ -46,7 +46,7 @@
46
46
  "smoke": "node ./test/smoke.mjs"
47
47
  },
48
48
  "dependencies": {
49
- "@modelcontextprotocol/sdk": "^1.30.0",
49
+ "@modelcontextprotocol/sdk": "^1.32.1",
50
50
  "zod": "^4.6.5"
51
51
  }
52
52
  }
package/src/index.mjs CHANGED
@@ -218,13 +218,31 @@ server.registerTool(
218
218
  {
219
219
  title: "Get a WunderUI component",
220
220
  description:
221
- "Everything needed to use one component correctly: its exported names, the TypeScript prop signature straight from the build, the import line and a link to its documentation page.",
221
+ "Everything needed to use one component correctly: its exported names, the TypeScript prop signature straight from the build, a props table per export (type, allowed values, default, required, description), its anatomy (parts and data-slot names), best practices, the CSS variables it reads with their defaults, the import line and a link to its documentation page. Also answers for hooks (signature, parameters, return value, notes), providers and utilities (useToast, ThemeProvider …).",
222
222
  inputSchema: {
223
223
  name: z.string().describe('Export name ("DataGrid"), slug ("data-grid") or title ("Data Grid")'),
224
224
  },
225
225
  },
226
226
  async ({ name }) => {
227
227
  const component = findComponent(name)
228
+ const other = component ? null : (design.otherExports ?? []).find((entry) => normalize(entry.name) === normalize(name))
229
+ if (other) {
230
+ return text({
231
+ name: other.name,
232
+ kind: other.kind,
233
+ plan: other.tier ?? "pro",
234
+ description: other.summary ?? other.description ?? undefined,
235
+ guide: other.guide ?? undefined,
236
+ import: `import { ${other.name} } from "${design.name}"`,
237
+ signature: other.signature ?? undefined,
238
+ params: other.params?.length ? other.params : undefined,
239
+ returns: other.returns ?? undefined,
240
+ notes: other.notes ?? undefined,
241
+ examples: other.examples ?? undefined,
242
+ propsTable: other.props ? [{ name: other.name, props: other.props, inherits: other.inherits }] : undefined,
243
+ docs: other.docs ?? undefined,
244
+ })
245
+ }
228
246
  if (!component) {
229
247
  const suggestions = design.components
230
248
  .filter((candidate) => matches(candidate, name))
@@ -249,6 +267,13 @@ server.registerTool(
249
267
  exports: component.exports,
250
268
  example: component.example ?? undefined,
251
269
  props: component.props ?? "No generated signature — see the documentation page.",
270
+ // per export: name, type, values (literal unions), default, required, description; DOM attributes summed up in inherits
271
+ propsTable: component.api?.length ? component.api : undefined,
272
+ // the guide: parts (export, data-slot, required, a sentence), do/don't/when notes, and the CSS
273
+ // variables the component reads with their defaults; `source` "generated" = drafted from the code
274
+ anatomy: component.anatomy ?? undefined,
275
+ bestPractices: component.bestPractices ?? undefined,
276
+ themingVariables: component.themingVariables ?? undefined,
252
277
  docs: component.docs,
253
278
  })
254
279
  }