DEV3 Technologies home

theme.json colour, type and spacing presets

A compact theme.json that defines a palette, fluid font sizes, a spacing scale and element styles for the block editor.

WordPressThemingtheme-jsonblock-themedesign-tokensfse

Code

{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 3,
  "settings": {
    "appearanceTools": true,
    "layout": { "contentSize": "720px", "wideSize": "1200px" },
    "color": {
      "defaultPalette": false,
      "palette": [
        { "slug": "base", "name": "Base", "color": "#ffffff" },
        { "slug": "contrast", "name": "Contrast", "color": "#10231f" },
        { "slug": "primary", "name": "Primary", "color": "#0f766e" },
        { "slug": "accent", "name": "Accent", "color": "#e9a23b" },
        { "slug": "surface", "name": "Surface", "color": "#f1f5f4" }
      ]
    },
    "typography": {
      "fluid": true,
      "fontFamilies": [
        {
          "slug": "body",
          "name": "System sans",
          "fontFamily": "system-ui, -apple-system, 'Segoe UI', sans-serif"
        },
        {
          "slug": "display",
          "name": "System serif",
          "fontFamily": "Georgia, 'Times New Roman', serif"
        }
      ],
      "fontSizes": [
        { "slug": "small", "name": "Small", "size": "0.875rem" },
        { "slug": "medium", "name": "Medium", "size": "1.125rem", "fluid": { "min": "1rem", "max": "1.25rem" } },
        { "slug": "large", "name": "Large", "size": "1.75rem", "fluid": { "min": "1.4rem", "max": "2.1rem" } },
        { "slug": "x-large", "name": "Extra large", "size": "2.75rem", "fluid": { "min": "2rem", "max": "3.5rem" } }
      ]
    },
    "spacing": {
      "spacingScale": { "steps": 0 },
      "spacingSizes": [
        { "slug": "20", "name": "S", "size": "0.75rem" },
        { "slug": "40", "name": "M", "size": "1.5rem" },
        { "slug": "60", "name": "L", "size": "clamp(2rem, 4vw, 3.5rem)" },
        { "slug": "80", "name": "XL", "size": "clamp(3rem, 8vw, 7rem)" }
      ]
    }
  },
  "styles": {
    "color": { "background": "var(--wp--preset--color--base)", "text": "var(--wp--preset--color--contrast)" },
    "typography": { "fontFamily": "var(--wp--preset--font-family--body)", "fontSize": "var(--wp--preset--font-size--medium)", "lineHeight": "1.65" },
    "elements": {
      "link": {
        "color": { "text": "var(--wp--preset--color--primary)" },
        ":hover": { "typography": { "textDecoration": "none" } }
      },
      "heading": {
        "typography": { "fontFamily": "var(--wp--preset--font-family--display)", "lineHeight": "1.15" }
      },
      "button": {
        "color": { "background": "var(--wp--preset--color--primary)", "text": "var(--wp--preset--color--base)" },
        "border": { "radius": "999px" }
      }
    },
    "blocks": {
      "core/group": { "spacing": { "padding": { "top": "var(--wp--preset--spacing--60)", "bottom": "var(--wp--preset--spacing--60)" } } }
    }
  }
}

How it works

What it solves

Design tokens scattered across stylesheets drift apart and cannot be reached from the editor. A single theme.json publishes colours, font sizes and spacing as presets, so editors pick from the same scale the stylesheet uses.

How it works

Each preset becomes a CSS custom property such as --wp--preset--color--primary, and WordPress also generates utility classes for them. Setting defaultPalette to false hides the stock colours so only brand colours appear in the picker. Font sizes use the fluid option, which emits clamp() values that scale between the stated minimum and maximum without media queries.

The styles section then applies the tokens to the body, links, headings, buttons and the group block, so a fresh page already looks designed.

Adapting it

Replace the hex values and font stacks, keep the slugs stable because saved content references them. Add templateParts and customTemplates entries when you ship templates. For a classic theme this file still works: it drives editor colours and spacing controls. Self-hosted fonts go through fontFace entries under each family.

Pitfalls

  • A trailing comma or a comment makes the whole file invalid, and the editor silently falls back to defaults.
  • Renaming a slug orphans blocks that used it.
  • spacingScale.steps: 0 is required when you supply your own spacingSizes.
  • Keep contrast ratios checked; a palette is not accessible by default.

How to use it

Place the file at the theme root next to style.css.

Open the Site Editor, then Styles, to see the palette, type scale and spacing sizes.

Use tokens in CSS with var(--wp--preset--color--accent) or var(--wp--preset--spacing--40).

Validate the JSON with any linter before shipping.

Licence: GPL-2.0-or-later. This snippet comes from WordPress code and is GPL-2.0-or-later.

Related snippets