Creating Themes with Tamagui
Generate typed themes from color tokens and a recipe tree
Tamagui v3 creates theme suites with one function: createThemes(tokens, tree, { getTheme }). The tree describes names and inheritance. Your getTheme
function turns each recipe into a plain theme record.
The default recipe helpers live in @tamagui/themes/builder. The generated
default themes live in @tamagui/themes, so applications using
@tamagui/config/v6 do not run the generator at startup.
Start with the default themes
tamagui.config.ts
The default suite contains light and dark roots, relative levels, inverse,
accent, brand, and the red, yellow, and green semantic colors.
Build a theme tree
themes.ts
The roots must be named light and dark. A child inherits its parent’s recipe
fields by shallow merge. children and values are structural keys; every
other field is application-defined data passed to getTheme.
Generated names flatten the path:
The same children attach below dark. Definition-local children attach only
below that definition.
Resolve recipes with getTheme
The default resolver combines two small helpers:
ramp() emits the adaptive color-1 through color-11 keys. color-1 is nearest
the background and color-11 is nearest the foreground in every theme. Pass the
scale so a bold light theme, whose background is deeper than its type, runs its
ramp deep to pale; without it the direction follows the scheme alone.
fromShades() maps numeric shades in a semantic scale to the active palette and
passes exact token names or color literals through.
These numbers describe steps relative to the theme’s own scheme. They are not
Tailwind’s absolute 50 through 950 scale. Only the hyphenated spelling
changed.
All generated values resolve against tokens.color immediately. A misspelled
token or unsupported value fails generation and reports the nearest token name.
Relative levels
levels() creates self-nesting level2, level3, and level4 children.
Their names are relative operations:
At the configured maximum, deeper levels saturate. The generated names remain valid aliases, but identical resolved maps share one parsed theme and one CSS declaration block.
Pass a lower maximum for color themes when only one raised step is useful:
This creates a real light_red_level2 map. Deeper relative paths resolve to the
same map instead of falling back to a neutral level.
One-theme values
Recipe fields inherit. values apply only to the theme defined at that node:
Values are applied after getTheme, so they are useful for a precise override.
Put data in the recipe when descendants must retain it.
You may omit getTheme entirely when every node supplies its theme through
values.
Function children
A definition function receives the resolved parent recipe and may return a
definition or null:
Returning null skips that path. The default inverse theme uses this ordinary
mechanism. Its resolved maps deduplicate with the opposite root, so inverse adds
names without duplicating declarations.
Customize scales and treatments
The default scales object has normal, bold, and tint treatments in both
schemes at levels 1 through 4. Each level is plain data. raise(scale, steps)
shifts background and border shades along the white, 50 through 950,
black ladder and clamps at its ends.
Add a key to your own scales object and reference it with a recipe field. Then
use that field in your resolver. createThemes does not reserve scheme,
palette, level, or treatment; these are conventions implemented by the
default resolver.
No component themes, templates, or masks
V3 does not inspect styled component display names or look for uppercase theme name
segments. A Button gets a raised theme because its skin creates a level2
boundary, the same operation available to application code.
The v2 builder options componentThemes, templates, masks,
childrenThemes, and grandChildrenThemes have no v3 equivalent. Express the
structure directly in tree, semantic values in scale objects, and exact
single-theme changes in values.
Pre-generate a custom suite
Runtime generation is small, but a shared design system can emit the flat module once:
yarn
npm
bun
pnpm
The input module exports themes or exports it as the default. Import the
generated module from your Tamagui config so the authoring helpers stay out of
the client bundle.
See v6 Colors for a complete custom-palette example and Surfaces and levels for using relative levels in components.