Components & UI kit
The dashboard’s UI lives under src/components. It is organized into three layers:
- UI kit — low-level, reusable primitives (
src/components/ui). - Feature modules — domain-specific component groups (e.g.
product-editor,features,behaviors,intent). - Mobile system — a parallel set of mobile screens and primitives
(
src/components/mobile) that share the desktop data layer.
Styling convention
- Tailwind CSS v4 (
@tailwindcss/postcss) is the styling system. - Class composition goes through a single helper,
cn(), insrc/lib/utils.ts— it mergesclsxconditional classes withtailwind-mergeso conflicting Tailwind utilities resolve predictably.
import { cn } from '@/lib/utils'
<button className={cn('px-3 py-2', isActive && 'bg-blue-600', className)} />New components should compose classes with cn() rather than string
concatenation, and lean on the UI kit primitives below instead of re-styling raw
elements.
UI kit (@/components/ui)
The kit exposes a typed barrel (src/components/ui/index.ts) plus additional
primitives imported by path. Barrel exports ship with their prop types:
| Component | Export | Purpose |
|---|---|---|
Button | Button, ButtonProps | Primary action button |
Input | Input, InputProps | Text input |
Card | Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter (+ prop types) | Content card family |
Badge | Badge, BadgeProps | Status / label pill |
Avatar | Avatar, AvatarProps | User / entity avatar |
Divider | Divider | Horizontal rule |
Logo | Logo | Box / brand mark |
RefreshButton | RefreshButton, RefreshButtonProps | Refetch trigger |
TableEmptyState | TableEmptyState, TableEmptyStateProps | Empty-table placeholder |
Additional primitives in the same directory (imported by path, e.g.
@/components/ui/data-table):
| Area | Primitives |
|---|---|
| Tables | table, data-table, table-pagination, table-empty-state |
| Overlays | dialog, alert-dialog |
| Forms | select, currency-input, pricing-fields, date-range-filter, collection-picker, form-stepper-panel, settings-field-renderer |
| Media | sortable-gallery, image-upload, image-upload-modal |
| Feedback | skeleton, page-skeletons, boxes-loader, use-toast, inline-delete-button |
| Layout | separator, divider, tab-nav |
Feature modules
Domain UI is grouped into feature folders under src/components. Each owns the
composite components for a slice of the dashboard:
| Module | Responsibility |
|---|---|
product-editor | The multi-step product/catalog editor (sections, sidebar, mobile variant) |
features | Feature-toggle forms and capability configuration |
behaviors | Product behavior configuration (subscribable, bookable, gating, etc.) |
intent | Business-intent selection and switching |
customer-editor / customers | Customer create/edit and CRM views |
invoice-editor | Invoice builder |
plan-editor | Subscription plan builder |
discount-editor | Discount builder |
collection-editor | Collection builder |
template-editor | Storefront template editor |
editor-shared | Shared editor scaffolding |
storefront-preview / preview-layout | Live storefront preview surfaces |
command-palette | Global command palette overlay |
analytics | Analytics dashboards |
auth | Auth flow components |
dashboard | Home / “Today” widgets and empty states |
shared | Cross-feature helpers |
Top-level shared components include the desktop sidebar.tsx
(which builds intent-driven navigation), workspace-switcher, feedback-widget,
dynamic-form, and the mobile-bottom-nav.
Mobile system
The mobile experience is delivered on the same routes as desktop (see
App architecture).
It has its own component system under src/components/mobile:
- Screens (
mobile/screens/*) — one mobile body per route, rendered inside amd:hiddenwrapper while the desktop body stays inhidden md:block. - Primitives (
mobile/primitives/*) —bottom-sheet,chrome,layout,inputs,inline,progressive-list,sticky-cta-bar,intent-switcher,domain. - Chrome —
MobileShell,mobile-nav-bar,nav-group, mounted from the dashboard layout on small viewports.
The mobile screens follow a strict visual contract: each screen must match
its mockup in mobile-redesign-mockups.tsx, use only mobile/primitives/*, and
use theme tokens (tokens.* / lightTokens.* or var(--mb-*)) — never
hardcoded hex. The accent is always #116DFF (Potter brand). See the dashboard
repo’s AGENTS.md for the full mobile contract.