Styleguide

A comprehensive design system for building consistent, accessible, and beautiful user interfaces. Built with React, Tailwind CSS, and Base UI.

Foundations

Colors, typography, spacing, and core visual elements

Components

Reusable UI components with multiple variants and states

Guidelines

Accessibility standards, theming, and best practices

Quick Start

Install the UI package and start building

bash
npm install @repo/ui
app/page.tsx
import { Button, Card, Badge } from "@repo/ui";

export default function Page() {
  return (
    <Card>
      <Badge variant="accent">New</Badge>
      <h1>Welcome</h1>
      <Button>Get Started</Button>
    </Card>
  );
}

Colors

A semantic color system with light and dark mode support. Colors are defined as CSS custom properties for easy theming.

Background & Surface

Base colors for layouts and containers

Background

--color-bg

Page background

Surface

--color-surface

Card backgrounds

Surface 2

--color-surface-2

Nested elements

Text

Text colors for hierarchy

Text

--color-text

Primary text

Text Secondary

--color-text-secondary

Secondary text

Text Muted

--color-text-muted

Placeholder, disabled

Border

Border and divider colors

Border

--color-border

Default borders

Border Strong

--color-border-strong

Emphasized borders

Brand

Primary and accent colors

Primary

--color-primary

Primary actions

Accent

--color-accent

Highlights, links

Accent Subtle

--color-accent-subtle

Accent backgrounds

Semantic

Status and feedback colors

Success

--color-success

Success Subtle

--color-success-subtle

Warning

--color-warning

Warning Subtle

--color-warning-subtle

Danger

--color-danger

Danger Subtle

--color-danger-subtle

Info

--color-info

Info Subtle

--color-info-subtle

Usage

css
/* Use CSS variables directly */
.my-element {
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
}

/* Or with Tailwind arbitrary values */
<div className="bg-[var(--color-surface)] text-[var(--color-text)]">
  Content
</div>

Typography

A type scale designed for clarity and hierarchy. Uses Inter for UI text and a monospace font for code.

Type Scale

Predefined text styles for consistent hierarchy

Display 164px / 68px / -0.02em / 800

Display

Heading 148px / 52px / -0.02em / 700

Heading 1

Heading 236px / 40px / -0.01em / 700

Heading 2

Heading 328px / 32px / 0 / 650

Heading 3

Heading 422px / 28px / 0 / 650

Heading 4

Body Large18px / 28px / 0 / 400

Body large text for introductions and emphasis

Body16px / 24px / 0 / 400

Body text for paragraphs and general content

Body Small14px / 20px / 0 / 400

Small body text for captions and metadata

Eyebrow12px / 16px / 0.06em / 600 / Mono

/EYEBROW LABEL

Font Families

Sans (UI)

Inter, system-ui, sans-serif

Mono (Code)

Geist Mono, JetBrains Mono, monospace

Guidelines

• Keep line length between 60-90 characters for readability

