Theme it, own it.

Wensity is built on plain CSS variables exposed through Tailwind v4's @theme inline. No theme provider, no JS context, no runtime config. Change one file, every component re-skins.

A core promise of Wensity: there is no vendor lock-in. The CLI installs real .tsx files into your repo, and those files reference CSS variables you define. The brand ramp ships as Wensity Red under the token name chili purely as a sensible default. You decide the values, and if you don't like the name you can rename the tokens entirely. We cover both paths below.

Design tokens

Drop this into your globals.css (or any file imported once at the root). It defines the light/dark surface tokens every Wensity component reads.

globals.css
@import "tailwindcss";
/* Dark mode via .dark class on <html> */
@custom-variant dark (&:where(.dark, .dark *));
@theme inline {
/* Surfaces — your values */
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-surface: var(--surface);
--color-surface-muted: var(--surface-muted);
--color-border: var(--border);
--color-border-strong: var(--border-strong);
--color-muted-foreground: var(--muted-foreground);
/* Brand ramp — the components reference these three steps. */
--color-chili-300: #ff8a73;
--color-chili-500: #cd1c18;
--color-chili-800: #6a0d0e;
}
:root {
--background: #fafafa;
--foreground: #0a0a0a;
--surface: #ffffff;
--surface-muted: #f4f4f5;
--border: rgba(10, 10, 10, 0.08);
--border-strong: rgba(10, 10, 10, 0.16);
--muted-foreground: #52525b;
--pattern-fg: rgba(10, 10, 10, 0.07);
}
.dark {
--background: #0a0a0b;
--foreground: #f5f5f6;
--surface: #111113;
--surface-muted: #18181b;
--border: rgba(255, 255, 255, 0.08);
--border-strong: rgba(255, 255, 255, 0.14);
--muted-foreground: #a1a1aa;
--pattern-fg: rgba(255, 255, 255, 0.06);
}

A note on the structure: the variables under @theme inline are the names Tailwind generates utility classes from (bg-surface, text-foreground, etc). The variables under :root and .dark are where the actual values live. Keep that separation. It lets you swap a whole palette without restating every mapping.

Dark mode

We use the class strategy. Toggle a .dark class on <html>. Pair with next-themes for system / persisted preference.

Wensity is dark-first by design. The dark palette is what we tune against, and most components look slightly more polished in dark mode by default. If your product is light-only, you can ignore the .dark block entirely; nothing will break.

One small but important detail: add suppressHydrationWarning to your <html> tag in the root layout. next-themes (and a few browser extensions) inject attributes before React hydrates, and Next will surface a benign mismatch without it.

Your brand color

Every accent in the library reads from a three-step ramp (300, 500, 800). Override the values and every component re-skins. You're not replacing "our brand" with yours. The slot was always yours. We just shipped a default in it.

globals.css
/* Override the three brand steps with your values —
every component picks them up automatically. */
@theme inline {
--color-chili-300: #93c5fd; /* tint */
--color-chili-500: #2563eb; /* brand */
--color-chili-800: #1e3a8a; /* deep */
}

Three steps is intentional. The 500 is your on-screen brand color, the 300 is the tint we lean on for highlights and gradient stops, the 800 is the deep tone behind shadows. If you want a fuller scale (50 → 950), declare the entire ramp under @theme inline.

Wensity Red (default)
#ff8a73 · #cd1c18 · #6a0d0e
Royal
#a78bfa · #6d28d9 · #3b1390
Atlantic
#7dd3fc · #0284c7 · #0c4a6e
Forest
#86efac · #16a34a · #14532d
Sunset
#fdba74 · #ea580c · #7c2d12
Mono
#d4d4d8 · #3f3f46 · #18181b

Rename the palette

Don't want the word chili in your stylesheet? You own every component file. Run one project-wide rename and your palette is whatever you want: brand, primary, accent, anything.

terminal
$# In your editor: project-wide find/replace
$# chili-300 → brand-300
$# chili-500 → brand-500
$# chili-800 → brand-800
$
$# Or with ripgrep + sed:
$rg -l 'chili-(300|500|800)' src \
$| xargs sed -i '' -E 's/chili-(300|500|800)/brand-\1/g'

Then update your @theme inline block so the new names are defined:

globals.css
/* Prefer brand-* instead of chili-*? You own the source. */
@theme inline {
--color-brand-300: #93c5fd;
--color-brand-500: #2563eb;
--color-brand-800: #1e3a8a;
}

That's it. There's no upstream package shouting at you for renaming a token.

Display font

Wensity ships designed against Cabinet Grotesk, but any tight-tracking display face works. Swap the family on --font-display.

globals.css
@font-face {
font-family: "Cabinet Grotesk";
src: url("/fonts/CabinetGrotesk-Extrabold.woff2") format("woff2");
font-weight: 800;
font-display: swap;
}
@theme inline {
--font-display: "Cabinet Grotesk", ui-sans-serif, system-ui, sans-serif;
}

The library uses Cabinet for every type role: headlines, body, UI, labels, snippets, and code-like surfaces. We deliberately avoid a second body family or a separate mono role. Most products read better when the rhythm does the work instead of another font.

Pattern + grain

The hatch / grid backgrounds you see in the studio site read from --pattern-fg. Tweak its alpha to dial intensity globally.

The grid texture is always rendered as a CSS background. Never an SVG, never an image. That keeps it crisp at every zoom level and removes a network round-trip from the critical path. We pair it with a fixed-position grain overlay (see components/site/Grain.tsx) for the subtle film noise on dark backgrounds.

Best practices

A short list of theming habits that have saved us pain on dozens of studio engagements.

  • Restrict yourself to one accent color. Wensity components are tuned for a single brand hue plus muted grays. The moment you add a second accent, half of them stop reading premium.
  • Keep border alphas low (8 to 12% in dark, 6 to 10% in light). Push them above 20% alpha and surfaces start feeling skeuomorphic instead of editorial.
  • Avoid inset shadows on cards. We use flat outer shadows plus a 1px border for depth. It scales better across dark and light and reads cleaner on retina than any inner glow trick.
  • Don't override component-level colors with className if a token would do the job. Tokens compose, classes fight each other.