ForgeWP

Design Tokens & theme.json v3

How wp.config.ts acts as the single source of truth for color palettes, fluid typography, layout constraints, and theme.json v3 generation.

When building modern WordPress themes, maintaining design consistency has historically been difficult. Developers often found themselves declaring colors and fonts in two separate places: once in CSS or Tailwind for their React components, and again in WordPress theme.json so Gutenberg editor controls would display the same options to site administrators.

ForgeWP solves this friction by establishing wp.config.ts as the single source of truth for your entire design system.

During development and export, the ForgeWP compiler translates your typed TypeScript tokens directly into a standard WordPress theme.json v3 specification and generates corresponding CSS custom properties.


The Two-Way Design Token Architecture

ForgeWP creates a bidirectional bridge between modern developer tooling and native WordPress site controls:

Flowchart
Rendering diagram...
  1. At Export (pnpm export): The compiler outputs theme.json version 3 with design settings such as palettes, typography, and layout boundaries.
  2. At Runtime: WordPress injects standard CSS custom properties (--wp--preset--color--*, --wp--preset--font-size--*).
  3. In Tailwind 4: Your src/app/globals.css can map these variables into utilities (bg-brand, text-secondary, font-heading). WordPress block content and editor controls can use the exported presets; ForgeWP's PHP-backed hybrid theme does not provide full Site Editor template editing.

Defining Tokens in wp.config.ts

Open wp.config.ts in your theme's root directory. The settings object is where you define your design tokens:

wp.config.ts
1import { defineConfig } from "@forgewp/compiler/define-config"
2
3export default defineConfig({
4  name: "Velvet & Oak",
5  slug: "velvet-oak",
6  version: "1.0.0",
7
8  settings: {
9    // 1. Layout Constraints
10    layout: {
11      contentSize: "720px", // Default reading container width
12      wideSize: "1200px", // Gutenberg wide-aligned block container width
13    },
14
15    // 2. Color Palette
16    color: {
17      custom: true, // Allow editors to pick custom hex colors in Gutenberg
18      palette: [
19        { name: "Brand Primary", slug: "brand", color: "#0f172a" },
20        { name: "Brand Secondary", slug: "secondary", color: "#4f46e5" },
21        { name: "Accent Amber", slug: "accent", color: "#f59e0b" },
22        { name: "Background Light", slug: "background", color: "#fafafa" },
23        { name: "Text Slate", slug: "text", color: "#09090b" },
24      ],
25    },
26
27    // 3. Typography & Fonts
28    typography: {
29      fontSizes: [
30        { name: "Small", slug: "sm", size: "0.875rem" },
31        { name: "Base", slug: "base", size: "1rem" },
32        { name: "Large", slug: "lg", size: "1.25rem" },
33        { name: "Extra Large", slug: "xl", size: "1.75rem" },
34        { name: "Display", slug: "2xl", size: "2.5rem" },
35      ],
36      fontFamilies: [
37        {
38          name: "Sans Serif (Outfit)",
39          slug: "sans",
40          fontFamily: "Outfit, system-ui, sans-serif",
41        },
42        {
43          name: "Heading (Space Grotesk)",
44          slug: "heading",
45          fontFamily: "'Space Grotesk', var(--wp--preset--font-family--sans)",
46        },
47      ],
48      googleFonts: [
49        "Outfit:wght@400;500;600;700",
50        "Space Grotesk:wght@500;700;900",
51      ],
52    },
53  },
54})

How the Compiler Generates theme.json v3

When you run pnpm export, ForgeWP inspects your wp.config.ts and creates .forgewp/out/<theme-slug>/theme.json (packaged into the final theme root).

1. Generated Color Variables

For every item in settings.color.palette, WordPress generates:

  • A CSS variable: --wp--preset--color--<slug> (e.g. --wp--preset--color--brand)
  • An editor color swatch in Gutenberg's sidebar inspector.
  • A WordPress utility class: .has-brand-color and .has-brand-background-color.

Furthermore, the ForgeWP compiler automatically assigns the background and text palette colors to theme.json's top-level styles.color.background and styles.color.text, ensuring the Gutenberg editor canvas immediately mirrors your site's default canvas.

2. Generated Typography Variables

For every item in settings.typography.fontSizes and fontFamilies, WordPress generates:

  • --wp--preset--font-size--<slug> (e.g. --wp--preset--font-size--lg)
  • --wp--preset--font-family--<slug> (e.g. --wp--preset--font-family--heading)

3. Layout Dimensions

The contentSize and wideSize values become the sizing bounds for WordPress Gutenberg containers:

  • --wp--style--global--content-size: 720px;
  • --wp--style--global--wide-size: 1200px;
Generated theme.json (Excerpt)
1{
2  "$schema": "https://schemas.wp.org/trunk/theme.json",
3  "version": 3,
4  "settings": {
5    "appearanceTools": true,
6    "layout": {
7      "contentSize": "720px",
8      "wideSize": "1200px"
9    },
10    "color": {
11      "custom": true,
12      "palette": [
13        { "name": "Brand Primary", "slug": "brand", "color": "#0f172a" },
14        { "name": "Brand Secondary", "slug": "secondary", "color": "#4f46e5" }
15      ]
16    },
17    "typography": {
18      "fontFamilies": [
19        {
20          "name": "Sans Serif (Outfit)",
21          "slug": "sans",
22          "fontFamily": "Outfit, system-ui, sans-serif"
23        }
24      ]
25    }
26  },
27  "styles": {
28    "color": {
29      "background": "#fafafa",
30      "text": "#09090b"
31    }
32  }
33}

Fluid Typography & Responsive Scales

Modern responsive themes often require text to scale smoothly between mobile screens and wide desktop monitors without abrupt media query jumps.

