Default
Sits flat on the surface. Border picks up primary/50 on hover.
Foundations and components, laid out in reading order. Every swatch, font, badge, button, alert, card, tab, stat, and table the system ships — on one page, in dark mode, without clicking around.
pnpm add @seaowl/web
# or
npm install @seaowl/web// Once in your app's root layout
import "@seaowl/web/styles";
import { Button } from "@seaowl/web";<Header title="SEAOWL" subtitle="// design system" year="2026" /><Header
title="SEAOWL"
titleHref="/"
subtitle="// component gallery"
year="2026"
rightContent={<Badge variant="success">live</Badge>}
/><Badge>Default</Badge>
<Badge variant="success">Success</Badge>
<Badge variant="warning">Warning</Badge>
<Badge variant="destructive">Error</Badge>
<Badge variant="outline">Outline</Badge>
<Badge variant="active">Active</Badge>
<Badge variant="tech">typescript</Badge>
<Badge variant="tech">radix-ui</Badge>
<Badge variant="tech">tailwind v4</Badge><Status status="online" label="Online" />
<Status status="offline" label="Offline" />
<Status status="warning" label="Warning" />
<Status status="capturing" />idle
2px cyan ring, 3px halo
Invalid email address
<Input
label="Default"
placeholder="Enter value"
hint="idle"
/>
<Input
label="Focused"
placeholder="search query"
hint="2px cyan ring, 3px halo"
defaultValue="search query"
/>
<Input
label="Error"
defaultValue="bad@"
error="Invalid email address"
/><label><Checkbox defaultChecked /> Remember me</label>
<label><Checkbox /> Send me emails</label><label><Switch defaultChecked /> <Label>Dark mode</Label></label>
<label><Switch /> <Label>Notifications</Label></label><Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="[email protected]" /><Alert variant="info">
<Alert.Title>INFO</Alert.Title>
<Alert.Description>Components honour prefers-reduced-motion.</Alert.Description>
</Alert>
<Alert variant="success">
<Alert.Title>BUILD COMPLETE</Alert.Title>
<Alert.Description>dist/styles/index.css written — under 40 KB.</Alert.Description>
</Alert>
<Alert variant="warning">
<Alert.Title>DEPRECATION</Alert.Title>
<Alert.Description>Button prop 'kind' renamed to 'variant' in v1.0.</Alert.Description>
</Alert>
<Alert variant="destructive">
<Alert.Title>CONNECTION LOST</Alert.Title>
<Alert.Description>Could not reach the capture host. Retrying in 4s.</Alert.Description>
</Alert>Sits flat on the surface. Border picks up primary/50 on hover.
Lifted 2px with a cyan-tinted cast shadow. For popovers and modal chrome.
Primary/30 border with a persistent cyan halo. Reserved for one-at-a-time highlights.
<Card>
<Card.Header><Card.Title>Default</Card.Title></Card.Header>
<Card.Content>
<Card.Description>Sits flat on the surface. Border picks up primary/50 on hover.</Card.Description>
</Card.Content>
</Card>
<Card variant="elevated">
<Card.Header><Card.Title>Elevated</Card.Title></Card.Header>
<Card.Content>
<Card.Description>Lifted 2px with a cyan-tinted cast shadow. For popovers and modal chrome.</Card.Description>
</Card.Content>
</Card>
<Card variant="featured">
<Card.Header>
<Badge variant="success">NEW</Badge>
<Card.Title>Featured</Card.Title>
</Card.Header>
<Card.Content>
<Card.Description>Primary/30 border with a persistent cyan halo. Reserved for one-at-a-time highlights.</Card.Description>
</Card.Content>
</Card>Tabs use mono labels, and the active state glows.
<Tabs defaultValue="overview">
<Tabs.List>
<Tabs.Trigger value="overview">OVERVIEW</Tabs.Trigger>
<Tabs.Trigger value="usage">USAGE</Tabs.Trigger>
<Tabs.Trigger value="props">PROPS</Tabs.Trigger>
<Tabs.Trigger value="examples">EXAMPLES</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="overview">…</Tabs.Content>
<Tabs.Content value="usage">…</Tabs.Content>
<Tabs.Content value="props">…</Tabs.Content>
<Tabs.Content value="examples">…</Tabs.Content>
</Tabs><Progress value={72} />
<Progress value={34} /><Stat value="26" label="components" trend="up" trendValue="+3" />
<Stat value="40+" label="radix prims" trend="neutral" trendValue="— stable" />
<Stat value="4.5:1" label="min contrast" trend="up" trendValue="AA" />
<Stat value="44px" label="min target" trend="neutral" trendValue="touch" />| Prop | Type | Default | Description |
|---|---|---|---|
| variant | string | "default" | default · outline · ghost · seaowl · destructive · link |
| size | string | "default" | sm · default · lg · icon |
| asChild | boolean | false | Merge props onto child (for <a>, Next <Link>, etc.) |
| disabled | boolean | false | Disables the button. |
<Table>
<Table.Header>
<Table.Row>
<Table.Head>Prop</Table.Head>
<Table.Head>Type</Table.Head>
<Table.Head>Default</Table.Head>
<Table.Head>Description</Table.Head>
</Table.Row>
</Table.Header>
<Table.Body>
{rows.map((r) => (
<Table.Row key={r.prop}>
<Table.Cell>{r.prop}</Table.Cell>
<Table.Cell><code>{r.type}</code></Table.Cell>
<Table.Cell><code>{r.dflt}</code></Table.Cell>
<Table.Cell>{r.desc}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table>const items = [
{ year: "2026", count: 27, label: "components" },
{ year: "2025", count: 3, label: "sites" },
{ year: "2024", count: 1, label: "tokens drafted" },
];
<Timeline items={items} activeYear="2026" /><Accordion type="single" collapsible defaultValue="item-1">
<Accordion.Item value="item-1">
<Accordion.Trigger>What ships with @seaowl/web?</Accordion.Trigger>
<Accordion.Content>26 components, 40+ Radix primitives, OKLCH tokens.</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="item-2">
<Accordion.Trigger>Does it support light mode?</Accordion.Trigger>
<Accordion.Content>Tokens exist for completeness; the product lives in dark.</Accordion.Content>
</Accordion.Item>
</Accordion><Dialog>
<Dialog.Trigger asChild>
<Button variant="seaowl">Open dialog</Button>
</Dialog.Trigger>
<Dialog.Content>
<Dialog.Header>
<Dialog.Title>Confirm rollout</Dialog.Title>
<Dialog.Description>This will deploy v2.4 to production. Proceed?</Dialog.Description>
</Dialog.Header>
<Dialog.Footer>
<Button variant="ghost">Cancel</Button>
<Button variant="destructive">Deploy</Button>
</Dialog.Footer>
</Dialog.Content>
</Dialog><Tooltip.Provider>
<Tooltip>
<Tooltip.Trigger asChild>
<Button variant="outline">Hover me</Button>
</Tooltip.Trigger>
<Tooltip.Content>Brand-cyan tinted overlay</Tooltip.Content>
</Tooltip>
</Tooltip.Provider><Select defaultValue="dark">
<Select.Trigger>
<Select.Value placeholder="Pick a theme" />
</Select.Trigger>
<Select.Content>
<Select.Item value="dark">SeaOwl Dark</Select.Item>
<Select.Item value="light">SeaOwl Light</Select.Item>
<Select.Item value="hi-contrast">High Contrast</Select.Item>
</Select.Content>
</Select><Label htmlFor="release-notes">Release notes</Label>
<Textarea id="release-notes" placeholder="What changed in this build?" rows={4} /><RadioGroup defaultValue="oklch">
<div><RadioGroup.Item value="oklch" id="oklch" /> <Label htmlFor="oklch">OKLCH</Label></div>
<div><RadioGroup.Item value="hsl" id="hsl" /> <Label htmlFor="hsl">HSL</Label></div>
<div><RadioGroup.Item value="hex" id="hex" /> <Label htmlFor="hex">Hex</Label></div>
</RadioGroup><Label>Glow intensity</Label>
<Slider defaultValue={[60]} max={100} step={5} /><Separator />
<Separator orientation="vertical" /><Divider />
<Divider variant="gradient" />
<Divider variant="dashed" label="section" /><Skeleton className="h-6 w-3/4" />
<Skeleton className="h-4 w-1/2" />
<Skeleton className="h-4 w-2/3" /><TechStack
technologies={["React 19", "TypeScript", "Tailwind v4", "Radix UI", "OKLCH", "Vitest"]}
/>// Import the compiled CSS once in your app root.
import "@seaowl/web/styles.css";
import { Button, Alert } from "@seaowl/web";
export default function App() {
return (
<Button variant="seaowl">Get started</Button>
);
}