Themes and
Family Planning.

Build your themes
child-ready

- best practices.

Aussi en français

Paulina Hetman aka @PeHaa

Paulina Hetman aka @PeHaa

Child Theme

Does not exist without another theme, called parent theme

Inherits the functionalities and the templates from its parent

Without touching the parent theme, a child theme allows:

  • modifications
  • adding new functionalities and templates

Your modifications will be preserved when the parent theme is updated.

themes.php view
themes.php Theme details view

WordPress theme

  • a directory
  • in "theme directory" (default: wp-content/themes)
  • must contain style.css
  • template must be defined
  • wp-content
    • themes
      • theme-1
      • theme-1-child
      • theme-2
  • wp-content
    • themes
      • theme-1
      • theme-1-child
      • theme-2
stylesheet : ? ...
template : ? ...
  • wp-content
    • themes
      • theme-1
        • functions.php
        • index.php
        • style.css
        • footer.php
        • header.php
      • theme-1-child
      • theme-2
stylesheet : ? ...
template : ? ...
  • wp-content
    • themes
      • theme-1
        • functions.php
        • index.php
        • style.css
        • footer.php
        • header.php
      • theme-1-child
      • theme-2
stylesheet : ? ...
template : ? ...
  • wp-content
    • themes
      • theme-1
        • functions.php
        • index.php
        • style.css
          css checking for Template in style.css header
        • footer.php
        • header.php
      • theme-1-child
      • theme-2
stylesheet :theme-1
template : ? ...
  • wp-content
    • themes
      • theme-1
        • functions.php
        • index.php
        • style.css
        • footer.php
        • header.php
      • theme-1-child
      • theme-2
stylesheet :theme-1
template : ? ...
  • wp-content
    • themes
      • theme-1
        • functions.php
        • index.php
        • style.css
        • footer.php
        • header.php
      • theme-1-child
      • theme-2
stylesheet :theme-1
template : theme-1
  • wp-content
    • themes
      • theme-1
      • theme-1-child
      • theme-2
stylesheet : ? ...
template : ? ...
  • wp-content
    • themes
      • theme-1
      • theme-1-child
        • style.css
      • theme-2
stylesheet :theme-1-child
template : ? ...
  • wp-content
    • themes
      • theme-1
      • theme-1-child
        • style.css
          css checking for Template in style.css header
      • theme-2
stylesheet :theme-1-child
template : theme-1
Some precision:

If a directory in wp-content/themes does not contain a style.css in its root, all its first-level subfolders will be scanned as the potential themes.

This would be ok:

  • wp-content
    • themes
      • pehaa-package
        • pehaa-theme
        • pehaa-theme-child
Some precision:

If a directory in wp-content/themes does not contain a style.css in its root, all its first-level subfolders will be scanned as the potential themes.

This is no longer ok:

  • wp-content
    • themes
      • pehaa-package
        • themes
          • pehaa-theme
          • pehaa-theme-child

Themes

template

vs

stylesheet

TEMPLATEPATH STYLESHEETPATH
get_template(); get_stylesheet();
get_template_directory(); get_stylesheet_directory();
... ...
template directory stylesheet directory
parent directorytheme directory (child)

Parent Theme

template

vs

stylesheet

TEMPLATEPATH = STYLESHEETPATH
get_template(); = get_stylesheet();
get_template_directory(); = get_stylesheet_directory();
... = ...
template directory = stylesheet directory
parent directorytheme directory (child)

Child Theme

template

vs

stylesheet

TEMPLATEPATHSTYLESHEETPATH
get_template();get_stylesheet();
get_template_directory();get_stylesheet_directory();
......
template directorystylesheet directory
parent directorytheme directory (child)

Important child-ready theme tip:

Be careful when using get_stylesheet_directory() / get_template_directory() etc. These functions are not interchangeable.

They will be no longer equivalent if a child theme is activated.

Templates

Any template file found in the child directory (STYLESHEETPATH) overrides its counterpart from the parent directory (TEMPLATEPATH).

