Why Your Block Styles Enqueue in the Wrong Order When theme.json and PHP Both Declare Them

Block styles in WordPress are the CSS rules that shape how core blocks, custom blocks, and theme-defined patterns render on the front end. When a theme declares styles in theme.json and also enqueues stylesheets through PHP, the resulting cascade order can break your most deliberate design decisions. This matters for small-to-mid publishing teams that maintain their own production installs because a single misplaced stylesheet can override editorial components, alter the reading experience, and force editors to fight the block editor instead of publishing. The failure mode is not theoretical: it appears in the wp_enqueue_scripts hook, in the generated global-styles-inline-css output, and in the order WordPress prints styles in the document head.

This article explains the exact mechanism behind the ordering problem, shows how to reproduce it with a minimal theme, and gives you WP-CLI and SQL checks to confirm what is happening on your own site. The focus is on WordPress core internals, database schema, block editor behavior, and editorial workflow automation for teams that run their own production installs.

Code editor showing CSS and PHP files side by side

The Core Conflict: Two Declaration Paths, One Cascade

WordPress supports two primary ways for a theme to declare block styles. The first is theme.json, the configuration file that defines global styles, block-specific styles, and settings for the block editor. The second is the traditional PHP enqueue system, where a theme calls wp_enqueue_style() or wp_enqueue_block_style() inside a hook such as wp_enqueue_scripts or enqueue_block_assets. Both paths are valid, but they are processed at different times and with different priorities.

When both paths declare styles for the same block, the final order in the document head is not always what you expect. WordPress prints theme.json-generated styles through the global-styles-inline-css handle, which is registered and enqueued by the core block styles engine. PHP-enqueued stylesheets are printed according to their own dependencies and priorities. The result is that a PHP stylesheet with a lower priority can load before the inline global styles, or after them, depending on the hook and the dependency graph.

The practical consequence is that a style declared in theme.json can be overridden by a PHP stylesheet that loads later, even if the PHP stylesheet was intended to be a fallback. Conversely, a PHP stylesheet that loads before the inline global styles can be overridden by the theme.json output, which may surprise developers who assumed PHP always wins.

How WordPress Generates and Prints Block Styles

To understand the ordering problem, you need to know the sequence WordPress follows when it builds the front-end page. The block styles engine reads theme.json during the wp_enqueue_scripts action, but the actual CSS is generated later, when the wp_print_styles action runs. The generated CSS is attached to the global-styles-inline-css handle, which is a dependency of the global-styles handle. This means the inline CSS is printed after the global-styles stylesheet, but before any stylesheet that depends on global-styles.

PHP-enqueued stylesheets do not automatically depend on global-styles. If you enqueue a stylesheet with wp_enqueue_style() and do not declare a dependency on global-styles, WordPress may print it before the inline global styles. This is the most common cause of the wrong order: a PHP stylesheet that loads before the theme.json output, so the theme.json rules win.

Here is a minimal reproduction. Create a theme with a theme.json that sets a custom color for the core paragraph block:

{
  "version": 2,
  "settings": {
    "color": {
      "palette": [
        {
          "slug": "brand",
          "color": "#0a5c8a",
          "name": "Brand"
        }
      ]
    }
  },
  "styles": {
    "blocks": {
      "core/paragraph": {
        "color": {
          "text": "var(--wp--preset--color--brand)"
        }
      }
    }
  }
}

Then enqueue a PHP stylesheet in functions.php that sets the paragraph color to red:

add_action( 'wp_enqueue_scripts', function() {
    wp_enqueue_style(
        'my-theme-paragraph-override',
        get_stylesheet_directory_uri() . '/paragraph-override.css',
        array(),
        '1.0.0'
    );
} );

In paragraph-override.css, add:

p {
    color: red;
}

On the front end, the paragraph text will be the brand color from theme.json, not red. The reason is that the PHP stylesheet is printed before the inline global styles, so the theme.json rule wins. If you inspect the document head, you will see the PHP stylesheet link before the style id="global-styles-inline-css" block.

Browser inspector showing stylesheet order in the document head

Reproducing the Order with WP-CLI and SQL

You can confirm the order without opening a browser. First, use WP-CLI to list the enqueued styles and their dependencies:

wp eval 'global $wp_styles; foreach ( $wp_styles->queue as $handle ) { echo $handle . "\n"; }'

This prints the queue in the order WordPress will process it. If my-theme-paragraph-override appears before global-styles-inline-css, you have the ordering problem. You can also check the dependencies of a specific handle:

wp eval 'global $wp_styles; var_dump( $wp_styles->registered["my-theme-paragraph-override"]->deps );'

If the dependencies array is empty, the stylesheet has no relationship to global-styles, and WordPress will print it in the default order.

For a database-level check, you can query the wp_posts table for the wp_global_styles post type, which stores the compiled theme.json data. This is useful when you suspect that a cached or stale global styles post is affecting the output:

SELECT ID, post_title, post_content FROM wp_posts WHERE post_type = 'wp_global_styles' AND post_status = 'publish';

The post_content field contains the JSON representation of the global styles, including any block-specific rules. If you see rules that you did not expect, the database may be holding an older version of theme.json that is still being used by the block styles engine.

