Use theme.json Global Styles and Fluid Typography in WordPress Block Themes

The theme.json file is the single configuration layer that replaces dozens of add_theme_support() calls and scattered functions.php style enqueues in block themes — it declares a site’s design tokens (colors, font sizes, spacing, border radii), controls which block editor UI panels are available to editors, and generates CSS custom properties automatically so that both the block editor preview and the front-end use identical values without any duplication. The file uses a nested JSON schema with top-level keys: version (currently 2), settings (controls available design options), styles (applies default styles to elements and blocks), and customTemplates / templateParts (declares template files). The settings.typography.fluid option (introduced in WordPress 6.1) enables fluid typography — font sizes that scale smoothly between a minimum and maximum value using CSS clamp() without JavaScript or media queries. Each font size in the settings.typography.fontSizes array can include a fluid object with min and max values, and WordPress generates the appropriate clamp() expression automatically: for a size declared as { "slug": "large", "size": "2rem", "fluid": { "min": "1.5rem", "max": "2.5rem" } }, WordPress generates clamp(1.5rem, 3.5vw + 0.5rem, 2.5rem) as the CSS custom property value. The styles.elements section allows applying typography, color, and spacing to HTML elements (h1h6, a, button) without writing any CSS — values reference the design tokens using the var:preset|color|primary shorthand syntax. A child theme.json in a child theme merges with and overrides the parent theme’s theme.json — the merge is deep, allowing the child to add colors to the parent’s palette without redefining the entire palette. The block patterns post covers composing blocks into reusable layouts; theme.json provides the design system those patterns draw from.

Problem: A block theme has font sizes defined in both theme.json and a separate style.css via media queries — when a designer changes the heading scale, they must update both files, and the block editor preview does not match the front-end because the media-query breakpoints differ from what the editor renders. On mobile the headings are too large; on desktop they are understated.

Solution: Remove the media query font-size rules from style.css, define the complete typography scale in theme.json with fluid font sizes using clamp() — one source of truth that applies identically in both the editor and the front-end at every viewport width.

{
    "$schema": "https://schemas.wp.org/trunk/theme.json",
    "version": 2,
    "settings": {
        "typography": {
            "fluid": true,
            "fontFamilies": [
                {
                    "fontFamily": "'Inter', sans-serif",
                    "slug": "inter",
                    "name": "Inter",
                    "fontFace": [
                        {
                            "fontFamily": "Inter",
                            "fontStyle": "normal",
                            "fontWeight": "100 900",
                            "src": [ "file:./fonts/inter-var.woff2" ],
                            "fontDisplay": "swap"
                        }
                    ]
                }
            ],
            "fontSizes": [
                { "slug": "sm",    "size": "0.875rem", "name": "Small" },
                { "slug": "base",  "size": "1rem",     "name": "Base" },
                {
                    "slug": "lg",
                    "size": "1.25rem",
                    "name": "Large",
                    "fluid": { "min": "1.125rem", "max": "1.375rem" }
                },
                {
                    "slug": "xl",
                    "size": "1.5rem",
                    "name": "XL",
                    "fluid": { "min": "1.25rem", "max": "1.75rem" }
                },
                {
                    "slug": "2xl",
                    "size": "2rem",
                    "name": "2XL",
                    "fluid": { "min": "1.5rem", "max": "2.5rem" }
                },
                {
                    "slug": "3xl",
                    "size": "3rem",
                    "name": "3XL",
                    "fluid": { "min": "2rem", "max": "3.5rem" }
                }
            ]
        },
        "color": {
            "palette": [
                { "slug": "primary",   "color": "#1a56db", "name": "Primary" },
                { "slug": "secondary", "color": "#6b7280", "name": "Secondary" },
                { "slug": "accent",    "color": "#f59e0b", "name": "Accent" },
                { "slug": "base",      "color": "#ffffff", "name": "Base" },
                { "slug": "contrast",  "color": "#111827", "name": "Contrast" }
            ]
        },
        "spacing": {
            "spacingScale": {
                "operator": "*",
                "increment": 1.5,
                "steps": 7,
                "mediumStep": 1.5,
                "unit": "rem"
            },
            "units": [ "px", "rem", "em", "%" ]
        }
    },
    "styles": {
        "typography": {
            "fontFamily": "var(--wp--preset--font-family--inter)",
            "fontSize":   "var(--wp--preset--font-size--base)",
            "lineHeight": "1.6"
        },
        "color": {
            "background": "var(--wp--preset--color--base)",
            "text":       "var(--wp--preset--color--contrast)"
        },
        "elements": {
            "h1": {
                "typography": {
                    "fontSize":    "var(--wp--preset--font-size--3xl)",
                    "fontWeight":  "700",
                    "lineHeight":  "1.15",
                    "letterSpacing": "-0.025em"
                }
            },
            "h2": {
                "typography": {
                    "fontSize":   "var(--wp--preset--font-size--2xl)",
                    "fontWeight": "600",
                    "lineHeight": "1.25"
                }
            },
            "link": {
                "color": { "text": "var(--wp--preset--color--primary)" },
                ":hover": { "color": { "text": "var(--wp--preset--color--accent)" } }
            }
        }
    }
}

NOTE: Setting "fluid": true at the settings.typography level enables fluid sizing for all font sizes that have a fluid property defined — it does not automatically make every font size fluid. Font sizes without a fluid property remain fixed. To disable fluid typography for a specific font size while the global setting is true, set "fluid": false on that individual font size entry. Also note that theme.json is cached by WordPress — when editing it during development, clear the site transients or use WP_DEBUG mode to ensure changes are reflected immediately.