Unthought UIv0.1.0

Component kit

The building blocks behind Unthought's internal tools. Copy a component in with `npx unthought-ui add <name>` — it lands in your repo as source you own, not a dependency you upgrade.

Tokens

Lifted from the Unthought guide design system so tools, guides and artifacts read as one system. Every colour flips with the theme.

Surfaces & text
Background
--bg
Paper
--paper
Wash
--wash
Wash deep
--wash-deep
Ink
--ink
Body
--body
Muted
--muted
Line
--line
Brand & status
Accent
--accent
Accent soft
--accent-soft
Cyan
--cyan
Success
--success
Warn
--warn
Danger
--danger

Button

Defaults to type=button — an unspecified button inside a form submits it, which is rarely what a toolbar wants.

Variants
<Button variant="primary">Save</Button>
Sizes
States
<Button loading>Saving</Button>  {/* width stays stable */}

Form

Field wires up id, aria-describedby and the invalid state for you — the plumbing people skip when hand-rolling forms.

Field

Lowercase, kebab-case. Becomes the subdomain.

<Field label="Email" error="That address isn't on the allowlist.">
  <Input type="email" defaultValue="[email protected]" />
</Field>

Badge

Tones
NeutralAccentActivePast dueFailed
<Badge tone="success" dot>Active</Badge>

Alert

Danger and warn use role=alert so they interrupt; info and success are read in document order.

Tones
Heads up
Dev login is enabled on this environment.
Migration applied cleanly.

Card

Composed

Zingler Strength

Revenue dashboard

Live
Deployed to crm.zinglerstrength.com from the main branch.

Table

The wrapper scrolls horizontally on its own, so a wide table never makes the page body scroll sideways.

With numeric column
ClientPlanStatusMRR
Zingler StrengthProactive$249
Kavanagh SportsStarteractive$99
LD LandscapingPropast due$249
Collegiate CleanersStarterchurned$0

Empty & loading

EmptyState

No tools yet

Scaffold one from the template and it'll show up here.

Skeleton
Skeleton primitives

Checkbox & Switch

Base UI underneath — the hidden native input, focus handling and indeterminate state are tedious to hand-roll and easy to get subtly wrong.

Checkbox

Records who changed what, on every mutation.

Switch

Turning this off would expose the tool publicly.

Select

Inherits id, aria-describedby and the invalid state from Field, exactly like Input does.

With Field

Dev login only works outside production.

Combobox

A select you can type in. Reach for it once a list is long enough that scanning is slower than typing — roughly a dozen options. Matching covers hidden keywords, so “pacific” finds Los Angeles.

Searchable select

Type to filter — try “pacific” or “EST”, which match on keywords, not the label.

Dialog, Menu & Tooltip

Focus trap, scroll lock, Escape and return-focus come from Base UI. Getting those wrong is what makes an app unusable by keyboard.

Dialog
Dropdown menu
Tooltip

Toast

Errors don't auto-dismiss — if something failed, you need to still be able to read it after looking away.

Types
const toast = useToast();
toast.success("Saved", "Changes are live.");

Tabs & Pagination

Tabs
Deployed from main, healthy for 6 days.
Pagination

5175 of 412

Page 3 of 17

Separator

Optional centred label for the 'or' between two paths.

Variants
or

DataTable

Sort, search, paginate and export in one component — what most internal-tool screens actually are. CSV export writes every filtered row, not just the visible page, and neutralises spreadsheet formula injection on the way out.

Full featured
Actions
Zinglerrevenue-dashboardlive2026-08-04$249
Zinglercrmlive2026-07-28
Kavanaghwebsitelive2026-07-30$99
Unthoughtads-dashboardbuilding2026-08-05
LD Landscapingsms-reminderslive2026-07-19$149

15 of 8

Rows
Page 1 of 2

AppShell

Sidebar + responsive drawer + sticky header, with active-route highlighting. Not previewed inline because it owns the whole viewport — it's what wraps every page in a tool.

Usage

Renders a fixed 15rem sidebar above lg, and a dismissible drawer below it that closes on navigation.

<AppShell
  brand={<Logo />}
  nav={[
    { href: "/", label: "Overview", icon: Home },
    { href: "/clients", label: "Clients", icon: Users, badge: <Badge>4</Badge> },
  ]}
  footer={<UserMenu />}
>
  {children}
</AppShell>