During the past month or so a new method for inheriting parent styles in WordPress child themes has been established, replacing the old method of importing parent styles with an @import call in the child theme style.css file.
The new method involves enqueueing first the parent and then the child style.css file through a functions.php file in the child theme.
While at first glance this seems a valid and much improved approach, further testing has uncovered an inherent flaw in its logic: The new method makes several assumptions about the scenario in which it would be used that make it hard to use and prone to failure.
The current (new) method is described as follows in the Codex:
<?php
add_action( 'wp_enqueue_scripts', 'enqueue_child_theme_styles', PHP_INT_MAX);
function enqueue_child_theme_styles() {
wp_enqueue_style( 'parent-style', get_template_directory_uri().'/style.css' );
wp_enqueue_style( 'child-style', get_stylesheet_uri(), array('parent-style') );
}
The Theme Handbook describes a variant of this same approach:
<?php
function get_parent_theme_css() {
wp_enqueue_style( 'your-child-theme-name', get_template_directory_uri() . '/style.css' );
}
add_action( 'wp_enqueue_scripts', 'get_parent_theme_css' );
In both cases one important assumption is made: That the parent theme has one main stylesheet with the filename “style.css” and that it does not use any other stylesheets. This is not a realistic situation, and in cases where the parent theme relies on stylesheet dependencies it can cause some serious issues.
What happens
First, let’s look at each of the examples and see what happens:
In the Codex example we set up an action that hooks onto the wp_enqueue_scripts event and calls in a function. The function contains within it two calls to enqueue first the parent theme style.css file and then the child theme style.css file. This results in the parent theme style.css file loading first, then the child theme style.css file, and you get page markup where new styles added to the child theme style.css file will override the parent theme styles. This is as intended and in line with how things worked with the @import function. This works as long as all parent theme styles are contained within a file called “style.css” which as we will see in a bit is not always the case.
The Handbook example sets up the same way but only calls the parent theme style.css file. Because of how WordPress works, this overrides the inherent logic and prevents the child theme stylesheet from loading. As a result any style change added to the child theme style.css file will not take effect making changes to styles through this file unavailable. This is likely an omission in the documentation so not worth dwelling on further.
The Problem of Dependencies
Now for the problem: As indicated above not all parent themes operate with a single style.css file that contains within it all styles. In many cases themes use a series of stylesheets to achieve optional layouts or other effects. These are normally enqueued as dependencies to ensure everything is added in the correct order.
Take this example: A theme ships with two layouts, right sidebar and no sidebar. To achieve this without cluttering up the style.css file with two different sets of layout styles the theme developer has separated out the layout styles in their own stylesheets under a dedicated folder. This is common and the recommended approach in the _s starter theme.
To get it all to work in the parent theme we have something like this:
<?php
// Create function to enqueue styles
function themename_scripts() {
// Register main stylesheet with the handle 'themename-style'
wp_register_style( 'themename-style', get_template_directory_uri().'/style.css' );
// Test for nosidebar template or no sidebar and select correct layout stylesheet.
// Layout stylesheet is enqueued with the label ‘themename-layout’ and made dependent on 'themename-style' above
if (is_page_template('page-templates/page-nosidebar.php') || ! is_active_sidebar( 'sidebar-1' )) {
wp_enqueue_style( 'themename-layout' , get_template_directory_uri() . '/layouts/no-sidebar.css', array('themename-style') );
} else {
wp_enqueue_style( 'themename-layout' , get_template_directory_uri() . '/layouts/content-sidebar.css', array('themename-style') );
}
}
add_action( 'wp_enqueue_scripts', 'themename_scripts' );
The function above is typical and straight-forward: When the parent theme is loaded a test is run for the current layout condition and based on its result one of the two layout stylesheets are enqueued. These stylesheets are made dependent on the main style.css file which has been registered with the handle “themename-style”. When a page is loaded the dependency (style.css) is loaded first,, then the layout styles.
This means for the theme to display properly with the correct layouts it is the layout styles that are called, not the style.css file.
Now consider the proposed approach in the codex: Here the child theme explicitly calls for the main stylesheet – style.css. That means when a child theme is activated the layout styles are not loaded!
A possible (but not user friendly or uniform) solution
The problem described above is caused by the assumption that the parent theme uses the style.css file as its only stylesheet and ignores other circumstances like a theme that uses dependencies. To solve this problem the approach must be changed to something like this:
<?php
function enqueue_child_theme_styles() {
wp_enqueue_style( 'child-style', get_stylesheet_uri(), array('[parent_theme_name]-style') );
}
add_action( 'wp_enqueue_scripts', 'enqueue_child_theme_styles', PHP_INT_MAX);
In this example we are enqueueing only the child theme stylesheet and making it dependent on the parent theme stylesheet stack in whatever way that is set up. This preserves any dependencies in the parent theme and provides the correct ordering of all stylesheets. The problem of dependencies is solved, but this solution puts the onus on the child theme author to know what stylesheet is “in control” of the parent theme and know what its label is. That means tunnelling into the parent theme functions.php file and finding the correct enqueue function – something requiring a level of skill previously not needed for building child themes. As a result the threshold on getting started with building child themes and eventually themes becomes much higher than it used to be.
Moving forward
The change in recommendation about how to inherit styles from parent themes brings up important questions about the role of child themes in the overall training philosophy of WordPress. Some would argue increasing the difficulty level of creating child themes as this new approach does is a good thing because it takes away some of the “hand holding” that in some cases leave users with the ability to do things they do not fully understand. On the other hand increasing the difficulty level also makes entry into the space and non-destructive customization of themes harder, something that goes against the original reasoning for introducing child themes in the first place.
As I see it we need to provide a simple and consistent approach to creating child themes and inheriting parent theme styles that does not require this in-depth understanding of how WordPress themes work. To facilitate this a new function or filter could be created to get parent- and child theme stylesheets queued up correctly.
In the end there are only three scenarios in play here that need to be made available:
- Child theme only uses its own style.css file. No inheritance.
- Child theme loads parent theme styles as they are loaded in the parent theme and then loads its own style.css file to add / override styles.
- Child theme only uses parent theme styles and is built to add other features that do not include CSS.
And this means the new function or filter would only have to do one of three things:
- Load only the child theme style.css file
- Load the full stack of parent theme stylesheet files as they are loaded in the parent theme, then the child theme style.css file
- Load only the full stack of parent theme stylesheet files as they are loaded in the parent theme
How exactly this function or filter would be built should be handled by developers wiser than myself.