Why the Hook and Dependency Graph Matter

The order problem is not just about theme.json versus PHP. It is about the hook you use and the dependency graph you build. WordPress processes styles in the order they are enqueued, but dependencies can reorder the queue. If you enqueue a PHP stylesheet on wp_enqueue_scripts with a dependency on global-styles, WordPress will print it after the inline global styles. This is the correct way to ensure that your PHP stylesheet can override theme.json rules when you intend it to.

Here is the corrected enqueue:

add_action( 'wp_enqueue_scripts', function() {
    wp_enqueue_style(
        'my-theme-paragraph-override',
        get_stylesheet_directory_uri() . '/paragraph-override.css',
        array( 'global-styles' ),
        '1.0.0'
    );
} );

With this dependency, the PHP stylesheet is printed after the inline global styles, and the red color wins. The dependency graph is the key to controlling the cascade order when both declaration paths are in play.

However, there is a tradeoff. If you add a dependency on global-styles, your stylesheet will not load if the global styles handle is not enqueued. In most block themes, global-styles is always enqueued, but in a classic theme that does not use theme.json, the handle may not exist. You need to check for the handle before adding the dependency, or use a conditional enqueue.

The Block Editor Side: Editor Styles and Front-End Styles

The ordering problem also appears in the block editor, but the mechanism is slightly different. In the editor, WordPress enqueues editor styles through the enqueue_block_editor_assets hook, and it also generates editor-specific inline styles from theme.json. The editor uses an iframe for the block canvas, and the styles are loaded inside that iframe. If you enqueue a PHP stylesheet for the editor without the correct dependency, it can load before the theme.json editor styles, causing the same override problem in the editing experience.

For editorial teams, this is a workflow issue. If the editor preview does not match the front end, editors will make decisions based on incorrect styling. They may add inline styles to blocks to compensate, which then creates a new layer of overrides that is even harder to debug. The fix is the same: declare a dependency on the appropriate global styles handle for the editor context.

Here is an example for the editor:

add_action( 'enqueue_block_editor_assets', function() {
    wp_enqueue_style(
        'my-theme-editor-override',
        get_stylesheet_directory_uri() . '/editor-override.css',
        array( 'wp-edit-blocks' ),
        '1.0.0'
    );
} );

The wp-edit-blocks handle is a dependency of the editor styles, but it does not guarantee that your stylesheet loads after the theme.json editor styles. To be precise, you should depend on the handle that WordPress uses for the editor global styles, which is wp-block-editor in recent versions. Test the order in the editor with the browser inspector, and adjust the dependency until your stylesheet appears after the inline global styles.

Database Schema and the Global Styles Post

The wp_global_styles post type is part of the database schema that WordPress uses to store theme.json data. When you save changes in the site editor, WordPress writes a new wp_global_styles post with the modified JSON. This post is then used to generate the inline CSS on the front end. If you have a stale wp_global_styles post, the generated CSS may not match your current theme.json, which can make the ordering problem appear even when your PHP enqueue is correct.

You can check for stale global styles posts with WP-CLI:

wp post list --post_type=wp_global_styles --post_status=publish --format=table

If you see multiple posts, the most recent one is the active global styles. Older posts are kept for revisions, but they should not affect the front end. If you suspect a stale post is causing problems, you can delete the revisions with a SQL query, but be careful: this is a destructive operation. Always back up the database first.

DELETE FROM wp_posts WHERE post_type = 'wp_global_styles' AND post_status = 'inherit';

This removes the revision posts, leaving only the published global styles. After running this, clear any caching plugins and re-check the front-end order.

Practical Fixes for Production Installs

The most reliable fix is to stop declaring the same block styles in two places. Choose one source of truth for each block style. If a style belongs to the theme’s design system, declare it in theme.json. If a style is a one-off override for a specific template or context, enqueue it through PHP with a dependency on global-styles. This separation prevents the cascade from becoming a guessing game.

For teams that need to override theme.json styles from PHP, the dependency approach is the correct pattern. Here is a complete example that works in a block theme:

add_action( 'wp_enqueue_scripts', function() {
    $handle = 'my-theme-contextual-override';
    $src = get_stylesheet_directory_uri() . '/contextual-override.css';
    $deps = array( 'global-styles' );
    wp_enqueue_style( $handle, $src, $deps, '1.0.0' );
} );

In contextual-override.css, use the same specificity as the theme.json rule, or higher, to ensure the override applies. For example, if theme.json targets .wp-block-paragraph, your PHP stylesheet should target the same class or a more specific selector.

If you need to override a theme.json style only in the editor, use the enqueue_block_editor_assets hook and depend on the editor global styles handle. Test the order in the editor iframe, because the dependency graph in the editor is not identical to the front end.

When the Order Is Correct but the Style Still Fails

Sometimes the enqueue order is correct, but the style still does not apply. This can happen when the CSS specificity of the theme.json rule is higher than your PHP rule. WordPress generates theme.json styles with a specific selector structure, often using :root and .wp-block-* classes. If your PHP stylesheet uses a less specific selector, the theme.json rule wins even if it loads earlier.

