Design tokens
Availability: prepared for Core 0.2.106, template capabilities 0.54.0 and MCP 0.45.80. These features are not in Core 0.2.104. Check your connected portal and rebuild a static site after upgrading; older published files do not change when the portal is upgraded.
Start with site settings
Section titled “Start with site settings”Open Site settings → Colors to set the palette. In Typography, choose
font families and expand Named typography and spacing scales to adjust the
steps. Use Styles to apply these steps to body text, heading roles and
reusable component styles. An AI agent can do the same through
update_site_settings and the style tools.
| Role | Use |
|---|---|
primary |
Main brand colour, links and primary actions |
secondary |
Supporting brand surfaces |
accent |
Occasional emphasis |
background |
Page canvas |
surface |
Cards and panels |
surface_hover |
Interactive surface; a darker derived shade unless overridden |
text |
Body copy |
text_light |
Muted copy, still with readable contrast |
on_primary, on_secondary, on_accent |
Text on the corresponding brand colour; automatic contrast-aware defaults |
A starting light palette is primary #2563eb, secondary #1e293b, accent
#f59e0b, background #ffffff, surface #f8fafc, text #0f172a, and
text_light #64748b. These values belong in settings, not copied into blocks.
Choose the role by meaning, not by which colour happens to look identical today.
The block editor shows palette choices first. Custom colour (exception) is
an explicit choice; it does not track palette changes. Record exceptions in
your starter workspace’s decisions.md, for example white text on a photograph.
Use a named style for a new component role, or a site CSS variable derived from
existing palette variables for a new shade. Avoid inventing unsupported bare
palette names. A derived shade can use
color-mix(in srgb, var(--color-surface) 92%, var(--color-primary)).
Typography and spacing
Section titled “Typography and spacing”| Scale | Steps |
|---|---|
type_size |
xs, sm, md, lg, xl, 2xl, 3xl |
space |
none, xs, sm, md, lg, xl |
leading |
tight, snug, normal, relaxed, loose |
Presentation fields keep their existing API names for compatibility. For example:
{ "type": "core/prose", "data": { "html": "<p>Editable text</p>", "font_size_px": { "mobile": "md", "desktop": "lg" }, "line_height": "relaxed", "font": "body" }}gap_px: "lg" uses the spacing scale. Numbers remain explicit overrides;
counts, breakpoints and image dimensions are not automatically scale values.
Read the block schema’s design_token metadata before using steps in a custom
field. Font choices are heading, body, mono or inherited; family names
belong only in settings.fonts. Configure the numeric scale once in
settings.design_scales, then use step names in role styles and block fields.
Palette names remain names in storage and become CSS variables at rendering.
Unknown colour values produce field_type_mismatch warnings with field paths.
Audit and migrate an existing design
Section titled “Audit and migrate an existing design”Run lint_design_tokens for saved content in the selected Version. It checks
Pages, partials, page templates, custom block types, styles and custom CSS and
returns source paths, counts and suggested tokens. The migration launch report
includes the same non-blocking findings. Unsaved drafts are not included.
CSS checking is heuristic: it cannot prove that dynamically constructed CSS or
third-party styles follow the palette. Suggestions require visual review.
css_findings lists custom CSS that works against the theme, each with the
platform feature that does the same job:
| Code | Finding | Use instead |
|---|---|---|
css_important_overuse |
10 or more !important declarations in one stylesheet |
Site settings, named Styles and block settings |
css_font_not_in_theme |
A font family that is not the heading, body or mono font | Set it as a theme font and use the font role |
css_duplicate_font_load |
A theme font loaded again by @font-face, @import or a font link in scripts_head |
Remove it; the theme loads its fonts once |
css_first_section_background_image |
The first section’s background image comes from custom CSS, so it loads late | The section’s background image setting or an image or cover block |
css_block_class_override |
Rules on platform classes such as .block-*, [data-block] or .site-header |
A named Style, the block’s settings, or the header and footer partials |
read_site_readiness summarizes the same findings in its
custom_css_overrides and duplicate_fonts items.
The site starter contains
scripts/typeroll/color_tokens.py. Run a dry run first:
python3 scripts/typeroll/color_tokens.py --plan /tmp/design-token-plan.jsonReview the plan and remove deliberate exceptions. Exact, unambiguous hex matches in direct block fields can be applied in a dedicated non-main Version:
python3 scripts/typeroll/color_tokens.py --apply /tmp/design-token-plan.jsonStop other editing in that Version during this operation. It checks scope, palette and current values, refuses existing unsaved drafts, and does not retry uncertain writes or publish. Templates save immediately; Pages and partials are saved through their normal working-copy flow. If interrupted, inspect the drafts before continuing. There is no site-wide transaction.
Ambiguous roles, nested/responsive colour values and custom CSS need manual review. Equal hex values do not establish that two elements should share a role. After migration, change primary and surface in a test Version and inspect header, footer, cards, menus and buttons at mobile, tablet and desktop widths without editing their blocks. Restore the test palette. Report dry-run counts separately from applied changes, then publish only after the normal approval and checks.
Fluid typography and component rhythm
Section titled “Fluid typography and component rhythm”Core 0.2.114 / MCP 0.45.85 / capabilities 0.55.0 adds responsive scales. Check the connected versions before using this contract. New sites use render version 10 in that release. For an existing site, test a Version first; upgrading rendering and replacing existing roles are separate, deliberate choices.
Use fonts.size_base (default 16), typography.ratio_mobile (1.2),
typography.ratio_desktop (1.25), typography.rhythm (1), and
typography.line_length (65 ch). Set the controls once, then use named type,
spacing and line-height steps. apply_standard_styles uses those steps;
overwrite: true resets existing roles, so review their current values first.
Explicit sizes and breakpoint overrides still win and are not silently migrated.
Heading role margins belong to editorial flow. Component titles use their own
padding/gap, and opening headings in containers/sections add no top margin.
Use role-linked core/heading size auto or theme; explicit hero sizes remain
deliberate exceptions. Do not compensate with per-card negative margins.
Compare 375, 768 and 1280px in Site settings → Design and in a full site preview.
After a rendering upgrade/rebuild, recheck cards, boxed headings, leading text
and webfonts. Boxes should get their outer air from padding. Keep exceptions
in decisions.md, and run lint_design_tokens before launch.
Contextual spacing (planned: Core 0.2.115, render version 11)
Section titled “Contextual spacing (planned: Core 0.2.115, render version 11)”This version is not yet released. It restores heading rhythm inside columns and grids, including the gap after an image. The first heading in a container still has no extra top margin. Article spacing overrides apply once, and named heading styles remain authoritative. A site without saved heading roles also gets proportional spacing.
An explicit named style with space_before: "0" still means zero. If a site
used that as a workaround for opening headings, check whether the same style
was also assigned to headings later in articles. Preview removing the workaround
from those uses; the new opening-heading rule handles the first heading without
removing spacing from later headings. Do not overwrite all saved roles.
Navigation links keep their default 44px touch targets; they no longer add a
second vertical gap between those targets. Explicit gap_px and compact
settings remain available. When reviewing a footer, compare the heading-to-link
gap with the gaps inside the link group, not only the overall footer height.
A reserved video area is not a heading margin. Review embeds with the actual player visible and after consent choices; do not hide iframes in screenshots used to approve a layout. Test unavailable or blocked embeds separately.
Responsive step overrides
Section titled “Responsive step overrides”{ "typography": { "ratio_mobile": 1.2, "ratio_desktop": 1.25, "rhythm": 1, "line_length": 65 }, "design_scales": { "type_size": { "3xl": "clamp(2rem, 1rem + 3vw, 3.5rem)" }, "space": { "xl": { "mobile": "2rem", "tablet": "3rem", "desktop": "4rem" } } }}Supported map keys are mobile, tablet, laptop, desktop and wide, using the
site’s breakpoint widths. Missing lower map entries keep the platform default;
subsequent widths inherit the latest override. A step accepts nonnegative
px/rem/em lengths or clamp(min, length + vw, max) (minus is also supported).
Line-height steps accept unitless values from 1 to 2, including maps. Unknown
keys and unsafe CSS are refused. design_scales and typography each replace
their complete settings object: read, modify, then write to preserve other overrides.
Fluid default endpoints are 360px and 1280px. rem bounds respect root font size, including browser text-size preferences. Prose defaults to the configured line length; explicit block widths remain authoritative. The preview uses saved role styles with unsaved controls and disables font downloads for isolation. Always check the full site with its actual webfonts before publishing.
Check it
Section titled “Check it”Run lint_design_tokens. Its findings, including css_findings for custom CSS
that works against the theme, should be fixed or recorded as deliberate
exceptions. The launch readiness report
shows the same CSS findings for the whole site.