You can use CSS clamp() directly inside settings.typography.fontSizes:

wp.config.ts (Fluid Font Sizes)
1typography: {
2  fontSizes: [
3    {
4      name: "Hero Title",
5      slug: "hero",
6      size: "clamp(2rem, 5vw + 1rem, 4.5rem)", // Smooth scaling from 32px to 72px
7    },
8    {
9      name: "Subheading",
10      slug: "subheading",
11      size: "clamp(1.125rem, 2vw + 0.5rem, 1.75rem)",
12    },
13  ],
14}

Because size accepts valid CSS strings, WordPress generates:

Generated CSS Preset
1:root {
2  --wp--preset--font-size--hero: clamp(2rem, 5vw + 1rem, 4.5rem);
3}

Design Aesthetic Mode (style: "forgewp" | "shadcn")

In addition to colors and typography, wp.config.ts includes a top-level style property that controls the geometric feel of your theme:

wp.config.ts
1export default defineConfig({
2  name: "Velvet & Oak",
3  slug: "velvet-oak",
4  version: "1.0.0",
5
6  // Design aesthetic preset:
7  // "forgewp" -> Sharp geometric borders (--radius: 0px, --border-width: 2px)
8  // "shadcn"  -> Smooth modern curves (--radius: 0.5rem, --border-width: 1px)
9  style: "forgewp",
10
11  settings: {
12    // ...
13  },
14})

During local development and production export, ForgeWP uses this setting to configure baseline CSS tokens like --radius, --border-width, and --shadow-offset, ensuring that UI primitives (like buttons, dialogs, and cards) adapt cleanly to your selected aesthetic.


Google Fonts & The Font Orchestrator

ForgeWP includes a built-in Font Orchestrator (@forgewp/compiler) that handles typography effortlessly:

1. Declaring Google Fonts

Add the font families and weights to settings.typography.googleFonts in wp.config.ts:

wp.config.ts
1typography: {
2  googleFonts: [
3    "Outfit:wght@300;400;500;600;700",
4    "Space Grotesk:wght@500;700;900",
5  ],
6}

2. Self-Hosting Google Fonts

By default, when configured Google Fonts can be fetched during pnpm export, the Font Orchestrator downloads WOFF2 files and writes local font CSS. The download requires network access. If self-hosting fails, the compiler can fall back to remote font loading; privacy behavior therefore depends on the completed export and configuration.

  1. Scans Font Weight Usage: Inspects your theme's code to detect which configured weights are actually used. If you configured weights that are never referenced, the compiler outputs a warning in your terminal:
BASH
⚠️ [ForgeWP Fonts] Unused Font Weight: Outfit 300. Removing it saves ~24 KB.
  1. Downloads WOFF2 Files: Fetches modern .woff2 font files directly from Google's font repository and saves them into .forgewp/out/<theme-slug>/assets/fonts/ (cached locally in .forgewp/cache/fonts/).
  2. Generates Local @font-face CSS: Produces assets/fonts/fonts.css containing self-hosted declarations with font-display: swap.
  3. Enqueues Local Fonts with Fallback: In functions.php, ForgeWP generates an enqueue script that automatically loads local self-hosted files, falling back to Google CDN only if local files are missing:
Generated functions.php (Excerpt)
1$fonts_file = get_template_directory() . '/assets/fonts/fonts.css';
2   if (file_exists($fonts_file)) {
3       wp_enqueue_style('forgewp-fonts', get_theme_file_uri('assets/fonts/fonts.css'), array(), filemtime($fonts_file));
4   } elseif (!empty($fontFamilies)) {
5       wp_enqueue_style('forgewp-google-fonts', 'https://fonts.googleapis.com/css2?family=' . $fontFamilies . '&display=swap', array(), null);
6   }
Why does ForgeWP self-host Google Fonts as local WOFF2 files?

Self-hosted fonts let browsers load font files from your site. Inspect the exported files and fallback requests as part of your site's font-loading and privacy configuration.

3. Local Development Bridge (PresetsStyle)

During local Vite development (pnpm dev), the compiler's internal PresetsStyle component injects the Google Fonts preconnect/stylesheet links and all --wp--preset--* CSS custom properties dynamically into the dev server HTML, ensuring instant hot-reloading with zero lag!

4. Local Custom Web Fonts

For custom or proprietary fonts not on Google Fonts, place your .woff2 font files in public/fonts/ and declare them using standard @font-face rules in src/app/globals.css:

src/app/globals.css
1@font-face {
2  font-family: "Geist";
3  src: url("/fonts/Geist-Variable.woff2") format("woff2");
4  font-weight: 100 900;
5  font-display: swap;
6}

Then register the font in wp.config.ts:

wp.config.ts
1fontFamilies: [
2  {
3    name: "Geist Variable",
4    slug: "geist",
5    fontFamily: "'Geist', system-ui, sans-serif",
6  },
7]

Advanced: Merging Custom cms/theme.json

If your project requires advanced WordPress Block Theme features that go beyond colors and fonts (such as custom block style variations, custom duotone filters, or custom spacing scales), you can create an optional cms/theme.json file in your theme root.

When cms/theme.json is detected:

  1. The compiler reads your custom JSON properties as the base configuration.
  2. It intelligently deep-merges the typed settings from wp.config.ts on top.
  3. It ensures you have full access to 100% of WordPress theme.json capabilities without sacrificing TypeScript type safety in your React app.

Next Step: Connecting Tokens to Tailwind 4

Now that you understand how wp.config.ts produces WordPress design tokens, proceed to the next guide to see how Tailwind CSS 4 maps those variables into ultra-fast utility classes:

Tailwind CSS 4 Theming & Tokens