new lab released: S T R A N D ui component library + design language (MIT Licensed)

Strand Design Language

Open Source
STRAND

Design tokens + UI components. Zero-runtime CSS. Ship faster.

Install

npm install @dillingerstaffing/strand @dillingerstaffing/strand-ui

Features

Components 31

Input, display, layout, navigation, and feedback. Every interaction state. Keyboard accessible.

Runtime CSS 0

All tokens are CSS custom properties. No ThemeProvider. No CSS-in-JS. No runtime style computation.

Accessibility AA

WCAG 2.2 AA. Every color pairing passes contrast. WAI-ARIA compliant. Reduced motion supported.

Bundle <50KB

Total library gzipped. Static CSS. Tree-shakeable exports. Framework-agnostic tokens.

Patterns

What are you building?

A form
FormField + Input + Button
A dashboard
Users 12,847
Revenue $94K
Grid + Card + DataReadout
Navigation
Breadcrumb + Tabs
User feedback
Changes saved successfully.
Connection lost. Retrying...
Alert + Toast
Content layout
New
Feature Update
Components now support reduced motion preferences.
Card + Tag + Stack + Button
Setup

Get running in three steps

1

Install

npm install @dillingerstaffing/strand @dillingerstaffing/strand-ui
2

Import CSS

@import '@dillingerstaffing/strand/css/reset.css';
@import '@dillingerstaffing/strand/css/tokens.css';
@import '@dillingerstaffing/strand/css/base.css';
@import '@dillingerstaffing/strand-ui/css/strand-ui.css';

Components are unstyled without these imports.

3

Use

import { Button, Card, Stack } from '@dillingerstaffing/strand-ui';

<Card variant="elevated">
  <Stack gap={4}>
    <Button>Get Started</Button>
  </Stack>
</Card>

Preact: Works natively. No configuration needed.

React: Install preact and alias in your bundler. See framework setup.

Reference

Every component, rendered

Input

Button

Primary action trigger. 4 variants, 3 sizes, loading state.

Sm
Md
Lg
Loading
Disabled
PropTypeDefault
variant"primary" | "secondary" | "ghost" | "danger""primary"
size"sm" | "md" | "lg""md"
loadingbooleanfalse
iconOnlybooleanfalse
fullWidthbooleanfalse
disabledbooleanfalse
<Button variant="primary" size="md">Submit</Button>

Input

Single-line text entry. 5 types, leading/trailing addons, error state.

PropTypeDefault
type"text" | "email" | "password" | "search" | "number""text"
errorbooleanfalse
leadingAddonReactNode-
trailingAddonReactNode-
disabledbooleanfalse
<Input type="email" placeholder="you@company.com" />

Textarea

Multi-line text entry. Auto-resize, character count, error state.

PropTypeDefault
autoResizebooleanfalse
showCountbooleanfalse
maxLengthnumber-
errorbooleanfalse
<Textarea autoResize placeholder="Message" />

Select

Option selection. Native select with custom styling and error state.

PropTypeDefault
optionsSelectOption[][]
valuestring-
errorbooleanfalse
placeholderstring-
<Select options={items} onChange={handleChange} />

Checkbox

Binary toggle for multiple selections. Supports indeterminate state.

PropTypeDefault
checkedbooleanfalse
indeterminatebooleanfalse
labelstring-
disabledbooleanfalse
<Checkbox checked={value} onChange={fn} label="Accept terms" />

Radio

Single selection from a set. Group with shared name attribute.

PropTypeDefault
checkedbooleanfalse
namestring-
valuestring-
labelstring-
<Radio name="plan" value="pro" label="Pro plan" />

Switch

Binary toggle for a single setting. On/off with inline label.

PropTypeDefault
checkedbooleanfalse
labelstring-
onChange(checked: boolean) => void-
<Switch checked={enabled} onChange={setEnabled} label="Notifications" />

Slider

Range value selection with single thumb.

PropTypeDefault
minnumber0
maxnumber100
stepnumber1
valuenumber50
<Slider min={0} max={100} value={vol} onChange={setVol} />

FormField

Wraps any input with label, hint text, error message, and required indicator.

As it appears on your ID.

PropTypeDefault
labelstring-
hintstring-
errorstring-
requiredbooleanfalse
<FormField label="Email" error={err} required>
  <Input type="email" />
</FormField>

Display

Card

Content container. Elevated, outlined, or interactive with hover lift.

Elevated
Outlined
Interactive (hover lifts)
PropTypeDefault
variant"elevated" | "outlined" | "interactive""elevated"
padding"none" | "sm" | "md" | "lg""md"
<Card variant="elevated" padding="lg">...</Card>

Badge

Status dot or count indicator. 5 status colors.

5 99+
PropTypeDefault
variant"dot" | "count""dot"
status"default" | "teal" | "blue" | "amber" | "red""default"
countnumber-
<Badge variant="count" status="red" count={notifications} />

Avatar

User or entity representation. Image, initials fallback. 4 sizes.

PropTypeDefault
srcstring-
initialsstring-
size"sm" | "md" | "lg" | "xl""md"
<Avatar src="/photo.jpg" alt="Jane" size="lg" />

