Theme

The color scale plus the variables shadcn components read, so a component from either registry agrees with the other on what --background means.

Everything here is a CSS variable, so it lands in your globals.css and nothing else changes. There is no component to import and no wrapper to mount. The values become what bg-background, text-muted-foreground and the rest resolve to.

light
dark
The gray scale in both modes, 100 through 1000, painted with the values the registry ships. Every step is named with its value under Tokens.

Those swatches are not bg-gray-500 in this app's stylesheet. They are the literal values read back out of the built JSON. The palette this site runs on and the palette it publishes are free to drift, so a preview reading the wrong one would show you a theme you cannot install.

Install

bunx shadcn@latest add @vernostudio/theme

Or, if components.json has no registry namespace to resolve @vernostudio against, point the CLI straight at the file:

bunx shadcn@latest add https://verno-studio.vercel.app/r/theme.json

Why it is not just a palette

Two registries can each ship a perfectly good set of colors and still disagree about which one --background means. This item defines both halves: the raw scale, and the contract names that shadcn's own components read. Install it and a component pulled from ui.shadcn.com lands in the same theme as one pulled from here.

globals.css
:root {
  --background: var(--background-100);
  --foreground: var(--gray-1000);
  --muted-foreground: var(--gray-900);
  --border: var(--gray-alpha-400);
  --ring: var(--blue-700);
}

Every contract name points at a step in the scale rather than at a color, so retuning the scale retunes everything downstream of it and there is exactly one place to edit.

Six hues, one step

The accents are all step 700, so reaching for a different one stays a change of a single token. Their hues sit far apart on purpose: 258° blue, 306° purple, 182° teal, 76° amber, 1° pink. Chart series read them in that order, so two neighboring lines never land on the same part of the wheel. Red sits the rotation out, because it already means destructive everywhere else.

These are Geist's published values rather than a ramp generated here, so the six share a step and not a lightness. Amber lands at 81.87% and purple at 55.5%, which is why amber carries further across a chart than its neighbors do.

blue
red
purple
teal
amber
pink
The six accents at step 700, light mode. Each one is named with its value under Tokens.

Tokens

Every variable below lands in your stylesheet. Dark mode redeclares the same names, so nothing downstream has to know which mode it is in.

--background-100
oklch(1 0 0)
--background-200
oklch(0.984 0 0)
--gray-100
oklch(0.961 0 0)
--gray-200
oklch(0.94 0 0)
--gray-300
oklch(0.925 0 0)
--gray-400
oklch(0.937 0 0)
--gray-500
oklch(0.836 0 0)
--gray-600
oklch(0.732 0 0)
--gray-700
oklch(0.65 0 0)
--gray-800
oklch(0.59 0 0)
--gray-900
oklch(0.42 0 0)
--gray-1000
oklch(0.205 0 0)
--gray-alpha-300
oklch(0 0 0 / 0.1)
--gray-alpha-400
oklch(0 0 0 / 0.08)
--gray-alpha-700
oklch(0 0 0 / 0.44)
--blue-700
oklch(57.61% 0.2508 258.23)
--red-700
oklch(62.56% 0.2524 23.03)
--purple-700
oklch(55.5% 0.3008 306.12)
--teal-700
oklch(64.92% 0.1572 181.95)
--amber-700
oklch(81.87% 0.1969 76.46)
--pink-700
oklch(63.52% 0.238 1.01)
--shadow-tint
oklch(0 0 0 / 0.04)
--background
var(--background-100)
--foreground
var(--gray-1000)
--card
var(--background-100)
--card-foreground
var(--gray-1000)
--popover
var(--background-100)
--popover-foreground
var(--gray-1000)
--primary
var(--gray-1000)
--primary-foreground
var(--background-100)
--secondary
var(--gray-100)
--secondary-foreground
var(--gray-1000)
--muted
var(--gray-100)
--muted-foreground
var(--gray-900)
--accent
var(--gray-200)
--accent-foreground
var(--gray-1000)
--destructive
var(--red-700)
--destructive-foreground
oklch(1 0 0)
--border
var(--gray-alpha-400)
--border-strong
var(--gray-alpha-700)
--input
var(--gray-alpha-400)
--ring
var(--blue-700)
--sidebar
var(--background-100)
--sidebar-foreground
var(--gray-1000)
--sidebar-primary
var(--gray-1000)
--sidebar-primary-foreground
var(--background-100)
--sidebar-accent
var(--gray-200)
--sidebar-accent-foreground
var(--gray-1000)
--sidebar-border
var(--gray-alpha-400)
--sidebar-ring
var(--blue-700)
--chart-1
var(--blue-700)
--chart-2
var(--purple-700)
--chart-3
var(--teal-700)
--chart-4
var(--amber-700)
--chart-5
var(--pink-700)
--radius-small
0.375rem
--radius
0.5rem
--color-scheme
light

Dependencies

One package comes along with it. It carries the animation keyframes shadcn's own components expect to already be there:

  • tw-animate-css

What it does not ship

cn is not published alongside it. shadcn init already writes one, and a second copy would only drift from the first.