Skip to main content
SEAOWL//
WebIDEsSOONTerminalsSOON
26 components·v2.3.2
SeaOwl products
Web→IDEsSOONTerminalsSOON
Introduction
OverviewInstallationSurfacesBrand & colorTypographyRadii, glow & shadowApp themes
Components
HeaderButtonBadgeStatusInputCheckboxSwitchLabelAlertCardTabs + ProgressProgressStatTableTimelineAccordionDialogTooltipSelectTextareaRadioGroupSliderSeparatorDividerSkeletonTechStackCode block
// ONE PAGE, EVERYTHING

The full kit.

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.

DARK-FIRSTOKLCH TOKENSINTER + SPACE MONOREADABLE
00

Installation#

Install the package, import the compiled CSS, drop components in.
Package
pnpm add @seaowl/web
# or
npm install @seaowl/web
Import
// Once in your app's root layout
import "@seaowl/web/styles";
import { Button } from "@seaowl/web";
01

Surfaces#

Dark-first. Five steps of lightness between 0.08 and 0.18.
background
0.08 .02 240
card
0.12 .025 240
popover
0.14 .025 240
muted
0.16 .02 240
secondary
0.18 .03 240
02

Brand + semantic color#

Brand cyan leads. States and their light complements sit under it.
Brand
primary
0.75 0.16 185
accent
0.78 0.18 185
SeaOwl semantic
seaowl-glow
0.75 0.18 185
seaowl-featured
0.75 0.16 185
seaowl-active
0.72 0.15 155
seaowl-price
0.65 0.16 22
Chart · 5 slots
chart-1
cyan
chart-2
green
chart-3
amber
chart-4
red
chart-5
violet
Interactive · ring, border, input
ring
0.75 0.16 185
border
0.28 0.04 240
input
0.22 0.03 240
Text
foreground
0.92 0.01 240
muted-foreground
0.65 0.02 240
Sidebar · dedicated slot
sidebar
0.10 0.02 240
sidebar-foreground
0.92 0.01 240
sidebar-primary
0.75 0.16 185
sidebar-accent
0.18 0.03 240
sidebar-border
0.28 0.04 240
States
success
0.72 0.15 155
warning
0.78 0.13 90
destructive
0.65 0.16 22
Light complements · text-on-tinted-fill
primary-light
0.90 0.08 185
success-light
0.87 0.11 155
warning-light
0.90 0.10 90
destructive-light
0.80 0.13 22
03

Typography#

Inter body + display. Space Mono for machine-y roles.
Scale
display
The full kit.
44 / 700 / -0.025
h1
Typographic scale
32 / 700 / -0.02
h2
Foundations first
22 / 700 / -0.01
h3
Sub-headers carry weight
17 / 600
body
Body copy stays at 15px with a 1.55 line height.
15 / 400 / 1.55
mono
SEAOWL // v1.2.0
Space Mono · 700
04

Radii, glow & shadow#

Five radii, signature cyan glow, cyan-tinted elevation.
Radius · sm md lg xl full
sm · 4
md · 6
lg · 8
xl · 12
full
Glow · signature treatment
glow-sm
glow
glow-lg
Shadow · cyan-tinted elevation
shadow-xs
shadow-sm
shadow-md
shadow-lg
shadow-xl
//

App themes#

Drop-in re-skins for the apps SeaOwl users already live in. Every theme tracks the brand palette and ships with copy-paste install steps.
kagi
Kagi

Custom CSS for kagi.com — cyan result titles, mono URL breadcrumbs, no widget chrome.

Install →→
firefox
Firefox

userChrome.css that re-skins the browser without touching your extensions or about:config.

Install →→
thunderbird
Thunderbird

Same userChrome treatment for the mail client — folder pane, message list, calendar.

Install →→
gnome
GNOME

GTK 4 + Shell theme. Drop into ~/.config/gtk-4.0/ and toggle in gnome-tweaks.

Install →→
// SOONComing SoonTheme-store submissions in progress. Visit each theme's detail page directly while we wait for review.
05

Header#

source ↗Sticky chrome. Wordmark, mark, and version.

SEAOWL

//// design system
2026
import { Header } from "@seaowl/web";
<Header title="SEAOWL" subtitle="// design system" year="2026" />
SEAOWL//// component gallery
live2026
With link + right content
<Header
  title="SEAOWL"
  titleHref="/"
  subtitle="// component gallery"
  year="2026"
  rightContent={<Badge variant="success">live</Badge>}
/>
06

Button#

source ↗Six variants, four sizes, with/without icons.
Variants
import { Button } from "@seaowl/web";
<Button>Default</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="seaowl">SeaOwl</Button>
<Button variant="destructive">Destructive</Button>
<Button variant="link">Link</Button>
Sizes
<Button size="sm">Small</Button>
<Button>Default</Button>
<Button size="lg">Large</Button>
<Button size="icon" variant="outline" aria-label="Search">
  <Search size={16} />
</Button>
With icons
import { Download, Send, Trash2 } from "lucide-react";
<Button>
  <Download size={16} /> Download
</Button>
<Button variant="seaowl">
  <Send size={16} /> Send
</Button>
<Button variant="destructive">
  <Trash2 size={16} /> Delete
</Button>
07

Badge#

source ↗Rounded pills. Primary, states, outline, tech.
DefaultSuccessWarningErrorOutlineActivetypescriptradix-uitailwind v4
import { Badge } from "@seaowl/web";
<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>
08

Status#

source ↗Dot + label. Online pulses; others stay still.
Online
Offline
Warning
Capturing...
import { Status } from "@seaowl/web";
<Status status="online" label="Online" />
<Status status="offline" label="Offline" />
<Status status="warning" label="Warning" />
<Status status="capturing" />
09

Input + fields#

