One schema, unlimited themes

Stop writing dark: variants everywhere. Define your theme shape once, get full TypeScript inference, and scale from light/dark to multi-tenant.

Full TypeScript inference
From light/dark to multi-tenant
Works with Tailwind & shadcn
SSR or CSR, static or dynamic
import { createSchema, t, InferTheme } from '@livery/core'
const schema = createSchema({
definition: {
primary: t.color(),
background: t.color(),
}
})
const theme: InferTheme<typeof schema.definition> = {
primary: '#171717',
background: '#ffffff',
accent: '#f97316', // ❌ Type error
}
// Works in every theme
<button className="bg-primary text-background">
Click me
</button>

The Problem vs The Solution

Stop duplicating styles for every theme. Use semantic tokens that just work.

Without Livery

Every component needs explicit styles for each theme.

  • Duplicate styles for every theme variant
  • Adding a third theme means editing every file
  • Easy to miss a dark: variant somewhere
  • No type safety — typos fail silently
  • Hard to maintain consistency across components
<div className="
bg-white dark:bg-slate-900
text-slate-900 dark:text-white
">
<button className="
bg-blue-500 dark:bg-blue-400
hover:bg-blue-600 dark:hover:bg-blue-500
">
Click me
</button>
</div>

With Livery

Use semantic tokens that Livery resolves for you — at build time, on the server, or at runtime. Same markup works for any theme.

  • Write styles once, works in every theme
  • Add new themes without touching components
  • Full TypeScript inference catches errors
  • Runtime theme switching with no flash
  • Scales from light/dark to multi-tenant
  • Single source of truth for design tokens
<div className="bg-background text-foreground">
<button className="bg-primary hover:bg-primary-hover">
Click me
</button>
</div>

How You'll Actually Use Livery

Four steps to type-safe, dynamic theming.

1

Define Schema

Describe your theme shape with TypeScript. Livery infers types from your schema automatically.

import { createSchema, t, InferTheme } from '@livery/core';
export const schema = createSchema({
colors: {
primary: t.color(),
background: t.color(),
},
});
type Theme = InferTheme<typeof schema.definition>;
2

Create Themes

Define theme objects that match your schema. TypeScript ensures every theme has the right shape.

const light: Theme = {
colors: { primary: '#171717', background: '#FFFFFF' },
};
const dark: Theme = {
colors: { primary: '#FFFFFF', background: '#0A0A0A' },
};
3

Map to Tailwind

Reference Livery CSS variables in your Tailwind config. Works with any CSS framework.

@theme {
--color-primary: var(--colors-primary);
--color-bg: var(--colors-background);
}
4

Use in Components

Write your styles once using semantic tokens. They work automatically in every theme.

<button className="bg-primary text-white">
Click me
</button>

Livery generates CSS variables from your schema — at build time, on the server, or dynamically at runtime. Switch themes instantly with no flash.

Live Demo

See it in action

This entire page is themed with Livery. Click a theme below — fonts, borders, radii, shadows, everything changes.

L
Livery App

Dashboard

Your personalized overview of recent activity and quick actions.

Quick Actions

Access your most used features instantly.

Analytics

Track your progress and growth metrics.

Typography Preview

Display Heading

Body text uses the sans-serif font stack. Clean and readable.

const theme = 'default';

Current theme: Default Clean, minimal design

Which package do you need?

Answer a few questions to find the right integration for your use case.

What kind of theming do you need?

Scales With You

And When You Need More

Start with light/dark mode. The same schema and type safety scales to multi-tenant, white-label, and beyond.

Multi-Tenant SaaS

Each customer gets their own brand colors, loaded from your database at runtime.

User Preferences

Let users customize their experience with validated, type-safe theme options.

A/B Testing

Test different visual treatments. Each variant is just a theme object.

White-Label Products

Ship one codebase that adapts to each customer's brand automatically.

Resolvers handle fetching and caching

Create a resolver to fetch themes from your API or database. Built-in caching keeps things fast, and TypeScript ensures the response matches your schema.

import { createResolver } from '@livery/core';
const resolver = createResolver({
fetcher: async ({ themeId }) => {
const res = await fetch(`/api/themes/${themeId}`);
return res.json();
},
cache: { ttl: 60_000 }, // 1 minute
});
const theme = await resolver.resolve({ themeId: 'acme-corp' });

Same schema. Same components. Any data source.

Coming Soon

Livery Dashboard

A visual interface for managing your themes. Perfect for teams that want designers to create themes without touching code.

Visual Theme Editor
Design themes with a live preview interface
Tenant Management
Manage themes for multiple tenants in one place
Theme Analytics
Track which themes perform best
Team Collaboration
Invite designers and developers to collaborate
Livery Dashboard