diff --git a/src/App.tsx b/src/App.tsx
index 4ac17ca..2107880 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,6 +4,7 @@ import { Metrics } from './pages/Metrics/Metrics'
import { RouteDetail } from './pages/RouteDetail/RouteDetail'
import { ExchangeDetail } from './pages/ExchangeDetail/ExchangeDetail'
import { AgentHealth } from './pages/AgentHealth/AgentHealth'
+import { Inventory } from './pages/Inventory/Inventory'
export default function App() {
return (
@@ -13,6 +14,7 @@ export default function App() {
} />
} />
} />
+ } />
)
}
diff --git a/src/pages/Inventory/Inventory.module.css b/src/pages/Inventory/Inventory.module.css
new file mode 100644
index 0000000..fc58819
--- /dev/null
+++ b/src/pages/Inventory/Inventory.module.css
@@ -0,0 +1,87 @@
+.page {
+ min-height: 100vh;
+ background: var(--bg-body);
+ font-family: var(--font-body);
+}
+
+.header {
+ position: sticky;
+ top: 0;
+ z-index: 10;
+ background: var(--bg-surface);
+ border-bottom: 1px solid var(--border);
+ padding: 12px 24px;
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.headerTitle {
+ font-size: 18px;
+ font-weight: 600;
+ color: var(--text-primary);
+ margin: 0;
+}
+
+.backLink {
+ font-size: 13px;
+ color: var(--amber);
+ text-decoration: none;
+ margin-left: auto;
+}
+
+.backLink:hover {
+ text-decoration: underline;
+}
+
+.body {
+ display: flex;
+ gap: 0;
+ max-width: 1200px;
+ margin: 0 auto;
+ padding: 24px;
+}
+
+.nav {
+ width: 200px;
+ flex-shrink: 0;
+ position: sticky;
+ top: 57px;
+ height: calc(100vh - 57px);
+ overflow-y: auto;
+ padding-right: 16px;
+}
+
+.navSection {
+ margin-bottom: 8px;
+}
+
+.navLabel {
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--text-muted);
+ padding: 8px 8px 4px;
+ display: block;
+}
+
+.navLink {
+ display: block;
+ font-size: 13px;
+ color: var(--text-secondary);
+ text-decoration: none;
+ padding: 4px 8px;
+ border-radius: var(--radius-sm);
+ line-height: 1.5;
+}
+
+.navLink:hover {
+ background: var(--bg-hover);
+ color: var(--text-primary);
+}
+
+.content {
+ flex: 1;
+ min-width: 0;
+}
diff --git a/src/pages/Inventory/Inventory.tsx b/src/pages/Inventory/Inventory.tsx
new file mode 100644
index 0000000..8fa13e7
--- /dev/null
+++ b/src/pages/Inventory/Inventory.tsx
@@ -0,0 +1,37 @@
+import { Link } from 'react-router-dom'
+import styles from './Inventory.module.css'
+import { PrimitivesSection } from './sections/PrimitivesSection'
+
+const NAV_ITEMS = [
+ { label: 'Primitives', href: '#primitives' },
+ { label: 'Composites', href: '#composites' },
+ { label: 'Layout', href: '#layout' },
+]
+
+export function Inventory() {
+ return (
+
+
+ Component Inventory
+ ← Back to app
+
+
+
+
+ )
+}
diff --git a/src/pages/Inventory/sections/PrimitivesSection.module.css b/src/pages/Inventory/sections/PrimitivesSection.module.css
new file mode 100644
index 0000000..24d9c69
--- /dev/null
+++ b/src/pages/Inventory/sections/PrimitivesSection.module.css
@@ -0,0 +1,74 @@
+.section {
+ margin-bottom: 40px;
+}
+
+.sectionTitle {
+ font-size: 22px;
+ font-weight: 700;
+ color: var(--text-primary);
+ margin: 0 0 24px;
+}
+
+.componentCard {
+ background: var(--bg-surface);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-md);
+ padding: 20px;
+ margin-bottom: 16px;
+ box-shadow: var(--shadow-sm);
+}
+
+.componentTitle {
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--text-primary);
+ margin: 0 0 4px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.componentDesc {
+ font-size: 12px;
+ color: var(--text-muted);
+ margin: 0 0 16px;
+}
+
+.demoArea {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ align-items: flex-start;
+}
+
+.demoAreaColumn {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ align-items: flex-start;
+}
+
+.demoAreaRow {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ align-items: center;
+}
+
+.demoLabel {
+ font-size: 11px;
+ color: var(--text-muted);
+ font-weight: 500;
+}
+
+.demoGroup {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.demoGroupRow {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
diff --git a/src/pages/Inventory/sections/PrimitivesSection.tsx b/src/pages/Inventory/sections/PrimitivesSection.tsx
new file mode 100644
index 0000000..148bb60
--- /dev/null
+++ b/src/pages/Inventory/sections/PrimitivesSection.tsx
@@ -0,0 +1,591 @@
+import { useState } from 'react'
+import styles from './PrimitivesSection.module.css'
+import {
+ Alert,
+ Avatar,
+ Badge,
+ Button,
+ Card,
+ Checkbox,
+ CodeBlock,
+ Collapsible,
+ DateRangePicker,
+ DateTimePicker,
+ EmptyState,
+ FilterPill,
+ FormField,
+ InfoCallout,
+ Input,
+ KeyboardHint,
+ Label,
+ MonoText,
+ Pagination,
+ ProgressBar,
+ RadioGroup,
+ RadioItem,
+ SectionHeader,
+ Select,
+ Skeleton,
+ Sparkline,
+ Spinner,
+ StatCard,
+ StatusDot,
+ Tag,
+ Textarea,
+ Toggle,
+ Tooltip,
+} from '../../../design-system/primitives'
+
+// ── helpers ──────────────────────────────────────────────────────────────────
+
+interface DemoCardProps {
+ id: string
+ title: string
+ description: string
+ children: React.ReactNode
+}
+
+function DemoCard({ id, title, description, children }: DemoCardProps) {
+ return (
+
+
{title}
+
{description}
+
{children}
+
+ )
+}
+
+// ── Sample data ───────────────────────────────────────────────────────────────
+
+const SPARKLINE_DATA = [10, 25, 15, 30, 20, 35, 28]
+
+const CODE_JSON = JSON.stringify(
+ { status: 'ok', version: '2.4.1', routes: 42 },
+ null,
+ 2,
+)
+
+// ── PrimitivesSection ─────────────────────────────────────────────────────────
+
+export function PrimitivesSection() {
+ // Alert state
+ const [alertDismissed, setAlertDismissed] = useState(false)
+
+ // Checkbox state
+ const [checked1, setChecked1] = useState(false)
+ const [checked2, setChecked2] = useState(true)
+
+ // Toggle state
+ const [toggleOn, setToggleOn] = useState(true)
+ const [toggleOff, setToggleOff] = useState(false)
+
+ // Radio state
+ const [radioV, setRadioV] = useState('option-a')
+ const [radioH, setRadioH] = useState('beta')
+
+ // Pagination state
+ const [page, setPage] = useState(5)
+
+ // DateTimePicker state
+ const [dtValue, setDtValue] = useState(new Date('2026-03-18T09:00'))
+
+ // DateRangePicker state
+ const [dateRange, setDateRange] = useState({
+ start: new Date('2026-03-11T00:00'),
+ end: new Date('2026-03-18T23:59'),
+ })
+
+ return (
+
+ Primitives
+
+ {/* 1. Alert */}
+
+
+
This is an informational message.
+
Operation completed successfully.
+
This action may have side effects.
+ {!alertDismissed && (
+
setAlertDismissed(true)}
+ >
+ Something went wrong. Dismiss to clear.
+
+ )}
+ {alertDismissed && (
+
setAlertDismissed(false)}>
+ Reset dismissed alert
+
+ )}
+
+
+
+ {/* 2. Avatar */}
+
+
+
+
+
+
+ {/* 3. Badge */}
+
+
+
+
+
+
+
+
+
+
+ {/* 4. Button */}
+
+
+
+ Primary
+ Secondary
+ Danger
+ Ghost
+
+
+ Small
+ Medium
+ Loading
+
+
+
+
+ {/* 5. Card */}
+
+ Plain card
+ Amber accent
+ Success accent
+ Error accent
+
+
+ {/* 6. Checkbox */}
+
+ setChecked1(e.target.checked)}
+ />
+ setChecked2(e.target.checked)}
+ />
+
+
+
+
+ {/* 7. CodeBlock */}
+
+
+
+
+
+
+ {/* 8. Collapsible */}
+
+
+
+ Hidden content revealed on expand.
+
+
+ This content is visible from the start.
+
+
+
+
+ {/* 9. DateTimePicker */}
+
+ d && setDtValue(d)}
+ />
+
+
+ {/* 10. DateRangePicker */}
+
+
+
+
+
+
+ {/* 11. EmptyState */}
+
+ 📭}
+ title="No results found"
+ description="Try adjusting your filters or search query."
+ action={Clear filters }
+ />
+
+
+ {/* 12. FilterPill */}
+
+
+
+
+
+
+
+ {/* 13. FormField */}
+
+
+
+
+
+
+
+
+
+
+
+ {/* 14. InfoCallout */}
+
+
+ Review before publishing.
+ Deployment completed.
+ Rate limit approaching.
+ Build failed — check logs.
+
+
+
+ {/* 15. Input */}
+
+
+
+
+
+ {/* 16. KeyboardHint */}
+
+
+
+
+
+
+ {/* 17. Label */}
+
+ Plain label
+ Required label
+
+
+ {/* 18. MonoText */}
+
+ xs: route-id-001
+ sm: route-id-001
+ md: route-id-001
+
+
+ {/* 19. Pagination */}
+
+
+ {/* 20. ProgressBar */}
+
+
+
+
+ {/* 21. Radio */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 22. SectionHeader */}
+
+
+ {/* 23. Select */}
+
+
+
+
+ {/* 24. Skeleton */}
+
+
+
+
+
+
+
+
+ {/* 25. Sparkline */}
+
+
+
+
+
+
+ {/* 26. Spinner */}
+
+
+
+
+
+
+ {/* 27. StatCard */}
+
+
+
+
+
+
+ {/* 28. StatusDot */}
+
+ {(['live', 'stale', 'dead', 'success', 'warning', 'error', 'running'] as const).map(
+ (v) => (
+
+
+ {v}
+
+ ),
+ )}
+
+
+ live + pulse
+
+
+
+ {/* 29. Tag */}
+
+
+
+
+
+
+ undefined} />
+
+
+ {/* 30. Textarea */}
+
+
+
+
+ {/* 31. Toggle */}
+
+ setToggleOn(e.target.checked)}
+ />
+ setToggleOff(e.target.checked)}
+ />
+
+
+
+
+ {/* 32. Tooltip */}
+
+
+ Top
+
+
+ Bottom
+
+
+ Left
+
+
+ Right
+
+
+
+ )
+}