@squadbase/vantage 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/README.md +21 -4
  2. package/dist/chunk-A2UUGASH.js +12 -0
  3. package/dist/chunk-A2UUGASH.js.map +1 -0
  4. package/dist/{chunk-ZGDU5YLH.js → chunk-PRFBSQA4.js} +9 -5
  5. package/dist/chunk-PRFBSQA4.js.map +1 -0
  6. package/dist/chunk-WQZYXXQW.js +2249 -0
  7. package/dist/chunk-WQZYXXQW.js.map +1 -0
  8. package/dist/{chunk-73J5ZD4C.js → chunk-YLAB6UQS.js} +17 -3
  9. package/dist/chunk-YLAB6UQS.js.map +1 -0
  10. package/dist/cli.js +607 -36
  11. package/dist/cli.js.map +1 -1
  12. package/dist/components/index.d.ts +554 -0
  13. package/dist/components/index.js +1983 -0
  14. package/dist/components/index.js.map +1 -0
  15. package/dist/markdown/index.d.ts +9 -0
  16. package/dist/markdown/index.js +26 -0
  17. package/dist/markdown/index.js.map +1 -0
  18. package/dist/query/index.d.ts +27 -1
  19. package/dist/query/index.js +18 -0
  20. package/dist/query/index.js.map +1 -1
  21. package/dist/server/node.js +6 -1
  22. package/dist/server/node.js.map +1 -1
  23. package/dist/ui/index.d.ts +316 -200
  24. package/dist/ui/index.js +566 -511
  25. package/dist/ui/index.js.map +1 -1
  26. package/dist/vite/index.js +2 -2
  27. package/docs/en/agent-skills.md +89 -0
  28. package/docs/en/api-and-server.md +84 -0
  29. package/docs/en/build-and-deploy.md +280 -0
  30. package/docs/en/cli-reference.md +311 -0
  31. package/docs/en/components.md +233 -0
  32. package/docs/en/data-fetching.md +78 -0
  33. package/docs/en/environment-variables.md +69 -0
  34. package/docs/en/getting-started.md +95 -0
  35. package/docs/en/index.md +65 -0
  36. package/docs/en/markdown/markdown-renderer.md +44 -0
  37. package/docs/en/pages-and-metadata.md +56 -0
  38. package/docs/en/parts/app-shell.md +54 -0
  39. package/docs/en/parts/dashboard-card.md +64 -0
  40. package/docs/en/parts/data-table.md +86 -0
  41. package/docs/en/parts/date-range-picker.md +41 -0
  42. package/docs/en/parts/echart.md +53 -0
  43. package/docs/en/parts/filter-bar.md +66 -0
  44. package/docs/en/parts/funnel-steps.md +33 -0
  45. package/docs/en/parts/metric-value.md +23 -0
  46. package/docs/en/parts/multi-select.md +27 -0
  47. package/docs/en/parts/page-shell.md +45 -0
  48. package/docs/en/parts/searchable-select.md +47 -0
  49. package/docs/en/parts/section-header.md +20 -0
  50. package/docs/en/parts/segmented-control.md +25 -0
  51. package/docs/en/parts/status-badge.md +44 -0
  52. package/docs/en/parts/trend-indicator.md +26 -0
  53. package/docs/en/routing.md +95 -0
  54. package/docs/en/ui/accordion.md +36 -0
  55. package/docs/en/ui/alert.md +31 -0
  56. package/docs/en/ui/badge.md +23 -0
  57. package/docs/en/ui/breadcrumb.md +38 -0
  58. package/docs/en/ui/button.md +39 -0
  59. package/docs/en/ui/calendar.md +28 -0
  60. package/docs/en/ui/card.md +35 -0
  61. package/docs/en/ui/checkbox.md +34 -0
  62. package/docs/en/ui/cn.md +34 -0
  63. package/docs/en/ui/collapsible.md +21 -0
  64. package/docs/en/ui/command.md +37 -0
  65. package/docs/en/ui/dialog.md +52 -0
  66. package/docs/en/ui/dropdown-menu.md +50 -0
  67. package/docs/en/ui/empty.md +22 -0
  68. package/docs/en/ui/error-state.md +50 -0
  69. package/docs/en/ui/input-group.md +30 -0
  70. package/docs/en/ui/input.md +28 -0
  71. package/docs/en/ui/label.md +19 -0
  72. package/docs/en/ui/loading.md +19 -0
  73. package/docs/en/ui/popover.md +34 -0
  74. package/docs/en/ui/progress.md +28 -0
  75. package/docs/en/ui/scroll-area.md +23 -0
  76. package/docs/en/ui/select.md +63 -0
  77. package/docs/en/ui/separator.md +15 -0
  78. package/docs/en/ui/sheet.md +28 -0
  79. package/docs/en/ui/sidebar.md +92 -0
  80. package/docs/en/ui/skeleton.md +16 -0
  81. package/docs/en/ui/slider.md +21 -0
  82. package/docs/en/ui/spinner.md +14 -0
  83. package/docs/en/ui/switch.md +19 -0
  84. package/docs/en/ui/table.md +24 -0
  85. package/docs/en/ui/tabs.md +23 -0
  86. package/docs/en/ui/textarea.md +15 -0
  87. package/docs/en/ui/toggle-group.md +26 -0
  88. package/docs/en/ui/toggle.md +19 -0
  89. package/docs/en/ui/tooltip.md +22 -0
  90. package/docs/en/ui/use-is-mobile.md +18 -0
  91. package/docs/en/ui-and-theming.md +95 -0
  92. package/docs/index.json +1306 -0
  93. package/docs/ja/agent-skills.md +87 -0
  94. package/docs/ja/api-and-server.md +84 -0
  95. package/docs/ja/build-and-deploy.md +279 -0
  96. package/docs/ja/cli-reference.md +305 -0
  97. package/docs/ja/components.md +229 -0
  98. package/docs/ja/data-fetching.md +78 -0
  99. package/docs/ja/environment-variables.md +68 -0
  100. package/docs/ja/getting-started.md +95 -0
  101. package/docs/ja/index.md +65 -0
  102. package/docs/ja/markdown/markdown-renderer.md +43 -0
  103. package/docs/ja/pages-and-metadata.md +56 -0
  104. package/docs/ja/parts/app-shell.md +54 -0
  105. package/docs/ja/parts/dashboard-card.md +64 -0
  106. package/docs/ja/parts/data-table.md +85 -0
  107. package/docs/ja/parts/date-range-picker.md +41 -0
  108. package/docs/ja/parts/echart.md +52 -0
  109. package/docs/ja/parts/filter-bar.md +64 -0
  110. package/docs/ja/parts/funnel-steps.md +33 -0
  111. package/docs/ja/parts/metric-value.md +25 -0
  112. package/docs/ja/parts/multi-select.md +27 -0
  113. package/docs/ja/parts/page-shell.md +45 -0
  114. package/docs/ja/parts/searchable-select.md +47 -0
  115. package/docs/ja/parts/section-header.md +20 -0
  116. package/docs/ja/parts/segmented-control.md +26 -0
  117. package/docs/ja/parts/status-badge.md +44 -0
  118. package/docs/ja/parts/trend-indicator.md +26 -0
  119. package/docs/ja/routing.md +95 -0
  120. package/docs/ja/ui/accordion.md +37 -0
  121. package/docs/ja/ui/alert.md +31 -0
  122. package/docs/ja/ui/badge.md +23 -0
  123. package/docs/ja/ui/breadcrumb.md +38 -0
  124. package/docs/ja/ui/button.md +39 -0
  125. package/docs/ja/ui/calendar.md +28 -0
  126. package/docs/ja/ui/card.md +35 -0
  127. package/docs/ja/ui/checkbox.md +34 -0
  128. package/docs/ja/ui/cn.md +34 -0
  129. package/docs/ja/ui/collapsible.md +21 -0
  130. package/docs/ja/ui/command.md +36 -0
  131. package/docs/ja/ui/dialog.md +51 -0
  132. package/docs/ja/ui/dropdown-menu.md +50 -0
  133. package/docs/ja/ui/empty.md +22 -0
  134. package/docs/ja/ui/error-state.md +50 -0
  135. package/docs/ja/ui/input-group.md +30 -0
  136. package/docs/ja/ui/input.md +28 -0
  137. package/docs/ja/ui/label.md +19 -0
  138. package/docs/ja/ui/loading.md +19 -0
  139. package/docs/ja/ui/popover.md +34 -0
  140. package/docs/ja/ui/progress.md +28 -0
  141. package/docs/ja/ui/scroll-area.md +24 -0
  142. package/docs/ja/ui/select.md +64 -0
  143. package/docs/ja/ui/separator.md +15 -0
  144. package/docs/ja/ui/sheet.md +28 -0
  145. package/docs/ja/ui/sidebar.md +92 -0
  146. package/docs/ja/ui/skeleton.md +16 -0
  147. package/docs/ja/ui/slider.md +21 -0
  148. package/docs/ja/ui/spinner.md +14 -0
  149. package/docs/ja/ui/switch.md +19 -0
  150. package/docs/ja/ui/table.md +24 -0
  151. package/docs/ja/ui/tabs.md +24 -0
  152. package/docs/ja/ui/textarea.md +15 -0
  153. package/docs/ja/ui/toggle-group.md +27 -0
  154. package/docs/ja/ui/toggle.md +19 -0
  155. package/docs/ja/ui/tooltip.md +22 -0
  156. package/docs/ja/ui/use-is-mobile.md +18 -0
  157. package/docs/ja/ui-and-theming.md +95 -0
  158. package/package.json +29 -4
  159. package/registry/blocks/sales-overview.tsx +43 -19
  160. package/registry/ui/data-table.tsx +702 -102
  161. package/skills/vantage-add-feature/SKILL.md +150 -0
  162. package/skills/vantage-app/SKILL.md +258 -0
  163. package/skills/vantage-pitfalls/SKILL.md +115 -0
  164. package/templates/AGENTS.md +268 -0
  165. package/theme.css +178 -40
  166. package/dist/chunk-73J5ZD4C.js.map +0 -1
  167. package/dist/chunk-ZGDU5YLH.js.map +0 -1
