Consuming tokens
Design Controller publishes the site's design system as CSS custom
properties named --dc-* on :root โ printed once at wp_head from a
single autoloaded option, so reading them costs no query โ and as three
PHP helpers for the cases where you need the actual value. This page is
for anyone whose theme or plugin should "follow the site" without a
settings screen of its own.
The one rule: var(--dc-โฆ, fallback)
- A variable has a value only while Design Controller is active and a design is published. Always give a fallback โ your old value โ so a site without the plugin looks exactly as it did.
- Never read a value and write it into an option of your own. Copies are what the plugin exists to remove.
- The variable names are a public contract (schema v1). Existing names do not change or disappear; new ones are only ever added.
Example: a plugin that follows the site
Say your plugin's stylesheet declares its own variables:
:root {
--my-primary: #0b67b2;
--my-primary-dark: #084d86;
--my-radius: 8px;
}
Make each one "the site's value if there is one, mine otherwise" โ a single line per variable, and no other selector changes:
:root {
--my-primary: var(--dc-color-brand-primary, #0b67b2);
--my-primary-dark: var(--dc-color-brand-primary-strong, #084d86);
--my-font: var(--dc-font-family-body, "Sarabun", sans-serif);
--my-radius: var(--dc-radius-md, 8px);
}
.my-button {
background: var(--my-primary);
border-radius: var(--my-radius);
font-family: var(--my-font);
}
Change the brand colour or the body font in Design Controller and the plugin follows at once โ inside the live preview too โ without knowing the plugin exists.
The variables you will use most
| Group | Variables | Notes |
| --- | --- | --- |
| Brand colours | --dc-color-brand-primary, -secondary, -accent | each with -soft, -strong, -contrast (derived; -contrast is the text colour on it) |
| Neutrals | --dc-color-neutral-50 โฆ -950 | a ladder from one seed |
| Semantic | --dc-color-semantic-success, -warning, -danger, -info | with -soft, -contrast |
| Surfaces and text | --dc-color-surface-page, -card, -muted, -inverse; --dc-color-text-default, -muted, -inverse, -link, -link-hover; --dc-color-border-default, -strong | |
| Fonts | --dc-font-family-heading, -body, -ui, -mono | the full stack, metric-matched fallbacks included |
| Sizes | --dc-font-size-xs โฆ -5xl (-md is the base), --dc-font-heading-h1-size โฆ -h6-size | fluid clamp() when enabled |
| Weights and leading | --dc-font-weight-regular, -medium, -semibold, -bold; --dc-font-line-height-tight, -normal, -relaxed | |
| Spacing | --dc-space-1 โฆ -12, --dc-space-section-sm, -md, -lg, --dc-layout-container-sm โฆ -xl, --dc-layout-gutter | |
| Shape and shadow | --dc-radius-sm, -md, -lg, -xl, -full; --dc-shadow-sm, -md, -lg; --dc-border-width-thin, -thick | |
| Motion | --dc-motion-duration-fast, -normal, --dc-motion-easing | |
| Components | --dc-component-button-primary-background, -text, -border, -radius, -padding-x, -padding-y, -weight; โฆ-secondary-*, โฆ-outline-*; --dc-component-input-*, --dc-component-card-*, --dc-component-nav-* | always set, even with the recipe off |
A variable name is the token path with camelCase turned to kebab-case
and . to -: font.lineHeight.normal becomes
--dc-font-line-height-normal. wp dctl export --resolved prints every
resolved value as DTCG JSON.
In PHP, when you need the value itself
For a PDF, an email or a chart โ anything that cannot read the page's CSS:
// A resolved hex (aliases and derived values computed); $fallback when there is no plugin or no value.
$primary = function_exists( 'dctl_token' ) ? dctl_token( 'color.brand.primary' ) : null;
$primary = $primary ?? '#0b67b2';
// A ready-made var(--dc-โฆ, fallback) string for an inline style.
$css = function_exists( 'dctl_var' ) ? dctl_var( 'radius.md', '8px' ) : '8px';
// The font stack for a role (heading | body | ui | mono), as a CSS string.
$font = function_exists( 'dctl_font_stack' ) ? dctl_font_stack( 'body' ) : '"Sarabun", sans-serif';
All three read the autoloaded option โ no query โ and are safe in any
template. The font files Design Controller hosts live in wp_font_dir();
wp dctl fonts list prints them.
Hooks worth knowing
| Hook | When |
| --- | --- |
| dctl_design_published (action; $design_id, $compiled) | something must happen after a publish โ regenerate a CSS file of your own, say |
| dctl_compiled_updated (action; $design_id, $compiled, $published) | every time the CSS on the site changes, recompiles from fonts or settings included |
| dctl_cache_purged (action; $ids) | after a purge โ purge your own cache too |
| dctl_cache_targets (filter) | add a host or plugin cache the plugin does not know: ['id', 'label', 'detect' => callable, 'purge' => callable] |
| dctl_component_selectors (filter; $map, $theme) | let the button, field, card and menu recipes cover your plugin's selectors |
| dctl_frontend_css (filter; $css, $compiled) | print more CSS in <style id="dctl-bridges"> โ only var(--dc-*), never a query |
| dctl_bridges (filter) | add a whole bridge for another module (BridgeInterface) |
Three things not to do
- Do not read
dctl_compiledor thedctl_designpost directly โ the internal shape may change; use the helpers or the variables. - Do not
!importantover a--dc-*variable. If you want a value that differs from the site's, declare your own variable as above and simply do not reference the plugin's. - Do not load a font from Google that Design Controller already hosts.
Use
var(--dc-font-family-body); the@font-facerules come from WordPress core.