Create a WordPress block theme with theme.json and block templates

Full Site Editing (FSE) arrived in WordPress 5.9 and introduced a new theme architecture based on two concepts: a theme.json configuration file that replaces dozens of add_theme_support() calls and PHP filters, and HTML block template files that replace the classic PHP template hierarchy. A block theme requires no functions.php to render pages — every layout is expressed as serialised block markup stored in /templates/ and /parts/ directories. theme.json defines the colour palette, typography scale, spacing scale, layout widths, and which Gutenberg features are enabled or disabled across the entire site without writing a single line of CSS for design tokens. The Site Editor in WordPress admin then exposes all these settings to the end user in a visual interface. Despite the paradigm shift, building a minimal block theme from scratch is straightforward: you need style.css with the theme header, theme.json, and at least one template file (templates/index.html). This article walks through the required directory structure, a commented theme.json covering palette, typography, and layout, and the block markup for a single-post template that reuses a header and footer block part. Understanding this foundation lets you take advantage of global styles, fluid typography, and the new patterns API that are all part of the FSE ecosystem. Pair this with the CSS custom properties guide if you need additional design tokens outside of theme.json.

Problem: You want to build a WordPress theme that uses Full Site Editing with global styles and block templates, without relying on classic PHP templates or add_theme_support() calls.

Solution: Create a theme.json file to define your colour palette, typography, and layout, then add an HTML block template in the templates/ directory:

// theme.json — place in theme root
{
  "$schema": "https://schemas.wp.org/trunk/theme.json",
  "version": 2,
  "settings": {
    "color": {
      "palette": [
        { "slug": "primary",    "color": "#1a1a2e", "name": "Primary"    },
        { "slug": "accent",     "color": "#e94560", "name": "Accent"     },
        { "slug": "background", "color": "#f5f5f5", "name": "Background" },
        { "slug": "white",      "color": "#ffffff", "name": "White"      }
      ]
    },
    "typography": {
      "fontSizes": [
        { "slug": "small",  "size": "0.875rem", "name": "Small"  },
        { "slug": "normal", "size": "1rem",     "name": "Normal" },
        { "slug": "large",  "size": "1.5rem",   "name": "Large"  },
        { "slug": "huge",   "size": "2.25rem",  "name": "Huge"   }
      ],
      "fluid": true
    },
    "layout": {
      "contentSize": "720px",
      "wideSize":    "1200px"
    },
    "spacing": {
      "units": [ "px", "em", "rem", "vw" ]
    }
  },
  "styles": {
    "color": {
      "background": "var(--wp--preset--color--background)",
      "text":       "var(--wp--preset--color--primary)"
    },
    "typography": {
      "fontSize":   "var(--wp--preset--font-size--normal)",
      "lineHeight": "1.6"
    }
  }
}

<!-- templates/single.html — single post template -->
<!-- wp:template-part {"slug":"header","tagName":"header"} /-->

<!-- wp:group {"tagName":"main","layout":{"type":"constrained"}} -->
<main class="wp-block-group">
  <!-- wp:post-title {"level":1} /-->
  <!-- wp:post-featured-image /-->
  <!-- wp:post-content /-->
  <!-- wp:post-terms {"term":"category"} /-->
</main>
<!-- /wp:group -->

<!-- wp:template-part {"slug":"footer","tagName":"footer"} /-->

NOTE: Every template file lives in themes/your-theme/templates/ and every reusable part in themes/your-theme/parts/. WordPress automatically maps the classic template hierarchy (single, page, archive, 404, etc.) to the corresponding HTML file in the templates/ directory. If a template is missing, WordPress falls back to index.html. Use wp_theme_json_data filter or a child theme’s own theme.json to override parent theme settings without editing the parent directly.