Particles UIDOCSDESIGN DECISION INFRASTRUCTUREStudioCLIMCP
Getting startedStudioTokensToken architectureThemes & modesChanges & governanceBranches & reviewsDesign documentationFigma pluginCLIConnect AI agentsMCP playbookWebhooks & CI/CDSecurityPrivacyTerms
Docs / Studio / Themes & modes

Themes & modes

Dark mode. A compact layout. High contrast. Every product ends up with a few switches — and modes let you build each one once, without ever duplicating your token set.

Think of your phone's Settings#

Your phone has an Appearance setting with two options — Light and Dark — and a separate Text Size setting with its own options. You pick one option per setting, and they never interfere: changing text size doesn't change your colours.

Particles works exactly the same way:

On your phoneIn ParticlesExample
A setting (Appearance)A mode groupTheme · Size · Contrast
An option (Dark)A modedark · compact · high
Your current choicesThe active modesdark + compact

Every project starts with one group, called Theme — that's where light and dark live. You can add more groups whenever your product needs another independent switch.

your projectMODE GROUPThemethe switch every project starts withlight● darkmodes — pick exactly oneMODE GROUPSizeadded the day you needed itcomfortable● compactindependent — flipping it never touches colourwhat you see = dark + compact

One pick per group. The picks combine on their own — there is no third switch for "dark and compact".

A mode stores only what changes#

Your project's base tokens are the complete set — what developers get by default. A mode is a small overlay on top: it lists only the tokens that differ when it's active. A dark mode might override 15 colour tokens (backgrounds, text, borders) and nothing else — every spacing, radius, and font value simply shines through from the base.

That's why modes are cheap to maintain: when you redesign a button's padding, you change it once in the base, and every mode picks it up automatically.

Creating a mode#

In the token editor, open the mode dropdown at the top and click Add mode. A two-step wizard asks exactly two questions:

StepQuestionWhat you do
1Which group does this mode belong to?Pick Theme, another existing group — or type a new group name (that alone creates the group).
2What is the mode called?Name it (dark, compact…) and optionally pick a mode in the same group to extend.

There is no separate "create a group" screen. A group exists the moment a mode names it, and disappears with its last mode — nothing extra to manage.

Never name a combination#

Without groups, teams end up encoding combinations into names: dark, compact-dark, high-contrast-compact-dark… Every new switch doubles the list, and each of those needs its own maintenance.

With groups, a combination is never a thing you build — it's just the current position of two switches. Two groups with two modes each give you four looks from four small overlays; adding a third group adds a couple of overlays, not a rewrite:

terminal
Theme:  ○ light   ● dark
Size:   ○ comfortable   ● compact

what you see  =  dark + compact     ← composed live, never built or named
i

Rule of thumb: if you're tempted to put a dash in a mode name to mean "and also" — that's two groups.

Keep each switch in its lane#

The whole system stays predictable because of one discipline: each group owns its own kind of token. Theme changes colours. Size changes spacing, radii, and type sizes. Because the two switches touch different tokens, they can never disagree — flip dark on and off all day, your layout never moves.

The rare value that depends on both — say, text that needs a heavier weight on dark backgrounds — isn't a conflict either: point the component token at a Theme-owned token, and the Theme switch carries the change.

Extending a mode#

A mode can extend another mode in the same group. "Brand A Dark" can extend "dark" and override only the accent colour — every other dark value is inherited. The value a token shows resolves top-down:

WinsSourceExample
1stOverride in the active modebrand-a-dark sets color/primary
2ndInherited from the extended modedark sets color/background
3rdThe base tokenbase sets everything else

Editing tokens while a mode is active#

With a mode selected in the editor, saving a token offers two choices:

ActionWhat happens
Save in [mode name]Adds an override to this mode only. The base token is untouched.
Save in baseEdits the base value — every mode that does not override it follows along.

Tokens overridden by the active mode show an orange badge; values inherited from an extended mode show a gray badge; everything else is the base value. To send a token back to its base value, click the reset button next to its override.

Live preview#

The preview panel always reflects the currently selected mode — switch in the dropdown and watch the components restyle instantly, no rebuild needed.

Modules ship their mode groups#

In multi-level setups (Foundation → Brand → Product, a Business plan capability), any module can own modes and every product that links it inherits them. The usual division: Foundation ships the modes its own primitives vary by — a surface ramp that flips in dark mode — and Brand ships the appearance modes that carry its identity, so light and dark travel with the brand. Structural groups like Size usually belong to each product, because density is a product decision. See Token architecture for the layering model.

Modes compose in the same order as tokens — Foundation, then Brand, then Product — so when two layers define a mode of the same name, the higher layer wins the tokens they both touch, and your product's own overrides win last.

Choosing which modes you inherit

Linking a module gives you all of its modes by default. To take only some, open Project → Token modules, expand Inherited themes on that module and switch to Choose themes. Two ways to pin, and the difference is what happens when the module publishes a new mode later:

PinYou getWhen the module adds a mode
An entire mode groupEvery mode in that groupIt arrives automatically
Individual modes by nameExactly the ones you pickedNothing changes until you pick it

A mode you don't inherit is gone completely: it never appears in your switcher, and it contributes nothing when your tokens resolve. Excluding a module's modes does not affect its tokens — you keep the whole base layer either way.

Products consume a module at a pinned release — upstream mode edits never change your product until you deliberately accept the update in Project → Token modules.

The big picture#

Putting it all together — where values come from, who owns each switch, and what actually ships:

FOUNDATIONcore valuesthe raw palette, scales, radii — defined oncefeedsBRANDcolour & voicenames the accents, ships appearanceTheme group · light | darkfeedsPRODUCTyour appcomponents, layout — inherits both layers aboveSize group · comfortable | compactships asWHAT USERS SEEone attribute per switch:<html data-theme="dark" data-size="compact">the combination is composed live —never built, never namedstandalone projects: same picture, minus the Foundation and Brand boxes — the product owns everything.

What developers get#

Each mode ships as its own small CSS block, keyed by its group — so the browser composes the active combination by itself:

terminal
<html data-theme="dark" data-size="compact">

[data-theme="dark"]  { …colour overrides }
[data-size="compact"] { …structure overrides }
GoalCommand
Sync base tokensparticles token-studio sync
Generate one mode as CSSparticles theme generate dark --out src/themes/dark.css
Generate every mode at onceparticles theme generate-all --out-dir src/themes
Export one exact combinationparticles token-studio export --modes theme:dark,size:compact
i

Activating a mode in the product is one attribute on any container element — no separate stylesheet builds, and no dark-compact.css anywhere.

Modes in every export format#

A plain export (no --modes) carries every mode the branch defines, in whatever shape the target language can actually switch on. Pass --modes instead and the selected combination is baked into the base output with no mode blocks on top — that is the one-exact-combination case above.

FormatHow modes arrive
tailwind-v4, css[data-axis="name"] blocks after the base block
scss$modes map — Sass has no runtime cascade, so it ships as data
json{ tokens, modes: [{ axis, name, values }] } (a bare array when there are no modes)
ts, jsa `modes` export alongside `theme`
dtcg$extensions → "com.particlesui.modes"
style-dictionary$modes
flutterAppTokens.modes — Map<String, Map<String, String>>
i

In CSS and Tailwind a mode block redefines only the variables it directly overrides; semantics, component tokens and composition utilities all reference those variables through var(), so they follow automatically. Version-pinned exports (--version) read a release snapshot and carry base values only.

← PREVIOUS
Token architecture
NEXT →
Changes & governance