Skip to content

v0.5  ·  Apache 2.0  ·  Zero runtime

Styles that live in TypeScript, but behave like real CSS

Typed variants, design tokens as CSS variables, and predictable class names — without locking you into a proprietary runtime or unreadable hashes in the DOM.

npm i typestyles

No runtime deps Tree-shakable ESM Works with Vite · Next.js · Astro · Rollup

button.ts
import { styles, tokens } from 'typestyles';

const color = tokens.create('color', { accent: '#2563eb' });

const button = styles.component('button', {
  base: {
    border: '1px solid #000',
    transition: 'transform 120ms ease',
  },
  variants: {
    intent: {
      primary: { background: color.accent },
      ghost: { background: 'transparent' },
    },
  },
  defaultVariants: { intent: 'primary' },
});

Why TypeStyles

CSS ergonomics with TypeScript rigor

Most CSS-in-JS forces a tradeoff: readable output or great types. TypeStyles is built around CSS primitives — variables, layers, selectors — so your styles stay inspectable and your API stays strict.

Class names you can actually read

Variants compile to predictable, hyphenated selectors that mirror what you author. Open DevTools and you’ll see button-base button-intent-primary, not _1g8a3b2. No more archeology; no more guessing which style came from where.

In the DOM

<button class="button-base button-intent-primary"> <button class="button-base button-intent-ghost"> <div class="card-base card-tone-warning">

01 — Tokens

Design tokens as CSS variables

Tokens compile to real custom properties on the cascade, so legacy stylesheets and third-party CSS can consume the same values — one source of truth across the stack.

02 — Adoption

Incremental by default

Ship TypeStyles beside existing CSS and frameworks. Colocate or split files, adopt one component at a time, and mix with global styles without an all-or-nothing migration.

03 — Runtime

Lean output, predictable shape

No heavyweight styled-runtime in the browser: styles register up front, CSS injects when needed, and the runtime story stays small compared to classic CSS-in-JS.

Tokens

One definition, every runtime surface

Tokens become standard custom properties, so TypeStyles, plain CSS, and even static assets can agree on spacing, color, and type scales — including nested theme overrides when you need them.

  • Theming via scoped class surfaces — swap full visual styles without rewiring components.
  • Autocomplete and literal types on token references inside your style objects.
  • Interop with hand-written stylesheets during long migrations.
tokens + legacy CSS
const spacing = tokens.create('spacing', {
  md: '16px',
  lg: '24px',
});

export const card = styles.component('card', {
  base: {
    padding: spacing.md,
    gap: spacing.lg,
  },
});

/* legacy.css — same custom properties */
.card-legacy { padding: var(--spacing-md); }
button.ts
import { styles, tokens } from 'typestyles';

const color = tokens.create('color', { accent: '#2563eb' });

const button = styles.component('button', {
  base: {
    border: '1px solid #000',
    transition: 'transform 120ms ease',
  },
  variants: {
    intent: {
      primary: { background: color.accent },
      ghost: { background: 'transparent' },
    },
  },
  defaultVariants: { intent: 'primary' },
});

Workflow

From single file to design system

Keep styles beside components for speed, or extract shared definitions when patterns stabilize. The API stays the same: callable style objects, destructurable classes, and composition helpers that scale with your codebase.

  • Components are callable for props-driven class lists and destructurable for granular control.
  • Shared utilities like cx() merge internal and external classes safely.
  • Works with Vite, Next.js, Rollup, and Astro — pick the integration that matches your app.
ViteNext.jsAstroRollup

Build the design system you actually want to maintain

Start with one component, keep your CSS mental model, and let TypeScript catch mistakes before they ship.