New template files can be added to the child directory.


/* wp-includes/template.php */
if ( file_exists(STYLESHEETPATH . '/' . $template_name)) {
  $located = STYLESHEETPATH . '/' . $template_name;
  break;
} else if ( file_exists(TEMPLATEPATH . '/' . $template_name) ) {
  $located = TEMPLATEPATH . '/' . $template_name;
  break;
}
						

Functions.php

Both child & parent functions.php are loaded (if the files exist).
The child functions.php is loaded right before the parent’s file.

/* wp-settings.php */
if ( TEMPLATEPATH !== STYLESHEETPATH && file_exists( STYLESHEETPATH . '/functions.php' ) )
  include( STYLESHEETPATH . '/functions.php' );
if ( file_exists( TEMPLATEPATH . '/functions.php' ) )
  include( TEMPLATEPATH . '/functions.php' );
						

Functions.php

New function

Create functions.php in your child theme and add an new function.


/* pehaa-theme-child/functions.php */
function my_child_function() {
  ...
}

						

Functions.php

Modifications


/* pehaa-theme/functions.php */
function my_function() {
  ...
}

						

How do I modify my_function?


/* pehaa-theme-child/functions.php */
function my_function() {
  ...
}
						

Fatal error: Cannot redeclare my_function()


/* pehaa-theme-child/functions.php */
function my_function() {
  ...							
}
						

/* pehaa-theme/functions.php */
if ( !function_exists( 'my_function' ) ) :
  function my_function() {
    ...
  }
endif;
						

OK: my_function is declared in the child theme.

Important child-ready theme tip:
make the user functions pluggable by declaring them conditionally.

Style.css

I guess you do it this way


/* child style.css */
@import url('../parent-theme/style.css');
/* Theme customization starts here */
						

/* parent : functions.php */
add_action( 'wp_enqueue_scripts', 'my_enqueue_styles' );
function my_enqueue_styles() {
	wp_enqueue_style( 'style', get_stylesheet_uri() );
}
						

The @import method has a negative impact on web page performance.

Style.css

I guess you do it this way


/* child style.css */
@import url('../parent-theme/style.css');
/* Theme customization starts here */
						

/* parent : functions.php */
add_action( 'wp_enqueue_scripts', 'my_enqueue_styles' );
function my_enqueue_styles() {
	wp_enqueue_style( 'style', get_stylesheet_uri() );
}
						

Codex : Note that the previous method was to import the parent theme stylesheet using @import: this is no longer best practice.

Style.css

Codex


/* child : functions.php */
add_action( 'wp_enqueue_scripts', 'theme_enqueue_styles' );
function theme_enqueue_styles() {
  wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
}
						

/* twentyfifteen-child : functions.php */
add_action( 'wp_enqueue_scripts', 'theme_enqueue_styles' );
function theme_enqueue_styles() {
  wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
}
						

/* twentyfifteen : functions.php */
add_action( 'wp_enqueue_scripts', 'twentyfifteen_scripts' );
function twentyfifteen_scripts() {
  ...
  // Load our main stylesheet.
  wp_enqueue_style( 'twentyfifteen-style', get_stylesheet_uri() );
  ...
}
						
Web Inspector

3 situations

1 (default) :
modify your css in the child
2 :
build your site css from scratch in the child
3 :
do not modify css in the child at all
T
S
-
S
T
-

T - style.css from the template directory (parent)
S - style.css from the stylesheet directory (child)
					
/* parent : functions.php */
add_action( 'wp_enqueue_scripts', 'my_enqueue_styles' );
function my_enqueue_styles() {
  $deps = array();
  if ( apply_filters( 'load_template_css', is_child_theme() ) ) {
    wp_enqueue_style( 'T-style', get_template_directory_uri().'/style.css', $deps );
    $deps[] = 'T-style';
  }
  if ( apply_filters( 'load_stylesheet_css', true ) ) {
    wp_enqueue_style( 'S-style', get_stylesheet_uri(), $deps );
  }
}
						

Parent theme is activated : S-style is loaded

