Tokens and contracts

Terminology

The current system has two token layers:

  1. Primitive — a technical calculated value without a UI role.
  2. 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:

  1. Is there a suitable WordPress preset?
  2. Is there a suitable semantic/Core variable?
  3. Can the existing API be used without losing meaning?
  4. Will the value be reused?
  5. Must child themes be able to override it?
  6. Must it be available to a Gutenberg user?
  7. Is it not CSS unique to a specific element?
SituationDefine in
Calculated scaleprimitives.css
Shared role outside theme.jsonsemantic.css
Parent/child contracttheme-core.css
Gutenberg-managed valuetheme.json
Site brand valueChild-theme CSS variables
Local component roleComponent CSS
One-off valuePlain 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.