Proper UI

Theme generator

Pick a preset or any brand colour, choose a base gray and a radius, and get the CSS, a shareable preset code and the CLI command.

A preset is four decisions: an eleven-step brand ramp, a base gray, a radius scale and, optionally, fonts. Pick a shipped preset or type any hex colour (the ramp is derived in OKLCH so every step keeps the same hue and step 600 stays readable under white text), then adjust the gray and the radius. The preview renders real Proper UI components with the result, in light or dark mode.

Start from a preset
Derives an 11-step ramp in OKLCH.
  • 50
  • 100
  • 200
  • 300
  • 400
  • 500
  • 600
  • 700
  • 800
  • 900
  • 950
Base gray
Radius
Lowercase letters, digits and dashes. Stored in the preset code.
Preview

Invite your team

Collaborators get access to every project in this workspace.

New
We will send them a link to join.
BrandGraySuccessModern

Preset code

AZMABWJyYW5k

Apply it with the CLI

npx @properui/cli@latest theme apply AZMABWJyYW5k

Or paste this after your Proper UI stylesheet import

/* properui:theme-preset */
/*
 * Proper UI theme preset "Brand (default)" (code AZMABWJyYW5k).
 * Re-apply with `npx @properui/cli@latest theme apply AZMABWJyYW5k` rather than editing by hand.
 */
@theme {
    /* Brand ramp */
    --color-brand-50: rgb(249 245 255);
    --color-brand-100: rgb(244 235 255);
    --color-brand-200: rgb(233 215 254);
    --color-brand-300: rgb(214 187 251);
    --color-brand-400: rgb(182 146 246);
    --color-brand-500: rgb(158 119 237);
    --color-brand-600: rgb(127 86 217);
    --color-brand-700: rgb(105 65 198);
    --color-brand-800: rgb(83 56 158);
    --color-brand-900: rgb(66 48 125);
    --color-brand-950: rgb(44 28 95);

    /* Base gray: neutral. Every gray semantic token reads the neutral ramp, in both modes. */
    --color-neutral-50: oklch(98.5% 0 none);
    --color-neutral-100: oklch(97% 0 none);
    --color-neutral-200: oklch(92.2% 0 none);
    --color-neutral-300: oklch(87% 0 none);
    --color-neutral-400: oklch(70.8% 0 none);
    --color-neutral-500: oklch(55.6% 0 none);
    --color-neutral-600: oklch(43.9% 0 none);
    --color-neutral-700: oklch(37.1% 0 none);
    --color-neutral-800: oklch(26.9% 0 none);
    --color-neutral-900: oklch(20.5% 0 none);
    --color-neutral-950: oklch(14.5% 0 none);

    /* Radius: md */
    --radius-xs: 0.125rem;
    --radius-sm: 0.25rem;
    --radius-md: 0.375rem;
    --radius-lg: 0.5rem;
    --radius-xl: 0.75rem;
    --radius-2xl: 1rem;
    --radius-3xl: 1.5rem;
    --radius-4xl: 2rem;
}
/* /properui:theme-preset */

Applying the result

Any of these three does the same thing:

  • CLI: npx @properui/cli@latest theme apply <code> writes the block into your global stylesheet between /* properui:theme-preset */ markers. Run it again with another preset or code and only that block is replaced.
  • During setup: npx @properui/cli@latest init --preset <name|code> applies the preset as part of init.
  • By hand: paste the generated CSS after the Proper UI stylesheet import (@import "@properui/ui/styles/globals.css", or your copied theme.css). Tailwind merges @theme blocks in source order, so the later block wins.

npx @properui/cli@latest theme list prints the shipped presets with their codes.

What a preset changes

A preset only redeclares primitives. Every semantic token (bg-brand-solid, text-primary, border-secondary, ring-brand and the rest) reads them, in light and dark mode alike, so nothing else needs to change:

DecisionTokens it redeclares
Brand--color-brand-50 to --color-brand-950
Base gray--color-neutral-50 to --color-neutral-950
Radius--radius-xs to --radius-4xl
Fonts--font-body, --font-display (only when set)

The base gray works by redeclaring the neutral ramp with the values of Tailwind's gray, slate, zinc or stone, because that ramp is what every gray surface, border and text token resolves to. The radius options scale Tailwind's stock radius scale: none is square, md is the default, xl doubles it. rounded-full pills stay pills.

The preset code is a short url-safe string that encodes all of it, so it can be pasted into the CLI, a pull request or an agent prompt. This page's address updates with the code, so the link reproduces the theme.

FAQs