29

Context

I built a child theme based on Twenty Thirteen which works quite well. After updating the parent theme to version 1.3, I noticed strange behavior with the styling which was caused by a cached parent theme's style.css.

Here is the content of my child theme's style.css (omitting headers)

/* =Imports styles from the parent theme
-------------------------------------------------------------- */
@import url('../twentythirteen/style.css');

So the child theme's style.css does nothing more than import the parent theme's style.css.

I also have another css file with my child theme's customizations which I enqueue like so in functions.php:

// Enqueue parent theme's style.css (faster than using @import in our style.css)
$themeVersion = wp_get_theme()->get('Version');

// Enqueue child theme customizations
wp_enqueue_style('child_main', get_stylesheet_directory_uri() . '/css/main.css',
    null, $themeVersion);

This gives me a very nice css url like this: domain.com/wp-content/themes/toutprettoutbon/css/main.css?ver=1.0.1 that makes sure the style sheet is reloaded when the child theme is updated.

Now the problem

The statement @import url('../twentythirteen/style.css'); is completely independent of the underlying parent theme's version. In fact, the parent theme can be updated without updating the child theme but browsers will still use cached versions of the old ../twentythirteen/style.css.

Relevant code in Twenty Thirteen that enqueues the style.css:

function twentythirteen_scripts_styles() {
    // ...

    // Add Genericons font, used in the main stylesheet.
    wp_enqueue_style( 'genericons', get_template_directory_uri() . '/genericons/genericons.css', array(), '3.03' );

    // Loads our main stylesheet.
    wp_enqueue_style( 'twentythirteen-style', get_stylesheet_uri(), array(), '2013-07-18' );
    // Note usage of get_stylesheet_uri() which actually enqueues child-theme/style.css

    // Loads the Internet Explorer specific stylesheet.
    wp_enqueue_style( 'twentythirteen-ie', get_template_directory_uri() . '/css/ie.css', array( 'twentythirteen-style' ), '2013-07-18' );
}
add_action( 'wp_enqueue_scripts', 'twentythirteen_scripts_styles' );

I can think of a few ways to solve this problem but none are really satisfactory:

  1. Update my child theme every time the parent theme is updated to change a version string in style.css (e.g. @import url('../twentythirteen/style.css?ver=NEW_VERSION');). This creates an unnecessary and annoying link between parent theme version and child.

  2. In my child's functions.php, 1) wp_dequeue_style the included child theme's style.css and 2) wp_enqueue_style the parent theme's style.css directly WITH version string. This messes up the order of queued css in the parent theme.

  3. Use the style_loader_tag filter to modify the generated css <link> tag for style.css and modify the path to point directly to the parent theme's style.css WITH a version string. Seems rather obscure for such a common need (cache busting).

  4. Dump the parent theme's style.css in my child theme's style.css. Same as (1) really, but a bit faster.

  5. Make my child theme's style.css be a symlink to the parent theme's style.css. This seems quite hackish...

Have I missed something? Any suggestions?

edit

Added genericicons.css and ie.css style sheets in parent theme to clarify why I can't change the @import css statement to wp_enqueue_style in my child theme. Currently, with an @import statement in my child theme's style.css, I have this order in generated pages:

  1. twentythirteen/genericons/genericons.css -> enqueued by parent theme
  2. child-theme/style.css -> enqueued by parent theme, @imports twentythirteen/style.css
  3. twentythirteen/css/ie.css -> enqueued by parent theme
  4. child-theme/css/main.css -> enqueued by child theme

If I enqueue the parent's style.css as a dependency of main.css, this will become:

  1. twentythirteen/genericons/genericons.css -> enqueued by parent theme
  2. child-theme/style.css -> empty, enqueued by parent theme
  3. twentythirteen/css/ie.css -> enqueued by parent theme
  4. twentythirteen/style.css -> enqueued by child theme as dependency of main.css
  5. child-theme/css/main.css -> enqueued by child theme

Note that ie.css is now included before the parent theme's style.css. I do not want to change the enqueuing order of the parent theme's css files because I cannot presume that this won't cause problems with the priority of css rules.

