Blog

Extending theme.json to Custom Post Types and Plugin Output: A Practical Guide

Learn how to apply theme.json settings to custom post types and plugin content for consistent design across your WordPress site.

Summary

Many WordPress developers build custom post types or plugins that output content (e.g., testimonials, portfolios). However, theme.json—the central configuration for global styles—often ignores these custom elements by default, leading to disjointed design. This article walks you through registering custom post types to inherit theme.json styles, overriding settings for specific blocks, and bridging plugin output with core design tokens. You'll get step-by-step instructions, real-world examples like styling a 'Team Member' CPT, and caveats about performance and third-party conflicts. By the end, you can ensure any custom content matches your site's editor-defined design system.

Have you ever built a custom post type or integrated a third-party plugin, only to find that its output completely ignores the beautiful design system you painstakingly configured in theme.json? You're not alone. By default, theme.json only applies to core WordPress blocks and registered block styles, leaving custom post types and plugin-generated content to fend for themselves. This typically results in inline CSS, duplicate font stacks, or a mismatched color palette that breaks user experience. But there's a better way: you can extend theme.json to cover these custom elements, ensuring a consistent, maintainable design system across your entire site. In this guide, we'll cover four practical steps to make that happen, complete with code examples and real-world caveats.

Understanding theme.json Scope

Before diving in, it's crucial to understand that theme.json is a block theme concept. It defines global styles, settings (like color palettes and font sizes), and block-specific styles for the block editor and front end. However, its reach is limited: it controls only blocks that explicitly declare support for theme.json properties via register_block_type or the older add_theme_support. Custom post types created with register_post_type do not automatically inherit these styles unless they are built with blocks or explicitly styled.

Step 1: Registering Custom Post Types with Block Editor Support

The first step is to ensure your custom post type uses the block editor. When calling register_post_type, set 'show_in_rest' => true and include 'editor' in the supports array. This enables the block editor for that post type, allowing authors to use block-level styles from theme.json. For example, here's a "Team Member" CPT:

function create_team_member_cpt() {
    $args = array(
        'public' => true,
        'show_in_rest' => true,
        'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
        'show_in_menu' => true,
        'labels' => array(
            'name' => 'Team Members',
            'singular_name' => 'Team Member',
        ),
        'rewrite' => array( 'slug' => 'team' ),
    );
    register_post_type( 'team_member', $args );
}
add_action( 'init', 'create_team_member_cpt' );

Now, when you edit a team member post, you'll see the block editor with your theme's global styles applied to core blocks like Paragraph, Heading, and Image. This alone often solves most styling issues. If you need more granular control, proceed to the next step. For a deeper dive on building block themes with theme.json, see How to Build a Custom WordPress Block Theme with theme.json.

Step 2: Adding Custom Block Styles via theme.json

Sometimes you want to apply specific styles to all instances of your custom post type's content—for example, giving the entire post a background color or special padding. You can target core/post-content or wrap your content in a custom group block and style that via theme.json. Add the following to your theme.json file under the styles object:

{
  "styles": {
    "blocks": {
      "core/post-content": {
        "css": "background-color: var(--wp--preset--color--light); padding: 2rem;"
      }
    }
  }
}

This will apply a light background and padding to the content area of all posts, including your Team Member CPT. You can also define block-specific variations inside settings.blocks to provide style options in the editor.

Step 3: Using theme.json Custom Properties for Plugin Output

Plugins often output content via shortcodes, widgets, or custom blocks. To style these consistently, leverage CSS custom properties that theme.json generates. For example, if your theme defines a primary color as --wp--preset--color--primary, you can use it in your plugin's CSS:

.my-plugin-button {
    background-color: var(--wp--preset--color--primary);
    font-size: var(--wp--preset--font-size--medium);
}

This ensures that when the site admin changes the color palette in theme.json, your plugin's button updates automatically. To make this robust, always use the preset CSS variables instead of hardcoding values. Additionally, if your plugin registers its own blocks, you can inherit theme.json settings by using useSetting in JavaScript or passing $attributes in PHP rendering, as covered in Mastering WordPress Dynamic Blocks.

Step 4: Handling Dynamic Blocks

For custom blocks that output dynamic content (e.g., a testimonials slider), you need to register the block with block editor support and explicitly tie it to theme.json. In PHP, use register_block_type with $args that include 'render_callback' and ensure your block's block.json file sets 'supports' => array( 'color' => true, 'typography' => true ). Then, in your render callback, you can access the global styles via wp_get_global_styles() and apply them. For example:

function render_testimonial_block( $attributes, $content ) {
    $styles = wp_get_global_styles();
    $color = $styles['elements']['link']['color']['text'] ?? '#000';
    return '<div style="color: ' . esc_attr( $color ) . ';">' . $content . '</div>';
}

This approach ensures your custom block respects the theme's design tokens. For more on block registration, see Building Robust WordPress Plugins.

Caveats

While extending theme.json is powerful, watch out for these pitfalls:

  1. Performance: Adding too many custom styles in theme.json can bloat the file and increase CSS parsing time. Stick to global settings and use block-specific styles sparingly.
  2. Plugin Conflicts: Some plugins reset or override theme.json settings. Always test with a minimal plugin set and consider hooking into after_setup_theme to reapply your customizations.
  3. Classic Themes: If your theme doesn't use the block editor (i.e., it's a classic theme), theme.json has limited effect. Consider migrating to a block theme or using the wp_enqueue_global_styles function to load the styles.
  4. Browser Cache: Changes to theme.json may not appear immediately due to caching. Clear browser cache and use versioning for your stylesheet.

Conclusion

By now, you have a clear roadmap to make custom post types and plugin outputs play nicely with your theme.json design system. The key is to register your CPT with block editor support, define specific styles for the post content block, use CSS custom properties in plugin output, and register your custom blocks with proper theme.json integration. This approach eliminates inconsistency, reduces maintenance, and gives site administrators the power to control the look and feel from a single source of truth. Start implementing these steps today to bring cohesion to your WordPress projects.

Sources (5)