@vector-stack-usa/ui
Five products. One identity. Zero drift.
The shared visual language behind Nexus360, AI, Storefront, Repricer and Rentals. Tokens, primitives, compositions — built once, adopted everywhere.
Size scale
Canonical dimensions for v0.6.0+. Bump touches every consumer.
| Component | Dimensions |
|---|---|
| Standard size | 24px (h-6) · text-[11px] · rounded-[4px] — Button, Input, Select, StatusChip default |
| Button | h-6 · text-[11px] · px-2.5 · gap-1 · rounded-[4px] · font-semibold |
| Input | h-6 · text-[11px] · px-2 — matches Button. No size variants. |
| Select | h-6 · text-[11px] · pl-2 pr-7 — matches Input. No size variants. |
| StatusChip sm (default) | h-6 · text-[11px] · rounded-[4px] |
| StatusChip xs | h-5 · text-[10px] · rounded-[3px] — only component with a compact variant |
| Spinner | h-3 w-3 (12px) · border-[1.5px] · animate-spin |
| Progress track | h-[9px] · rounded-[3px] · 1px solid border, recess inset shadow, outer halo glow in tone color (only when filled) |
| Typography | Geist (display + body) · Geist Mono (code) · loaded via next/font, exposed as --vs-font-display/body/mono |
Palette
Live tokens from colors.css. Click any swatch to edit it with HSL sliders — changes apply instantly to every component on this page and persist in localStorage. Use 'Reset to default' to revert. Toggle dark mode to edit the dark-theme value of the same token independently.
Input
3 sizes (sm / md default / lg) × states. Always renders inside a flex-col wrapper with optional label + helper/error.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | ReactNode | — | Visible label above (stacked) or beside (inline) the input. |
| helper | ReactNode | — | Helper text below. Replaced by `error` when set. |
| error | ReactNode | — | Error message — switches the input to critical styling. |
| leadingIcon | ReactNode | — | Icon rendered inside the input on the left. |
| trailingIcon | ReactNode | — | Icon rendered inside the input on the right. |
| prefix | ReactNode | — | Static text before the value (e.g. "$"). |
| suffix | ReactNode | — | Static text after the value (e.g. ".com"). |
| layout | 'stacked' | 'inline' | 'stacked' | `inline` puts the label left-of-field for compose-style rows. |
| labelWidth | number | string | 'auto' | When `layout="inline"`, locks the label column width so rows align. |
| reserveHelperSpace | boolean | — | Reserve fixed height for helper/error so layout does not jitter on toggle. Default: from `<Form>`. |
Checkbox
Custom box matching primary. States: default / checked / disabled / error.
Radio
Group via shared `name`. Pick exactly one.
Toggle
32×16 pill switch. Use for binary on/off (auto-reprice, dry-run, sandbox).
Select
Native <select> wrapped to match Input. Single 24px size.
| Prop | Type | Default | Description |
|---|---|---|---|
| label | ReactNode | — | Label above (stacked) or beside (inline) the control. |
| helper | ReactNode | — | Helper text below. |
| error | ReactNode | — | Error message — replaces helper and tints the border. |
| layout | 'stacked' | 'inline' | 'stacked' | Inline puts the label left-of-field. Same semantics as Input. |
| labelWidth | number | string | 'auto' | When `layout="inline"`, fixes the label column width. |
| reserveHelperSpace | boolean | — | Reserve fixed height for helper/error. Default: from `<Form>`. |
Field
Generic label + helper/error wrapper. Use it around anything that isn't already an Input/Select (e.g. a Checkbox group, a custom date control).
Form layout primitives
NEW · v0.51Form / FormSection / FormRow / FormGrid / FormActions / FilterBar. Impose consistent rhythm + alignment without changing how individual fields render. Inside <Form>, every field reserves a fixed-height helper line so layout doesn't jump on validation toggle.
Title (text input) absorbs the leftover space; Due date (DatePicker, ~14rem) and Assign to (Select, max-option width) sit at their natural sizes. Powered by data-vs-flex="grow" on text-style field wrappers.
Card
4 variants — default / muted / compact / compactMuted. Just a div with border + shadow.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | 'default' | 'muted' | 'compact' | 'compactMuted' | 'default' | Visual surface preset. `compact*` reduce the inset padding. `*muted` swaps to panel-muted background. |
| className | string | — | Extends the variant classes. |
StatusChip
6 tones × 2 sizes (sm default, xs compact) × with/without dot. Use for inline status labels and table cells.
TagWithInfo
Hover any chip below to reveal popup. Harvested from repricer table tags.
Tabs
UPDATED · v0.78Controlled, uncontrolled, or anchor-based for SSR. Use to break long pages without losing the visual identity. Mix-and-match `href` per item — items with `href` render as <a>, items without keep <button>.
Overview panel — the active tab is overview.
Avatar
Image OR initials (auto from name, color hashed). 4 sizes — xs / sm / md / lg. Pass `colorSeed` to keep the color stable when the display name changes (e.g. seed by email or user-id).
Logo
UPDATED · v0.92Fixed-size slot for a brand mark — square (favicons / app tiles / identity) or wide (3.5:1 navbar / hero wordmarks). Sizes match the surfaces they're named for (xs/sm/md/lg). Robust placeholders cover every empty state: initial letter, generic image-off icon, deterministic color seed, skeleton while loading, automatic fallback when the image URL fails.


