Styling
CSS variables for color, radius and motion, exposed as Tailwind utilities. Neutral by default, so your brand sets the color.
Overview
Tokens follow the shadcn convention: a surface variable and a matching -foreground, defined on :root for light and .dark for dark, then mapped to Tailwind in @theme inline. The palette is pure grayscale, with color reserved for status.
Colors
Primary color
Neutral is the default: no brand color, and agent activity reads through motion and contrast. To add one, pick a preset. It only sets --primary, --primary-foreground and --ring, so buttons, focus rings and the Voice Orb follow it. Try them in Customize, the color dot in the header.
Presets: color-blue, color-violet, color-rose, color-emerald, color-amber. For any other color, set the three variables yourself in both themes.
Base color
Tints the whole gray scale while keeping every lightness step, so contrast doesn't change. Stone is warm, Zinc is cool, Slate leans blue. Install a base before a primary color preset, so the preset's --primary wins.
Status
success, warning, info and destructive each have a -foreground variant tuned for text on the page background.
Dark mode
Dark is the default and uses the .dark class on html. Borders and inputs are translucent foreground (9% and 13%) so they stay visible on any surface. A .light class scopes the light theme inside a dark page, which is how the palettes above render side by side.
:root,
.light {
--background: oklch(0.985 0 0);
--foreground: oklch(0.145 0 0);
--ring: oklch(0.6 0 0);
/* … */
}
.dark {
--background: oklch(0.185 0 0);
--foreground: oklch(0.94 0 0);
--border: oklch(1 0 0 / 9%);
--ring: oklch(0.55 0 0);
/* … */
}Font
Components read --font-sans, so changing the font is one variable. This site offers Geist (the default), Inter and the system font in Customize. See Get Started for wiring a font with next/font.
Radius
One base value, --radius: 0.625rem. The rounded-sm to rounded-4xl scale derives from it, so changing it reshapes everything consistently. Try values from square (0) to soft (1rem) in Customize.
Mobile
On touch screens controls grow to 44px targets and text fields stay at 16px, so iOS doesn't zoom on focus. Sheets, drawers and recipes keep clear of the notch and home indicator through --safe-top, --safe-right, --safe-bottom and --safe-left. They read zero until the page opts in to the full screen:
// app/layout.tsx
export const viewport: Viewport = { viewportFit: "cover" }Native apps
The same tokens are generated for native apps from these variables: colors for light and dark (Display P3 on Apple platforms, sRGB elsewhere), the radius scale, spacing, touch target size, durations, easing curves and the three springs. Add the theme query from Customize to get your theme, e.g. ?color=blue&radius=0.5.
- /tokens/swift for SwiftUI:
JDS.Colors.primary,JDS.Radius.lg,JDS.Spring.snappy. - /tokens/kotlin for Jetpack Compose:
jdsColors().primary,JdsRadius.lg,JdsSpring.snappy(). - /tokens/react-native for React Native:
useColors().primary,radius.lg, andwithSpring(value, spring.snappy)in Reanimated. - /tokens/json in the Design Tokens (DTCG) format, for Style Dictionary or Tokens Studio.
Springs keep the web's mass, stiffness and damping, so motion feels the same everywhere. Re-download after changing the theme; the files say which theme they hold.
Customizing
Change the variables, not the components. To brand it, set --primary and --ring, or tint the neutrals by giving them a small chroma. To add a color, declare --name in both themes and --color-name in @theme inline.