Components
All components are imported from @nomadahq/sm-ui. Styles require the CSS imports:
js
import '@nomadahq/sm-ui/css'
import '@nomadahq/sm-ui/css/components'Card / CardBody
A bordered, rounded card container.
jsx
import { Card, CardBody } from '@nomadahq/sm-ui'
<Card>
<CardBody>Content here</CardBody>
</Card>
// Accent border (product color highlight)
<Card accent="#7947d1">
<CardBody>Studios-accented card</CardBody>
</Card>Props:
| Prop | Type | Description |
|---|---|---|
accent | string | CSS color for left/top accent border |
className | string | Additional CSS class |
style | object | Inline styles |
Button
jsx
import { Button } from '@nomadahq/sm-ui'
<Button variant="primary" onClick={handleClick}>Save</Button>
<Button variant="secondary">Cancel</Button>
<Button variant="danger" size="sm">Delete</Button>
<Button href="/dashboard">Go to Dashboard</Button>| Prop | Type | Values | Description |
|---|---|---|---|
variant | string | primary, secondary, danger | Visual style. Default: secondary |
size | string | sm, lg | Size modifier |
onClick | function | — | Click handler |
disabled | boolean | — | Disabled state |
href | string | — | Renders as <a> instead of <button> |
Badge / Pill
Inline status labels.
jsx
import { Badge, Pill } from '@nomadahq/sm-ui'
<Badge color="green">Active</Badge>
<Badge color="amber">Pending</Badge>
<Badge color="red">Overdue</Badge>
<Badge color="blue">Studios</Badge>
<Badge color="gray">Draft</Badge>
<Pill color="blue">New</Pill>Colors: green, amber, red, blue, gray
StatCard / Stats
KPI display strip.
jsx
import { Stats, StatCard } from '@nomadahq/sm-ui'
<Stats>
<StatCard label="MRR" value="$45,000" sub="+12% vs last month" />
<StatCard label="Active Clients" value="12" />
<StatCard
label="Pipeline"
value="$280k"
valueColor="var(--blue)"
valueSize="2rem"
/>
</Stats>| Prop | Type | Description |
|---|---|---|
label | string | Metric label (shown above value) |
value | string/number | Main value |
sub | string | Subtext below value |
valueColor | string | CSS color for value |
valueSize | string | CSS font-size for value |
Progress
A simple percentage progress bar. Uses var(--accent) color by default.
jsx
import { Progress } from '@nomadahq/sm-ui'
<Progress value={72} />
<Progress value={45} color="#1fac6a" />Tabs
Horizontal tab navigation.
jsx
import { Tabs } from '@nomadahq/sm-ui'
import { useState } from 'react'
const TABS = [
{ key: 'overview', label: 'Overview' },
{ key: 'engagements', label: 'Engagements' },
{ key: 'invoices', label: 'Invoices' },
]
function MyPage() {
const [tab, setTab] = useState('overview')
return (
<>
<Tabs tabs={TABS} active={tab} onChange={setTab} />
{tab === 'overview' && <Overview />}
{tab === 'engagements' && <Engagements />}
</>
)
}PageHeader
Standardized page header with breadcrumb, title, and action slot.
jsx
import { PageHeader, Button } from '@nomadahq/sm-ui'
<PageHeader
breadcrumb="Clients"
breadcrumbProduct="Studios"
title="Acme Corp"
subtitle="Active client since Jan 2025"
>
<Button variant="primary">New Invoice</Button>
</PageHeader>Table
Data table with optional row click handler.
jsx
import { Table } from '@nomadahq/sm-ui'
<Table
headers={['Name', 'Status', 'MRR']}
rows={companies.map(c => ({
cells: [c.name, <Badge color="green">Active</Badge>, `$${c.monthly_revenue}`]
}))}
onRowClick={(row, index) => navigate(`/clients/${companies[index].id}`)}
empty={<Empty title="No clients yet" message="Add your first client to get started." />}
/>| Prop | Type | Description |
|---|---|---|
headers | string[] | Column header labels |
rows | { cells: ReactNode[] }[] | Table rows — each row has a cells array |
onRowClick | (row, index) => void | Optional click handler |
empty | ReactNode | Shown when rows is empty |
Empty
Empty state with icon, title, and message.
jsx
import { Empty } from '@nomadahq/sm-ui'
<Empty
icon="📋"
title="No candidates yet"
message="Import from Manatal or add candidates manually."
/>Spinner
Full-width centered loading spinner.
jsx
import { Spinner } from '@nomadahq/sm-ui'
{loading && <Spinner />}ScoreRing
SVG ring chart for displaying a 0–100 score (ICP score, health score, etc.).
jsx
import { ScoreRing } from '@nomadahq/sm-ui'
<ScoreRing score={82} label="ICP Score" />Ring fill color uses var(--accent).
