Skip to main content

Configure themes

Theming follows the shadcn/ui CSS variable model (light under :root, dark under .dark). Tokens are full CSS colors - prefer OKLCH (oklch(...)). There is no live theme editor; configuration lives in shellui.config.json (or theme JSON under themesDir).

shellui init sets the official palette:

{
"theme": "shellui"
}

JS framework starters call shellui.applyTheme() and listen for theme with @shellui/sdk/tiny - see Framework starters.

The shell writes variables as full colors on :root. Tailwind consumes var(--background) (not hsl(var(--background))). Hex and legacy HSL channel triples are accepted and normalized.

Design palettes in tweakcn or any shadcn-compatible export, then map :root / .dark into light / dark (kebab-case keys work). Registry JSON (cssVars.light / cssVars.dark) maps the same way.

Built-in themes​

Curated JSON (version: 1) ships in @shellui/core (packages/core/src/features/theme/curated/*.json), validated against @shellui/core/schemas/shellui.theme.schema.json. Named exports (shelluiTheme, claudeTheme, themeNames, …) come from @shellui/core. Community palettes are adapted from tweakcn and shadcn/ui unless noted.

NameLabelNotes
shelluiShelluiOfficial gold brand
claudeClaudeWarm terracotta, Outfit + Geist Mono
light-greenLight GreenLime primary, Inter + JetBrains Mono
zen-inspiredZen InspiredParchment neutrals, Inter + Playfair Display
astro-vistaAstro VistaCool neutrals, orange primary
shadcnshadcnDefault shadcn/ui neutrals, system fonts
amber-minimalAmber MinimalWhite neutrals, amber primary
amethyst-hazeAmethyst HazeLavender neutrals
bold-techBold TechViolet primary
bubblegumBubblegumPink primary, candy pastels
caffeineCaffeineCoffee browns
candylandCandylandPastel pink, blue, lime
catppuccinCatppuccinMauve primary
claymorphismClaymorphismClay surfaces, violet primary
clean-slateClean SlateSlate neutrals
cosmic-nightCosmic NightSoft violet
cyberpunkCyberpunkMagenta and cyan
darkmatterDarkmatterAmber primary, mono-first
doom-64Doom 64Retro game palette
elegant-luxuryElegant LuxuryBurgundy
graphiteGraphiteGreyscale
kodama-groveKodama GroveMoss greens
midnight-bloomMidnight BloomViolet bloom
mocha-mousseMocha MousseMocha browns
modern-minimalModern MinimalBlue primary
monoMonoGeist Mono throughout
natureNatureForest green
neo-brutalismNeo BrutalismBold blocks
northern-lightsNorthern LightsAurora green
notebookNotebookArchitects Daughter
ocean-breezeOcean BreezeTeal primary
pastel-dreamsPastel DreamsLavender, large radius
perpetuityPerpetuityTeal terminal
quantum-roseQuantum RoseRose primary
retro-arcadeRetro ArcadeMagenta and teal
sage-gardenSage GardenMuted sage
soft-popSoft PopViolet and teal
solar-duskSolar DuskDusk oranges
starry-nightStarry NightNight blues, gold
sunset-horizonSunset HorizonSunset oranges
supabaseSupabaseSupabase green
t3-chatT3 ChatMagenta-rose
tangerineTangerineTangerine on slate
twitterTwitterSky blue
vercelVercelBlack and white, Geist
vintage-paperVintage PaperAged paper
violet-bloomViolet BloomBold violet

Config shapes​

Single built-in name:

{
"theme": "shellui"
}

Inline object (partial light / dark merges onto the Shellui theme; camelCase or kebab-case keys):

import type { ShellUIConfig } from '@shellui/core';

const config: ShellUIConfig = {
theme: {
name: 'brand',
label: 'Brand',
radius: '0.5rem',
fonts: { sans: 'Inter, system-ui, sans-serif' },
light: {
primary: 'oklch(0.55 0.15 250)',
'primary-foreground': 'oklch(1 0 0)',
},
dark: {
primary: 'oklch(0.7 0.12 250)',
},
},
};

Folder of JSON themes:

{
"themesDir": "./themes",
"theme": "acme"
}
themes/
acme.json
acme/
fonts/
Inter-Regular.woff2

Theme JSON must include "version": 1 and pass the theme schema.

Several themes for the Settings picker:

{
"themes": ["shellui", "claude", "shadcn"],
"activeTheme": "shellui"
}

Or a map of id → name | path | object. defaultTheme is an alias of activeTheme. Legacy theme: "default" resolves to shellui.

Theme JSON (version 1)​

{
"$schema": "./node_modules/@shellui/core/schemas/shellui.theme.schema.json",
"version": 1,
"name": "acme",
"label": "Acme",
"description": "Acme brand theme",
"recommended": false,
"radius": "0.5rem",
"fonts": {
"sans": "Outfit, sans-serif",
"mono": "Geist Mono, monospace",
"files": ["https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap"]
},
"light": {
"background": "oklch(0.98 0.005 95)",
"foreground": "oklch(0.34 0.027 96)",
"primary": "oklch(0.62 0.14 39)",
"sidebarBackground": "oklch(0.97 0.008 99)"
},
"dark": {
"background": "oklch(0.27 0.004 107)",
"foreground": "oklch(0.96 0.003 106)",
"primary": "oklch(0.67 0.13 39)"
}
}

Theme selector​

Settings → Appearance scales with how many themes you configured (1 / few / many). Recommended themes sort first. Each preview uses that theme's swatches, typography, and radius. Fonts from fonts.files apply when the theme is active.

Do not ship a second theme picker inside a hosted iframe unless that iframe is the settings surface. Read settings.appearance from the SDK instead.