• Use heading hierarchy consistently (don't skip levels)

• Reserve ALL CAPS for short labels and eyebrows only

• Use monospace font for code, technical values, and IDs

Spacing

An 8px-based spacing scale for consistent layouts. Use these values for margins, padding, and gaps.

Spacing Scale

Based on 8px increments for visual rhythm

space-0
0px
space-1
4px
space-2
8px
space-3
12px
space-4
16px
space-6
24px
space-8
32px
space-10
40px
space-12
48px
space-16
64px
space-20
80px
space-24
96px

Usage Examples

tsx
// Tailwind classes
<div className="p-4 m-6 gap-3">
  <div className="space-y-4">
    <p>Vertical spacing</p>
  </div>
</div>

// CSS variables
.element {
  padding: var(--space-4);
  margin: var(--space-6);
  gap: var(--space-3);
}

Elevation

Shadow levels for creating depth and visual hierarchy. Use sparingly for a clean, flat aesthetic.

No shadow

Level 0

Default state, use borders

Small

Level 1

Dropdowns, tooltips

--shadow-sm
Medium

Level 2

Modals, dialogs

--shadow-md

Border Radius

Consistent corner rounding for UI elements. Smaller radii for inputs and buttons, larger for cards.

None

0px

Small

4px

Medium

8px

Large

12px

XL

16px

Full

9999px

Usage Guidelines

None/Small (0-4px): Terminal elements, code blocks

Medium (8px): Buttons, inputs, badges

Large (12px): Cards, modals, containers

Full: Avatars, pills, circular buttons

Motion

Animation timing and easing for smooth, purposeful interactions. Motion should provide feedback, not decoration.

Duration

Timing for different interaction types

Fast

Hover, focus states

120ms

Default

Most transitions

200ms

Slow

Complex animations

300ms

Easing

Curves for natural movement

Default

cubic-bezier(0.2, 0, 0, 1)

Exit

cubic-bezier(0.4, 0, 1, 1)

Spring

cubic-bezier(0.34, 1.56, 0.64, 1)

Reduced Motion

All animations respect the prefers-reduced-motion media query. When reduced motion is preferred, animations are disabled or significantly reduced.

css
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

Icons

Using Lucide React icons for consistent, accessible iconography.

Icon Sizes

Standard sizes for different contexts

16px

Inline

20px

Default

24px

Large

32px

Hero

Common Icons

ArrowRight
Check
X
Plus
Minus
Search
Settings
User
Mail
Lock
Eye
EyeOff
Copy
ExternalLink
Menu
LogOut

Buttons

Interactive button components with multiple variants, sizes, and states.

Variants

Different visual styles for different actions

Primary: Main actions, form submissions

Secondary: Alternative actions, cancel buttons

Ghost: Tertiary actions, toolbar buttons

Danger: Destructive actions, delete buttons

Accent: Highlighted promotional actions

Sizes

States

With Icons

Usage

tsx
import { Button } from "@repo/ui/button";

// Basic
<Button>Click me</Button>

// Variants
<Button variant="secondary">Cancel</Button>
<Button variant="danger">Delete</Button>

// Sizes
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>

// With icon
<Button icon={<Star />}>Favorite</Button>
<Button icon={<ArrowRight />} iconPosition="right">Next</Button>

// States
<Button loading>Saving...</Button>
<Button disabled>Unavailable</Button>

Form Inputs

Text inputs, labels, and form controls for user data entry.

Input Variants

This field is required

Textarea

This field is required

Select

Selection is required

Usage

tsx
import { Input } from "@repo/ui/input";
import { Label } from "@repo/ui/label";
import { Textarea } from "@repo/ui/textarea";
import { Select } from "@repo/ui/select";

<div className="space-y-2">
  <Label htmlFor="email">Email</Label>
  <Input
    id="email"
    type="email"
    placeholder="[email protected]"
    icon={<Mail className="h-4 w-4" />}
  />
</div>

<Textarea placeholder="Enter description..." />
<Textarea error placeholder="Required" />

<Select>
  <option value="">Select...</option>
  <option value="admin">Admin</option>
</Select>

// Error state
<Input error placeholder="Invalid" />
<Label error>Error message</Label>

Cards

Container components for grouping related content.

Default Card

Standard card with border

Use for static content grouping.

Interactive Card

Hover for effect

Use for clickable cards and links.

Ghost Card

No background

Use for subtle content grouping.

Badges

Small labels for status, categories, and counts.

Variants

DefaultAccentSuccessWarningDangerOutline

Sizes

SmallMedium

Use Cases

NEWACTIVEBETADEPRECATEDv2.0.0TypeScriptReact

Alerts

Contextual feedback messages with semantic coloring and auto-icons.

Variants

Each variant has an automatic icon. Use the Alert component from @repo/ui/alert.

Connection failed. The endpoint returned a 500 error.
Response time exceeded 30s. Results may be incomplete.
All tests passed. Score: 87.4/100.
New benchmark available. Add it to your configuration.

Usage

tsx
import { Alert } from "@repo/ui/alert";

// Variants: danger, warning, success, info
<Alert variant="danger">Error message</Alert>
<Alert variant="success">Success message</Alert>

// Custom icon override
<Alert variant="info" icon={<CustomIcon />}>
  Custom icon alert
</Alert>

Tables

Data tables with header, body, row hover, and horizontal scroll on mobile.

Example

NameRoleStatusLast Active
AliceAdminActive2 min ago
BobDeveloperAway1 hour ago
CharlieViewerOffline3 days ago

Usage

tsx
import {
  Table, TableHeader, TableBody,
  TableRow, TableHead, TableCell
} from "@repo/ui/table";

<Table>
  <TableHeader>
    <TableRow>
      <TableHead>Column</TableHead>
    </TableRow>
  </TableHeader>
  <TableBody>
    <TableRow>
      <TableCell>Value</TableCell>
    </TableRow>
  </TableBody>
</Table>

// Features:
// • Horizontal scroll wrapper for mobile
// • Bold header with 2px bottom border
// • Row hover highlighting
// • min-width: 640px to prevent column collapse

Toggle Group

Segmented controls for switching between related views or filters.

Interactive Demo

Selected: all

Usage

tsx
import { ToggleGroup } from "@repo/ui/toggle-group";

<ToggleGroup
  options={[
    { value: "all", label: "All" },
    { value: "active", label: "Active" },
    { value: "archived", label: "Archived" },
  ]}
  value={selected}
  onChange={setSelected}
/>

// Uses role="radiogroup" with role="radio" buttons
// Active state: surface-2 background with shadow
// Keyboard: arrow keys to navigate within group

Toasts

Non-blocking notifications that auto-dismiss. Success, error, info, and loading variants.

Live Demo

Click a button to trigger a toast notification.

Usage

tsx
import { useToast } from "@repo/ui/toast";

function MyComponent() {
  const { toast, dismiss, update } = useToast();

  // Show toast (returns ID for programmatic control)
  const id = toast("Changes saved!", "success");
  toast("Something went wrong", "error");
  toast("Processing...", "loading");
  toast("New feature available", "info", 8000); // custom duration

  // Update a loading toast to success
  update(id, "Done!", "success");

  // Dismiss programmatically
  dismiss(id);
}

// Variants: success (4s), error (4s), info (4s), loading (persistent)
// Position: bottom-right, stacked
// Animation: slide-in from right, slide-out on dismiss

Dialogs

Modal dialogs for focused interactions and confirmations.

Features

• Focus trap keeps keyboard navigation inside the dialog

• Escape key closes the dialog

• Click outside to dismiss (backdrop click)

• Animated entrance and exit

• Scroll lock on body when open

• Accessible with proper ARIA attributes

Code Blocks

Syntax-highlighted code display with copy functionality.

Inline Code

Use npm install to install packages. Import components like import { Button } from "@repo/ui".

Code Block with Language

typescript
interface User {
  id: string;
  email: string;
  name: string;
}

async function getUser(id: string): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

Code Block with Filename

components/Button.tsx
export function Button({ children, onClick }) {
  return (
    <button
      onClick={onClick}
      className="px-4 py-2 rounded-lg bg-primary"
    >
      {children}
    </button>
  );
}

Terminal

Terminal-style display for command-line output and examples.

Installation

$ npm create blah-app@latest my-app

Creating a new application...

Installing dependencies...

Project created successfully!

 

$ cd my-app

$ npm run dev

Starting development server...

Local: http://localhost:3000

Feedback

Components for communicating status, errors, and information to users.

Alert Component

Use the Alert component from @repo/ui/alert instead of hand-rolling feedback messages.

This is an informational message for the user.
Your changes have been saved successfully.
Please review your input before continuing.
Something went wrong. Please try again.

Error Message Patterns

Do

  • Say what happened and how to fix it
  • Use specific, actionable language
  • Display field-level errors below inputs
  • Use appropriate variant for severity

Don't

  • Use generic “An error occurred”
  • Show errors before user interaction
  • Use danger variant for informational messages
  • Stack multiple alerts for the same issue

Interaction States

How components communicate their state through visual feedback.

State Layer System

StateVisual TreatmentCSS Approach
EnabledDefault appearance, no overlayBase styles
Hovered8-12% overlay or background shift::after overlay on filled buttons, hover:bg- on others
Focused2px accent ring with 2px offsetbox-shadow: var(--focus-ring)
Pressed10% dark overlay, returns to rest elevation:active::after with rgba(0,0,0,0.1)
Disabled50% opacity, not-allowed cursordisabled:opacity-50
LoadingSpinner replaces icon, no pointer eventspointer-events-none + animate-spin

Live Examples

Button States

Input States

Focus Ring Spec

css
--focus-ring: 0 0 0 2px var(--color-bg), 0 0 0 4px var(--color-accent);

/* Inner ring: 2px, matches background (creates gap) */
/* Outer ring: 4px, accent color (visible indicator) */

/* Applied to all focusable elements via: */
:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

Layout

Responsive breakpoints, grid system, and canonical layout patterns.

Breakpoints

ClassWidthColumnsBehavior
Compact0-639px1Single column, full-width cards, dialog nav
Medium640-1023px2-3Grid layouts, collapsible sidebar
Expanded1024-1599px12Sidebar + main, list-detail views
Large1600px+12Max content width, centered layout

Component Adaptation

How components transform across breakpoints

CompactExpanded
Dialog nav menuPersistent sidebar
Full-width cardsGrid cards (2-3 columns)
Stacked form fieldsSide-by-side fields
Horizontal scroll tablesFull-width tables

Touch Targets

• Minimum touch target: 48x48dp (visual size can be smaller)

• Spacing between targets: at least 8dp

• Interactive elements must support both touch and pointer input

• All interactions must be achievable via keyboard

Accessibility

Guidelines for building inclusive and accessible interfaces.

Keyboard Navigation

KeyAction
TabMove focus to next interactive element
Shift+TabMove focus to previous interactive element
Enter / SpaceActivate button, link, or control
EscapeClose dialog, dismiss toast, cancel action
Arrow keysNavigate within toggle groups, radio groups, menus

Color Contrast

• Normal text: 4.5:1 minimum (WCAG AA)

• Large text (18px+ or 14px bold): 3:1 minimum

• UI components and icons: 3:1 minimum

• Never rely on color alone to convey information

Screen Readers

• Semantic HTML elements used throughout

• ARIA labels for icon-only buttons

• Live regions for dynamic content

• Proper heading hierarchy (never skip levels)

• Meaningful alt text for images

• ARIA landmarks: main, nav, header, footer

Motion

• Respects prefers-reduced-motion

• All durations set to 0ms when reduced motion active

• Functional animations (spinners, scroll) are exempt

• No flashing content (<3 flashes/sec)

Forms

• All inputs have visible Labels (not just placeholder)

• Error messages explain what and how to fix

• Touch targets: 48x48dp minimum

• Support dynamic type / font scaling to 200%

Focus Ring

All focusable elements display a visible focus ring when navigated via keyboard. Tab through these elements to see the ring.

Theming

Light and dark mode support with CSS custom properties.

Theme Toggle

Switch between light and dark modes

Click to toggle theme. Preference is saved to localStorage.

Implementation

tsx
// Theme is controlled via data-theme attribute on <html>
<html data-theme="dark">

// Or use system preference (default)
<html> // No data-theme = follows system

// Toggle theme with JavaScript
function toggleTheme() {
  const current = document.documentElement.getAttribute('data-theme');
  const next = current === 'dark' ? 'light' : 'dark';
  document.documentElement.setAttribute('data-theme', next);
  localStorage.setItem('theme', next);
}

Token Architecture

LayerFilePurpose
Referencepackages/ui/src/tokens.tsSource of truth. JS constants for all raw values.
Semanticpackages/ui/src/styles/tokens.cssCSS custom properties. Light/dark theme definitions.
App Overrideapp/globals.cssApp-level overrides (fonts, typography utilities).

CSS Variables

All colors are defined as CSS custom properties

css
:root, [data-theme="light"] {
  --color-bg: #FFFFFF;
  --color-text: #111111;
  --color-accent: #F5A623;
  /* ...all tokens defined here */
}

[data-theme="dark"] {
  --color-bg: #0B0B0B;
  --color-text: #F2F2F2;
  --color-accent: #F5A623;
  /* Shadows stronger, semantic bgs semi-transparent */
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    /* Dark fallback when no data-theme set */
  }
}

Content Guidelines

Voice, tone, and writing conventions for UI text.

Voice & Tone

Technical

Precise, no hand-waving. Use correct terminology. The audience knows their tools.

Concise

Say it once, say it clearly. No marketing fluff. Every word earns its place.

Direct

Address the user as “you”. Use active voice. Lead with the action.

UI Text Conventions

ElementConventionExample
ButtonsVerb-first, sentence case, 1-3 words“Save changes”, “Delete”
LabelsSentence case, no trailing colon“Email address”, “Client name”
HeadingsSentence case, descriptive“OAuth clients”, “Recent activity”
ErrorsWhat happened + how to fix“Invalid redirect URI. Must be an HTTPS URL.”
Empty statesFriendly + actionable CTA“No clients yet. Create your first OAuth client.”
LoadingBrief, contextual“Loading clients...”
ConfirmationsClear question + specific labels“Delete this client?” / “Delete” / “Cancel”

Tone by Context

Success

Celebratory but brief. “Client created successfully.”

Error

Empathetic, solution-oriented. “Connection failed. Check the URL and retry.”

Instructional

Clear, step-by-step. No jargon beyond what the audience expects.

Warning

Calm, informative. “This action cannot be undone.” Not alarmist.