Tag

Categorization label. Solid or outlined, removable, 5 status colors.

Default Teal Blue Amber Red Remove
PropTypeDefault
variant"solid" | "outlined""solid"
status"default" | "teal" | "blue" | "amber" | "red""default"
removablebooleanfalse
<Tag status="blue" removable onRemove={fn}>React</Tag>

Table

Tabular data display. Sortable headers, responsive horizontal scroll.

NameRoleStatus
Jane SmithEngineerActive
Alex ChenDesignerAway
PropTypeDefault
columnsTableColumn[]-
dataT[]-
onSort(key, direction) => void-
<Table columns={cols} data={rows} onSort={handleSort} />

DataReadout

Monospace instrument readout. Overline label + large value display.

Active Users 12,847
Uptime 99.97%
Latency 12ms
PropTypeDefault
labelstring-
valuestring | number-
<DataReadout label="Revenue" value="$94,200" />

Layout

Stack

Flex layout primitive. Vertical or horizontal stacking with gap control.

PropTypeDefault
direction"vertical" | "horizontal""vertical"
gapnumber0
align"start" | "center" | "end" | "stretch""stretch"
wrapbooleanfalse
<Stack direction="horizontal" gap={4} align="center">...</Stack>

Grid

CSS grid layout primitive. Column count and gap control.

PropTypeDefault
columnsnumber1
gapnumber0
<Grid columns={3} gap={6}>...</Grid>

Container

Width constraint. 4 tiers: narrow (640px), default (768px), wide (1024px), full (1280px).

full 1280wide 1024default 768narrow 640
PropTypeDefault
size"narrow" | "default" | "wide" | "full""default"
<Container size="wide">...</Container>

Divider

Visual separator. Horizontal or vertical, with optional label.

PropTypeDefault
direction"horizontal" | "vertical""horizontal"
labelstring-
<Divider label="or" />

Section

Page section with consistent padding rhythm. Standard or hero variant.

Standard
Hero
PropTypeDefault
variant"standard" | "hero""standard"
background"primary" | "elevated" | "recessed""primary"
<Section variant="hero" background="elevated">...</Section>

Navigation

Tabs

Content switching. Full WAI-ARIA tabs with keyboard arrow navigation.

PropTypeDefault
tabsTabItem[]-
activeTabstring-
onChange(id: string) => void-
<Tabs tabs={items} activeTab={current} onChange={setTab} />

Breadcrumb

Hierarchical location indicator with custom separator.

PropTypeDefault
itemsBreadcrumbItem[]-
separatorstring"/"
<Breadcrumb items={[{ label: "Home", href: "/" }, { label: "Page" }]} />

Nav

Site or app navigation bar. Responsive hamburger collapse on mobile.

PropTypeDefault
logoReactNode-
itemsNavItem[]-
actionsReactNode-
<Nav logo={<Logo />} items={navItems}>
  <Button size="sm">Sign In</Button>
</Nav>

Feedback

Toast

Transient notification. 4 statuses. Auto-dismiss. Use with ToastProvider + useToast().

Info: New version available.
Success: Profile saved.
Warning: Low storage.
Error: Save failed.
PropTypeDefault
status"info" | "success" | "warning" | "error""info"
messagestring-
durationnumber (ms)5000
const { toast } = useToast();
toast({ message: "Saved!", status: "success" });

Alert

Persistent inline notification. 4 statuses, optional dismiss.

Tip: You can sort columns by clicking the header.
Changes saved successfully.
PropTypeDefault
status"info" | "success" | "warning" | "error""info"
dismissiblebooleanfalse
onDismiss() => void-
<Alert status="success" dismissible onDismiss={fn}>Saved!</Alert>

Dialog

Modal overlay. Focus trap, escape-to-close, portal rendering, scroll lock.

PropTypeDefault
openbooleanfalse
onClose() => void-
titlestring-
closeOnEscapebooleantrue
<Dialog open={show} onClose={close} title="Confirm">
  Are you sure?
</Dialog>

Tooltip

Contextual hint on hover/focus. 4 positions, configurable delay.

Tooltip text
PropTypeDefault
contentstring-
position"top" | "right" | "bottom" | "left""top"
delaynumber (ms)200
<Tooltip content="More info" position="top">
  <Button>Hover me</Button>
</Tooltip>

Progress

Completion indicator. Bar or ring variant, determinate or indeterminate.

PropTypeDefault
variant"bar" | "ring""bar"
valuenumber (0-100)- (indeterminate)
size"sm" | "md" | "lg""md"
<Progress variant="ring" value={75} />

Spinner

Loading indicator. Thin ring animation with screen reader text.

Loading Loading Loading
PropTypeDefault
size"sm" | "md" | "lg""md"
<Spinner size="md" />

Skeleton

Content placeholder with shimmer animation. Text, rectangle, or circle.

PropTypeDefault
variant"text" | "rectangle" | "circle""text"
widthstring"100%"
heightstring"1em"
<Skeleton variant="text" width="200px" />
<Skeleton variant="circle" width="40px" height="40px" />

The Right Person. The Right Role.

Matches that last, by understanding what actually matters about a role, a team, and a person.