# WordPress File Headers Have Overstayed Their Welcome

**URL:** https://discourse.roots.io/t/wordpress-file-headers-have-overstayed-their-welcome/30210
**Category:** blog
**Created:** 2026-03-05T20:12:28Z
**Posts:** 2

## Post 1 by @ben — 2026-03-05T20:12:29Z

WordPress already parses `theme.json` for settings and styles. Why is theme metadata still in CSS comments? We think it’s time to move metadata into `theme.json` and introduce `plugin.json`.

> **[WordPress File Headers Have Overstayed Their Welcome](https://roots.io/wordpress-file-headers-have-overstayed-their-welcome/)**
>
> WordPress still parses CSS comments for theme metadata, even though theme.json already exists. It’s time to replace file headers with JSON.

---

## Post 2 by @ben — 2026-03-08T14:38:35Z

PR submitted:

> <https://github.com/WordPress/wordpress-develop/pull/11200>
>
> Trac ticket: https://core.trac.wordpress.org/ticket/24152
> 
> This adds support f…or declaring plugin and theme metadata in JSON files as an alternative to PHP/CSS comment-based file headers.
> 
> - **Plugins** can now use a `plugin.json` file alongside the main PHP file
> - **Themes** can now declare metadata in the `metadata` property of `theme.json`
> - JSON metadata takes priority when present; existing file headers work as a fallback
> - Backwards compatible fallback behavior remains: if JSON metadata is missing or invalid, existing file headers are still used
> 
> ### Motivation
> 
> PHP/CSS file headers are parsed via regex on the first 8KB of a file. JSON provides a structured, machine-readable, language-agnostic alternative that:
> 
> - Eliminates regex parsing fragility
> - Aligns with modern conventions
> - Allows themes to work without `style.css` (block themes already don't need one for styling)
> 
> See: [WordPress File Headers Have Overstayed Their Welcome](https://roots.io/wordpress-file-headers-have-overstayed-their-welcome/)
> 
> ## Examples
> 
> ### `plugin.json`
> 
> ```json
> {
> "name": "My Plugin",
> "uri": "https://example.com/my-plugin",
> "description": "A short description of the plugin.",
> "author": "Plugin Author",
> "authorUri": "https://example.com",
> "version": "1.0.0",
> "textDomain": "my-plugin",
> "requires": {
> "wordpress": "7.0",
> "php": "8.0",
> "plugins": ["woocommerce", "jetpack"]
> },
> "network": true,
> "mainFile": "bootstrap.php"
> }
> ```
> 
> ### `theme.json` (metadata property)
> 
> ```json
> {
> "version": 3,
> "metadata": {
> "name": "My Theme",
> "uri": "https://example.com/my-theme",
> "description": "A short description of the theme.",
> "author": "Theme Author",
> "authorUri": "https://example.com",
> "version": "1.0.0",
> "tags": ["blog", "one-column"],
> "textDomain": "my-theme",
> "template": "parent-theme"
> },
> "settings": { ... },
> "styles": { ... }
> }
> ```
> 
> Other examples from my demo repo: https://github.com/retlehs/json-metadata-theme-and-plugin
> 
> ## Changes
> 
> ### Plugin metadata (`plugin.json`)
> 
> - New `_get_plugin_json_data()` function reads `plugin.json` from a plugin's directory
> - `get_plugin_data()` checks for JSON metadata first, then falls back to PHP file headers
> - Only applies to direct children of `WP_PLUGIN_DIR` (not mu-plugins or drop-ins)
> - Supports a `mainFile` property to designate the main entry point (defaults to `{dir-name}/{dir-name}.php`)
> - `requires.plugins` array maps to comma-separated `RequiresPlugins` header format
> - `network` field uses strict boolean checking (`true` only)
> - Uses silent JSON decoding (not `wp_json_file_decode()`) to avoid errors during enumeration
> - Files outside direct child plugin directories (including mu-plugin subdirectories and drop-ins) are ignored
> - JSON parsing honors headers registered via the existing `extra_plugin_headers filter`
> 
> ### Theme metadata (`theme.json` → `metadata`)
> 
> - New `read_json_metadata()` private method on `WP_Theme` reads `metadata` from `theme.json`
> - Constructor tries JSON metadata before `style.css` headers
> - Themes with `theme.json` metadata can now work without a `style.css` file
> - Updated `validate_current_theme()` to allow active themes (including child themes) without `style.css` when `theme.json` contains valid `metadata.name`
> - Uses silent JSON decoding to avoid errors when enumerating themes with malformed `theme.json`
> - JSON metadata parsing honors headers registered via the existing `extra_theme_headers` filter
> 
> ## Schema notes
> 
> The `theme.json` schema (maintained in the Gutenberg repo) will need a companion PR to add the `metadata` property definition.
> 
> Schema publication/documentation for `plugin.json` will need to be done.
> 
> ## Testing
> 
> ```bash
> npm run test:php -- --filter "Tests_Admin_IncludesPluginJsonMetadata|Tests_Admin_WPPluginDependencies_Initialize|Tests_Theme_ThemeDir|Tests_Theme_wpThemeJsonMetadata"
> ```
> 
> All filtered tests passed locally in Docker
> 
> ## Demo
> 
> See https://github.com/retlehs/json-metadata-theme-and-plugin for an example plugin and theme, along with instructions on how to setup your local environment with this PR
> 
> | Plugin Metadata | Theme Metadata | Theme Front End |
> | --- | --- | --- |
> | ![JSON metadata plugin](https://cdn.roots.io/app/uploads/json-metadata-plugin.png) | ![JSON metadata theme](https://cdn.roots.io/app/uploads/json-metadata-theme.png) | ![JSON metadata theme front end](https://cdn.roots.io/app/uploads/json-metadata-theme-front-end.png) |
> 
> 
> ## Use of AI Tools
> 
> This PR was authored with Claude Code and Codex. Both were used for implementation, test writing, and code review. I reviewed and validated all changes, and ran tests locally in Docker.
