Theming

Customize Cotton UI with Tailwind CSS v4's theming system.

Every part of Cotton UI's look is driven by CSS variables you can override. Grays use Tailwind's built-in zinc and work out of the box. A neutral accent ships by default, and radius, surfaces, shadows and the focus ring are all themeable tokens. Override any of them with a :root block (and a .dark block for dark mode), wherever you set your tokens. Start with the main ones below, then see All Tokens for the complete list.

Changing the Accent Color

The accent drives primary buttons, active states and focus rings. Cotton UI uses a 3-value accent system:

  • --color-accent - primary fills (button backgrounds, active states)
  • --color-accent-content - hover states and readable accent text
  • --color-accent-foreground - text and icons on accent fills

A neutral zinc accent ships by default. To brand the kit, override the accent tokens with a :root block and a .dark block for dark mode. This goes in the inline <style> from the no-build setup, or your app.css on a custom build, the same three tokens either way:

app.css
:root {
    --color-accent: var(--color-blue-500);
    --color-accent-content: var(--color-blue-600);
    --color-accent-foreground: var(--color-white);
}

.dark {
    --color-accent: var(--color-blue-400);
    --color-accent-content: var(--color-blue-300);
    --color-accent-foreground: var(--color-blue-950);
}

Use :root / .dark, not @theme: the kit already registers these tokens (so utilities like bg-accent and bg-accent/10 work), and a plain :root override beats the kit's baked-in value, where @theme would lose. Swap blue for any Tailwind hue, or your own values.

Prefer a live preview? The Theme Builder lets you tweak the accent, radius, surfaces and focus ring, then copies the generated CSS.

Accent Toggle

By default, a component's selected and active states use your accent colour: a checked radio, an on switch, a selected card. Adding :accent="False" turns those off for that component and renders them in a neutral tone instead, giving you a monochrome control without touching your theme.

The neutral tracks the current text colour, so it shows dark on a light background and light on a dark background, switching with light and dark mode on its own. There is nothing to set up.

If you want the neutral to be a fixed colour rather than the inherited text colour, define the --color-muted token (with a .dark value for dark mode). It is entirely optional: when it is unset, components fall back to the text colour.

app.css
:root {
    --color-muted: var(--color-zinc-900);
}

.dark {
    --color-muted: var(--color-zinc-100);
}

Side by side

With Accent (default)

Without Accent (:accent="False")

Wi-Fi
Wi-Fi

Border Radius

Two radius tokens control rounding. Form controls and structural surfaces are kept separate so you can, for example, round cards more than inputs:

  • --radius-control - form controls (inputs, buttons, segments)
  • --radius-box - structural surfaces (cards, dialogs, menus)
app.css
:root {
    --radius-control: 0.5rem;      /* form controls */
    --radius-box: 0.75rem;  /* structural surfaces */
}

Surfaces

Surface tokens set the page base and the raised surfaces drawn on top of it. The faint tinted base lets white cards and menus read as elevated:

  • --color-bg - page base background
  • --color-surface - raised surfaces (cards, dialogs, menus)
  • --color-input-bg - form field fill (transparent in light so fields are border-defined; a faint lift in dark)
app.css
:root {
    --color-bg: var(--color-zinc-50);     /* page base */
    --color-surface: #ffffff;             /* cards, dialogs, menus */
    --color-input-bg: transparent;        /* form fields (border-defined) */
}

.dark {
    --color-bg: var(--color-zinc-900);
    --color-surface: var(--color-zinc-800);
}

All Tokens

The complete set of themeable tokens and their defaults. Override any of them in a :root block, and set dark-mode values under .dark.

Token Default Dark Purpose
Accent
--color-accent zinc-900 zinc-100 Primary fills: button backgrounds, active states
--color-accent-content zinc-700 zinc-300 Hover states and readable accent text
--color-accent-foreground white zinc-900 Text and icons on accent fills
--color-muted currentColor Fill and border for :accent="False" controls. Falls back to the foreground when unset.
Surfaces
--color-bg zinc-50 zinc-900 Page base background
--color-surface white zinc-800 Raised surfaces: cards, dialogs, menus
--color-input-bg transparent +4% white Form field fill: transparent in light (border-defined), a faint lift in dark
--color-box-border zinc-200 zinc-700 Border on box surfaces: cards, dialogs, menus, popovers. Set transparent for a shadow-only look
--color-card-border = box-border = box-border Card border only. Falls back to --color-box-border; override it alone (e.g. transparent) to affect just cards
Radius
--radius-control 0.375rem Form controls: inputs, buttons, segments
--radius-box 0.5rem Structural surfaces: cards, dialogs, menus
Shadows
--shadow-input 0 1px 2px 0 rgb(0 0 0 / 0.05) Form input elevation (set to none to flatten)
--shadow-box 0 1px 2px 0 rgb(0 0 0 / 0.05) Card and dialog elevation
Focus ring
--focus-ring-width 2px Focus ring thickness
--focus-ring-offset 0px Gap between the control and the ring
--focus-ring-color = accent Focus ring color
--focus-ring-offset-color white zinc-900 Color behind the offset gap

Advanced: Changing the Base Gray

Cotton UI uses Tailwind's built-in zinc for grays, which works with zero configuration. If you prefer a different gray family, remap zinc to another palette:

app.css
/* Remap zinc to slate */
@theme {
    --color-zinc-50: var(--color-slate-50);
    --color-zinc-100: var(--color-slate-100);
    --color-zinc-200: var(--color-slate-200);
    --color-zinc-300: var(--color-slate-300);
    --color-zinc-400: var(--color-slate-400);
    --color-zinc-500: var(--color-slate-500);
    --color-zinc-600: var(--color-slate-600);
    --color-zinc-700: var(--color-slate-700);
    --color-zinc-800: var(--color-slate-800);
    --color-zinc-900: var(--color-slate-900);
    --color-zinc-950: var(--color-slate-950);
}

Theming approach inspired by Flux UI.