Contents:
Terminology
The current system has two token layers:
- Primitive — a technical calculated value without a UI role.
- Semantic — a value with a shared purpose.
Additionally, there are:
- Core contract — values that the parent theme provides for child-theme overrides;
- WordPress presets — a public API generated from
theme.json; - local component variables — an optional tool within a specific component.
Do not call every file a separate token layer.
Primitive
Source: assets/css/vars/primitives.css.
A primitive is needed when a value is calculated by the system and serves as a building block for a higher layer.
--size-fluid300-16px: clamp(...);
--easing-ease: cubic-bezier(.4, 0, .2, 1);
Permitted consumers:
core-fse/theme.json;semantic.css;theme-core.css;- deliberate configuration of a system value in a child theme.
Do not use a primitive as a standard styling API:
/* Incorrect */
.promo-card {
gap: var(--size-fluid300-16px);
}
/* Correct */
.promo-card {
gap: var(--wp--preset--spacing--fluid-md);
}
The complete primitive-value catalog is not duplicated in the documentation. The CSS file itself is the source of truth.
Semantic
Source: assets/css/vars/semantic.css.
Use semantic variables where WordPress does not provide a named set of presets:
.dialog {
border-radius: var(--radius-medium);
z-index: var(--z-overlay);
transition-duration: var(--motion-duration-fast);
}
Existing letter-spacing variables are available for intentional typographic
requirements, not as mandatory block styling. Prefer inherited theme
typography and set letter-spacing only when the design clearly requires a
meaningful difference.
A semantic variable must describe a shared role or scale. It must not name a single block or page.
/* Acceptable */
--radius-medium: 10px;
/* Do not add globally for a single element */
--homepage-product-hero-left-column: 37.5rem;
Core contract
Source: assets/css/vars/theme-core.css.
Required brand values
Every production child theme must deliberately define:
--color-base;
--color-contrast;
--color-accent;
--color-on-accent;
--color-on-image;
--color-links;
--color-links-hover;
Parent-theme values are fallbacks, not a finished brand configuration.
--color-links intentionally uses the plural form. The name --color-link would conflict with the WordPress .has-link-color class.
Optional overrides
When needed, the child theme changes:
--_spacing--edge-x;
--_spacing--base-gap;
--_font-size-huge;
--_font-size-huge-fluid;
You can also override semantic values, for example:
--line-height-normal;
--letter-spacing-tight;
--radius-medium;
The --_ prefix denotes an internal helper, not a public CSS API. Its reason for existing is described in limitations.
WordPress presets
If a suitable value is published through theme.json, consumers must use the generated preset:
color: var(--wp--preset--color--accent);
padding: var(--wp--preset--spacing--fluid-md);
font-size: var(--wp--preset--font-size--lg);
In theme.json, WordPress supports a special reference syntax:
{
"fontFamily": "var:preset|font-family|basic"
}
Plain CSS uses the generated custom property:
font-family: var(--wp--preset--font-family--basic);
Component variables
There is currently no separate required component-token layer.
A local variable is acceptable when it:
- is repeated within the component;
- helps describe states or variants;
- remains local and does not clutter global
:root.
.card {
--card-media-ratio: 4 / 3;
}
Do not automatically move such a variable into semantic.css or theme.json.
When plain CSS is needed
Plain CSS is preferable to a new token when the value:
- is unique to one layout;
- should not be configurable by a child theme;
- is not exposed in Gutenberg;
- does not express a shared semantic role.
.product-hero {
grid-template-columns: minmax(0, 37.5rem) 1fr;
}
Process for adding a new value
Before adding it, answer:
- Is there a suitable WordPress preset?
- Is there a suitable semantic/Core variable?
- Can the existing API be used without losing meaning?
- Will the value be reused?
- Must child themes be able to override it?
- Must it be available to a Gutenberg user?
- Is it not CSS unique to a specific element?
| Situation | Define in |
|---|---|
| Calculated scale | primitives.css |
Shared role outside theme.json | semantic.css |
| Parent/child contract | theme-core.css |
| Gutenberg-managed value | theme.json |
| Site brand value | Child-theme CSS variables |
| Local component role | Component CSS |
| One-off value | Plain CSS |
Additional requirements:
- a new preset receives a stable, meaningful
slug; - a preset is not added solely for one block;
- a new Core contract always has a fallback;
- a primitive without a consumer is not added;
- removing or renaming a preset requires analysis of saved content;
- a public change is reflected in the documentation at the same time.