Style variations let users switch your entire theme’s visual identity from the Site Editor. One click changes colors, typography, spacing, and layout, without touching code or losing content.
WordPress introduced style variations in version 6.0, and they’ve become one of the most powerful features in block theme development. Instead of building multiple child themes for different color schemes, you create JSON files that override your base theme.json values. Users get design options. You maintain a single codebase.
This guide covers how to build style variations from scratch, organize them effectively, and test them before shipping your theme.
What Style Variations Actually Do
A style variation is a partial theme.json file stored in your theme’s styles/ directory. When a user selects it in the Site Editor (Appearance > Editor > Styles > Browse Styles), WordPress merges it with your base theme.json, overriding any matching values.
The key word is override. Style variations don’t replace your entire theme.json, they only change the properties they define. Your base font sizes, spacing scales, layout settings, and block configurations remain intact unless the variation explicitly changes them.
Here’s what happens technically:
- WordPress reads your base
theme.json - User selects a style variation from the Site Editor
- WordPress deep-merges the variation file on top of the base
- All CSS custom properties regenerate with the merged values
- The theme renders with the new visual identity
Because style variations work through CSS custom properties, every block that references those variables updates automatically. No additional CSS overrides needed.
File Structure and Naming
Style variations live in the styles/ directory at your theme’s root:
my-theme/
├── theme.json (base configuration)
├── style.css
├── templates/
├── parts/
├── patterns/
└── styles/
├── dark.json
├── warm.json
├── ocean.json
└── monochrome.json
Each JSON file becomes a selectable variation in the Site Editor. The filename doesn’t matter for display, WordPress uses the title property inside the file. But keep filenames descriptive for your own sanity.
Minimum Required Structure
Every style variation file needs three things:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Dark"
}
The $schema gives you autocomplete in VS Code. The version must match your base theme.json (version 3 for WordPress 6.6+). The title is what users see in the style picker.
Building a Color Variation
The most common style variation changes the color palette. Here’s a complete dark mode variation:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Dark",
"settings": {
"color": {
"palette": [
{ "slug": "primary", "color": "#6ea8fe", "name": "Primary" },
{ "slug": "secondary", "color": "#a3cfbb", "name": "Secondary" },
{ "slug": "accent", "color": "#ff6b6b", "name": "Accent" },
{ "slug": "light", "color": "#1a1a2e", "name": "Light" },
{ "slug": "dark", "color": "#e8e8e8", "name": "Dark" },
{ "slug": "base", "color": "#0f0f23", "name": "Base" },
{ "slug": "contrast", "color": "#f0f0f0", "name": "Contrast" }
]
}
},
"styles": {
"color": {
"background": "var:preset|color|base",
"text": "var:preset|color|contrast"
},
"elements": {
"link": {
"color": {
"text": "var:preset|color|primary"
},
":hover": {
"color": {
"text": "var:preset|color|accent"
}
}
},
"heading": {
"color": {
"text": "var:preset|color|contrast"
}
},
"button": {
"color": {
"background": "var:preset|color|primary",
"text": "var:preset|color|base"
},
":hover": {
"color": {
"background": "var:preset|color|accent"
}
}
}
}
}
}
Notice how the variation uses the same slug names as the base palette (primary, secondary, light, dark). This is critical. When you change the color value for slug primary, every block and pattern referencing var:preset|color|primary picks up the new value. The connection works because slugs are identifiers, the actual hex values are just the current assignment.
Typography Variations
Font combinations dramatically change a theme’s personality. A serif heading with monospace body feels editorial. A geometric sans-serif throughout feels modern and clean.
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Editorial",
"settings": {
"typography": {
"fontFamilies": [
{
"slug": "heading",
"name": "Heading",
"fontFamily": "'Playfair Display', Georgia, serif",
"fontFace": [
{
"fontFamily": "Playfair Display",
"fontWeight": "400 900",
"fontStyle": "normal",
"fontDisplay": "swap",
"src": ["file:./assets/fonts/playfair-display.woff2"]
},
{
"fontFamily": "Playfair Display",
"fontWeight": "400 900",
"fontStyle": "italic",
"fontDisplay": "swap",
"src": ["file:./assets/fonts/playfair-display-italic.woff2"]
}
]
},
{
"slug": "body",
"name": "Body",
"fontFamily": "'Source Serif Pro', 'Times New Roman', serif",
"fontFace": [
{
"fontFamily": "Source Serif Pro",
"fontWeight": "400",
"fontStyle": "normal",
"fontDisplay": "swap",
"src": ["file:./assets/fonts/source-serif-pro.woff2"]
}
]
}
]
}
},
"styles": {
"typography": {
"fontFamily": "var:preset|font-family|body",
"lineHeight": "1.8"
},
"elements": {
"heading": {
"typography": {
"fontFamily": "var:preset|font-family|heading",
"letterSpacing": "-0.02em"
}
}
}
}
}
Bundling Fonts with Variations
If your variation uses fonts that aren’t in the base theme, include the font files in your theme’s assets/fonts/ directory. WordPress loads fonts declared in fontFace automatically, no separate wp_enqueue_style needed.
Use variable fonts where possible. A single variable font file replaces multiple weight-specific files, reducing HTTP requests and total file size.
Combined Color and Typography Variations
The most effective style variations change both color and typography together. Each combination creates a distinct visual identity:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Warm Minimal",
"settings": {
"color": {
"palette": [
{ "slug": "primary", "color": "#8b4513", "name": "Primary" },
{ "slug": "secondary", "color": "#a0522d", "name": "Secondary" },
{ "slug": "accent", "color": "#cd853f", "name": "Accent" },
{ "slug": "base", "color": "#faf6f1", "name": "Base" },
{ "slug": "contrast", "color": "#2c1810", "name": "Contrast" }
],
"gradients": [
{
"slug": "warm-fade",
"gradient": "linear-gradient(135deg, #faf6f1 0%, #f0e6d8 100%)",
"name": "Warm Fade"
}
]
},
"typography": {
"fontFamilies": [
{
"slug": "heading",
"name": "Heading",
"fontFamily": "'DM Serif Display', Georgia, serif"
},
{
"slug": "body",
"name": "Body",
"fontFamily": "'Inter', -apple-system, sans-serif"
}
],
"fontSizes": [
{ "slug": "small", "size": "0.875rem", "name": "Small" },
{ "slug": "medium", "size": "1rem", "name": "Medium" },
{ "slug": "large", "size": "1.25rem", "name": "Large" },
{ "slug": "x-large", "size": "2rem", "name": "Extra Large" },
{ "slug": "xx-large", "size": "3.5rem", "name": "Huge" }
]
}
},
"styles": {
"color": {
"background": "var:preset|color|base",
"text": "var:preset|color|contrast"
},
"typography": {
"fontFamily": "var:preset|font-family|body",
"fontSize": "var:preset|font-size|medium",
"lineHeight": "1.7"
},
"elements": {
"heading": {
"typography": {
"fontFamily": "var:preset|font-family|heading"
},
"color": {
"text": "var:preset|color|primary"
}
}
}
}
}
Block-Level Overrides in Variations
Style variations can override individual block styles. This is useful when a color change requires adjustments to specific blocks, like ensuring a code block stays readable in a dark variation:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Ocean",
"settings": {
"color": {
"palette": [
{ "slug": "primary", "color": "#0077b6", "name": "Primary" },
{ "slug": "base", "color": "#caf0f8", "name": "Base" },
{ "slug": "contrast", "color": "#03045e", "name": "Contrast" }
]
}
},
"styles": {
"color": {
"background": "var:preset|color|base",
"text": "var:preset|color|contrast"
},
"blocks": {
"core/code": {
"color": {
"background": "#03045e",
"text": "#caf0f8"
},
"border": {
"radius": "8px"
}
},
"core/pullquote": {
"border": {
"color": "var:preset|color|primary",
"width": "4px",
"style": "solid"
},
"typography": {
"fontStyle": "italic",
"fontSize": "var:preset|font-size|large"
}
},
"core/separator": {
"color": {
"background": "var:preset|color|primary"
}
}
}
}
}
Custom Property Overrides
If your base theme.json uses the custom section for design tokens, border radii, transitions, spacing scales, your variations can override those too:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Rounded",
"settings": {
"custom": {
"borderRadius": {
"small": "8px",
"medium": "16px",
"large": "24px",
"pill": "9999px"
},
"shadow": {
"small": "0 2px 8px rgba(0,0,0,0.08)",
"medium": "0 4px 16px rgba(0,0,0,0.12)",
"large": "0 8px 32px rgba(0,0,0,0.16)"
}
}
}
}
This variation makes everything softer, larger border radii, visible shadows, while keeping all colors and typography from the base. Users who want a friendlier look select this variation. The Global Styles system handles the rest.
Spacing and Layout Variations
Style variations aren’t limited to visual properties. You can create variations that change content density and layout behavior:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Compact",
"settings": {
"spacing": {
"spacingSizes": [
{ "slug": "20", "size": "0.25rem", "name": "Extra Small" },
{ "slug": "30", "size": "0.5rem", "name": "Small" },
{ "slug": "40", "size": "0.75rem", "name": "Medium" },
{ "slug": "50", "size": "1rem", "name": "Large" },
{ "slug": "60", "size": "1.5rem", "name": "Extra Large" }
]
},
"layout": {
"contentSize": "640px",
"wideSize": "1000px"
}
},
"styles": {
"spacing": {
"blockGap": "0.75rem"
}
}
}
Compare this to an “Airy” variation with generous spacing:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"title": "Airy",
"settings": {
"spacing": {
"spacingSizes": [
{ "slug": "20", "size": "0.75rem", "name": "Extra Small" },
{ "slug": "30", "size": "1.5rem", "name": "Small" },
{ "slug": "40", "size": "2.5rem", "name": "Medium" },
{ "slug": "50", "size": "4rem", "name": "Large" },
{ "slug": "60", "size": "6rem", "name": "Extra Large" }
]
},
"layout": {
"contentSize": "720px",
"wideSize": "1280px"
}
},
"styles": {
"spacing": {
"blockGap": "2rem"
}
}
}
Same content, different breathing room. Content-heavy sites benefit from compact spacing. Portfolio and agency sites benefit from airy layouts.
Variation Design Strategy
Effective style variations follow patterns. Here are strategies that work:
The Core Four
Most block themes benefit from these four variations:
| Variation | What It Changes | Use Case |
|---|---|---|
| Default (base theme.json) | Nothing, the baseline | General purpose |
| Dark | Background/text colors inverted | Low-light readability |
| Brand Alternative | Different color family, same structure | Seasonal campaigns, sub-brands |
| High Contrast | Stronger contrast ratios, larger text | Accessibility, readability |
Keep Slugs Consistent
Every variation should define the same palette slugs as the base theme. If your base has primary, secondary, accent, base, and contrast, every variation should define those same five slugs with different values.
When slugs match, block patterns work across all variations without modification. A hero pattern using var:preset|color|primary looks correct whether the user picks Light, Dark, or Ocean.
Test Every Variation Against Your Patterns
Before shipping, load each variation and verify these elements:
- Text readability, Does body text have sufficient contrast against the background? WCAG AA requires a 4.5:1 ratio for normal text.
- Button visibility, Can users distinguish primary buttons from secondary ones? Are hover states visible?
- Code blocks, Do syntax-highlighted or plain code blocks remain readable?
- Images, Do images with transparent backgrounds still look correct?
- Form elements, Are input fields visible against the background? Do focus states show clearly?
How Users Interact with Variations
Users access style variations through Appearance > Editor > Styles (the half-filled circle icon) > Browse styles. WordPress shows a visual preview of each variation as a thumbnail card.
The thumbnail shows your theme’s homepage template rendered with the variation’s settings. WordPress auto-generates these previews, you don’t need to create screenshot images.
When a user selects a variation:
- WordPress stores the selection in the database (not in theme files)
- The user’s other customizations (if any) are preserved and layered on top
- Switching back to the default variation restores the original look
- The selection persists across theme updates
This means users can safely experiment. Switching variations is non-destructive, no content is lost, no settings are permanently changed.
Advanced Techniques
Variation-Specific Block Styles
Register custom block styles that complement specific variations. For example, a “glass” button style that only makes sense on dark backgrounds:
// In your theme's functions.php or block-styles.php
register_block_style( 'core/button', array(
'name' => 'glass',
'label' => 'Glass',
) );
Then in your dark variation, style the glass button:
{
"styles": {
"blocks": {
"core/button": {
"variations": {
"glass": {
"color": {
"background": "rgba(255, 255, 255, 0.1)",
"text": "#ffffff"
},
"border": {
"color": "rgba(255, 255, 255, 0.2)",
"width": "1px",
"style": "solid"
}
}
}
}
}
}
}
Restricting Feature Access Per Variation
You can’t directly restrict features per variation through theme.json alone. But you can change which options are available. For example, a monochrome variation might define a limited gradient palette:
{
"settings": {
"color": {
"gradients": [
{
"slug": "subtle",
"gradient": "linear-gradient(180deg, #f8f8f8, #e0e0e0)",
"name": "Subtle"
}
],
"duotone": []
}
}
}
Setting duotone to an empty array removes duotone options from the editor when this variation is active.
Common Mistakes to Avoid
- Inconsistent slug sets, If your base has
primary,secondary,accentbut a variation only definesprimaryandsecondary, theaccentcolor from the base bleeds through. This usually looks wrong in the new color context. - Forgetting element styles, Changing background from light to dark but not updating link colors, heading colors, or button text creates invisible or unreadable elements.
- Missing hover/focus states, A link color that contrasts well on a dark background might have a hover color that disappears. Test interactive states.
- Font loading bloat, If each variation loads different fonts, a theme with five variations ships five font families. Users only use one at a time, but all font files increase the theme’s download size. Consider sharing base fonts and only varying weights or styles.
- Version mismatch, All variation files must use the same
versionnumber as the base theme.json. Mixing version 2 and version 3 causes merge failures.
Debugging Style Variations
When a variation doesn’t apply correctly:
- Check the JSON syntax, A single trailing comma or missing bracket breaks the entire file. WordPress silently ignores malformed variation files. Use
jsonlintor your editor’s JSON validation. - Inspect the
bodyelement, Browser DevTools shows which CSS custom properties are active. Compare the values against your variation file to verify the merge happened. - Check file location, Variation files must be in the
styles/directory at the theme root. Notassets/styles/, notjson/. - Clear caches, WordPress caches the merged theme.json output. Clear object cache and any full-page caches after adding or modifying variations.
- Check user overrides, If a user has customized Global Styles through the editor, their changes layer on top of the variation. Reset user styles to see the variation’s pure output.
Performance Impact
Style variations add negligible overhead:
- At rest, Variation files exist on disk but WordPress only reads the active one. Unused variations don’t affect page load.
- On activation, WordPress re-generates the CSS output from the merged theme.json. This is a one-time cost that’s cached.
- Font loading, Only fonts declared in the active variation are enqueued. Fonts from inactive variations are not loaded.
- CSS output, The generated CSS is the same size regardless of how many variations exist. It reflects only the active configuration.
The only real cost is theme download size. Each variation file is typically 1-3 KB of JSON. Font files bundled for variations can be larger, plan your font strategy accordingly.
Frequently Asked Questions
Can users customize a style variation after selecting it?
Yes. After selecting a variation, users can further customize colors, fonts, and other settings through the Global Styles panel. Their customizations layer on top of the variation. If they later switch to a different variation, their per-variation customizations are preserved separately.
Do style variations work with child themes?
Yes. A child theme can add its own styles/ directory with variations that override or extend the parent theme’s options. If a child theme variation has the same filename as a parent variation, the child’s version takes priority.
How many style variations should a theme include?
Three to six is the sweet spot. Enough to offer real variety, few enough that each variation is meaningfully different. The WordPress theme review guidelines don’t set a limit, but themes with dozens of near-identical variations provide a poor experience.
Can I include a variation that changes only typography?
Absolutely. A typography-only variation is a valid and useful option. Users who like your color palette but want a different font personality can select it without losing their preferred colors.
Do variations affect the block editor experience?
Yes. The block editor renders blocks using the active variation’s styles. Colors in the editor color picker, font options, and spacing presets all reflect the selected variation. What you see in the editor matches the frontend.
What to Build Next
Start with a dark variation, it’s the most requested option and the best test of your theme’s CSS custom property architecture. If your block theme uses consistent preset slugs and references variables instead of hardcoded values, the dark variation will work with minimal effort.
Then add one variation that changes both color and typography to create a genuinely distinct look. Test it against all your block patterns and templates. Fix any contrast issues, verify code block readability, and check button states.
The goal is a theme where each variation feels intentional, not just a palette swap, but a complete visual identity that works across every page and pattern in your theme.
