npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

wp-theme-json

v1.1.0

Published

JS-first theme.json compiler for WordPress block themes

Downloads

192

Readme

wp-theme-json

wp-theme-json lets you author WordPress block theme theme.json files in JavaScript and compile them into a real theme.json.

It is built for themes that want:

  • structured settings and styles in JS instead of hand-written JSON
  • Sass escape hatches for selectors that theme.json cannot express cleanly
  • validation against the official WordPress schema and bundled core block support data

theme.json stays generated output. Your source of truth lives in theme-config/.

Install

Install the package in your theme root:

npm install --save-dev wp-theme-json

Recommended scripts:

{
  "scripts": {
    "theme:init": "wp-theme-json init",
    "build:theme": "wp-theme-json build",
    "start:theme": "wp-theme-json start",
    "validate:theme": "wp-theme-json validate",
    "inspect:theme": "wp-theme-json inspect"
  }
}

Quick Start

For a working first setup, start with the example scaffold:

npx wp-theme-json init --example

Then build:

npx wp-theme-json build

That writes theme.json in the current project root.

Useful commands during development:

npx wp-theme-json start
npx wp-theme-json validate
npx wp-theme-json inspect

More guides:

What init Creates

wp-theme-json init creates a minimal starter:

theme-json.config.js
theme-config/
  index.js
  settings.js
  styles.js
  scss/
    blocks/
    elements/

wp-theme-json init --example creates the same structure plus:

  • starter color, spacing, and font-size presets
  • one fluid font-size example
  • a working block style example using css('./scss/blocks/core-navigation.scss')
  • style variations in theme-config/variations/

Use --example if you want something you can build immediately. Use plain init if you want a clean starting point.

If scaffolded files already exist:

npx wp-theme-json init --force

For npm scripts, both of these work:

npm run theme:init -- --force
npm run theme:init --force

Practical Example

theme-json.config.js

import { defineConfig } from 'wp-theme-json';

export default defineConfig({
  validation: {
    level: 'error',
  },
});

theme-config/index.js

import { defineTheme } from 'wp-theme-json';
import settings from './settings.js';
import styles from './styles.js';

export default defineTheme({
  settings,
  styles,
});

theme-config/settings.js

import { defineSettings } from 'wp-theme-json';

export default defineSettings({
  color: {
    palette: [
      { color: '#f6f1e8', name: 'Base', slug: 'base' },
      { color: '#1f1a17', name: 'Contrast', slug: 'contrast' },
      { color: '#a2512b', name: 'Accent', slug: 'accent' },
    ],
  },
  spacing: {
    spacingSizes: [
      { name: 'Small', size: '0.875rem', slug: 'sm' },
      { name: 'Medium', size: '1.25rem', slug: 'md' },
    ],
  },
  typography: {
    fontSizes: [
      { name: 'Body', size: '1rem', slug: 'body' },
      {
        fluid: {
          min: '2.25rem',
          max: '4rem',
        },
        name: 'Display',
        size: '2.75rem',
        slug: 'display',
      },
    ],
  },
});

theme-config/styles.js

import { css, defineStyles } from 'wp-theme-json';

export default defineStyles({
  color: {
    background: 'var:preset|color|base',
    text: 'var:preset|color|contrast',
  },
  elements: {
    link: {
      color: {
        text: 'var:preset|color|accent',
      },
    },
  },
  blocks: {
    'core/paragraph': {
      color: {
        text: 'var:preset|color|contrast',
      },
      typography: {
        fontSize: 'var:preset|font-size|body',
      },
    },
    'core/navigation': {
      css: css('./scss/blocks/core-navigation.scss'),
    },
  },
});

theme-config/scss/blocks/core-navigation.scss

& .wp-block-navigation-item__content:hover {
  text-decoration: underline;
}

Build it:

npx wp-theme-json build

The generated theme.json automatically includes:

  • $schema
  • version
  • sorted output for stable rebuilds
  • minified CSS strings for every css() field

Commands

  • wp-theme-json init
  • wp-theme-json build
  • wp-theme-json start
  • wp-theme-json validate
  • wp-theme-json inspect

What they do:

  • build compiles and writes theme.json
  • start performs an initial build and then rebuilds on file changes
  • validate compiles and validates without writing output
  • inspect prints the compiled theme.json to stdout

Configuration

theme-json.config.js exports defineConfig({...}).

import { defineConfig } from 'wp-theme-json';

export default defineConfig({
  entry: 'theme-config/index.js',
  output: 'theme.json',
  schema: 'https://schemas.wp.org/trunk/theme.json',
  schemaVersion: 3,
  sass: {
    loadPaths: ['theme-config/scss'],
  },
  watch: {
    ignore: ['dist/**'],
    poll: false, // false | true | number
  },
  validation: {
    level: 'error', // error | warn | off
  },
});

Notes:

  • entry is the main source module
  • output is where compiled theme.json is written
  • schemaVersion: 3 is the currently supported version
  • schema controls the emitted $schema value
  • watch.ignore adds extra paths or globs for watch mode to skip
  • watch.poll helps on Docker mounts, network drives, and other unreliable file-watch environments
  • css() output is always minified
  • top-level templates compile to customTemplates
  • top-level variations compile to styles.variations

Deprecated config keys such as source, theme, watch.ignored, and validation.strict still load, but they emit deprecation warnings.

Validation

Validation runs during build, start, and validate unless you set validation.level: 'off'.

Levels:

  • error: validation findings are errors and invalid builds do not write theme.json
  • warn: validation findings are warnings and builds still write theme.json
  • off: validation is skipped

Validation covers:

  • unsupported or misplaced theme.json keys
  • wrong value types and invalid nesting
  • malformed var:preset|... references
  • invalid css() placement outside styles.*.css
  • Sass compile failures with source file and line context
  • unsupported core block feature usage based on bundled WordPress metadata

Import failures, config load failures, and Sass runtime errors still fail normally even if validation is set to warn or off.

When to Use css()

Keep styles in structured JS whenever theme.json already supports the property.

Use css() for:

  • descendant selectors
  • pseudo selectors that do not map cleanly to theme.json
  • media queries
  • advanced structural selectors

Top-level & is supported inside css() SCSS files. In this package, it means the current WordPress block or element selector.

Example:

& .wp-block-navigation-item__content:hover {
  text-decoration: underline;
}

Nested Sass parent selectors still work as usual inside that root block.

Advanced Path Targeting

Most users do not need a path flag. If the package is installed in the theme root, just run the commands there.

If the package is installed in an ancestor project instead of directly in the theme folder, use --theme-path or --config.

Examples:

wp-theme-json build --theme-path ./wp-content/themes/your-theme
wp-theme-json start --theme-path ./wp-content/themes/your-theme
wp-theme-json validate --config ./wp-content/themes/your-theme/theme-json.config.js

Notes:

  • --theme-path tells the CLI which theme folder to treat as the project root
  • --config points at a specific theme-json.config.js file
  • legacy --cwd is still supported as a backward-compatible alias
  • watch mode follows the resolved theme/config paths, not just the shell working directory

Public Helpers

Main authoring helpers:

  • defineConfig()
  • defineTheme()
  • defineSettings()
  • defineStyles()
  • css(filePath, options?)

Advanced exports:

  • defineTokens()
  • compileProject
  • loadConfig