Skip to content

Theme builder

Pick a color, fonts and shape. Every component on this site, and every preview, switches to your theme as you go. It’s remembered on this device, so you can browse the kit in it.

Quiet interfaces for loud ideas.

Independent designer and engineer. I help small teams ship software that feels considered, from the first sketch to the last pixel.

View workGet in touchAvailable in May
Light Available

Selected work

2025 / Halcyon LabsLumenA design system powering eleven products.
View workGet in touchRead more
Dark Available

Selected work

2025 / Halcyon LabsLumenA design system powering eleven products.
View workGet in touchRead more

Use it in your project

A theme is a set of CSS variables. The kit’s components and your shadcn/ui components both read them.

  1. 1.Install the fonts, then import them once in your app's entry file

    pnpm add @fontsource-variable/geist @fontsource-variable/geist-mono
    import "@fontsource-variable/geist"
    import "@fontsource-variable/geist-mono"
  2. 2.Replace the :root and .dark blocks in your global CSS

    globals.css
    :root {
      --radius: 0.75rem;
      --font-body: "Geist Variable", "Geist", ui-sans-serif, system-ui, sans-serif;
      --font-heading: "Geist Variable", "Geist", ui-sans-serif, system-ui, sans-serif;
      --font-code: "Geist Mono Variable", "Geist Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
      --heading-tracking: -0.035em;
      --heading-weight: 600;
      --background: oklch(1 0 0);
      --foreground: oklch(0.16 0 0);
      --card: oklch(1 0 0);
      --card-foreground: oklch(0.16 0 0);
      --popover: oklch(1 0 0);
      --popover-foreground: oklch(0.16 0 0);
      --primary: oklch(0.2 0 0);
      --primary-foreground: oklch(1 0 0);
      --secondary: oklch(0.955 0 0);
      --secondary-foreground: oklch(0.16 0 0);
      --muted: oklch(0.965 0 0);
      --muted-foreground: oklch(0.52 0 0);
      --accent: oklch(0.95 0 0);
      --accent-foreground: oklch(0.16 0 0);
      --destructive: oklch(0.577 0.245 27.3);
      --border: oklch(0.91 0 0);
      --input: oklch(0.89 0 0);
      --ring: oklch(0.57 0.21 262);
      --highlight: oklch(0.57 0.21 262);
      --highlight-foreground: oklch(1 0 0);
      --highlight-ink: oklch(0.54 0.21 262);
      --success: oklch(0.66 0.17 150);
      --card-shadow: 0 1px 2px 0 oklch(0 0 0 / 0.04), 0 2px 8px -2px oklch(0 0 0 / 0.04);
    }
    
    .dark {
      --background: oklch(0.15 0 0);
      --foreground: oklch(0.97 0 0);
      --card: oklch(0.185 0 0);
      --card-foreground: oklch(0.97 0 0);
      --popover: oklch(0.205 0 0);
      --popover-foreground: oklch(0.97 0 0);
      --primary: oklch(0.93 0 0);
      --primary-foreground: oklch(0.15 0 0);
      --secondary: oklch(0.26 0 0);
      --secondary-foreground: oklch(0.97 0 0);
      --muted: oklch(0.235 0 0);
      --muted-foreground: oklch(0.72 0 0);
      --accent: oklch(0.27 0 0);
      --accent-foreground: oklch(0.97 0 0);
      --destructive: oklch(0.704 0.191 22.2);
      --border: oklch(0.29 0 0);
      --input: oklch(0.33 0 0);
      --ring: oklch(0.74 0.134 262);
      --highlight: oklch(0.74 0.134 262);
      --highlight-foreground: oklch(0.15 0 0);
      --highlight-ink: oklch(0.74 0.134 262);
      --success: oklch(0.78 0.17 150);
      --card-shadow: 0 0 0 1px oklch(1 0 0 / 0.02), 0 8px 24px -12px oklch(0 0 0 / 0.6);
    }
    
    @theme inline {
      --font-sans: var(--font-body);
      --font-mono: var(--font-code);
    }