5
  • 5
    Never use @import, set the parent theme’s stylesheet as a dependency of your own stylesheet instead.
    – fuxia
    Commented Oct 3, 2014 at 1:03
  • I know it's not the best approach but it's recommended here: codex.wordpress.org/Child_Themes
    – bernie
    Commented Oct 3, 2014 at 1:09
  • Also, doing what you suggested does not fix my issue. The parent theme's style.css would not be included at the same place as it is now. The parent includes other css files which must come between its style.css and my child theme's css.
    – bernie
    Commented Oct 3, 2014 at 1:10
  • 3
    Please ignore the codex completely. It is full of misinformation. Using the dependency parameter will include the stylesheets in the correct order.
    – fuxia
    Commented Oct 3, 2014 at 1:11
  • Please see my edit.
    – bernie
    Commented Oct 3, 2014 at 16:53

3 Answers 3

19

You don't have to use @import. It's best not to, actually. Using an enqueued approach is probably better all around.

Here's the relevant part of twentythirteen's code:

function twentythirteen_scripts_styles() {
...
    // Loads our main stylesheet.
    wp_enqueue_style( 'twentythirteen-style', get_stylesheet_uri(), array(), '2013-07-18' );
...
}
add_action( 'wp_enqueue_scripts', 'twentythirteen_scripts_styles' );

Here's what you do in your code:

function child_scripts_styles() {
    wp_enqueue_style( 'child-style', get_stylesheet_directory_uri().'/css/main.css', array('twentythirteen-style'), 'YOUR_THEME_VERSION' );
}
add_action( 'wp_enqueue_scripts', 'child_scripts_styles' );

If your main.css has to come after the parent's style.css, then you just make it dependent on that.

Now, if you also have a B.css in the child, then you set up the dependencies accordingly:

function child_scripts_styles() {
    wp_enqueue_style( 'child-B-style', get_stylesheet_directory_uri().'/B.css', array('twentythirteen-style'), 'YOUR_THEME_VERSION' );
    wp_enqueue_style( 'child-style', get_stylesheet_directory_uri().'/css/main.css', array('child-B-style'), 'YOUR_THEME_VERSION' );
}
add_action( 'wp_enqueue_scripts', 'child_scripts_styles' );

Make the dependencies that you define for each item actually reflective of what those dependencies really are. If main.css must come after B.css, then it depends on it. If B.css must come after the parent's style.css, then B depends on that. The enqueue system will sort it out for you.

And if you're not actually using the child's style.css for anything, then you don't have to enqueue it at all. It can be just a placeholder to hold your theme's header information. Not using it? Don't load it.

Also, what exactly are you doing that is so dependent on ordering here? CSS doesn't care about load order in most situations. CSS is more dependent on specificity of the selectors. If you want to override something, you make your selector for it more specific. It can come first, or last, or anything in between, the more specific selector always wins.

Edit

Reading your comments and looking closer at the code, I see where the mistake is here. The twenty-thirteen code is enqueueing the "get_stylesheet_uri()", which in a child theme case, would be your child theme's style.css file, not the parent's file. That's why the @import works, and keeps the same ordering (which again, does not matter nearly as much as you think it does).

In that case, if you don't want to use import, I would recommend enqueueing the parent's style.css directly. Like so:

function child_scripts_styles() {
    wp_enqueue_style( 'parent-style', get_template_directory_uri().'/style.css', array() );
}
add_action( 'wp_enqueue_scripts', 'child_scripts_styles' );

Code in the child theme's functions.php runs first, so your own wp_enqueue_scripts will run first, and this will enqueue the parent theme's style.css, which the parent theme is not doing itself (because it's actually enqueueing your child's style.css). By not making it depend on anything, same as the parent, then it simply gets put in the output correctly. Note that the order of this file and the genericons.css does not matter, because the original "twentythirteen-style" does not have the genericons.css as a listed dependency.

Your own child's style.css will load, and honestly, this is where you should put your changes for the child theme, not into a separate main.css. There's nothing preventing you from putting your changes there, but there's no real reason to have an extra css file.

11
  • I totally agree that @imports are not the best way to go. Please see my "edit" section for more precise information. I don't have any particular needs regarding ordering of css. I simply don't want to modify the internal ordering of the parent theme's css files which can cause problems with css rules priority.
    – bernie
    Commented Oct 3, 2014 at 19:13
  • To clarify, B.css (now changed to ie.css in question) is not part of my child theme, but actually part of the parent theme.
    – bernie
    Commented Oct 3, 2014 at 19:26
  • 2
    If you want your style to come after the ie.css style, then make your own style depend on it. Its name is "twentythirteen-ie". The order is entirely managed by what dependencies you declare, but again, with CSS, the actual order of them in the document doesn't usually matter, so I'm not sure why you'd care about it overly much.
    – Otto
    Commented Oct 3, 2014 at 20:12
  • 2
    Edited my answer to include a different approach.
    – Otto
    Commented Oct 3, 2014 at 20:29
  • Yeah I guess I got carried away with the "need" to keep the css ordering. If the order was really important for the parent theme, it should be stated in the dependencies.
    – bernie
    Commented Oct 4, 2014 at 14:20