fallback="none" renders an empty slot (the smaller box on the right).Spinner
Indeterminate "still working" indicator. Single size (12px). For determinate % use Progress.
Progress
Determinate (0–100%) and indeterminate variants. Use determinate when you have a measurable %, indeterminate when you don't (slides infinitely).
Skeleton
3 shapes — line / block / circle. Shimmer animation respects prefers-reduced-motion.
EmptyState
Replaces literal 'No items yet' copy across surfaces.
Tooltip
Lightweight hover/focus tooltip for icon buttons + table cells. For tag-shaped triggers use TagWithInfo.
Pagination
Compact page-number variant with prev/next steppers, ellipsis, and an optional inline page-size selector.
Table primitives
<Table> + <TableHead/Body/Row/Cell/HeaderCell> — same look every consumer was already hand-rolling.
| SKU | Title | Status | Price | |
|---|---|---|---|---|
| ZX-000005936 | Under Armour Blitzing cap | positive | $22.99 | |
| ZX-000007112 | Nike Pro shorts | caution | $34.50 | |
| ZX-000009401 | Adidas Originals trefoil tee | critical | $48.00 |
CountUp
Animates a number from 0 (or `from`) to `value` over `duration` ms with easeOutCubic. Re-animates when value changes.
InfoCard
Bigger sibling of AppTile — full feature/integration card with icon-box, title, description, optional badge. 7 tones (same as AppTile).
Auto-fill listings from supplier data with the dual-branch dual-model pipeline.
Match the buy box, undercut by a configurable amount, or follow lowest offer — across multi-marketplace accounts.
Listings, orders, inventory across Amazon / eBay / Walmart / TikTok Shop in one console.
Qwen3 + Mistral on-prem, 12-month phased plan from teacher → distillation → per-task LoRA.
TC52 + Qualcomm devices for warehouse pick/pack flows. Gradle build, signed releases.
Self-hosted SearXNG over WireGuard for the AI stack's web-grounding tool.
PageState
Bigger empty/error/loading/success layouts for full-page contexts. Use as the entire main when there's nothing else to render.
No listings yet
Connect an Amazon SP-API token and we'll prefill your first 100 listings automatically.
- SP-API tokens with seller-scope are required.
- The repricer will run a dry-run on import.
- Skip the connection if you only want to manage listings manually.
Couldn't load
Network error · code 502
Generating report…
This usually takes 10–20s.
All set
Your store is connected and syncing.
DiffViewer
Line-by-line text diff. Tone-tinted bg for added (positive) and removed (critical) lines. Unified or split layout.
11 workers:22 - amazon-listings33 - amazon-orders4+ - amazon-feed45 - keepa56 repricer:6− default_strategy: buy_box7+ default_strategy: lowest_offer78 undercut_pct: 0.59+ cooldown_seconds: 60
1{ "tone": "primary",2 "size": "md",3 "variant": "solid" }
1{ "tone": "primary", 2 "size": "sm",3 "variant": "ghost",4 "halo": true }
Alert
Full-width inline banner. Different from Toast — sits in the document flow, persists, and can carry actions. 5 tones with default icons; supports a dismiss button.
InlineEdit
Click a value to edit it inline. Enter or check button to commit, Esc or × to cancel.
TagInput
Input that turns Enter / comma into a tag chip. Backspace on empty input removes the last tag. Paste comma-separated text to bulk-add.
Splitter
Two resizable panes separated by a draggable divider. Hover the divider to see the resize cursor.
Drag the divider on the left to resize. Min 15%, max 85%.
SegmentedControl
Pill-style single-select toggle group. Use for view-mode switchers (Day / Week / Month), filter scopes — anywhere there are 2–5 mutually-exclusive options.
ColorSwatchPicker
NEW · v0.76BETARadio group rendered as colored swatches. Use for tag-color pickers, account-color pickers, anywhere the user picks one of a curated palette. Real <input type=radio> under the hood — keyboard nav and form submission work natively.
emeraldRating
Star rating. Click to set, hover to preview, supports half-stars via step={0.5}. Read-only mode for displaying averages.
Tree
Hierarchical list with expandable folder nodes. Use for category navigation, file explorers, taxonomy.
CalendarHeatmap
GitHub-style activity grid. 7 rows (days of week, Sun→Sat) × N columns (weeks). Cell intensity bucketed by max value in the data.
NumberInput
Numeric field with −/+ steppers + format/parse hooks for currency, percent, etc. Default width is 'auto' (sizes to content with a sensible minimum); pass width='full' to align edges with sibling fields inside FormGrid.
InputGroup
Wrap an Input/NumberInput with text or button addons on either side. Useful for $ prefix, .com suffix, http:// prefix, /h suffix, etc.
MetaList
Definition-list-style label:value pairs for 'Order details', 'Account info', etc. Two layouts.
- SKU
- ZX-000005936
- ASIN
- B07ABC1234
- Brand
- Under Armour
- Marketplace
- United States
- Status
- LiveRepricer last ran 3 min ago
- Stock
- 47
- Order ID
- ord_3rABC123XYZ
- Customer
- Alice Andersonalice@example.com
- Total
- $124.50
- Placed
- 2 minutes ago
FileUpload
Drag-and-drop dropzone + click-to-select. Lists picked files with per-file remove buttons.
ImageGalleryViewer
Read-only photo grid → full-bleed dark lightbox. Click any thumb to open. Keyboard ← → Esc · scroll-wheel zoom · click-drag pan when zoomed · mobile swipe + double-tap zoom. Mono inline order badge replaces the 'primary' chip — slot #1 IS the primary.
ImageGalleryEditor
Upload (click + drag-drop + paste-from-clipboard), reorder via drag, set primary by promoting to slot #1 OR via primaryId prop, delete, optional crop/rotate/replace, per-tile progress + error. Two layouts: `grid` (default) for editing surfaces, `hero` for catalog primary-photo flows (large preview + thumb strip + arrows). Videos are first-class via `kind:'video'` — they always sort to the end and can never be primary.
CommandPalette
Cmd+K-style modal with fuzzy search across a flat command list. Esc + click-outside dismiss; ↑/↓/Enter to navigate.
Slider
UPDATED · v0.49Numeric range input. Single value or range, optional drag tooltip, labeled marks, tone-colored fill, painted track segments.
PinInput
Single-character cells for OTPs / verification codes. Auto-advances on type, Backspace moves back, paste distributes across cells.
CodeBlock
Pre/code container with mono font + recess shadow + inline copy button. Optional language label, title, line numbers, max height.
npm install @vector-stack-usa/ui
import '@vector-stack-usa/ui/colors.css';1import { Button, ToastProvider, useToast } from '@vector-stack-usa/ui';2 3export default function App() {4 const toast = useToast();5 return (6 <ToastProvider>7 <Button onClick={() => toast.push({ tone: 'positive', title: 'Saved' })}>Save</Button>8 </ToastProvider>9 );10}{
"name": "@vector-stack-usa/ui",
"version": "0.25.1"
}MarkdownRenderer
NEW · v0.54react-markdown + remark-gfm with every element styled to DS tokens. Headings, links, code, blockquotes, lists, tables, task list checkboxes use --vs-* colors and the package size scale. Safe-by-default (no raw HTML).
Repricer rule digest
This rule kicks in when buy-box price drops more than 5% in a 24h window. Affects ~14% of listings (mostly in home & kitchen).
Triggers
- Price delta > 5%
- Stock > 0
- Marketplace ∈
{Amazon US, Walmart}
Status
| asin | rule | last run |
|---|---|---|
| B07F9... | Match buy-box | ✅ 2 min ago |
| B08K3... | Floor + 2% | ⚠️ skipped |
| B07Y2... | Match buy-box | ❌ failed |
Tasks
- Audit ASINs flagged before 2026-04-01
- Sample 10 listings against the buy-box winner snapshot
- Re-run failed listings after the 14:00 sweep
- Email summary to ops@nexus360.us
When you change a rule's floor, the next sweep re-validates every listing in scope. ~3 min for 1k items.
See the runbook and docs/repricer-rules.md for the full schema.
import { useEffect } from 'react';
import { runRepricer } from '@/lib/repricer';
useEffect(() => {
runRepricer({ marketplace: 'amazon', mode: 'live' });
}, []);Hey — I just saw the order #A2842 arrived broken.
Could you:
- Mark it returned in
/admin/orders - Auto-credit the buyer
- Notify the warehouse
Refs: policy, SLA matrix.
Popover
Click-triggered floating panel. Heavier than Tooltip — supports interactive content like forms and action buttons. Click outside or Esc to dismiss.
Kbd
Typographic visual for keyboard shortcuts.
Stepper
Multi-step progress indicator. Use for wizards, onboarding, multi-step forms. Horizontal default; pass orientation='vertical' for sidebar wizards.
- AccountEmail + password
- 2PlanPick a tier
- 3PaymentCard details
- 4Done
- Connect Amazon
- Sync inventory
- Run pricing rulesAPI token expired
- 4Activate
- Catalog ingested2,400 items
- Embeddings computedQwen3-32B · 2m 14s
- 3Reviewing duplicates38 in queue
- 4Publish
Timeline
UPDATED · v0.47Event list with dots + connector. Vertical (default) for audit logs / activity feeds. Horizontal for status flows / deploy history / order tracking.
- Order placed2m agoCustomer checked out · 3 items · $124.50
- Payment captured2m agoStripe · pi_3R…ABCD
- Picked up by carrier1h agoUPS · tracking 1Z999
- Delayed at hub6h agoWeather event in MEM
- In transitYesterday
- Delivery exception14:32Address unreachable; rescheduling
- 12:42Build8m 14s
- 12:50Test312 passed · 30 failed
- 12:51LintNo issues
- 12:52StageDeploying…
- 12:54SmokeSlow homepage
- —ProdPending approval
- Apr 22Ordered
- Apr 23Picked
- Apr 24In transit
- Out for delivery
- Delivered
Charts (Sparkline · BarChart · LineChart · DonutChart)
SVG-only, no external deps. All inherit tokens + tones from the design system. Hover charts to see values.
StatCard
KPI / metric tile. Label · big number · optional delta with trend arrow · hint line · optional Sparkline. Bottom carries a 2px gradient accent in the tone so a row reads as one data strip. Tints value, icon, and bg in the tone. Pass `href` or `onClick` to make it clickable (hover halo + focus ring).
RunnerConsole
NEW · v0.63Player-style control panel for long-running jobs (training, inference, batch). Status badge + title/subtitle + progress + hardware metric strip (CPU/RAM/GPUs, extensible) + transport controls (start · pause · resume · stop) + live polling indicator. Designed to drive UIs that talk to a backend job and refresh every N ms.
CreatableField
NEW · v0.64Wraps a select-like control (Combobox / Select / MultiSelect / Input) with a tiny + icon in the top-right of the label row. Clicking it opens a modal with one input + Create / Cancel — no inline expanders, no bottom-of-the-card 'Add new' link. Use it for catalog-driven dropdowns where the user occasionally needs to add a new option without leaving the page.
Drawer
Slide-in panel from the side. Use for long-form edits or 'details about a row' without leaving the page. Backdrop + Esc + click-outside dismiss.
Accordion
Collapsible sections list. Single-open by default; pass `multiple` to allow several open at once.
ExpandPanel + ExpandableRow + ExpandedRow
UPDATED · v0.52Inline detail panel under a table row. v0.56 redesign: flush chrome by default (no card-in-card), ExpandedRow recess (panel-muted bg), tone-colored left rail, optional ExpandPanel.Section slots for grouping. Plus everything from v0.52 — slide animation, auto colSpan, Enter/Space, compound API, useExpandable hook.
| SKU | Title | Status | Stock | |
|---|---|---|---|---|
| ZX-000005936 | Under Armour Blitzing cap | positive | 47 | |
| ZX-000007112 | Nike Pro shorts | caution | 6 | |
| ZX-000009401 | Adidas Originals trefoil tee | critical | 0 | |
v0.56: chrome="flush" default (no border, no rounded, transparent — just a 4px tone rail). <ExpandedRow bg="panel-muted"> default — the detail recesses ~5% L below the parent Card so it reads as a sub-level, no chrome battle. <ExpandPanel.Section title> slots for grouping (replaces the old "card per sub-section" pattern). Pass chrome="card" on ExpandPanel for the v0.55 visual.
Dropdown
Click trigger to open. Click outside, Esc, or pick an item to dismiss.
Combobox
UPDATED · v0.77Searchable select with keyboard nav (↑/↓/Enter/Esc). Group keys cluster items under headings.
MultiSelect
Searchable dropdown with multi-selection. Selected items render as primary-tinted pills inside the trigger; click × to remove or backspace on empty search to pop the last one.
Toast
Wrap your app in <ToastProvider>; call useToast().push(…) anywhere. Auto-dismiss in 4s by default.
DatePicker
Single mode and range mode. Range: pick start, hover to preview, then pick end. Click an earlier date during step 2 to restart. Default width is 'auto' (sizes to content); pass width='full' to align edges with sibling Input/Select inside FormGrid.
AppTile + AppGrid
Launcher tile from the hub.vectorstack.us dashboard. Each tile has a colored icon-box (alpha-tinted from one hue) and a label; AppGrid wraps the responsive 2/3/6 column grid.
7 tones available: primary · blue · rose · teal · amber · emerald · violet. Tone gives you bg (alpha 0.10), border (alpha 0.25) and icon (full) from the same accent var, so each app carries its own identity color while every tile uses the same shape and motion.
AuthShell
Split layout for auth pages — left form, right hero. Pass <HeroRibbon> for the canonical signature visual.
Sign in
Enter your email and password to continue.
Modal
Click to open. Backdrop + Esc dismiss are wired by default.
| Prop | Type | Default | Description |
|---|---|---|---|
| open* | boolean | — | Controls visibility. |
| onClose* | () => void | — | Called on Esc, backdrop click, or close button. |
| title | ReactNode | — | Panel header title. |
| subtitle | ReactNode | — | Header subtitle, smaller text under the title. |
| footer | ReactNode | — | Footer slot — typically action buttons. |
| size | 'sm' | 'md' | 'lg' | 'xl' | 'full' | string | 'md' | Width preset or any CSS width. `full` is 95vw. |
| hideCloseButton | boolean | false | Hide the × in the header. |
| disableBackdropClose | boolean | false | Require explicit footer action / close button. |
| disableEscClose | boolean | false | Disable Esc dismiss. |
AppShell + TopNav + Sidebar + Footer + PageHeader
The full layout primitive. Compose: <AppShell topNav={...} sidebar={...} footer={...}>{children}</AppShell>. Sidebar is fixed 240px on desktop, drawer on mobile (controlled by `open`). Showcase mounts the whole thing inside a sized iframe-style box so it doesn't take over the page.
Console
Real-time view of repricing activity across your accounts.
Five components composing one app skeleton: AppShell (structural grid), TopNav + TopNavItem (sticky header), Sidebar + SidebarSection + SidebarItem (240px desktop, drawer mobile via open prop), Footer (links + copy), and PageHeader (eyebrow + title + actions inside the main region).
ActionIcon
NEW · v0.57Compact icon-only button for toolbars + table row actions. No background in idle (just a colored icon), soft tinted bg on hover, focus ring. Set `action` to pick a built-in (save/delete/refresh/etc) — wires the icon, tone, aria-label automatically. Or pass `icon` + `tone` for custom.
ActionTile
NEW · v0.60Icon-only sibling of `ActionIcon` styled like the icon-box from `InfoCard`: tinted bg + border in the action's tone, hover halo with `-translate-y-px` lift, focus ring. Use it when a bare `ActionIcon` feels too small or you want an action that reads as a tile. Acts as a `<button>` (or `<a>` when `href` is set).
PropsPlayground + PropsTable
Pair a live preview with a panel of controls (PropsPlayground) for exploring an API; or document the props formally (PropsTable). Both ship in the DS so the same patterns work in any consumer's docs.
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | 'primary' | 'secondary' | 'outline' | 'subtle' | 'ghost' | 'link' | 'critical' | 'positive' | 'caution' | 'info' | 'primary' | Visual treatment. |
| size | 'xs' | 'sm' | 'icon' | 'sm' | Size preset. icon = h-6 w-6 square. |
| asChild | boolean | false | Render as the single child element (Radix Slot pattern). Use to wrap Next Link. |
| disabled | boolean | — | Native HTML disabled. |
| onClick | (e: MouseEvent) => void | — | Click handler. |
HeroRibbon
SVG-animated ribbon — the signature visual on `nexus360.us`. Three variants: `default` (animated, masked left), `full` (animated, edge-to-edge), `quiet` (static — same paint, no animation). Wrapper MUST be `position: relative` + `overflow: hidden`. Honors `prefers-reduced-motion` automatically.
Default masking — soft-fades the left edge, ribbon pools on the right. Use when you have copy or a form on the left and want the ribbon as backdrop.
No left mask — fills the entire wrapper. Use for full-bleed marketing hero where you don't need to leave room for foreground copy.
Same paint, no animation. Use for screenshots, dashboards, or contexts where the wave would compete with foreground motion.
Run a marketplace, ship orders, repurchase smart.
One platform replacing the ten tools your SMB has been gluing together since 2019.
Foreground copy needs position: relative + a z-index above 0 (the ribbon sits at z-index: 0).
Welcome back to Nexus360
Pick up where you left off.
For full plumbing (responsive collapse, footer, branding), use the <AuthShell> component which already wires this up.
The ribbon was painted for dark wrappers — its highlights are warm whites that vanish on light backgrounds and the multiply blends muddy out. If you need a light hero, swap the ribbon for a static gradient instead.
All wave/strand/shimmer animations short-circuit when the OS reports prefers-reduced-motion: reduce. The ribbon still renders — only the motion is suppressed (matches the visual of variant="quiet"). No prop required.
Pending components
Anything still missing from the catalog.
| Component | Why it's needed |
|---|---|
| Button size="xs"|"icon" rollout audit | repricer: still has a few inline `<button>` elements (rules-section pencil/trash, runs-section show-more, period filter chips) that pre-date the size variants; sweep them once the variants land. Not DS work — consumer adoption. |
Order is suggestion-grade — pick whichever is most used in the surfaces you adopt next.