@@ -0,0 +1,95 @@
1
+ # File-based routing
2
+
3
+ > Dropping a file creates a route. Conventions for pages, layouts, dynamic params, and 404/error.
4
+
5
+ In Vantage, **where you place a file is its URL**. You never configure a router.
6
+
7
+ ## Page conventions
8
+
9
+ | File | Route |
10
+ |---|---|
11
+ | `index.tsx` | `/` |
12
+ | `monthly-analysis.tsx` | `/monthly-analysis` |
13
+ | `sales/index.tsx` | `/sales` |
14
+ | `sales/[customerId].tsx` | `/sales/:customerId` |
15
+ | `sales/[...path].tsx` | `/sales/*` (catchall) |
16
+ | `_layout.tsx` | Layout for that directory (nestable) |
17
+ | `_404.tsx` | Not-found UI (**root only**) |
18
+ | `_error.tsx` | Error UI (**root only**) |
19
+
20
+ - `[name]` → dynamic param, `[...name]` → catchall.
21
+ - `index` maps to its own directory (the name drops from the URL).
22
+ - Files starting with `_` are reserved (`_layout`/`_404`/`_error`) or private (not routed).
23
+
24
+ ## Directories that are never routed
25
+
26
+ These directories are never scanned. Put whatever code you like here.
27
+
28
+ ```text
29
+ components/ Reusable React components
30
+ hooks/ Custom hooks
31
+ lib/ Utilities
32
+ server/ API (its presence enables the server)
33
+ public/ Static assets
34
+ ```
35
+
36
+ > [!NOTE]
37
+ > Imports between files use **runtime `.js` specifiers** (e.g. `./components/revenue-chart.js`) —
38
+ > required for ESM Node resolution.
39
+
40
+ ## The three spellings of a dynamic param
41
+
42
+ The same param appears in three spellings depending on where it is. **They must match.**
43
+
44
+ | Where | Spelling | Example |
45
+ |---|---|---|
46
+ | Filename | `[id]` | `sales/[customerId].tsx` |
47
+ | Display route | `:id` | `/sales/:customerId` |
48
+ | Link | `$id` + `params` | `to="/sales/$customerId"` `params={{ customerId }}` |
49
+
50
+ ```tsx
51
+ import { Link, useParams } from "@squadbase/vantage/router";
52
+
53
+ function Row({ customerId }: { customerId: string }) {
54
+ return (
55
+ <Link to="/sales/$customerId" params={{ customerId }}>
56
+ Open customer
57
+ </Link>
58
+ );
59
+ }
60
+
61
+ // sales/[customerId].tsx
62
+ export default function Customer() {
63
+ const { customerId } = useParams({ strict: false });
64
+ return <div>Customer {customerId}</div>;
65
+ }
66
+ ```
67
+
68
+ ## Layouts and nesting
69
+
70
+ `_layout.tsx` renders an `<Outlet />` that wraps child routes. Place one per directory to nest them.
71
+
72
+ ```tsx
73
+ // _layout.tsx
74
+ import { Outlet } from "@squadbase/vantage/router";
75
+
76
+ export default function RootLayout() {
77
+ return (
78
+ <div className="min-h-screen">
79
+ <nav className="border-b p-4">My Dashboard</nav>
80
+ <Outlet />
81
+ </div>
82
+ );
83
+ }
84
+ ```
85
+
86
+ `_404.tsx` and `_error.tsx` are recognized at the **root only**.
87
+
88
+ ## HMR and route regeneration
89
+
90
+ Adding or removing a `.tsx` page or a `server/api` file regenerates routes and triggers a full
91
+ reload. Editing a page body applies in place via React Fast Refresh.
92
+
93
+ ## What to read next
94
+
95
+ - [Pages & definePage](pages-and-metadata) — the page contract and metadata
@@ -0,0 +1,36 @@
1
+ # Accordion
2
+
3
+ > A stack of headings that open and close. One at a time by default.
4
+
5
+ A list whose headings toggle their content open and closed. One panel at a time by default; add
6
+ `openMultiple` to let several stay open at once.
7
+
8
+ ```tsx
9
+ import {
10
+ Accordion,
11
+ AccordionItem,
12
+ AccordionTrigger,
13
+ AccordionContent,
14
+ } from "@squadbase/vantage/ui";
15
+ ```
16
+
17
+ ## Parts
18
+
19
+ | Part | Role |
20
+ | --- | --- |
21
+ | `Accordion` | The root. `value` / `defaultValue` is an **array of open items** |
22
+ | `AccordionItem` | One item, identified by `value` |
23
+ | `AccordionTrigger` | The heading button; the chevron comes for free |
24
+ | `AccordionContent` | The panel that opens |
25
+
26
+ ## Several open at once
27
+
28
+ ```tsx
29
+ <Accordion openMultiple defaultValue={["a", "b"]}>
30
+ ```
31
+
32
+ > [!NOTE]
33
+ > `value` is an **array**, not the string Radix used — that's Base UI's API. Even for a single open
34
+ > panel you pass `defaultValue={["freshness"]}`.
35
+
36
+ For one standalone disclosure, [`Collapsible`](ui/collapsible) is lighter.
@@ -0,0 +1,31 @@
1
+ # Alert
2
+
3
+ > An in-page notice that flags something while the content is still shown.
4
+
5
+ An in-page notice. Where [`ErrorState`](ui/error-state) says "this view failed",
6
+ `Alert` flags something while the content is still shown.
7
+
8
+ ```tsx
9
+ import { Alert, AlertTitle, AlertDescription, AlertAction } from "@squadbase/vantage/ui";
10
+ ```
11
+
12
+ ## Parts and variants
13
+
14
+ | Part | Role |
15
+ | --- | --- |
16
+ | `Alert` | The frame. `variant`: `default` \| `destructive` |
17
+ | `AlertTitle` | The heading |
18
+ | `AlertDescription` | The body |
19
+ | `AlertAction` | An action pinned to the right |
20
+
21
+ Drop an icon as a direct child of `Alert` and the grid becomes two columns on its own. Importing
22
+ straight from `lucide-react` is fine.
23
+
24
+ ```tsx
25
+ import { Info } from "lucide-react";
26
+
27
+ <Alert>
28
+ <Info />
29
+ <AlertTitle>This view is read-only</AlertTitle>
30
+ </Alert>;
31
+ ```
@@ -0,0 +1,23 @@
1
+ # Badge
2
+
3
+ > A small label for counts, states and tags.
4
+
5
+ A small label to sit beside text. For a row's status with a coloured dot, reach for
6
+ [`StatusBadge`](parts/status-badge) instead.
7
+
8
+ ```tsx
9
+ import { Badge, badgeVariants } from "@squadbase/vantage/ui";
10
+ ```
11
+
12
+ ## Variants
13
+
14
+ `default` / `secondary` / `destructive` / `outline` / `ghost` / `link`.
15
+
16
+ `badgeVariants` returns the class string, for when you want the badge look on a different element
17
+ such as an `<a>`.
18
+
19
+ ```tsx
20
+ <a href="/docs" className={badgeVariants({ variant: "outline" })}>
21
+ Documentation
22
+ </a>
23
+ ```
@@ -0,0 +1,38 @@
1
+ # Breadcrumb
2
+
3
+ > A breadcrumb trail showing where the page sits.
4
+
5
+ Shows where the current page sits in the hierarchy. Most useful on drill-down detail pages such as
6
+ `/sales/$customerId`.
7
+
8
+ ```tsx
9
+ import {
10
+ Breadcrumb,
11
+ BreadcrumbList,
12
+ BreadcrumbItem,
13
+ BreadcrumbLink,
14
+ BreadcrumbPage,
15
+ BreadcrumbSeparator,
16
+ BreadcrumbEllipsis,
17
+ } from "@squadbase/vantage/ui";
18
+ ```
19
+
20
+ ## Parts
21
+
22
+ | Part | Role |
23
+ | --- | --- |
24
+ | `BreadcrumbLink` | An ancestor (a link) |
25
+ | `BreadcrumbPage` | Where you are now — not a link |
26
+ | `BreadcrumbSeparator` | The divider; pass children to swap the glyph |
27
+ | `BreadcrumbEllipsis` | Collapsed middle levels |
28
+
29
+ ## With the router's link
30
+
31
+ `BreadcrumbLink` takes a `render` prop to become another component. Pass Vantage's
32
+ [`Link`](routing) for client-side navigation.
33
+
34
+ ```tsx
35
+ import { Link } from "@squadbase/vantage/router";
36
+
37
+ <BreadcrumbLink render={<Link to="/sales" />}>Sales</BreadcrumbLink>;
38
+ ```
@@ -0,0 +1,39 @@
1
+ # Button
2
+
3
+ > The button, with six variants and eight sizes.
4
+
5
+ The component you'll reach for most. `variant` carries the meaning, `size` the scale.
6
+
7
+ ```tsx
8
+ import { Button, buttonVariants } from "@squadbase/vantage/ui";
9
+ ```
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `variant` | `"default" \| "secondary" \| "outline" \| "ghost" \| "destructive" \| "link"` | `"default"` | Look and meaning. |
14
+ | `size` | `"default" \| "xs" \| "sm" \| "lg" \| "icon" \| "icon-xs" \| "icon-sm" \| "icon-lg"` | `"default"` | Height and padding. The `icon` sizes are square. |
15
+ | `render` | `ReactElement` | | Render as a different element (Radix's `asChild`). |
16
+
17
+ Drop an icon in as a child and its size and spacing are handled. For icon-only buttons always pass
18
+ `size="icon"` and an `aria-label`.
19
+
20
+ ## A link that looks like a button
21
+
22
+ Pass an element to `render` — Base UI has no `asChild`.
23
+
24
+ ```tsx
25
+ import { Link } from "@squadbase/vantage/router";
26
+
27
+ <Button render={<Link to="/sales" />}>View sales</Button>;
28
+ ```
29
+
30
+ When you only want the class string, call `buttonVariants({ variant, size })`.
31
+
32
+ ## Pending state
33
+
34
+ ```tsx
35
+ <Button disabled={isPending}>
36
+ {isPending && <Spinner />}
37
+ Save
38
+ </Button>
39
+ ```
@@ -0,0 +1,28 @@
1
+ # Calendar
2
+
3
+ > The date calendar, built on react-day-picker.
4
+
5
+ A calendar for single dates, multiple dates or a range. For picking a period on a dashboard,
6
+ [`DateRangePicker`](parts/date-range-picker) gets you there faster than using this
7
+ directly.
8
+
9
+ ```tsx
10
+ import { Calendar, CalendarDayButton } from "@squadbase/vantage/ui";
11
+ ```
12
+
13
+ The props are [react-day-picker](https://daypicker.dev)'s `DayPicker` props. Vantage adds only
14
+ three of its own:
15
+
16
+ | Prop | Type | Default | Description |
17
+ | --- | --- | --- | --- |
18
+ | `captionLayout` | `"label" \| "dropdown" \| "dropdown-months" \| "dropdown-years"` | `"label"` | Whether the month/year caption is text or a select. |
19
+ | `buttonVariant` | `Button variant` | `"ghost"` | Look of the previous/next month buttons. |
20
+ | `showOutsideDays` | `boolean` | `true` | Dim and show the neighbouring months' days. |
21
+
22
+ `mode` is `"single"` / `"multiple"` / `"range"`, and the types of `selected` and `onSelect` follow
23
+ it. `CalendarDayButton` is one day cell — swap it when you want a dot or label under the date.
24
+
25
+ > [!NOTE]
26
+ > Change the locale by passing a `date-fns` locale to the `locale` prop. `date-fns` is one of
27
+ > Vantage's dependencies, but avoid importing it directly from app code — keeping locale handling
28
+ > inside [`DateRangePicker`](parts/date-range-picker) is the safer path.
@@ -0,0 +1,35 @@
1
+ # Card
2
+
3
+ > The generic card. For dashboard tiles use DashboardCard.
4
+
5
+ The generic card. For dashboard tiles, [`DashboardCard`](parts/dashboard-card) —
6
+ which already has the spacing and a skeleton — fits better.
7
+
8
+ ```tsx
9
+ import {
10
+ Card,
11
+ CardHeader,
12
+ CardTitle,
13
+ CardDescription,
14
+ CardAction,
15
+ CardContent,
16
+ CardFooter,
17
+ } from "@squadbase/vantage/ui";
18
+ ```
19
+
20
+ ## Parts
21
+
22
+ | Part | Role |
23
+ | --- | --- |
24
+ | `Card` | The frame. `size`: `default` \| `sm` (changes inner spacing) |
25
+ | `CardHeader` | The heading row |
26
+ | `CardTitle` / `CardDescription` | Heading and supporting text |
27
+ | `CardAction` | The right end of the heading row; adding it makes the header two columns |
28
+ | `CardContent` | The body |
29
+ | `CardFooter` | The bottom; adding it tightens the card's bottom padding |
30
+
31
+ Spacing comes from a single `--card-spacing` CSS variable, so you can override it per card.
32
+
33
+ ```tsx
34
+ <Card className="[--card-spacing:--spacing(6)]">…</Card>
35
+ ```
@@ -0,0 +1,34 @@
1
+ # Checkbox
2
+
3
+ > The checkbox. The mixed state is a separate indeterminate prop.
4
+
5
+ An on/off input. In the Base UI build `checked` is strictly a boolean and the mixed state lives on
6
+ its own prop — the biggest difference if you're coming from Radix.
7
+
8
+ ```tsx
9
+ import { Checkbox } from "@squadbase/vantage/ui";
10
+ ```
11
+
12
+ | Prop | Type | Description |
13
+ | --- | --- | --- |
14
+ | `checked` | `boolean` | The checked state. `"indeterminate"` is not accepted. |
15
+ | `indeterminate` | `boolean` | The mixed state, independent of `checked`. |
16
+ | `onCheckedChange` | `(checked: boolean) => void` | Fired on change; the value is always a boolean. |
17
+ | `disabled` | `boolean` | Disable interaction. |
18
+
19
+ > [!WARNING]
20
+ > Radix's `checked={"indeterminate"}` is a type error here. Under Base UI, pass `indeterminate` as its
21
+ > own prop.
22
+
23
+ ## Select-all in a table
24
+
25
+ This is exactly what [`DataTable`](parts/data-table)'s selection column does.
26
+
27
+ ```tsx
28
+ <Checkbox
29
+ checked={table.getIsAllPageRowsSelected()}
30
+ indeterminate={table.getIsSomePageRowsSelected()}
31
+ onCheckedChange={(checked) => table.toggleAllPageRowsSelected(checked)}
32
+ aria-label="Select all"
33
+ />
34
+ ```
@@ -0,0 +1,34 @@
1
+ # cn()
2
+
3
+ > The helper that builds class names and resolves conflicts.
4
+
5
+ Builds a `className` conditionally: `clsx` resolves the conditions, `tailwind-merge` resolves
6
+ conflicting utilities.
7
+
8
+ ```tsx
9
+ import { cn } from "@squadbase/vantage/ui";
10
+ import type { ClassValue } from "@squadbase/vantage/ui";
11
+
12
+ <div className={cn("p-4", isActive && "bg-accent", className)} />;
13
+ ```
14
+
15
+ ## Last one wins
16
+
17
+ Because it runs through `tailwind-merge`, conflicting utilities resolve **in favour of the later
18
+ one**.
19
+
20
+ ```tsx
21
+ cn("p-2", "p-4"); // → "p-4"
22
+ ```
23
+
24
+ That is what lets a `className` you pass to a component override its defaults. Every built-in
25
+ component runs its classes through `cn()`, so `className` always behaves as an override.
26
+
27
+ ```tsx
28
+ // Reliably beats the default bg-primary
29
+ <Button className="bg-emerald-600 hover:bg-emerald-700">Run</Button>
30
+ ```
31
+
32
+ > [!NOTE]
33
+ > You never need to import `clsx` or `tailwind-merge` yourself — and shouldn't from app code. `cn()`
34
+ > is the public seam for both.
@@ -0,0 +1,21 @@
1
+ # Collapsible
2
+
3
+ > A single unstyled disclosure.
4
+
5
+ An unstyled primitive that opens and closes one region. For a stack of headings, use
6
+ [`Accordion`](ui/accordion).
7
+
8
+ ```tsx
9
+ import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@squadbase/vantage/ui";
10
+ ```
11
+
12
+ Control it with `open` / `defaultOpen` / `onOpenChange`. Both the trigger and the panel get
13
+ `data-open` while open, so the chevron can rotate in CSS alone.
14
+
15
+ ```tsx
16
+ <ChevronRight className="transition-transform data-open:rotate-90" />
17
+ ```
18
+
19
+ > [!NOTE]
20
+ > `data-open` is a custom variant declared in Vantage's `theme.css`. The Radix spelling
21
+ > `data-[state=open]:` is wired to the same variant, so either works.
@@ -0,0 +1,37 @@
1
+ # Command
2
+
3
+ > A filterable command list — the base for a ⌘K palette.
4
+
5
+ A list that narrows as you type. It is what powers
6
+ [`SearchableSelect`](parts/searchable-select) and
7
+ [`MultiSelect`](parts/multi-select), and on its own it makes a ⌘K palette.
8
+
9
+ ```tsx
10
+ import {
11
+ Command,
12
+ CommandDialog,
13
+ CommandInput,
14
+ CommandList,
15
+ CommandEmpty,
16
+ CommandGroup,
17
+ CommandItem,
18
+ CommandShortcut,
19
+ CommandSeparator,
20
+ } from "@squadbase/vantage/ui";
21
+ ```
22
+
23
+ ## Parts
24
+
25
+ | Part | Role |
26
+ | --- | --- |
27
+ | `Command` | The root; owns filtering and keyboard handling |
28
+ | `CommandDialog` | The same thing inside a `Dialog` — for a ⌘K palette |
29
+ | `CommandInput` | The search field |
30
+ | `CommandList` | The scroll region |
31
+ | `CommandEmpty` | Shown when nothing matches |
32
+ | `CommandGroup` | A group with a `heading` |
33
+ | `CommandItem` | One item; `value` / `onSelect` |
34
+ | `CommandShortcut` | The key hint on the right |
35
+
36
+ Filtering runs against the items' text by default. To search on the server, set
37
+ `shouldFilter={false}` and replace the items yourself.
@@ -0,0 +1,52 @@
1
+ # Dialog
2
+
3
+ > The modal dialog.
4
+
5
+ A modal centred on the screen. For one that slides in from the edge use
6
+ [`Sheet`](ui/sheet); for a light bubble use
7
+ [`Popover`](ui/popover).
8
+
9
+ ```tsx
10
+ import {
11
+ Dialog,
12
+ DialogTrigger,
13
+ DialogContent,
14
+ DialogHeader,
15
+ DialogTitle,
16
+ DialogDescription,
17
+ DialogFooter,
18
+ DialogClose,
19
+ DialogOverlay,
20
+ DialogPortal,
21
+ } from "@squadbase/vantage/ui";
22
+ ```
23
+
24
+ ## Parts
25
+
26
+ | Part | Role |
27
+ | --- | --- |
28
+ | `Dialog` | The state: `open` / `defaultOpen` / `onOpenChange` |
29
+ | `DialogTrigger` | What opens it; `render` turns it into a button |
30
+ | `DialogContent` | The panel. `showCloseButton` (default `true`) toggles the × |
31
+ | `DialogHeader` / `DialogTitle` / `DialogDescription` | The heading block |
32
+ | `DialogFooter` | The button row; `showCloseButton` adds a close button |
33
+ | `DialogClose` | Anything that closes it |
34
+
35
+ `DialogTitle` is required for assistive technology. If you don't want it visible, keep it and add
36
+ `className="sr-only"`.
37
+
38
+ ## Opening without a trigger
39
+
40
+ To open from code — after a form submit, say — own the `open` state yourself.
41
+
42
+ ```tsx
43
+ const [open, setOpen] = useState(false);
44
+
45
+ <Dialog open={open} onOpenChange={setOpen}>
46
+ <DialogContent>…</DialogContent>
47
+ </Dialog>;
48
+ ```
49
+
50
+ > [!NOTE]
51
+ > Base UI has no `asChild`. Write `<DialogTrigger render={<Button />}>Label</DialogTrigger>` — the
52
+ > label goes in `children`, not inside the element you pass to `render`.
@@ -0,0 +1,50 @@
1
+ # DropdownMenu
2
+
3
+ > A menu opened from a button, with checkbox, radio and submenu items.
4
+
5
+ An action menu opened from a button — the per-row "…" menu, or a table's column-visibility toggle.
6
+
7
+ ```tsx
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuTrigger,
11
+ DropdownMenuContent,
12
+ DropdownMenuGroup,
13
+ DropdownMenuLabel,
14
+ DropdownMenuItem,
15
+ DropdownMenuCheckboxItem,
16
+ DropdownMenuRadioGroup,
17
+ DropdownMenuRadioItem,
18
+ DropdownMenuSeparator,
19
+ DropdownMenuShortcut,
20
+ DropdownMenuSub,
21
+ DropdownMenuSubTrigger,
22
+ DropdownMenuSubContent,
23
+ DropdownMenuPortal,
24
+ } from "@squadbase/vantage/ui";
25
+ ```
26
+
27
+ ## The props you'll use
28
+
29
+ | Prop | Goes on | Meaning |
30
+ | --- | --- | --- |
31
+ | `align` / `side` / `sideOffset` | `DropdownMenuContent` | Where it opens |
32
+ | `variant="destructive"` | `DropdownMenuItem` | Destructive colouring |
33
+ | `inset` | `Item` / `Label` / `SubTrigger` | Indent past the icon column |
34
+ | `checked` / `onCheckedChange` | `DropdownMenuCheckboxItem` | Checked state |
35
+ | `value` / `onValueChange` | `DropdownMenuRadioGroup` | Single choice |
36
+
37
+ ## Submenus
38
+
39
+ ```tsx
40
+ <DropdownMenuSub>
41
+ <DropdownMenuSubTrigger>Export</DropdownMenuSubTrigger>
42
+ <DropdownMenuSubContent>
43
+ <DropdownMenuItem>CSV</DropdownMenuItem>
44
+ <DropdownMenuItem>JSON</DropdownMenuItem>
45
+ </DropdownMenuSubContent>
46
+ </DropdownMenuSub>
47
+ ```
48
+
49
+ > [!NOTE]
50
+ > Use `render` to make the trigger a button — Base UI has no `asChild`.
@@ -0,0 +1,22 @@
1
+ # Empty
2
+
3
+ > The zero-results state.
4
+
5
+ What to show when there are no results. The icon, description and action are all optional.
6
+
7
+ ```tsx
8
+ import { Empty } from "@squadbase/vantage/ui";
9
+ ```
10
+
11
+ | Prop | Type | Default | Description |
12
+ | --- | --- | --- | --- |
13
+ | `title` | `string` | `"Nothing here yet"` | The heading. |
14
+ | `description` | `string` | | Supporting text under the heading. |
15
+ | `icon` | `ReactNode` | | Artwork above the heading. |
16
+ | `action` | `ReactNode` | | The next step — a button or link — at the bottom. |
17
+ | `className` | `string` | | Classes to add. |
18
+
19
+ > [!NOTE]
20
+ > When it has no rows, [`DataTable`](parts/data-table) draws its own "no data" row
21
+ > inside the table. Reach for `Empty` when you want to replace the table entirely — for instance, when
22
+ > nothing has been created yet.
@@ -0,0 +1,50 @@
1
+ # ErrorState
2
+
3
+ > The failure state.
4
+
5
+ Shown when a load or an action failed. It carries `role="alert"` and is framed in the destructive
6
+ colours.
7
+
8
+ ```tsx
9
+ import { ErrorState } from "@squadbase/vantage/ui";
10
+ ```
11
+
12
+ | Prop | Type | Default | Description |
13
+ | --- | --- | --- | --- |
14
+ | `title` | `string` | `"Something went wrong"` | The heading. |
15
+ | `message` | `ReactNode` | | The details — an error message can go straight in. |
16
+ | `action` | `ReactNode` | | A retry button, typically. |
17
+ | `className` | `string` | | Classes to add. |
18
+
19
+ ## All three states together
20
+
21
+ The usual shape is to map the query's state onto the three branches.
22
+
23
+ ```tsx
24
+ import { useQuery, apiFetch } from "@squadbase/vantage/query";
25
+ import { Button, Loading, Empty, ErrorState } from "@squadbase/vantage/ui";
26
+ import { DataTablePreset } from "@squadbase/vantage/components";
27
+
28
+ export default function Customers() {
29
+ const { data, isLoading, error, refetch } = useQuery({
30
+ queryKey: ["customers"],
31
+ queryFn: () => apiFetch("/api/customers").then((r) => r.json()),
32
+ });
33
+
34
+ if (isLoading) return <Loading label="Loading customers…" />;
35
+ if (error)
36
+ return (
37
+ <ErrorState
38
+ message={error.message}
39
+ action={<Button variant="outline" onClick={() => refetch()}>Retry</Button>}
40
+ />
41
+ );
42
+ if (data.length === 0) return <Empty title="No customers yet" />;
43
+
44
+ return <DataTablePreset columns={columns} data={data} />;
45
+ }
46
+ ```
47
+
48
+ > [!NOTE]
49
+ > To surface a deliberate error message from your API, throw `HttpError(status, message)` in
50
+ > `server/api`. Anything else becomes a generic 500 — see [API & server](api-and-server).
@@ -0,0 +1,30 @@
1
+ # InputGroup
2
+
3
+ > Attach icons, text or buttons to either end of an input.
4
+
5
+ A frame that places icons, units or small buttons inside the field — search boxes, or inputs with a
6
+ `%` or currency suffix.
7
+
8
+ ```tsx
9
+ import {
10
+ InputGroup,
11
+ InputGroupInput,
12
+ InputGroupTextarea,
13
+ InputGroupAddon,
14
+ InputGroupText,
15
+ InputGroupButton,
16
+ } from "@squadbase/vantage/ui";
17
+ ```
18
+
19
+ ## Parts
20
+
21
+ | Part | Role |
22
+ | --- | --- |
23
+ | `InputGroup` | The frame; the focus ring lives here |
24
+ | `InputGroupInput` / `InputGroupTextarea` | The field itself, borderless |
25
+ | `InputGroupAddon` | Where attachments go. `align`: `inline-start` (default) \| `inline-end` \| `block-start` \| `block-end` |
26
+ | `InputGroupText` | Short text such as a unit |
27
+ | `InputGroupButton` | A small button. `size`: `xs` (default) \| `sm` \| `icon-xs` \| `icon-sm` |
28
+
29
+ `block-start` / `block-end` place the addon above or below the field — pair either with
30
+ `InputGroupTextarea` for a textarea with its own toolbar.