@nurix/etna 0.20.37 → 0.20.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/data/catalog/rules/frontend.md +5 -5
  2. package/data/catalog/rules/pnpm.md +2 -2
  3. package/data/harness-index.json +1 -1
  4. package/data/harnesses/code/harness.json +2 -2
  5. package/data/harnesses/code/hooks/approved-packages.md +1 -2
  6. package/data/harnesses/code/rules/frontend.md +5 -5
  7. package/data/harnesses/code/rules/pnpm.md +2 -2
  8. package/data/harnesses/code/skills/boundary/references/packs/build-config-runtime.md +1 -1
  9. package/data/harnesses/code/skills/design/components/charts.md +121 -43
  10. package/data/harnesses/code/skills/design/components/collections.md +209 -41
  11. package/data/harnesses/code/skills/design/components/feedback.md +86 -25
  12. package/data/harnesses/code/skills/design/components/hooks.md +198 -12
  13. package/data/harnesses/code/skills/design/components/index.md +93 -71
  14. package/data/harnesses/code/skills/design/components/inputs.md +249 -157
  15. package/data/harnesses/code/skills/design/components/layout.md +98 -38
  16. package/data/harnesses/code/skills/design/components/media.md +46 -25
  17. package/data/harnesses/code/skills/design/components/motion.md +56 -46
  18. package/data/harnesses/code/skills/design/components/navigation.md +54 -15
  19. package/data/harnesses/code/skills/design/components/overlays.md +51 -41
  20. package/data/harnesses/code/skills/design/components/setup.md +84 -5
  21. package/data/harnesses/code/skills/design/components/typography.md +72 -22
  22. package/data/harnesses/code/skills/design/components/utils.md +47 -13
  23. package/data/harnesses/code/skills/design/data/chrome/board-column.md +12 -12
  24. package/data/harnesses/code/skills/design/data/chrome/data-table.md +6 -6
  25. package/data/harnesses/code/skills/design/data/chrome/side-panel.md +15 -15
  26. package/data/harnesses/code/skills/design/data/chrome/sidebar.md +25 -25
  27. package/data/harnesses/code/skills/design/data/chrome/stat-cards.md +22 -22
  28. package/data/harnesses/code/skills/design/data/chrome/topbar.md +12 -12
  29. package/data/harnesses/code/skills/design/references/emission.md +6 -6
  30. package/data/harnesses/code/skills/design/scripts/README-validator.md +20 -8
  31. package/data/harnesses/code/skills/design/scripts/validate-design-md.mjs +15 -10
  32. package/data/harnesses/delivery/harness.json +1 -1
  33. package/data/harnesses/etna/harness.json +2 -2
  34. package/data/harnesses/etna/skills/boundary/references/packs/build-config-runtime.md +1 -1
  35. package/data/harnesses/hr/harness.json +1 -1
  36. package/package.json +2 -2
@@ -2,51 +2,111 @@
2
2
 
3
3
  > Generated reference (regen-owned; see [index.md](index.md)). Binds only under `primitives: nurix`.
4
4
 
5
- Content structure and containers. Import each from its own subpath, e.g. `import { Card, CardContent } from "@nurix/components/block/card";`.
5
+ Components: Accordion, AspectRatio, Block, Card, Carousel, Collapsible, Item, Resizable, Row, ScrollArea, Separator, SidePanel, Sortable, Spacer
6
6
 
