UI kit

Every token in theme.json — colour, type, spacing, radius, layout, motion — shown twice over: as live output, and as the property and value that produced it. Plus one instance of each core block the theme styles. If something looks wrong here it is wrong everywhere.

The body and UI face on this page is Proxima Nova, served from the client’s Adobe Fonts kit — the real brand face, not the stand-in this page used to carry. One thing to check before trusting a capture: the kit is remote and domain-allowlisted, so a host that is not on the web project falls back to a system sans silently. If the body text below looks like Arial or DejaVu Sans, the fonts did not load and nothing on this page is typography evidence.


How to read this page

SCSS does not use these properties directly: assets/scss/_variables.scss aliases each one to a short --chn-* name and component styles reference the alias. That alias layer is hand-written, so it is not listed here — read it there, in one place, rather than in two.


Colour

Swatches

Dark / Deepest
dark-deepest

Dark / Primary
dark-primary

Dark / Elevated
dark-elevated

Dark / Medium
dark-medium

Dark / Border
dark-border

Dark / Gray Gold
dark-gray-gold

Gold / Primary
gold-primary

Gold / Deep
gold-deep

Gold / Light
gold-light

Light / Light
light

Light / Off White 85
off-white-85

Properties, values and contrast

NameSlugCustom propertyValueContrast vs page ground
Dark / Deepestdark-deepest--wp--preset--color--dark-deepest#0c0b0b1.07:1 · fails AA
Dark / Primarydark-primary--wp--preset--color--dark-primary#151414the page ground itself
Dark / Elevateddark-elevated--wp--preset--color--dark-elevated#201e1d1.11:1 · fails AA
Dark / Mediumdark-medium--wp--preset--color--dark-medium#2e2c271.32:1 · fails AA
Dark / Borderdark-border--wp--preset--color--dark-border#2e2b291.31:1 · fails AA
Dark / Gray Golddark-gray-gold--wp--preset--color--dark-gray-gold#695f4f2.93:1 · fails AA
Gold / Primarygold-primary--wp--preset--color--gold-primary#c4b0728.58:1 · AA at any size
Gold / Deepgold-deep--wp--preset--color--gold-deep#94834e4.92:1 · AA at any size
Gold / Lightgold-light--wp--preset--color--gold-light#d9cda011.55:1 · AA at any size
Light / Lightlight--wp--preset--color--light#e1dac213.14:1 · AA at any size
Light / Off White 85off-white-85--wp--preset--color--off-white-85#cfcfcf11.80:1 · AA at any size
Ratios are computed on every request with the WCAG 2.1 relative-luminance formula, against the page ground (#151414) that styles.color.background resolves to. 4.5:1 is AA for normal text; 3:1 covers large text and non-text UI. A ratio is a floor, not a permission: a token that clears AA against the ground can still fail against another token.

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ DefinedPalettesettings.color.palette11 tokensEvery token from the approved design file
☑ DefinedPage groundstyles.color.backgroundvar(--wp--preset--color--dark-primary)The colour every ratio on this page is measured against
☑ DefinedPage textstyles.color.textvar(--wp--preset--color--off-white-85)The default foreground, inherited by everything that does not override it
⚠ Known gapLink colourstyles.elements.link.color.text— not set —Links are underlined and otherwise indistinguishable from body copy. WCAG 1.4.1 forbids colour as the only cue, which the underline satisfies — but a link that never differs in colour is still a decision nobody made
☐ Open decisionLink hover colourstyles.elements.link.:hover.color.text— not set —The hover state drops the underline. With no colour change, hover removes the only cue a link had
⚠ Known gapGrounds with no AA-safe foreground——dark-gray-gold — best available is light at 4.48:1 — normal-size text on these is a defect, not a judgement call
☐ Open decisionSemantic rolesno path — the shape is undecided— not set —Which token is the primary CTA, which is a section ground, which is a border. The palette names values, not roles, so every use is a judgement call — and theme.json has no path for a role, which is why this row has none
5 of the 10 other tokens clear 4.5:1 as text on the page ground, computed on this request. The rest are grounds, borders and elevations rather than foregrounds — a low ratio there is the point, not a defect.

Type

Families

display · --wp--preset--font-family--display

Handgloves — The quick brown fox jumps over the lazy dog

body · --wp--preset--font-family--body

Handgloves — The quick brown fox jumps over the lazy dog

quote · --wp--preset--font-family--quote

Handgloves — The quick brown fox jumps over the lazy dog

NameSlugCustom propertyStackFaces
Displaydisplay--wp--preset--font-family--displayForum, Georgia, 'Times New Roman', serif2 bundled (latin, latin-ext)
Body & UIbody--wp--preset--font-family--body'proxima-nova', system-ui, -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serifsystem stack — no bundled face
Quotequote--wp--preset--font-family--quote'EB Garamond', Georgia, 'Times New Roman', serif2 bundled (latin, latin-ext)
Every slug names the ROLE, not the face — which is what made swapping the body face to Proxima Nova a one-line change to one family definition, with no block or pattern touched. Two of the three are self-hosted latin and latin-ext only, and Proxima Nova is subset by Adobe: several of the languages under discussion have no glyph coverage in any of them.

Headings — h1 to h6

Real heading elements, so what you see is the element style itself and not a paragraph dressed up as one. Only h1, h2 and h3 are designed. The design defines three heading styles, so theme.json styles three elements; h4–h6 below are the browser’s own scale inheriting the body face, and they are not brand output. Nothing should reach for them until the design says what they are.

The specimen below is deliberately this page’s second h1 — a heading specimen has to be a real h1 to be worth anything. That is defensible on an internal reference page and nowhere else: do not copy a second h1 into a module or a client page.

h1 — display size · Display family · uppercase · fluid

Handgloves

h2 — heading-2 size · body family · uppercase · tracked

Handgloves

h3 — heading-3 size · body family

Handgloves

h4 — ⚠ no element style · browser default

Handgloves

h5 — ⚠ no element style · browser default

Handgloves

h6 — ⚠ no element style · browser default

Handgloves

Paragraph sizes

Each named size applied to a p, labelled with its slug, its custom property and its value. Core’s own small, medium, large and x-large are not in this theme — a stray reference to one silently resolves to a core default rather than erroring, which is the more expensive kind of mistake.

display · --wp--preset--font-size--display · clamp(4rem, 2.84rem + 4.762vw, 7.125rem)

Handgloves

display-sm · --wp--preset--font-size--display-sm · 4rem

The quick brown fox jumps over the lazy dog

display-xs · --wp--preset--font-size--display-xs · 2.25rem

The quick brown fox jumps over the lazy dog

heading-2 · --wp--preset--font-size--heading-2 · 2.25rem

Handgloves

heading-3 · --wp--preset--font-size--heading-3 · 1.5rem

The quick brown fox jumps over the lazy dog

body-xl · --wp--preset--font-size--body-xl · 1.3125rem

The quick brown fox jumps over the lazy dog

body-lg · --wp--preset--font-size--body-lg · 1.25rem

The quick brown fox jumps over the lazy dog

body · --wp--preset--font-size--body · 1rem

The quick brown fox jumps over the lazy dog

body-sm · --wp--preset--font-size--body-sm · 0.875rem

The quick brown fox jumps over the lazy dog

caption · --wp--preset--font-size--caption · 0.75rem

The quick brown fox jumps over the lazy dog

meta · --wp--preset--font-size--meta · 0.6875rem

The quick brown fox jumps over the lazy dog

button · --wp--preset--font-size--button · 0.875rem

The quick brown fox jumps over the lazy dog

quote · --wp--preset--font-size--quote · 1.6875rem

Handgloves

Size properties, values and metrics

NameSlugCustom propertySizeLine heightLetter spacing
Displaydisplay--wp--preset--font-size--displayclamp(4rem, 2.84rem + 4.762vw, 7.125rem)0.9—
Display Smalldisplay-sm--wp--preset--font-size--display-sm4rem1.3—
Display Extra Smalldisplay-xs--wp--preset--font-size--display-xs2.25rem0.9—
Section Headingheading-2--wp--preset--font-size--heading-22.25rem1.10.06em
Subheadingheading-3--wp--preset--font-size--heading-31.5rem1.6—
Lead Largebody-xl--wp--preset--font-size--body-xl1.3125rem1.5—
Leadbody-lg--wp--preset--font-size--body-lg1.25rem1.5—
Bodybody--wp--preset--font-size--body1rem1.5—
Body Smallbody-sm--wp--preset--font-size--body-sm0.875rem1.45—
Captioncaption--wp--preset--font-size--caption0.75rem1.4—
Metameta--wp--preset--font-size--meta0.6875rem1.20.1em
Buttonbutton--wp--preset--font-size--button0.875rem10.1em
Quotequote--wp--preset--font-size--quote1.6875rem1.45—
A fontSizes preset carries a size and nothing else, so line height and tracking live in settings.custom.text, emitted as --wp--custom--text--{slug}--line-height and --letter-spacing. A block reaches them through the matching type style in styles/ — the specimens above are rendered that way, which is why they carry their leading and tracking and a bare size pick does not. An em dash means the style does not track — which is not the same as 0 being specified. The Size column is the value each step takes at and above the 48rem breakpoint; below it the root font-size is viewport-derived, so every step takes a different value from assets/scss/_token-breakpoints.scss and the sizes here are not what a phone renders. That root is a rem + vw hybrid so the browser font-size preference still moves type, but it moves it by 23% at a 24px default rather than the 200% WCAG 1.4.4 asks for — so text resize is mitigated below 48rem, not satisfied, and that remainder is not yet accepted.

Element styles

ElementDeclared in theme.json
h1typography.fontFamily: var(--wp--preset--font-family--display)
typography.fontSize: var(--wp--preset--font-size--display)
typography.fontWeight: 400
typography.lineHeight: var(--wp--custom--text--display--line-height)
typography.textTransform: uppercase
h2typography.fontSize: var(--wp--preset--font-size--heading-2)
typography.fontWeight: 400
typography.letterSpacing: var(--wp--custom--text--heading-2--letter-spacing)
typography.lineHeight: var(--wp--custom--text--heading-2--line-height)
typography.textTransform: uppercase
h3typography.fontSize: var(--wp--preset--font-size--heading-3)
typography.fontWeight: 400
typography.lineHeight: var(--wp--custom--text--heading-3--line-height)
h4— none —
h5— none —
h6— none —
p (root)fontFamily: var(--wp--preset--font-family--body)
fontSize: var(--wp--preset--font-size--body)
lineHeight: var(--wp--custom--text--body--line-height)
a (link):hover.typography.textDecoration: none
typography.textDecoration: underline
What THIS THEME declares, read from its own theme.json — not the merged tree, which folds in core's defaults and would show the button already carrying a fill colour. Rows reading "none" are undesigned: the element still renders, using whatever core supplies plus the browser. WordPress emits element styles at :root :where(…), which has no specificity, so any preset class on a block wins over the row above.

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ DefinedFamiliessettings.typography.fontFamilies3 roles4 font faces bundled and self-hosted
☑ DefinedThe body and UI facesettings.typography.fontFamilies[slug=body].fontFamily'proxima-nova', system-ui, -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serifProxima Nova, the real brand face, from the client's Adobe Fonts kit — it replaced the Figtree stand-in and carries no fontFace here because Adobe's licence permits delivery only from their service. Two things it brought with it are still open: Adobe serves it font-display: auto rather than swap, which is a setting on their web project and not overridable from our CSS; and whether the kit is in scope for Consent Studio is unresolved (TRD open item 5), which now matters because the request ships on every page
☑ DefinedSizessettings.typography.fontSizes13 sizesOne per named design style
☑ DefinedMetricssettings.custom.text13 stylesline height on 13 styles, letter spacing on 3 — an absent tracking value means the style does not track, which is not the same as 0
☑ Definedh1 element stylestyles.elements.h1typography.fontFamily: var(--wp--preset--font-family--display)
typography.fontSize: var(--wp--preset--font-size--display)
typography.fontWeight: 400
typography.lineHeight: var(--wp--custom--text--display--line-height)
typography.textTransform: uppercase
Declared from the approved design
☑ Definedh2 element stylestyles.elements.h2typography.fontSize: var(--wp--preset--font-size--heading-2)
typography.fontWeight: 400
typography.letterSpacing: var(--wp--custom--text--heading-2--letter-spacing)
typography.lineHeight: var(--wp--custom--text--heading-2--line-height)
typography.textTransform: uppercase
Declared from the approved design
☑ Definedh3 element stylestyles.elements.h3typography.fontSize: var(--wp--preset--font-size--heading-3)
typography.fontWeight: 400
typography.lineHeight: var(--wp--custom--text--heading-3--line-height)
Declared from the approved design
⚠ Known gaph4 element stylestyles.elements.h4— not set —Falls through to the browser scale in the body face. The design defines three heading styles, so do not invent this one — and do not reach for the level to get a size
⚠ Known gaph5 element stylestyles.elements.h5— not set —Falls through to the browser scale in the body face. The design defines three heading styles, so do not invent this one — and do not reach for the level to get a size
⚠ Known gaph6 element stylestyles.elements.h6— not set —Falls through to the browser scale in the body face. The design defines three heading styles, so do not invent this one — and do not reach for the level to get a size
☑ DefinedRoot paragraph stylestyles.typographyfontFamily: var(--wp--preset--font-family--body)
fontSize: var(--wp--preset--font-size--body)
lineHeight: var(--wp--custom--text--body--line-height)
What an unclassed p inherits
☐ Open decisionNon-latin coverage——latin and latin-ext only. Several of the languages under discussion have no glyph coverage at all in the bundled faces — an i18n delivery question rather than a token one, but it does not stop being true here
Values and counts read from theme.json on this request; a row flips to Defined by itself the day its token lands.

Buttons and variants

Every CTA on this site is the native wp-block-button, so these are all the shapes one can take. Nothing here is a bespoke component, per docs/wordpress.md §4.

ElementDeclared in theme.json
buttonborder.color: var(--wp--preset--color--gold-light)
border.radius: 0
border.style: solid
border.width: 1px
color.background: transparent
color.text: var(--wp--preset--color--gold-light)
spacing.padding.bottom: var(--wp--preset--spacing--md)
spacing.padding.left: var(--wp--preset--spacing--xl)
spacing.padding.right: var(--wp--preset--spacing--xl)
spacing.padding.top: var(--wp--preset--spacing--md)
typography.fontFamily: var(--wp--preset--font-family--body)
typography.fontSize: var(--wp--preset--font-size--button)
typography.fontWeight: 500
typography.letterSpacing: var(--wp--custom--text--button--letter-spacing)
typography.lineHeight: var(--wp--custom--text--button--line-height)
typography.textTransform: uppercase
Typography only. No colour, no border, no radius, no states — which is what the first pair of specimens below is showing you.

Default — no colour tokens defined

That grey is core’s, not the brand’s. styles.elements.button sets typography only, so an uncoloured button falls back to WordPress’s default #32373c on white — a colour in no palette on this project. The fix is a design decision about the primary CTA, not a value to invent here, so it stays visible until it is ruled.

Palette fills

FillBackgroundTextContrast
Gold primarygold-primary · #c4b072dark-primary · #1514148.58:1 · AA at any size
Gold deepgold-deep · #94834edark-deepest · #0c0b0b5.26:1 · AA at any size
Lightlight · #e1dac2dark-primary · #15141413.14:1 · AA at any size
Three pairings from the approved palette, each computed rather than assumed. They are candidates, not a decision: which one is the primary CTA is the design call that is still open.

Palette outlines

The outline style draws its border from the text colour

Radius variants

The three radius tokens on one button

Width variants

Core’s 25 / 50 / 75 / 100% fills, for stacked mobile CTAs

Two things these specimens cannot show. No href: every button here is deliberately link-less, because a specimen pointing at # is a dead link that a keyboard or screen-reader user still has to tab through — the real link under Links and lists is where focus behaviour is demonstrated. No states: hover, focus and disabled are not in theme.json either, and are as undefined as the fill colour.

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ DefinedTypographystyles.elements.button.typographyfontFamily: var(--wp--preset--font-family--body)
fontSize: var(--wp--preset--font-size--button)
fontWeight: 500
letterSpacing: var(--wp--custom--text--button--letter-spacing)
lineHeight: var(--wp--custom--text--button--line-height)
textTransform: uppercase
The only thing the design settles about a button
☑ DefinedFill colourstyles.elements.button.color.backgroundtransparentUndeclared, so core's #32373c wins — a colour in no palette on this project. Every CTA has to carry its own fill today, and two authors will pick differently
☑ DefinedText colourstyles.elements.button.color.textvar(--wp--preset--color--gold-light)Undeclared, so core's #fff wins. The pairing that ships by default is therefore nobody's decision
☑ DefinedBorderstyles.elements.button.bordercolor: var(--wp--preset--color--gold-light)
radius: 0
style: solid
width: 1px
No width, colour or style. The outline variants above get their border from core's block style, not from a token
☑ DefinedRadiusstyles.elements.button.border.radius0A square CTA is the default. Which of the three radius tokens a button takes is undecided — the variants above are candidates, not a decision
⚠ Known gapHover statestyles.elements.button.:hover— not set —No hover treatment at all. On a pointer device the button is inert-looking until it is clicked
⚠ Known gapFocus statestyles.elements.button.:focus— not set —The global focus ring in _base.scss is the only thing keeping the button keyboard-visible — which satisfies WCAG 2.4.7 by backstop rather than by design
☐ Open decisionActive statestyles.elements.button.:active— not set —No pressed feedback
☑ DefinedPalette pairings shown above——all three clear AA; the tightest is 5.26:1
☐ Open decisionWhich variant is primary——The design has not named a primary CTA, so nothing here is the button — they are the shapes one could be
Read from the theme's own theme.json, so "not set" means THIS theme is silent — core still supplies a value, which is exactly why the default specimens are grey.

Spacing

Design tokens

The whole of what the design defines, each bar exactly the height of its token.

sm · 0.5rem · --wp--preset--spacing--sm

md · 1rem · --wp--preset--spacing--md

xl · 2rem · --wp--preset--spacing--xl

NameSlugCustom propertyValue
SMsm--wp--preset--spacing--sm0.5rem
MDmd--wp--preset--spacing--md1rem
XLxl--wp--preset--spacing--xl2rem
The global block gap is var(--wp--preset--spacing--md), so it is the vertical rhythm of this page. Root padding is clamp(1.25rem, 4.4444vw, 4rem) — a clamp between the mobile and desktop gutters rather than a step on the scale.

⚠ The scale stops there, and the gap is known. There is no lg and no section-padding token at all, while the approved frames pad their sections by five values that are not in the collection. Do not invent an lg and do not hard-code a section pad around it — raise it.

The generated fallback scale

SlugCustom propertyValue
20--wp--preset--spacing--200.44rem
30--wp--preset--spacing--300.67rem
40--wp--preset--spacing--401rem
50--wp--preset--spacing--501.5rem
60--wp--preset--spacing--602.25rem
70--wp--preset--spacing--703.38rem
80--wp--preset--spacing--805.06rem
⚠ These are NOT design tokens. They exist because core's own fallback templates and the editor's spacing UI need more than three steps, and they are generated from a ratio rather than drawn. Nothing in a block or pattern should reach for one.

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ Definedsm stepsettings.spacing.spacingSizes[slug=sm].size0.5remFrom the approved design
☑ Definedmd stepsettings.spacing.spacingSizes[slug=md].size1remFrom the approved design
☐ Open decisionlg stepsettings.spacing.spacingSizes[slug=lg].size— not set —Absent, and absent on purpose: adding it is a design decision about the section rhythm, not a gap to fill locally. The path is what it would be, since it would join the existing scale
☑ Definedxl stepsettings.spacing.spacingSizes[slug=xl].size2remFrom the approved design
☑ DefinedGlobal block gapstyles.spacing.blockGapvar(--wp--preset--spacing--md)The vertical rhythm of every stacked block, including this page
☑ DefinedRoot gutterstyles.spacing.padding.leftclamp(1.25rem, 4.4444vw, 4rem)A clamp between the mobile and desktop frames rather than a step on the scale
⚠ Known gapSection paddingno path — the shape is undecided— not set —No token at any size, and no path either — the approved desktop frames pad sections by five different values and stack children on a sixth, none of them in the collection, so there is nothing yet to name. Every section built today hard-codes a pad or invents one
☑ DefinedThe generated fallback scalesettings.spacing.spacingScaledefault.increment: 1.5
default.mediumStep: 1.5
default.operator: *
default.steps: 7
default.unit: rem
Declared, and NOT a design token. It generates 7 extra steps because core's fallback templates and the editor UI need a scale; they look like tokens in that UI and are not
Each step read back by slug from theme.json on this request.

Radius

md
0.5rem

pill
999rem

sm
0.25rem

TokenCustom propertyValue
md--wp--custom--radius--md0.5rem
pill--wp--custom--radius--pill999rem
sm--wp--custom--radius--sm0.25rem
Radius is not a preset, so there are no editor controls and no has-* classes for it: a block reaches the token through the custom property, which is why the button variants above carry it as an inline border-radius.

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ Definedsmsettings.custom.radius.sm0.25remFrom the approved design
☑ Definedmdsettings.custom.radius.md0.5remFrom the approved design
☐ Open decisionlgsettings.custom.radius.lg— not set —Not defined. Listed because a component scale usually grows one — if a design calls for it, this is where it goes, and until then nothing should approximate it
☑ Definedpillsettings.custom.radius.pill999remFrom the approved design
☐ Open decisionfullsettings.custom.radius.full— not set —Not defined. Listed because a component scale usually grows one — if a design calls for it, this is where it goes, and until then nothing should approximate it
☐ Open decisionWhich component takes which——Undecided. Nothing in the design maps a radius to a component, so a card, a button and an image could each end up with a different one by accident
⚠ Known gapEditor exposure——Radius lives in settings.custom, not in a preset, so an editor gets no control for it and cannot pick the token by name — only a developer can apply one

Layout widths

alignwide

default content width — what this paragraph is measured to

Nametheme.json pathCustom propertyValue
Contentsettings.layout.contentSize--wp--style--global--content-size44.4444rem
Widesettings.layout.wideSize--wp--style--global--wide-size68.8889rem
Media (media)settings.custom.width.media--wp--custom--width--media60rem
Root gutterstyles.spacing.padding.left / .right--wp--style--root--padding-left / -rightclamp(1.25rem, 4.4444vw, 4rem)
The media width is the 16:9 livestream and trailer embed; it has no specimen here because there is no designed module to hang it on yet. Layout was not tokenised in the design file — these were derived from the recurring inner containers on the approved desktop frame, which is why they are the values most likely to move.

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ Declared · not approvedContent widthsettings.layout.contentSize44.4444remThe default measure — what an unaligned block gets
☑ Declared · not approvedWide widthsettings.layout.wideSize68.8889remWhat alignwide gets. Both are live in the editor
☑ Declared · not approvedMedia widthsettings.custom.width.media60remThe 16:9 livestream and trailer embed. No specimen on this page — there is no designed module to hang it on yet
☑ Declared · not approvedRoot gutterstyles.spacing.padding.leftclamp(1.25rem, 4.4444vw, 4rem)The page edge inset, and the reason alignwide lands where it does
☐ Open decisionBreakpointsno path — the shape is undecided— not set —Not in theme.json at all — WordPress has no path for them, so they live as SCSS mixins in _breakpoints.scss, and that scale is a proposal rather than a ratified standard. Nothing on this page can show them
⚠ Known gapProvenance——Layout was not tokenised in the design file. Every value above was measured off the approved desktop frame, which makes these the tokens most likely to move when the design is revisited

Motion

TokenCustom propertyValue
duration--wp--custom--motion--duration0.3s
ease--wp--custom--motion--easecubic-bezier(0.4, 0, 0.2, 1)
⚠ These two are the scaffold's guess, not a spec — they are the only tokens on this page that did not come from the approved design. Motion is also the live performance risk on this build, since mobile performance is a named client success criterion. Every animation still has to degrade under prefers-reduced-motion, which _base.scss backstops globally.

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ Declared · not approvedDurationsettings.custom.motion.duration0.3sDeclared, but this value did not come from the design — it is the scaffold's guess, carried forward so motion has one source rather than none
☑ Declared · not approvedEasingsettings.custom.motion.easecubic-bezier(0.4, 0, 0.2, 1)Same: a plausible curve nobody approved
☐ Open decisionA slow duration for larger movessettings.custom.motion.duration-slow— not set —Not defined. One duration cannot serve both a hover tint and a section reveal, so this is the first token motion work will reach for
☐ Open decisionEntrance easingsettings.custom.motion.ease-out— not set —Not defined. A single symmetric curve is the giveaway that no motion spec exists
☐ Open decisionA motion spec——No durations, easings or triggers anywhere on record. Mobile performance is a named client success criterion, which makes this the token layer's highest-consequence gap
☑ DefinedReduced-motion backstop——_base.scss cancels animation and transition globally under prefers-reduced-motion, so a block that forgets the mixin still degrades

Core blocks

One instance of each core primitive the theme styles. Per docs/wordpress.md §4 these are what a module composes from — never a hand-rolled parallel component.

Links and lists

Body copy with an inline link back to the front page, underlined by default and losing the underline on hover. Tab to it to see the focus ring, which is never removed.

  1. Create a page with the slug ui-kit.
  2. templates/page-ui-kit.html binds to that slug on its own — nothing to select.
  3. The page’s own content still renders, below, so notes can be added without touching the theme.

Quotations

Tokens are the contract. A screenshot is only evidence of what the tokens produced.

docs/wordpress.md §4–5

Never report that tests pass. Report what you actually ran.

docs/wordpress.md §5

Disclosure

What this page is not

It is not the kitchen-sink accessibility page described in docs/wordpress.md §6, which harvests every theme block from real published content for a single axe run. And it is not a designed module: nothing here is an approved composition, only the raw material one would be built from.

Code

.chn-example {
	color: var(--chn-gold-primary);
	padding-block: var(--chn-space-xl);
	border-radius: var(--chn-radius-md);
}

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ DefinedPrimitives styled and rendering——Buttons, links, lists, quote, pullquote, table, details, separator, code — each one an instance on this page, so a change to any of them shows up here first
☐ Open decisionCore block style overridesstyles.blocks— not set —Nothing declared. Every core-block correction so far is SCSS in assets/scss/blocks/ rather than theme.json — which is the right place for the contrast fixes the dark ground needs, but it does mean the editor sees them only through the canvas stylesheet
⚠ Known gapPrimitives with no specimen——Image, gallery, embed, cover and navigation. Each needs real media or a real menu, and inventing either would make this page lie about what the site has
☐ Open decisionAn axe run against this URL——Not done. There is no a11y tooling in the repo and no CI gate, so the accessibility of this page is a construction claim (tier 1), not a measurement (tier 2) — and "zero violations" is only ever a claim about the pages actually audited
⚠ Known gapNeighbour-dependent findings——Heading order and landmark counts read oddly here by design: this page carries two h1s and stacks specimens out of their real layout. Read those on a real page, never on this one

Where the values live

The map from a token layer to the theme.json path that defines it and the property it emits. This is the one table on the page that is hand-written, because it describes shape rather than values — the shape is WordPress’s, not this project’s.

Token layertheme.json pathEmitted property
Coloursettings.color.palette--wp--preset--color--{slug}
Type sizessettings.typography.fontSizes--wp--preset--font-size--{slug}
Type familiessettings.typography.fontFamilies--wp--preset--font-family--{slug}
Type metricssettings.custom.text--wp--custom--text--{slug}--line-height
Spacingsettings.spacing.spacingSizes--wp--preset--spacing--{slug}
Radiussettings.custom.radius--wp--custom--radius--{slug}
Motionsettings.custom.motion--wp--custom--motion--{slug}
Layoutsettings.layout--wp--style--global--content-size, --wp--style--global--wide-size
Element stylesstyles.elements.{element}:root :where(…) rules, not properties

Theme blocks

The theme ships one block of its own so far, acf/placeholder-slot, and it is deliberately absent from this page. It is an ACF block, ACF Pro is not in the local plugin baseline, and every render.php here returns early without it — so an instance would render nothing and read as a broken kit rather than a missing plugin. Add it here in the same change that adds ACF Pro locally.

Recap

StatusToken or propertytheme.json pathValueWhere it stands
☑ Definedacf/placeholder-slot——Registers and renders — but only where ACF Pro is active, which the local stack is not
⚠ Known gapA specimen on this page——Blocked on the plugin baseline, not on the block. Add the instance in the same change that adds ACF Pro locally, or the kit shows an empty div and reads as broken
☐ Open decisionPer-module slot specs——Dimensions, load behaviour and expected content for each of the launch slots are undecided — which is exactly why they are block fields rather than constants
☐ Open decisionEvery other module——Nothing else is built. A designed module is the next thing this page should grow a section for — and when it does, it belongs on the kitchen-sink page too