To debug this, inspect the computed style in the browser and look at the selector that is winning. Then adjust your PHP stylesheet to use a selector with equal or higher specificity. You can also use the !important flag, but that creates a new layer of overrides that is hard to maintain. A better approach is to match the selector structure that WordPress generates.

Here is an example. If theme.json generates:

.wp-block-paragraph {
    color: var(--wp--preset--color--brand);
}

Your PHP override should use the same class:

.wp-block-paragraph {
    color: red;
}

If you use p as the selector, the .wp-block-paragraph rule has higher specificity and wins. This is a common mistake that looks like an enqueue order problem but is actually a specificity problem.

Editorial Workflow Implications

For a publishing team, the wrong block style order is not just a technical annoyance. It changes how editors perceive the content they are working on. If the editor preview shows a different style than the front end, editors will make formatting decisions based on false information. They may add inline styles, change block settings, or restructure content to compensate. This creates a feedback loop of overrides that makes the site harder to maintain.

The fix is to treat the block style order as part of the editorial workflow. When you change a style in theme.json, test the front end and the editor immediately. Use a staging environment that mirrors the production database, including the wp_global_styles posts. If you use a deployment process, include a step that checks the enqueue order with WP-CLI before the site goes live.

One practical approach is to add a WP-CLI command to your deployment script that prints the style queue and fails if a known override handle appears before global-styles-inline-css. This is a simple guard that catches the ordering problem before it reaches production.

Editor reviewing block styles in WordPress admin

Common Failure Modes and Their Symptoms

Here are the failure modes I see most often in production installs, along with the symptoms and the fix.

Failure Mode 1: PHP Stylesheet Loads Before Global Styles

Symptom: A style declared in theme.json overrides a PHP stylesheet, even though the PHP stylesheet was intended to be the override.

Cause: The PHP stylesheet has no dependency on global-styles.

Fix: Add array( 'global-styles' ) to the dependencies in wp_enqueue_style().

Failure Mode 2: Stale Global Styles Post

Symptom: The front end shows styles that do not match the current theme.json, even after clearing the cache.

Cause: A wp_global_styles post in the database is out of date.

Fix: Check the wp_global_styles posts with WP-CLI, delete revisions if necessary, and re-save the site editor settings.

Failure Mode 3: Specificity Mismatch

Symptom: The enqueue order is correct, but the PHP style still does not apply.

Cause: The theme.json rule has higher specificity than the PHP rule.

Fix: Match the selector structure of the generated theme.json CSS, or increase the specificity of the PHP rule.

FAQ

Why does my PHP stylesheet load before the theme.json styles?

Your PHP stylesheet loads before the theme.json styles because it has no dependency on the global-styles handle. WordPress prints styles in the order they are enqueued, and the inline global styles are attached to the global-styles-inline-css handle, which is a dependency of global-styles. If your stylesheet does not declare global-styles as a dependency, it will be printed before the inline global styles.

How do I make a PHP stylesheet override theme.json styles?

Add array( 'global-styles' ) as the dependencies argument in wp_enqueue_style(). This tells WordPress to print your stylesheet after the inline global styles, so your rules can override the theme.json output. Make sure your CSS selectors have equal or higher specificity than the generated theme.json selectors.

Can I check the enqueue order without opening a browser?

Yes. Use WP-CLI to print the style queue: wp eval 'global $wp_styles; foreach ( $wp_styles->queue as $handle ) { echo $handle . "\n"; }'. If your PHP stylesheet handle appears before global-styles-inline-css, the order is wrong. You can also check the dependencies of a specific handle with wp eval 'global $wp_styles; var_dump( $wp_styles->registered["your-handle"]->deps );'.

What is the wp_global_styles post type?

The wp_global_styles post type stores the compiled theme.json data in the WordPress database. When you save changes in the site editor, WordPress writes a new wp_global_styles post. The block styles engine reads this post to generate the inline CSS on the front end. If the post is stale, the generated CSS may not match your current theme.json.

Next Steps for Your Site

If you are maintaining a production install, start by auditing your current enqueue order. Run the WP-CLI command above and look for any PHP stylesheet that appears before global-styles-inline-css. Then check the wp_global_styles posts for staleness. Fix the dependencies first, because that is the most common cause of the wrong order. After that, test the editor preview against the front end to make sure the editorial workflow is not being distorted by the cascade.

This topic connects to a broader question about how WordPress handles the relationship between the database, the block editor, and the front end. If you are also dealing with a site that returns nothing found on new posts, the fix may be related to the rewrite rules or the database schema. See What to Fix First When a New WordPress Site Says Nothing Found for a concrete debugging path.

For a deeper look at the block styles engine, the WordPress core source for wp-includes/global-styles-and-settings.php is the authoritative reference. The function wp_enqueue_global_styles() shows how the global-styles handle is registered and how the inline CSS is attached. Reading that source will give you the exact sequence of events, which is more reliable than any summary.

The next time you change a block style, do it in one place. If you must override from PHP, declare the dependency and test the order. Your editors will see the same styles you see, and your production install will stop fighting itself.