7
- ## Statistics
7
+ Each imports from its own subpath as one namespace, with its parts as dotted members, e.g. `import { Card } from "@nurix/components/block/card"` then `Card.Header`, `Card.Content`, `Card.Footer`. Parts swap their element through `render` — there is no `asChild` (Resizable's parts are the exception: they take no `render`).
8
8
 
9
- Compound schema for dashboard stat / KPI cards — compose only the regions a card needs. Import from `@nurix/components/statistics`. Sub-components throw if rendered outside `<Statistics>`. The wrapper is `bare` (invisible — just a flex column) by default so consumers own its look; opt into a surface with `variant`.
9
+ ## Block · Row · Spacer
10
10
 
11
- **Sub-components:** `StatisticsHeader`, `StatisticsIcon`, `StatisticsHeading`, `StatisticsTitle`, `StatisticsDescription`, `StatisticsActions`, `StatisticsSection`, `StatisticsMetric`, `StatisticsValue`, `StatisticsUnit`, `StatisticsTrend`, `StatisticsComparison`, `StatisticsGroup`, `StatisticsVisual`, `StatisticsGauge`, `StatisticsProgress`, `StatisticsSegmentedBar`, `StatisticsSparkline`, `StatisticsLegend`, `StatisticsLegendItem`, `StatisticsContent`, `StatisticsFooter`, `StatisticsCaption`, `StatisticsLink`
11
+ The naked space primitives: `Block` (`block/block`) is a page region that stacks its children on the density gap and owns the region's insets; `Row` (`element/row`) owns only the gap between its children; `Spacer` (`element/spacer`) is placed empty space that pushes siblings apart. None of them draws anything — the dress is `className`.
12
12
 
13
- **Statistics props:** `size` [sm|md|lg] (headline value scale), `variant` [bare|card|elevated|outline|soft|ghost] (default `bare` draws nothing; the rest add padding + surface), `accent` [none|primary|success|warning|danger|info], `gap` [none|sm|md|lg], `justify` [start|center|end|between|around|evenly], `comparisonLabel`
14
- **StatisticsTitle:** `size` [sm|md|lg], `align` [left|center|right]
15
- **StatisticsTrend:** `value:number`, `direction?` [up|down], `invert?` (flip color, keep arrow), `pill?`, `format?:(n)=>string`
16
- **StatisticsIcon:** `variant` [default|primary|success|warning|danger|info], `tone` [soft|solid|outline|plain], `size` [xs|sm|md|lg|xl|oversized] (oversized bleeds out the top-right corner — needs a clipping surface variant), `shape` [circle|rounded|square]
17
- **StatisticsGauge:** `value` (0–100), `sweep?` [180|270], `size?`, `thickness?`, `trackColor?`, `fillColor?` — renders children centered in the dial
18
- **StatisticsSection:** `gap`, `justify` — a vertical group; nest two (top + bottom) in a `justify="between"` card to pin clusters to the top and bottom. `StatisticsVisual` is a generic slot: drop in a built-in visual or any chart (`PieGraph`, `LineGraph`, `BarGraph`, `ProgressBar`).
13
+ **Block props:** `render` (root element, default `section` — `<header />`, `<footer />`, `<aside />`, `<main />`), `height` [hug|hero] (default `hug`; `hero` reserves a 300px masthead band), `shouldSettle` (a content-driven height change travels instead of snapping; outermost settling region only)
14
+ **Row props:** `gap` [none|sm|md|lg] (default `md`, on the `--density-gap-*` tokens), `align` [start|center|end|baseline] (default `center`), `hasFloor` (reserves one control height, so a toolbar row measures the same with or without its buttons), `render`
15
+ **Spacer props:** `render` only — no size, no content; `aria-hidden`, and it grows only inside a flex parent
16
+
17
+ - Insets live only on the Block: never pad a Row or Spacer inside it, never put margins on its children.
18
+ - Distribution is positional — a Spacer between the sides replaces `justify-between` and `mt-auto`. Never use it for a fixed distance; that is the parent's gap.
19
19
 
20
20
  ```tsx
21
- <Statistics variant="card" comparisonLabel="vs last week">
22
- <StatisticsHeader>
23
- <StatisticsHeading>
24
- <StatisticsTitle>Revenue</StatisticsTitle>
25
- </StatisticsHeading>
26
- <StatisticsActions>
27
- <StatisticsIcon variant="success">{/* icon */}</StatisticsIcon>
28
- </StatisticsActions>
29
- </StatisticsHeader>
30
- <StatisticsMetric>
31
- <StatisticsValue>$21,434</StatisticsValue>
32
- <StatisticsTrend value={-12.2} invert pill />
33
- <StatisticsComparison />
34
- </StatisticsMetric>
35
- </Statistics>
21
+ <Block render={<header />} className="border-b bg-card">
22
+ <Row hasFloor>
23
+ <Breadcrumb />
24
+ <Spacer />
25
+ <Button>New</Button>
26
+ </Row>
27
+ </Block>
28
+ ```
29
+
30
+ ## Card
31
+
32
+ The surfaced container (`block/card`): ring edge, elevation and density insets, with a header grid that grows an end column only when an action is present.
33
+
34
+ **Sub-components:** `Card.Header` (`hasDivider`), `Card.Header.Action` (pinned to the header's end column), `Card.Title`, `Card.Description`, `Card.Content`, `Card.Footer` (`hasDivider`)
35
+
36
+ - No `padding`/`radius`/`shadow` props: insets read the `--density-pad-*` tokens. Depth rides `className` as `shadow-(--elevation-N)` or `shadow-none`, which keeps the ring edge.
37
+ - `hasDivider` draws the hairline rule and the density inset on that side; a bare `border-b` / `border-t` class adds no inset.
38
+ - A Title mints an id; the root points `aria-labelledby` at it only while the root carries a role (`role`, or `render={<section />}` / `article` / `aside` / `form` / `nav` / `fieldset`).
39
+ - Full-bleed media is a direct `<img>` child of `Card`. A grid of clickable cards is `block/list` with `view="card"`; there is no action-area part.
40
+
41
+ ```tsx
42
+ <Card render={<section />}>
43
+ <Card.Header hasDivider>
44
+ <Card.Title render={<h2 />}>Billing</Card.Title>
45
+ <Card.Description>Plan and payment method</Card.Description>
46
+ <Card.Header.Action><Button variant="ghost" size="sm">Edit</Button></Card.Header.Action>
47
+ </Card.Header>
48
+ <Card.Content>{/* … */}</Card.Content>
49
+ <Card.Footer hasDivider><Button>Upgrade</Button></Card.Footer>
50
+ </Card>
51
+ ```
52
+
53
+ ## Item
54
+
55
+ A list or menu row primitive (`element/item`): leading media, a title and description, trailing actions.
56
+
57
+ **Sub-components:** `Item.Media` (`variant` [default|icon|image]), `Item.Content`, `Item.Title`, `Item.Description`, `Item.Actions` (a `role="group"` labelled by the row's Title), `Item.Header`, `Item.Footer`, `Item.Group`, `Item.Separator`
58
+ **Item props:** `variant` [default|outline|muted], `inset` [default|sm], `side` [start|end] (`end` reverses the row), `render` (`render={<a href="…" />}` makes the whole row a link; an untyped `<button />` becomes `type="button"`)
59
+
60
+ - `Item.Media variant="image"` is what crops and sizes an `<img>`; `icon` is a bordered tile.
61
+ - `Item.Group` inserts no dividers and carries no list role — place `Item.Separator` yourself, and write `<Item.Group render={<ul />}>` with rows `render={<li />}` for list semantics.
62
+ - Data-driven rows with search, grouping or pagination are `block/list` with `Item` rows.
63
+
64
+ ```tsx
65
+ <Item variant="outline">
66
+ <Item.Media variant="icon"><Bell /></Item.Media>
67
+ <Item.Content>
68
+ <Item.Title>Deploy finished</Item.Title>
69
+ <Item.Description>web-app shipped to production.</Item.Description>
70
+ </Item.Content>
71
+ <Item.Actions><Button variant="ghost" size="sm">View</Button></Item.Actions>
72
+ </Item>
73
+ ```
74
+
75
+ ## Sortable
76
+
77
+ Drag-to-reorder list over dnd-kit (`compound/sortable`), keyboard sortable, with a floating drag overlay. Optional peer family `sortable` — install `@dnd-kit/core`, `@dnd-kit/modifiers`, `@dnd-kit/sortable` and `@dnd-kit/utilities` yourself.
78
+
79
+ **Sub-components:** `Sortable.Content`, `Sortable.Item` (`value`, `isHandle` drags the whole row), `Sortable.ItemHandle` (the grip), `Sortable.Overlay` (the drag preview, portalled to `document.body` or a `container`)
80
+ **Props:** `value` / `defaultValue` / `onValueChange(value, details)` (an uncontrolled caller refuses a move with `details.cancel()`), `onValueCommitted`, `getItemValue` (stable unique id; required when `value` holds objects), `orientation` [vertical|horizontal|mixed] (default `vertical`), `strategy`, `activationConstraint` (`distance` px or `delay` ms), `hasGrabCursor` (default on), `disabled`, `labels`
81
+
82
+ - Also exported: `useSortableItem`, `arrayMove`, `arraySwap`, the list/rect strategies, the `restrictTo*` and snap modifiers, and the collision detectors.
83
+ - Skip `Sortable.Overlay` and a dragged item only shifts in place, with nothing following the pointer.
84
+
85
+ ```tsx
86
+ <Sortable value={items} onValueChange={setItems} getItemValue={(item) => item.id}>
87
+ <Sortable.Content>
88
+ {items.map((item) => (
89
+ <Sortable.Item key={item.id} value={item.id}><Sortable.ItemHandle />{item.name}</Sortable.Item>
90
+ ))}
91
+ </Sortable.Content>
92
+ <Sortable.Overlay />
93
+ </Sortable>
36
94
  ```
37
95
 
38
96
  ## Other layout components
39
97
 
40
- - `accordion` — Accordion, AccordionItem, AccordionTrigger, AccordionContent
41
- - `aspect-ratio` — AspectRatio
42
- - `bento-grid` — BentoGrid, BentoCard
43
- - `card` — Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, CardAction, CardImage
44
- - `carousel` — Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext
45
- - `collapsible` — Collapsible, CollapsibleTrigger, CollapsibleContent
46
- - `collapsible-panel` — CollapsiblePanel
47
- - `editable-card` — EditableCard, EditableCardHeader, EditableCardBody, EditableCardFooter
48
- - `resizable` — ResizablePanelGroup, ResizablePanel, ResizableHandle
49
- - `scroll-area` — ScrollArea, ScrollBar
50
- - `separator` — Separator
51
- - `separator-with-text` — SeparatorWithText
52
- - `sortable` — Sortable, SortableContent, SortableItem, SortableItemHandle, SortableOverlay
98
+ - `compound/accordion` — `Accordion` with `Accordion.Item`, `.Header` (an `<h3>` by default; `render={<h4 />}` moves the level), `.Trigger`, `.Panel` (`hiddenUntilFound`, `keepMounted`); `value`/`defaultValue` are always arrays, `multiple` lets several stay open, `onValueChange` drives it from outside.
99
+ - `element/collapsible` — `Collapsible` with `Collapsible.Trigger` (`render` swaps the button), `.Panel` (unmounts when closed unless `keepMounted`; never give it a height class), `.Indicator` (turns on the trigger's `data-panel-open`); `open`/`defaultOpen`/`onOpenChange`.
100
+ - `block/side-panel` — `SidePanel` with `SidePanel.Trigger`, `SidePanel.Panel`: an in-flow region in a `flex` row that collapses to a rail and reflows its sibling; `side` [start|end] (default `end`), `open`/`onOpenChange`, `expandedWidth`/`collapsedWidth` (or the `--side-panel-expanded-width`/`--side-panel-collapsed-width` variables), `keepMounted`, `labels`.
101
+ - `block/resizable` — `Resizable` with `Resizable.Panel`, `Resizable.Handle` (`hasGrip`): draggable split panes inside a parent with a height; a numeric size is pixels and a string is a percentage (`defaultSize="30%"`), `orientation` [horizontal|vertical], `onLayoutCommitted` + `defaultLayout` persist the layout, `panelRef`/`groupRef` reach the imperative handles. Optional peer family `resizable` (`react-resizable-panels`).
102
+ - `compound/carousel` — `Carousel` with `Carousel.Content`, `.Item` (`basis-full` by default), `.Previous`, `.Next`; `value`/`defaultValue`/`onValueChange` (0-based snap index), `orientation`, `opts`, `setApi`, `labels`. `LoadingCarousel` is the ready-made tips strip (`data` tips with a stable `id`, `timeout`, `indicators` [progress|steps|none], `aspectRatio`, `textPlacement`). Optional peer family `carousel` (`embla-carousel-react`, `embla-carousel-autoplay`).
103
+ - `element/scroll-area` — `ScrollArea`: styled bars on both axes, each drawn only while its axis overflows; give it an explicit `h-*`/`max-h-*`, `hasFadeEdges` fades every edge with more content past it, and `aria-label` names the focusable viewport. Never pass `ScrollArea.Bar` as a child.
104
+ - `element/separator` — `Separator`: a hairline, `orientation` [horizontal|vertical] (vertical needs a bounded height); children render the labeled form, placed by `align` [start|center|end] (default `center`, ignored without children).
105
+ - `element/aspect-ratio` — `AspectRatio`: `ratio` is width / height (default `1`; a non-finite or non-positive ratio falls back to `1`), the child fills the box itself (`h-full w-full object-cover`), `render` swaps the element.
106
+
107
+ ## Fallback-only concepts
108
+
109
+ No primitive ships for these; build them from the catalog's fallback spec, composed over the components above.
110
+
111
+ - `bento-grid` — an asymmetric CSS grid of `Card`s with spanning cells.
112
+ - `editable-card` — a `Card` whose `Card.Content` edits in place and whose `Card.Footer` commits.
@@ -2,65 +2,86 @@
2
2
 
3
3
  > Generated reference (regen-owned; see [index.md](index.md)). Binds only under `primitives: nurix`.
4
4
 
5
- Icons, avatars, and media display; import each from its kebab-case subpath, e.g. `import { Avatar } from "@nurix/components/element/avatar"`.
5
+ Avatars, icons, images, the brand mark and a few media-adjacent controls; import each from its layer-prefixed subpath, e.g. `import { Avatar } from "@nurix/components/element/avatar"`.
6
6
 
7
7
  ## Avatar
8
8
 
9
- Wraps `facehash` (deterministic identicon fallback) with default sizing.
9
+ Profile image with an initials fallback and one stable accessible name, over Base UI's avatar. `AvatarGroup` stacks avatars into a facepile.
10
10
 
11
- **Props:** `size` (number, default 32) — sets both `width`/`height` and drives the round mask
11
+ **Props:** `label` (the one accessible name, `role="img"`; omit beside a visible name and the avatar is `aria-hidden`), `size` [xs|sm|default|lg] (24, 32, 36, 40px; default `default`)
12
12
 
13
- **Sub-components:** `AvatarImage`, `AvatarFallback` (`colors` — palette forwarded to facehash for its generated background, defaults to a 10-color set)
13
+ **Sub-components:** `Avatar.Image`, `Avatar.Fallback` (shows while the image loads and after it errors), `Avatar.Badge` (`intent` [neutral|info|success|warning|danger], status dot painted from `--intent-*`), `AvatarGroup` (`variant` [default|bare] — `bare` drops the hover lift; `role="group"`, name it with `aria-label`), `AvatarGroup.Count`
14
14
 
15
15
  ```tsx
16
- <Avatar size={40}>
17
- <AvatarImage src="/photo.jpg" />
18
- <AvatarFallback name="jane@example.com" />
16
+ <Avatar label="Jane Doe, online" size="lg">
17
+ <Avatar.Image src={user.avatarUrl} alt="" />
18
+ <Avatar.Fallback>JD</Avatar.Fallback>
19
+ <Avatar.Badge intent="success" />
19
20
  </Avatar>
20
21
  ```
21
22
 
22
23
  ## CopyButton
23
24
 
24
- Click-to-copy button with a check/copy icon swap.
25
+ Click-to-copy `Button` (`compound/copy-button`) that swaps its copy glyph for a check, publishes the outcome as `data-status` [success|error] and announces it.
25
26
 
26
- **Props:** `value`, `onCopy`, `copiedDuration` (ms before reverting, default 2000), `variant` [default|outline], `size` [sm|default|lg]
27
+ **Props:** `value` (string, or a function resolved at click time), `onCopy(value)`, `onCopyError(error)`, `timeout` (ms the outcome holds; defaults to the `--hold-dwell` token, `Infinity` holds success), `labels` (`{ copy, copied, failed }`), plus Button's `variant` (default `ghost`), `intent`, `size` [xs|sm|default|lg] and `isIconOnly` (on when there are no `children`)
27
28
 
28
29
  ```tsx
29
- <CopyButton value={apiKey} size="sm" />
30
+ <CopyButton value={message.text} size="sm" />
30
31
  ```
31
32
 
32
- ## Logo
33
+ ## FilePreview
34
+
35
+ One file tile — a staged attachment or a persisted file (`compound/file-preview`). It renders state only; choosing and uploading the file is `compound/file-picker`.
33
36
 
34
- Official brand logo — NuPlay mark + wordmark, inline SVG.
37
+ **Sub-components:** `FilePreview.Group` (snap-scrolling strip), `FilePreview.Media`, `FilePreview.Content`, `FilePreview.Title`, `FilePreview.Description`, `FilePreview.Actions`, `FilePreview.Action`, `FilePreview.Trigger` (overlay button or `render` anchor), `FilePreview.Progress`; also `getFileTypeIcon({ type, name })`
35
38
 
36
- **Props:** `variant` [blue-black|white-white|lime-white|blue-white] (fixed brand colorways, named mark-then-wordmark; blue-black is the default for light surfaces, the white-wordmark variants are for dark/animated backdrops), `height` (number|string, sizes via intrinsic aspect ratio)
39
+ **Props:** root — `status` [idle|uploading|processing|error|success] (omit for a resting file; `idle` dashes the border, `error` tints it), `size` [default|sm|xs], `orientation` [horizontal|vertical], `variant` [default|bare]; Media — `type`, `name` (picks a mime-aware glyph when given no child), `variant="image"`; Progress — `value` (0–100, `null` indeterminate), `variant` [linear|circular|fill], `keepMounted` (otherwise it unmounts at 100)
37
40
 
38
41
  ```tsx
39
- <Logo variant="white-white" height={20} />
42
+ <FilePreview status={file.status} size="sm">
43
+ <FilePreview.Media type={file.type} name={file.name} />
44
+ <FilePreview.Content>
45
+ <FilePreview.Title>{file.name}</FilePreview.Title>
46
+ <FilePreview.Description>{formatBytes(file.size)}</FilePreview.Description>
47
+ </FilePreview.Content>
48
+ <FilePreview.Actions>
49
+ <FilePreview.Action aria-label={`Remove ${file.name}`} onClick={() => remove(file.id)}><XIcon /></FilePreview.Action>
50
+ </FilePreview.Actions>
51
+ </FilePreview>
40
52
  ```
41
53
 
42
- ## ThemeToggler
54
+ ## Icon
43
55
 
44
- Light/dark toggle with a directional wipe reveal; cycles light → dark → system → light and persists to `localStorage`.
56
+ Draws a Lucide icon from a name held as data (`element/icon`) — a record field, a config value or an `IconPicker` (`compound/icon-picker`) pick. A fixed icon known at build time is a direct `lucide-react` import instead.
45
57
 
46
- **Props:** `style` [icon|text-icon|icon-text], `size` [sm|md|lg], `duration` (ms, default 700)
58
+ **Props:** `value` (Lucide's kebab-case name, e.g. `"arrow-up-right"`), `size` (numeric px, default 24), `label` (standalone meaningful icon → `role="img"`; omit beside visible text), `fallback` (drawn for an unknown name; an empty box of the icon's size otherwise), `strokeWidth` (defaults to the `--edge-stroke` token)
47
59
 
48
60
  ```tsx
49
- <ThemeToggler style="icon-text" size="md" />
61
+ <Icon value={record.icon} size={16} />
50
62
  ```
51
63
 
52
- ## ViewSwitcher
64
+ ## Image
53
65
 
54
- Segmented control for switching a view-type projection (e.g. table/card), with an animated sliding background.
66
+ Content photo or screenshot (`element/image`) with a skeleton while loading and a bordered placeholder (the `alt` text, or `labels.unavailable` — "Image unavailable") on error or an empty `src`, never the native broken-image glyph. A person's photo is `Avatar`.
55
67
 
56
- **Props:** `value`/`onChange` (controlled), `defaultValue` (default `"table"`), `options` (`{ value, label, icon }[]`, defaults to Table/Card), `size` [sm|md|lg]
68
+ **Props:** `src`, `alt`, `width`/`height` (both together reserve the box via `aspect-ratio`), `fallback` (replaces the placeholder glyph and caption), `labels` (`{ unavailable }`), `variant` [default|bare] (`bare` drops its own radius, border and fill inside a framing host). Takes no `onClick` — a lightbox wraps it in a trigger.
57
69
 
58
70
  ```tsx
59
- <ViewSwitcher value={view} onChange={setView} size="sm" />
71
+ <Image src={attachment.src} alt={attachment.name} width={320} height={180} />
72
+ ```
73
+
74
+ ## Logo
75
+
76
+ Official NuPlay brand lockup (`element/logo`) — mark + wordmark, inline SVG.
77
+
78
+ **Props:** `variant` [blue-black|white-white|lime-white|blue-white] (fixed literal fills, named mark-then-wordmark; default `blue-black`, for light surfaces only), `height` (number = px; default 28px via `h-7`; width follows the aspect), `label` (accessible name, default `"NuPlay"`; pass `aria-hidden` beside the brand name in text). `fill` is not a prop — recolor through `variant`.
79
+
80
+ ```tsx
81
+ <Logo variant="white-white" height={20} />
60
82
  ```
61
83
 
62
84
  ## Other Components
63
85
 
64
- - `icon-renderer` + `icons-data` — `IconRenderer`: `icon`/`family`/`size` [sm|md|lg|xl]/`color`; resolves a remote SVG from the configured icon CDN (`<cdn>/fa/<family>/<icon>.svg`) tinted via CSS filter, with a built-in fallback icon on load error. `icons-data` exports `iconsData` — `{ name, categories, tags }[]` — for building icon pickers over the local Lucide set (distinct from the remote family above).
65
- - `live-waveform` + `mic-icon` — `LiveWaveform`: canvas mic-level visualizer; `active`, `processing`, `mode` [static|history|frequency], `barWidth`/`barGap`/`barRadius`/`barColor`, `sensitivity`, `onStreamReady`/`onStreamEnd`/`onError` (draft API — most props are untyped passthroughs). `MicIcon`: animated mic glyph with an imperative ref (`startAnimation`/`stopAnimation`); `size`, `duration`, `isAnimated`.
66
- - `shine-border` — `ShineBorder`: absolutely-positioned animated gradient border overlay (parent needs `position: relative`); `borderWidth`, `duration` (seconds), `shineColor` (string or array, cycled through a radial gradient sweep)
86
+ - `lib/icons-data` — `iconsData`: `{ name, categories, tags }[]` over the Lucide set (~1,700 entries), for a custom icon search or browser; load it with a dynamic `import()`, keep only names present in the installed `lucide-react`, and render each match with `Icon`. A one-icon pick is `IconPicker` (`compound/icon-picker`), which takes these records as `data`.
87
+ - View switching (table/card and similar view-mode strips) has no dedicated component: compose a single-select `ToggleGroup` + `ToggleGroup.Item` (`compound/toggle-group`), guarding the `""` it publishes on deselect; reach for `Tabs` (`compound/tabs`) only when the pick also swaps a visible panel.
@@ -2,81 +2,91 @@
2
2
 
3
3
  > Generated reference (regen-owned; see [index.md](index.md)). Binds only under `primitives: nurix`.
4
4
 
5
- Animation primitives built on the `motion` library (`motion-primitives/*`) plus a small `animate-ui/*` layer. Compose sparingly — decorative and low-frequency surfaces only; never on data or on actions users repeat dozens of times a day. Import each from its own subpath, e.g. `import { AnimatedGroup } from "@nurix/components/effect/animated-group"`.
5
+ The effect tier (`effect/*`; the streaming cursor is a conversation surface, in [collections.md](collections.md)) plus the `element/animated-number` counter and the motion-aware `lib/slot`. Compose sparingly — decorative and low-frequency surfaces only; never on data or on actions users repeat dozens of times a day. Import each from its own subpath, e.g. `import { AnimatedList } from "@nurix/components/effect/animated-list"`.
6
6
 
7
- ## Animated numbers
7
+ Every effect times itself from the personality's motion ladder: durations follow the `pace` trait, and the caller periods below (seconds at neutral pace unless marked) scale with it. No effect takes a `transition`, `variants` or `initial` prop. Under `prefers-reduced-motion`, a zeroed ladder, or inside a `data-mode="inline"` collection, an effect goes still: a continuous effect renders its static frame and publishes `data-playing="false"` (it also pauses off screen and in a hidden tab), and a one-shot effect lands in its end state and publishes `data-reduced-motion`. Where an effect takes `as`, the value is a tag from the tier's closed motion tag table.
8
8
 
9
- Tick-up numerals that render as plain text until JS is ready (no layout shift, no flash of `0`).
9
+ ## Animated numbers
10
10
 
11
- - `motion-primitives/animated-number` — `AnimatedNumber`: spring-interpolated count-up; `value`, `springOptions`, `as` (element/component, default `span`)
12
- - `motion-primitives/sliding-number` — `SlidingNumber`: per-digit slot-machine roll; `value`, `padStart`, `decimalSeparator`
11
+ - `element/animated-number` — `AnimatedNumber`: rolling digits over static `Intl.NumberFormat` text on a `<data>` root (plain text through SSR, hydration and reduced motion — no layout shift, no flash of `0`); `value` (`null` or non-finite renders `fallback`), `format` (`Intl.NumberFormatOptions` minus two notations; default `{ notation: "standard", maximumFractionDigits: 0 }`), `locale`, `prefix`, `suffix`, `fallback`, `trend`, `digits`, `plugins` (`continuous`). The roll is timed by `pace`; there is no timing prop. `AnimatedNumber.Group` links counters that change together; `useIsSupported` is re-exported. **Optional peer:** `@number-flow/react` — install it or the subpath fails to resolve.
13
12
 
14
13
  ```tsx
15
- <AnimatedNumber value={total} springOptions={{ bounce: 0.2 }} />
14
+ <AnimatedNumber value={total} format={{ notation: "compact", maximumFractionDigits: 1 }} />
16
15
  ```
17
16
 
18
17
  ## Stagger entrance
19
18
 
20
- - `motion-primitives/animated-group` — `AnimatedGroup`: staggers children in on mount/update. **Props:** `preset` [fade|slide|scale|blur|blur-slide|zoom|flip|bounce|rotate|swing], `variants` (`{ container, item }`, overrides preset), `as`/`asChild`
19
+ - `effect/animated-list` — `AnimatedList`: animates keyed children entering, leaving and reflowing; `animation` [slide|fade|scale] (default `slide`), `animate` (play the entrance on first mount), `as`, `itemAs`, `layout` [true|position]. Every child needs a stable `key`; a leaver pops out of the flow against the `relative` root. Above 50 children, items render as plain elements with no motion. `block/list` already wraps it.
21
20
 
22
21
  ```tsx
23
- <AnimatedGroup preset="blur-slide">
24
- {items.map((item) => <Card key={item.id}>{item.label}</Card>)}
25
- </AnimatedGroup>
22
+ <AnimatedList className="space-y-2">
23
+ {items.map((item) => <Row key={item.id} item={item} />)}
24
+ </AnimatedList>
26
25
  ```
27
26
 
28
27
  ## Text effects
29
28
 
30
- Per-character/word/line text animation — low-frequency surfaces only (empty states, onboarding, marketing-adjacent copy).
29
+ Per-character/word/line text animation — low-frequency surfaces only (empty states, onboarding, marketing-adjacent copy). Each member renders a `span` by default and takes a string child. `trigger` means one thing across the family: a `false → true` edge replays the effect.
31
30
 
32
- - `motion-primitives/text-effect` — `TextEffect`: staggered reveal; `per` [word|char|line], `preset` [blur|fade-in-blur|scale|fade|slide], `delay`, `trigger`, `onAnimationComplete`
33
- - `motion-primitives/text-morph` — `TextMorph`: morphs character-by-character between successive `children` string values; `as`, `variants`, `transition`
34
- - `motion-primitives/text-scramble` — `TextScramble`: scrambles through random characters before resolving; `duration`, `speed`, `characterSet`, `trigger`, `onScrambleComplete`
35
- - `motion-primitives/text-shimmer` — `TextShimmer`: looping gradient shimmer across the text; `duration`, `spread`
31
+ - `effect/text-effects` — `TextEffect`: staggered reveal; `per` [word|char|line] (default `word`), `animation` [blur|fade-in-blur|scale|fade|slide] (default `fade`), `delay`, `speedReveal`, `speedSegment`, `trigger`, `onAnimationComplete`, `segmentWrapperClassName`. Prefer `per="word"` or `"line"` for long strings (above 50 segments, per-segment motion drops)
32
+ - `effect/text-effects` — `TextMorph`: cross-fades one short string into the next in place; change `children` in place, never force a new `key`
33
+ - `effect/text-effects` — `TextScramble`: decodes a string in from noise; `duration` (default `0.8`), `speed` (default `0.04`), `characterSet`, `trigger`, `onAnimationComplete`
34
+ - `effect/text-effects` — `TextShimmer`: continuous "still working" gradient sweep; `duration` (default `2`), `spread` (default `2`)
35
+ - `effect/text-effects` — `TypingAnimation`: types a string, or cycles `words`; `typeInterval` (default `0.1`), `deleteInterval` (default half `typeInterval`), `startDelay`, `holdDelay` (default `1`), `loop`, `start` [mount|view] (default `view`), `hasCursor` (default `true`), `isCursorBlinking` (default `true`), `cursorStyle` [line|block|underscore]
36
+ - `effect/dia-text-reveal` — `DiaTextReveal`: sweeps a colour band across a line, revealing it; `words` + `loop` rotate several strings, `colors` (band stops only — the text inherits its colour), `duration` (default `1.5`), `delay`, `holdDelay` (default `0.5`), `start` [mount|view|each-view] (default `view`), `hasFixedWidth`. The line is blank until the sweep passes
36
37
 
37
- ## Morphing overlays
38
+ ## Animated icons
39
+
40
+ - `effect/icons` — `AnimateIcon` plus 259 lucide-derived animated icons, each exported under its plain name and an `…Icon` alias (`Activity` / `ActivityIcon`, `ArrowDown` / `ArrowDownIcon`, …). Triggers sit on an icon or on an `AnimateIcon` wrapper that drives every icon inside it: `animateOnHover`, `animateOnTap`, `animateOnView` (`animateOnViewMargin`, `animateOnViewOnce`), or the boolean `animate` for an external trigger; `animation` names the variant (`default`, `default-loop`, … per icon), `loop`, `startDelay`, `holdDelay`, `persistOnAnimateEnd`, `completeOnStop`. Icon props: `size`, `label` (an unlabelled icon is `aria-hidden`). `AnimateIcon` drives only icons from this set — wrapping a plain `lucide-react` icon triggers nothing. A static icon is `lucide-react` directly; an icon from a stored name is `element/icon`.
38
41
 
39
- Trigger element morphs its own bounds into the overlay (shared-layout, not a fade) — interruptible mid-animation.
42
+ ## Morphing overlays
40
43
 
41
- - `motion-primitives/morphing-dialog` — `MorphingDialog` (root) + `MorphingDialogTrigger`, `MorphingDialogContainer` (portal), `MorphingDialogContent`, `MorphingDialogTitle`, `MorphingDialogSubtitle`, `MorphingDialogDescription` (`disableLayoutAnimation`), `MorphingDialogImage`, `MorphingDialogClose`; root takes `transition`
42
- - `motion-primitives/morphing-popover` — `MorphingPopover` (`defaultOpen`/`open`/`onOpenChange`, `variants`) + `MorphingPopoverTrigger`, `MorphingPopoverContent`
44
+ The trigger morphs its own bounds into the popup (shared layout on the slow motion step, not a fade); under reduced motion the popup opens on the plain transition. Both are parts of the overlay families in [overlays.md](overlays.md):
43
45
 
44
- ```tsx
45
- <MorphingDialog>
46
- <MorphingDialogTrigger>
47
- <img src={thumb} alt="" />
48
- </MorphingDialogTrigger>
49
- <MorphingDialogContainer>
50
- <MorphingDialogContent>
51
- <MorphingDialogImage src={fullSize} alt="" />
52
- </MorphingDialogContent>
53
- </MorphingDialogContainer>
54
- </MorphingDialog>
55
- ```
46
+ - `compound/dialog` — `Dialog.MorphTrigger`, in place of `Dialog.Trigger`; takes no `render` (style it with `className`)
47
+ - `element/popover` — `Popover.MorphTrigger`, in place of `Popover.Trigger`; takes no `render`
56
48
 
57
49
  ## Panel transitions
58
50
 
59
- Crossfade/slide between sibling panels, or a height-animated reveal. Distinct from the plain shadcn `accordion`/`tabs` subpaths in [layout.md](layout.md)/[navigation.md](navigation.md) — these are the animated variants.
51
+ Swap between sibling panels, or a height-animated reveal.
60
52
 
61
- - `motion-primitives/transition-panel` — `TransitionPanel`: crossfades between `children[activeIndex]`; `variants` (`{ enter, center, exit }`), `transition`
62
- - `motion-primitives/disclosure` — `Disclosure` (root, `open`/`onOpenChange`) + `DisclosureTrigger`, `DisclosureContent`; `useDisclosure()` for custom triggers
63
- - `motion-primitives/accordion` — animated `Accordion` (root, `expandedValue`/`onValueChange`, single-expand) + `AccordionItem`, `AccordionTrigger`, `AccordionContent`; `useAccordion()`
64
- - `animate-ui/components/animate/tabs` — animated `Tabs`: sliding active-tab highlight over the primitive below; `variant` [solid|underline], `size` [sm|md|lg]
53
+ - `effect/transition-panel` — `TransitionPanel` + `TransitionPanel.Panel` (each with a `value`): shows the Panel matching `value` (controlled only — no `defaultValue` or `onValueChange`; an unmatched `value` renders nothing); `animation` [fade|slide|scale] (default `fade`). Slide direction follows Panel order; panels of different heights change the root's height at the swap
54
+ - `compound/tabs` — `Tabs.Contents` settles the panel box to the active panel's height and fades the entering panel in; reach for Tabs rather than pairing a hand-rolled tab strip with `TransitionPanel` ([navigation.md](navigation.md))
55
+ - `compound/accordion` / `element/collapsible` — `Accordion.Panel` and `Collapsible.Panel` open on their own measured height ([layout.md](layout.md))
65
56
 
66
57
  ## Decorative effects
67
58
 
68
- Pointer-tracked washes and ambient loops — decorative chrome only, never load-bearing content.
59
+ Pointer-tracked washes, sliding highlights and ambient loops — decorative chrome only, never load-bearing content.
69
60
 
70
- - `motion-primitives/spotlight` — `Spotlight`: cursor-following radial glow inside a `position: relative` parent; `size`, `springOptions`
71
- - `motion-primitives/glow-effect` — `GlowEffect`: animated multi-color glow; `mode` [rotate|pulse|breathe|colorShift|flowHorizontal|static], `colors`, `blur` (preset or px number), `duration`, `scale`
72
- - `motion-primitives/animated-background` — `AnimatedBackground`: sliding highlight that tracks the active/hovered child (each child needs a `data-id`); `defaultValue`, `onValueChange`, `enableHover`
73
- - `motion-primitives/infinite-slider` — `InfiniteSlider`: seamless looping marquee; `gap`, `speed`, `speedOnHover`, `direction` [horizontal|vertical], `reverse`
74
- - `animate-ui/primitives/effects/highlight` — `Highlight`: the sliding-highlight primitive underneath `AnimatedBackground`/animated `Tabs`; `mode` [children|parent], `value`/`defaultValue`/`onValueChange` (controlled or uncontrolled), `hover`, `click`, `transition`, `exitDelay`
61
+ - `effect/spotlight` — `Spotlight`: cursor-following radial glow, first child of a `relative overflow-hidden` parent; `diameter` (px, default `200`), `as` (`span` inside phrasing content). No spring prop — the trail follows `pace`
62
+ - `effect/shine-border` — `ShineBorder`: absolutely-positioned animated gradient border, placed as the direct first child of a `relative overflow-hidden` rounded card; `borderWidth` (px, defaults to `--edge-hairline`), `duration` (seconds, default 14), `colors` (gradient stops, default `["var(--primary)"]`).
63
+ - `effect/glow-effect` — `GlowEffect`: animated multi-colour halo, first child of a `relative` parent with the content in a later `relative` sibling; `animation` [rotate|pulse|breathe|colorShift|flowHorizontal|static] (default `rotate`), `colors`, `blur` [softest|soft|medium|strong|stronger|strongest|none] or a px number (default `medium`), `duration` (default `5`), `scale` (default `1`; raise it to bleed past the parent)
64
+ - `effect/grainient` — `Grainient`: WebGL2 animated grain gradient for a hero or empty-state background; `colors` (three stops, default `GRAINIENT_DEFAULT_COLORS`; token stops follow `.dark`), `timeSpeed` (default `0.25`), `warpSpeed`, `isGrainAnimated`, `maxFps` (default `30`), plus shader tuning (`colorBalance`, `warpStrength`, `warpFrequency`, `warpAmplitude`, `blendAngle`, `blendSoftness`, `rotationAmount`, `noiseScale`, `grainAmount`, `grainScale`, `contrast`, `gamma`, `saturation`, `centerX`, `centerY`, `zoom`). Override its root to `absolute inset-0` inside a `relative` parent; one instance per screen
65
+ - `effect/infinite-slider` — `InfiniteSlider`: seamless looping marquee that pauses on hover and focus within; `gap` (px, default `16`), `speed` (default `100`), `orientation` [horizontal|vertical], `isReversed`, `isPaused`, `as`. Children render twice — give each a stable `key` and keep `id`s and form controls out
66
+ - `effect/highlight` — `Highlight` + `Highlight.Item` (each with a string `value`): one box slides behind the item the host's `value` names (controlled only; `value={null}` shows none); `mode` [children|parent] (`parent` for mixed-size items), `exitDelay` (ms, default `200`), `boundsOffset`, `containerClassName`, `as`; root `className` and `style` paint the box (add `inset-0` in `children` mode). `useHighlightHover(restingValue)` returns `value` plus `getItemProps(id)` for a box that follows the pointer — paint it neutral (`bg-muted`), never the accent
75
67
 
76
- ## Dynamic toolbar
68
+ ```tsx
69
+ const hover = useHighlightHover(currentPage);
70
+
71
+ <Highlight value={hover.value} className="inset-0 rounded-md bg-muted">
72
+ {pages.map((page) => (
73
+ <Highlight.Item key={page.id} value={page.id} {...hover.getItemProps(page.id)}>
74
+ <a href={page.href}>{page.label}</a>
75
+ </Highlight.Item>
76
+ ))}
77
+ </Highlight>
78
+ ```
77
79
 
78
- - `motion-primitives/toolbar-dynamic` — `ToolbarDynamic`: toolbar that expands to a contextual search input; `expandedWidth`, `collapsedWidth`, `placeholder`, `transition`. Ships `ToolbarButton` for the collapsed action icons.
80
+ ## Motion-aware slot
79
81
 
80
- ---
82
+ `Slot` (from `lib/slot`) — the motion-aware `asChild` slot: renders its single child element with the slot's props merged on (classes through `cn`, `style` merged with the slot's values winning, both refs kept), lifting the child onto a motion component so motion props reach a plain element. A non-element child renders nothing. `WithAsChild<Props>` is the matching props shape. Base UI parts compose through their `render` prop instead; `Slot` is for consumers building their own motion children.
83
+
84
+ ```tsx
85
+ <Slot whileHover={{ scale: 1.02 }} className="rounded-md">
86
+ <a href="/reports">Reports</a>
87
+ </Slot>
88
+ ```
89
+
90
+ ## Dynamic toolbar
81
91
 
82
- Two further `animate-ui` subpaths are low-level primitives consumed internally rather than composed directly: `animate-ui/primitives/animate/slot` (an `asChild`-merging `Slot`, the plumbing behind the animated `Tabs`) and `animate-ui/primitives/effects/theme-toggler` (the directional-wipe primitive behind the `theme-toggler` component in [media.md](media.md)).
92
+ No primitive (`dynamic-toolbar`) — a toolbar that expands into a contextual input follows the plain-spec fallback in [index.md](index.md).
@@ -2,28 +2,67 @@
2
2
 
3
3
  > Generated reference (regen-owned; see [index.md](index.md)). Binds only under `primitives: nurix`.
4
4
 
5
- Components: Breadcrumb, Menubar, Navbar, NavigationMenu, Pagination, Sidebar, SlideOutMenu, Tabs, VerticalIconBar
5
+ Components: Breadcrumb, Menubar, NavigationMenu, Pagination, Sidebar, Tabs, VerticalIconBar
6
6
 
7
- Each imports from its own subpath, with compound members alongside, e.g. `import { Tabs, TabsList, TabsTrigger, TabsContent } from "@nurix/components/compound/tabs"`.
7
+ Each imports from its own subpath as one namespace, with its parts as dotted members, e.g. `import { Tabs } from "@nurix/components/compound/tabs"` then `Tabs.List`, `Tabs.Trigger`, `Tabs.Content`.
8
8
 
9
9
  ## Sidebar
10
10
 
11
- App sidebar: full shadcn primitives plus an opinionated layer, all from the single `@nurix/components/block/sidebar` subpath.
11
+ Collapsible app shell: the shadcn primitives plus an opinionated kit, all as `Sidebar.*` members of `@nurix/components/block/sidebar`, with `useSidebar` beside it.
12
12
 
13
- **Sub-components:** `SidebarProvider`, `Sidebar` (`side` [left|right], `variant` [sidebar|floating|inset], `collapsible` [offcanvas|icon|none]), `SidebarTrigger`, `SidebarHeader`, `SidebarContent`, `SidebarFooter`, `SidebarGroup`, `SidebarMenu`, `SidebarMenuItem`, `SidebarMenuButton`, `SidebarRail`, `SidebarInset`, `useSidebar`. **Opinionated layer:** `Brand`, `Switcher` (`render` [simple|rich], `options`, `activeId`, `onSelect`), `ItemGroup` (`render` [list|tree|rich], `collapsible`, `compact`, `collapsedLabel` [hide|divider], `items`, `onItemClick`), `UserMenu`, `Callout`, `FileTree`, `SidebarCommandTrigger`.
13
+ **Sub-components:** `Sidebar.Provider` (`defaultOpen`, `open`, `onOpenChange`, `openMobile`, `onOpenMobileChange`, `keyboardShortcut` [default `"b"`, `null` unbinds], `labels`), `Sidebar` (`side` [start|end], `variant` [sidebar|floating|inset], `collapsible` [offcanvas|icon|none], default `offcanvas`), `Sidebar.Trigger`, `.Rail`, `.Inset`, `.Input`, `.Header`, `.Content`, `.Footer`, `.Separator`, `.Group`, `.GroupLabel`, `.GroupAction`, `.GroupContent`, `.Menu`, `.MenuItem`, `.MenuButton`, `.MenuAction` (`isHoverRevealed`), `.MenuBadge`, `.MenuSkeleton` (`hasIcon`), `.MenuSub`, `.MenuSubItem`, `.MenuSubButton`. **Kit:** `Sidebar.Brand` (`logo`, `name`, `caption`, `href`), `Sidebar.Switcher` (`variant` [simple|rich], `side` [bottom|inline-end], `options`, `value`/`defaultValue`/`onValueChange`, `addAction`), `Sidebar.ItemGroup` (`variant` [list|tree|rich], `items`, `isCollapsible`, `hasCollapsibleItems`, `defaultExpandedIds`, `collapsedLabel` [hide|divider], `onShowMore`, `onItemClick`), `Sidebar.UserMenu` (`user`, `menuItems`), `Sidebar.CommandTrigger` (`collapsedTooltip`).
14
+
15
+ - `useSidebar()` returns `{ state, open, setOpen, openMobile, setOpenMobile, isMobile, toggleSidebar, panelId, labels }` and throws outside `Sidebar.Provider`.
16
+ - Page content is a sibling `Sidebar.Inset`, never a child of `Sidebar`. The sidebar stores nothing — persist open state through `defaultOpen` + `onOpenChange`.
17
+ - A file tree in the nav is `Sidebar.ItemGroup variant="tree"`; a footer card is a `Sidebar.Group` composed inside `Sidebar.Footer`.
14
18
 
15
19
  ```tsx
16
- <SidebarProvider>
20
+ <Sidebar.Provider>
17
21
  <Sidebar collapsible="icon">
18
- <SidebarHeader>
19
- <Switcher options={workspaces} />
20
- </SidebarHeader>
21
- <SidebarContent>
22
- <ItemGroup label="Platform" render="tree" items={items} />
23
- </SidebarContent>
24
- <SidebarFooter>
25
- <UserMenu user={user} />
26
- </SidebarFooter>
22
+ <Sidebar.Header>
23
+ <Sidebar.Switcher options={workspaces} defaultValue={workspaces[0].id} />
24
+ </Sidebar.Header>
25
+ <Sidebar.Content>
26
+ <Sidebar.ItemGroup label="Platform" variant="tree" items={items} />
27
+ </Sidebar.Content>
28
+ <Sidebar.Footer>
29
+ <Sidebar.UserMenu user={user} menuItems={accountActions} />
30
+ </Sidebar.Footer>
31
+ <Sidebar.Rail />
27
32
  </Sidebar>
28
- </SidebarProvider>
33
+ <Sidebar.Inset><Sidebar.Trigger />{children}</Sidebar.Inset>
34
+ </Sidebar.Provider>
29
35
  ```
36
+
37
+ ## Pagination
38
+
39
+ Pager over a page count (`compound/pagination`); `getPageRange(page, pageCount, siblingCount = 1)` returns the page window as numbers and `"ellipsis"` markers.
40
+
41
+ **Sub-components:** `Pagination.Content`, `.Item`, `.Link` (`value` or `isActive`, `href`), `.Previous`, `.Next`, `.First`, `.Last`, `.Ellipsis`; every control takes `size` [sm|default|lg], `isIconOnly`, `disabled`.
42
+
43
+ **Props:** `value`/`defaultValue`/`onValueChange(value, details)`, `pageCount`, `labels`
44
+
45
+ - A link takes `value` (the root holds the page) or `isActive` (a router owns it, with `href`), never both. In value mode the step controls disable at the ends; in router mode pass `disabled` yourself.
46
+
47
+ ```tsx
48
+ <Pagination value={page} pageCount={12} onValueChange={setPage}>
49
+ <Pagination.Content>
50
+ <Pagination.Item><Pagination.Previous /></Pagination.Item>
51
+ {getPageRange(page, 12).map((entry, index) => (
52
+ <Pagination.Item key={index}>
53
+ {entry === "ellipsis" ? <Pagination.Ellipsis /> : <Pagination.Link value={entry}>{entry}</Pagination.Link>}
54
+ </Pagination.Item>
55
+ ))}
56
+ <Pagination.Item><Pagination.Next /></Pagination.Item>
57
+ </Pagination.Content>
58
+ </Pagination>
59
+ ```
60
+
61
+ ## Other Components
62
+
63
+ - `compound/tabs` — `Tabs` with `Tabs.List` (`align` [start|justify], `activateOnFocus`), `Tabs.Trigger`, `Tabs.Content` (`keepMounted`), optional `Tabs.Contents` (height-settling, fading panel box); root `value`/`defaultValue`/`onValueChange`, `variant` [default|underline], `size` [sm|default|lg], `orientation`
64
+ - `compound/breadcrumb` — `Breadcrumb` with `Breadcrumb.Item` (`href`, `render`; the last item always renders as the current page); `maxItems` with `itemsBeforeCollapse`/`itemsAfterCollapse` (default 1 and 2) folds the middle behind an ellipsis menu; `separator`, `labels`
65
+ - `compound/navigation-menu` — `NavigationMenu` with `.List`, `.Item`, `.Trigger`, `.Content`, `.Link` (`isActive`, `closeOnClick`, `render`); a plain link takes `className={navigationMenuTriggerStyle({ size })}`; placement and `hasArrow` sit on the root, beside `orientation` and `labels.navigation`
66
+ - `compound/menubar` — `Menubar` with `.Menu`, `.Trigger`, `.Popup`, `.Item` (fires `onClick`, never `onSelect`), `.LinkItem`, `.CheckboxItem`, `.RadioGroup`/`.RadioItem`, `.Group`, `.Label`, `.Separator`, `.Sub`/`.SubTrigger`/`.SubPopup`, `.Shortcut` (display only, binds no key)
67
+ - `block/vertical-icon-bar` — `VerticalIconBar`: narrow always-icon rail; `items` (`{ value, label, icon, href?, render?, description?, badge?, disabled? }`), `value`/`defaultValue`/`onValueChange`, `width` (default `5rem`), `footer`, `labels.navigation`
68
+ - No nurix primitive: `navbar` (nearest composition `NavigationMenu`) and `stepper` — build both from the `index.md` fallback spec