source ↗Default · focus · error · checkbox · switch.

idle

2px cyan ring, 3px halo

Invalid email address

import { Input } from "@seaowl/web";
<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"
/>
10

Checkbox#

source ↗Boolean selection. Brand-cyan check on a muted square.
import { Checkbox } from "@seaowl/web";
<label><Checkbox defaultChecked /> Remember me</label>
<label><Checkbox /> Send me emails</label>
11

Switch#

source ↗Toggle for binary settings. Slides on flip with a brand-cyan filled track.
import { Switch, Label } from "@seaowl/web";
<label><Switch defaultChecked /> <Label>Dark mode</Label></label>
<label><Switch /> <Label>Notifications</Label></label>
12

Label#

source ↗Form field label. Use htmlFor to bind to a sibling input/textarea/select.
import { Label, Input } from "@seaowl/web";
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="[email protected]" />
13

Alert#

source ↗Info · success (glows) · warning · error.
INFO
Components honour prefers-reduced-motion.
BUILD COMPLETE
dist/styles/index.css written — under 40 KB.
DEPRECATION
Button prop 'kind' renamed to 'variant' in v1.0.
CONNECTION LOST
Could not reach the capture host. Retrying in 4s.
import { Alert } from "@seaowl/web";
<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>
14

Card#

source ↗Default · elevated · featured.

Default

Sits flat on the surface. Border picks up primary/50 on hover.

Elevated

Lifted 2px with a cyan-tinted cast shadow. For popovers and modal chrome.

NEW

Featured

Primary/30 border with a persistent cyan halo. Reserved for one-at-a-time highlights.

import { Card, Badge } from "@seaowl/web";
<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>
15

Tabs#

source ↗Radix tabs with keyboard navigation. List + trigger + content tree.

Tabs use mono labels, and the active state glows.

import { Tabs } from "@seaowl/web";
<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>
16

Progress#

source ↗Determinate progress bars. 0–100, brand-cyan fill on a muted track.
BUILD PROGRESS72%
INDEX34%
import { Progress } from "@seaowl/web";
<Progress value={72} />
<Progress value={34} />
17

Stat#

source ↗Large number + mono label + delta.
26components↑+3
40+radix prims— stable
4.5:1min contrast↑AA
44pxmin targettouch
import { Stat } from "@seaowl/web";
<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" />
18

Table#

source ↗Compact data grid. Mono headers, monochrome rows.
PropTypeDefaultDescription
variantstring"default"default · outline · ghost · seaowl · destructive · link
sizestring"default"sm · default · lg · icon
asChildbooleanfalseMerge props onto child (for <a>, Next <Link>, etc.)
disabledbooleanfalseDisables the button.
import { Table } from "@seaowl/web";
<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>
19

Timeline#

source ↗Vertical rail of milestones. Cyan-rimmed bullets for completed.
  1. 2026
    202627 components
  2. 2025
    20253 sites
  3. 2024
    20241 tokens drafted
import { Timeline } from "@seaowl/web";
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" />
20

Accordion#

source ↗Expand/collapse sections — Radix-backed, single or multiple open.

26 components, 40+ Radix primitives, OKLCH tokens.

import { Accordion } from "@seaowl/web";
<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>
21

Dialog#

source ↗Modal overlay with focus-trap, ESC-to-close, and a built-in close button.
import { Dialog, Button } from "@seaowl/web";
<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>
22

Tooltip#

source ↗Hover affordance for keyboard-accessible hints. Wrap a tree once with TooltipProvider.
import { Tooltip, Button } from "@seaowl/web";
<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>
23

Select#

source ↗Dropdown picker. Radix-backed, full keyboard navigation, portal-rendered menu.
import { Select } from "@seaowl/web";
<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>
24

Textarea#

source ↗Multi-line text field. Pairs with Label for accessible binding.
import { Textarea, Label } from "@seaowl/web";
<Label htmlFor="release-notes">Release notes</Label>
<Textarea id="release-notes" placeholder="What changed in this build?" rows={4} />
25

RadioGroup#

source ↗Single-select among 2+ options. Items bind to a Label via htmlFor.
import { RadioGroup, Label } from "@seaowl/web";
<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>
26

Slider#

source ↗Continuous range input. Supports step, min/max, and multi-thumb.
import { Slider, Label } from "@seaowl/web";
<Label>Glow intensity</Label>
<Slider defaultValue={[60]} max={100} step={5} />
27

Separator#

source ↗Radix-backed semantic divider. Use for accessible region breaks.

import { Separator } from "@seaowl/web";
<Separator />
<Separator orientation="vertical" />
28

Divider#

source ↗Visual divider with default / gradient / dashed variants and an optional label.


section
import { Divider } from "@seaowl/web";
<Divider />
<Divider variant="gradient" />
<Divider variant="dashed" label="section" />
29

Skeleton#

source ↗Loading placeholder with a left-to-right cyan shimmer.
import { Skeleton } from "@seaowl/web";
<Skeleton className="h-6 w-3/4" />
<Skeleton className="h-4 w-1/2" />
<Skeleton className="h-4 w-2/3" />
30

TechStack#

source ↗Grouped tech-name badges. `maxVisible` clamps the row, surplus collapses to a +N badge.
React 19TypeScriptTailwind v4Radix UI+2
import { TechStack } from "@seaowl/web";
<TechStack
  technologies={["React 19", "TypeScript", "Tailwind v4", "Radix UI", "OKLCH", "Vitest"]}
/>
31

Code block#

Space Mono, cyan keywords, muted comments.
// 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>
  );
}

© 2026 Unfuck Cloud by Art Rosnovsky. Made with ❤️ and no fucks given.

SeaOwl v2.3.2 Web and UI components and IDE themes•Local, Human, and Mobile First