The 3rd paramenter of 'wp_enqueue_style' (dependencies) ensures that the 'T-style' (parent) will be loaded before the 'S-style' (child).

					
/* parent : functions.php */
add_action( 'wp_enqueue_scripts', 'my_enqueue_styles' );
function my_enqueue_styles() {
  $deps = array();
  if ( apply_filters( 'load_template_css', is_child_theme() ) ) {
    wp_enqueue_style( 'T-style', get_template_directory_uri().'/style.css', $deps );
    $deps[] = 'T-style';
  }
  if ( apply_filters( 'load_stylesheet_css', true ) ) {
    wp_enqueue_style( 'S-style', get_stylesheet_uri(), $deps );
  }
}
						

Child theme is activated : both T and S are loaded (S after T)

The 3rd paramenter of 'wp_enqueue_style' (dependencies) ensures that the 'T-style' (parent) will be loaded before the 'S-style' (child).

Situation 2 :

Build your site css from scratch in the child

					
/* child situation 2 : functions.php */
add_filter( 'load_template_css', 'do_not_load' );
function do_not_load() {
  return false;
}
						
					
/* parent : functions.php */
add_action( 'wp_enqueue_scripts', 'my_enqueue_styles' );
function my_enqueue_styles() {
  $deps = array();
  if ( apply_filters( 'load_template_css', is_child_theme() ) ) {
    wp_enqueue_style( 'T-style', get_template_directory_uri().'/style.css', $deps );
    $deps[] = 'T-style';
  }
  if ( apply_filters( 'load_stylesheet_css', true ) ) {
    wp_enqueue_style( 'S-style', get_stylesheet_uri(), $deps );
  }
}
						

Situation 3 :

Do not modify css in the child at all

	
/* child situation 3 : functions.php */
add_filter( 'load_stylesheet_css', 'do_not_load' );
function do_not_load() {
  return false;
}
						
					
/* parent : functions.php */
add_action( 'wp_enqueue_scripts', 'my_enqueue_styles' );
function my_enqueue_styles() {
  $deps = array();
  if ( apply_filters( 'load_template_css', is_child_theme() ) ) {
    wp_enqueue_style( 'T-style', get_template_directory_uri().'/style.css', $deps );
    $deps[] = 'T-style';
  }
  if ( apply_filters( 'load_stylesheet_css', true ) ) {
    wp_enqueue_style( 'S-style', get_stylesheet_uri(), $deps );
  }
}
						

Hooks : Actions & Filters


apply_filters( $tag, $value, $var, ... );
						

The callback functions attached to filter hook $tag are invoked.



do_action( $tag, $args );
						

Execute functions hooked on a specific action hook.



add_filter( $tag, $function, $priority, $acccepted_args );
add_action( $tag, $function, $priority, $acccepted_args );
						

Hook a function to a specific filter action, $priority specifies the order in which the functions associated with a particular action are executed.

Example 1

footer footer
	
/* twentyfifteen-child : functions.php */
add_action( 'twentyfifteen_credits', 'pehaa_credits' );
function pehaa_credits() {
  echo 'Customized by PeHaa.';
}
						
	
/* twentyfifteen : footer.php */
do_action( 'twentyfifteen_credits' );
						
footer

Example 2

Instead of:
					
/* parent : functions.php */
require get_template_directory() . 'my_file.php';
						
I'd rather use:
					
/* parent : functions.php */
require apply_filters( 'my_file_directory', get_template_directory() ) . 'my_file.php';
						
since it allows to add and modify 'my_file.php' in the child :
					
/* child : functions.php */
add_filter( 'my_file_directory', 'my_stylesheet_dir' );
function my_stylesheet_dir() {
	return get_stylesheet_directory();
}
						

Summary

Tips for theme developpers:

  • the difference between the 'template' and the 'stylesheet'
  • the order in which the theme files are loaded
  • making user functions pluggable
  • "apply_filters" and "do_action" as often as possible

Follow @PeHaa sur Twitter

Thanks and happy coding in 2015!