9

My previous answer is overly complicated and potentially doesn't respect the parent theme's dependency chain (see note in other answer).

Here's another much simpler take that should work much better:

function use_parent_theme_stylesheet() {
    // Use the parent theme's stylesheet
    return get_template_directory_uri() . '/style.css';
}

function my_theme_styles() {
    $themeVersion = wp_get_theme()->get('Version');

    // Enqueue our style.css with our own version
    wp_enqueue_style('child-theme-style', get_stylesheet_directory_uri() . '/style.css',
        array(), $themeVersion);
}

// Filter get_stylesheet_uri() to return the parent theme's stylesheet 
add_filter('stylesheet_uri', 'use_parent_theme_stylesheet');

// Enqueue this theme's scripts and styles (after parent theme)
add_action('wp_enqueue_scripts', 'my_theme_styles', 20);

The idea is to simply filter the call to get_stylesheet_uri() in the parent theme to return it's own stylesheet instead of the child theme's. The child theme's stylesheet is then enqueued later in the action hook my_theme_styles.

2
  • Just for the record: 1) Your code will generate the exact same html as using the old @import version, no impact on performance at all, there will be two separate style.css requests to the server 2) This answer drops the whole dependency thing all together... 3) You can check what get_template_directory_uri and get_template_stylesheet_uri are doing here: core.trac.wordpress.org/browser/tags/4.8/src/wp-includes/… Again, no need for most of that code.
    – bg17aw
    Commented Aug 31, 2017 at 22:19
  • 1
    @bg17aw using wp_enqueue_style automatically adds a cache busting query string to the url it generates (e.g. ?ver=2013-07-18) based on the theme's version. This not done by an @import statement.
    – bernie
    Commented Sep 14, 2017 at 18:01
2

warning

This solution doesn't respect the parent theme's dependencies! Changing the parent theme's handle name affects the chain of dependencies set in the parent theme. See my much simpler other answer.

orignal answer

Although Otto's answer is pretty good, I ended up with this in my child theme's functions.php

function my_theme_styles() {
    global $wp_styles;
    $parentOriginalHandle = 'twentythirteen-style';
    $parentNewHandle = 'parent-style';

    // Deregister our style.css which was enqueued by the parent theme; we want
    // to control the versioning ourself.
    $parentStyleVersion = $wp_styles->registered[$parentOriginalHandle]->ver;
    $parentDeps = $wp_styles->registered[$parentOriginalHandle]->deps;
    wp_deregister_style($parentOriginalHandle);

    // Enqueue the parent theme's style.css with whatever version it used instead
    // of @import-ing it in the child theme's style.css
    wp_register_style($parentNewHandle, get_template_directory_uri() . '/style.css',
        $parentDeps, $parentStyleVersion);

    // Enqueue our style.css with our own version
    $themeVersion = wp_get_theme()->get('Version');
    wp_enqueue_style($parentOriginalHandle, get_stylesheet_directory_uri() . '/style.css',
        [$parentNewHandle], $themeVersion);
}

// Run this action action the parent theme has enqueued its styles.
add_action('wp_enqueue_scripts', 'my_theme_styles', 20);

It maintains the parent theme's style.css ordering and version numbers while controlling the version of the child theme's style.css.

6
  • 5
    It boggles my mind that the most popular blog software requires 20+ lines of code just to tweak the CSS of an existing theme. I guess that's job security.
    – Carl G
    Commented Jan 25, 2015 at 21:45
  • I had to change [$parentNewHandle] to array($parentNewHandle)
    – Carl G
    Commented Jan 25, 2015 at 22:24
  • @CarlG : the array syntax I used (brackets) was introduced in PHP 5.4.
    – bernie
    Commented Jan 26, 2015 at 4:47
  • To upvoters: please see my other answer which solves problems with this one.
    – bernie
    Commented Mar 23, 2015 at 17:21
  • It's all a huge misunderstanding, there is no need for any of that. In fact, the old @import method works just as well, please compare both methods. As for the dependency of the child theme on the parent theme, no need for that either. Child style.css is always loaded after parent, at least from my tests. Love to be proven wrong.
    – bg17aw
    Commented Aug 31, 2017 at 22:23

Not the answer you're looking for? Browse other questions tagged or ask your own question.