Foundations

Product themes

The base and the product layer: how to add the ShadeNotes, portfolio or a new theme

The base goes into every product first and is the same for all. The product layer changes only the accent: --primary, --primary-foreground, --primary-ink, --primary-edge. A new product makes its own layer from this pattern

npx shadcn@latest add @umbra/theme-base

The foundation of the system: CSS variables (tokens.css), base styles (base.css) and a bridge to Tailwind. Files go to styles/umbra/

  • tokens.css: window, tile, nested surface, text, color roles, glass, geometry, font, typography, motion. Light theme in :root, dark in .dark
  • base.css: SF Pro via the system stack (Inter as fallback), text-wrap: balance on headings and pretty on paragraphs, a 4 px scrollbar in --text at 22%, .glass, .glass-soft, .glass-dense, .glass-pop, .edge-fade, .umbra-fill (cards two per row, one per row in a narrow window; an odd last card spans the full width), motion utilities (.umbra-appear, .umbra-rise, .umbra-leave, .umbra-exit, .umbra-stagger, .umbra-transition, .umbra-spin), prefers-reduced-motion and prefers-reduced-transparency rules
  • tailwind-v4.css: @theme inline and @utility for Tailwind 4
  • tailwind-v3-preset.cjs: a preset for Tailwind 3.4. The classes match v4 one to one

When to use

Always, as the first step in a new product and before any @umbra component

When not to use

If tokens.css is already added to the product by hand. Don’t install it twice: variables are defined in one place

Surfaces

Light

window

tile

nested

popover

skeleton

nav-active

Dark

window

tile

nested

popover

skeleton

nav-active

Color roles

Light

primary

indigo

indigo-soft

giants

record

destructive

Dark

primary

indigo

indigo-soft

giants

record

destructive

Typography

Light

Display 36/40

Title 22/28

Heading 20/28

Body 15/22: main app text

Label 14/20: button labels

Caption 13/18: meta

MONO 12/16 · TUE, 10:00

Dark

Display 36/40

Title 22/28

Heading 20/28

Body 15/22: main app text

Label 14/20: button labels

Caption 13/18: meta

MONO 12/16 · TUE, 10:00

Glass over content

Light

.glass

sticky header, menus, tooltips

Dark

.glass

sticky header, menus, tooltips

Do

  • Take color only through a role: bg-tile, text-muted, bg-primary
  • Role text goes on the soft tones bg-indigo-soft, bg-giants-soft; on the saturated bg-indigo, bg-giants only icons
  • Ink text-*-ink: icons and text. Small text only where the pair passes Lc 75 in pnpm apca: text-primary-ink everywhere, text-indigo-ink and text-giants-ink only in the light theme; in the dark theme they are only for large semibold text (text-title, text-heading)
  • Glass only on things that sit above content: sticky header, menus, tooltips, dialogs, dock

Don’t

  • Raw colors, p-[13px], rounded-lg, opacity modifiers (bg-primary/50): @umbra/eslint catches them
  • Rewrite tokens.css in a product. A product changes only the accent in product.css
  • The dark: modifier for colors: the theme switches via variables, the classes stay the same
  • Mix base.css classes (.umbra-transition, .glass) with utilities for the same property on one element (umbra-transition duration-base): in Tailwind 4 base.css sits outside layers and beats utilities, in 3.4 it loses. Need a different duration? Use transition-all duration-base ease-out without .umbra-transition