START here

WordPress child themes

In this post I’ll write about WordPress child themes. First I’ll explain what child themes are and why they are very useful. Then I’ll explain how they are made, with a few concrete examples for some popular WordPress themes.

Contents:

  1. What is a WordPress child theme?
  2. How to create a WordPress child theme
  3. Examples of WordPress child themes
    3.1. Storefront child theme
    3.2. Astra child theme
    3.3. GeneratePress child theme
  4. Appearance changes using child theme style.css
  5. Appearance and function changes using child theme functions.php
    5.1. Adding visual breadcrumbs navigation
  6. Why all this?
  7. Conclusion


1. What is a WordPress child theme?

First I’ll explain what a WordPress theme is:

Theme is a set of CSS and PHP code (and files) that determine what your website will look like: where the widgets will be positioned, what kind of fonts will be used, what the background colour will be etc. Most themes are customizable – some more, some less.

However, sometimes it is needed to get some design, or even functionality, that the theme author hadn’t thought of. This is where child themes step in, enabling introduction of the desired changes, without the use of any (additional) plugins, or making a new theme – so that the changes remain even after the theme updates (new versions).

For example: the “Powered by bacon & electricity” copyright at the bottom of this page is implemented using a child theme and a simple line of PHP/HTML.

Child theme “works” by changing certain elements of the main theme. A concrete example to help understand this:

// begin copyright changes - this is a comment

add_filter( 'generate_copyright','tu_custom_copyright' );
function tu_custom_copyright() {
    ?>
    <span class="copyright">© 2018 - <?php echo date("Y"); ?> I/O Gremlin</span> • Powered by bacon & electricity </div>
    <?php
}

// end copyright changes

So the original copyright created by the theme’s author is replaced with a custom made one. In this case this was done using a newly created function: “tu_custom_copyright”. We’ll be dealing with customizations in more details a bit later, but first to explain how to create a child theme.


2. How to create a WordPress child theme

Making a child of an existing theme is relatively simple. First make a directory for the child theme within the WordPress theme directory, which is:
wp-content/themes

For start, the child theme directory can be created on the local computer, then, along with files included, uploaded to the wp-content/themes directory. Directory name is usually created as “theme_name-child“, but you could name it whatever you like. No empty spaces and special characters in the directory name (slashes “-” and underscores “_” are allowed).

Next, create a text file (using any text editor) named “style.css” – no other name. That file should contain certain fields, created as a commend, that is otherwise disregarded by “computers”, but in a style.css file of a theme, WordPress will not ignore the contents of this particular comment added at the very beginning:

/*
 Theme Name:   GeneratePress child
 Theme URI:    https://io.bikegremlin.com/11334/child-theme/#3.3
 Description:  GeneratePress child theme
 Author:       BikeGremlin
 Author URI:   https://www.bikegremlin.com/about/
 Template:     generatepress
 Version:      1.0.0
 License:      GNU General Public License v2 or later
 License URI:  http://www.gnu.org/licenses/gpl-2.0.html
 Tags:         light, dark, two-columns, right-sidebar, responsive-layout, accessibility-ready, woo commerce ready
 Text Domain:  generatepress-child
*/

Template:” is the name of the directory in which the “mother” theme is installed, it must exist and be correctly written.

Other fields are whatever you like and won’t affect the child theme’s functionality.

Fields after (below) “Template:” can be omitted, but don’t forget to “close” the comment using “*/”.

It remains to create functions.php text file. Functions that add functionality, or that change the working of the existing theme functions (later on that) will be added here. It can generally be left blank for the start, though some themes “insist” on adding certain functions right from the start. This is usually enough:

<?php
add_action('wp_enqueue_scripts', 'add_child_theme_style');
function add_child_theme_style() {
wp_enqueue_style('parent-style', get_template_directory_uri() . '/style.css');
}
// your code goes right below this

?>

After this, when opening your themes menu, you will see your newly added child theme shown. Activate it. 🙂

Created (and uploaded) child theme will be shown in the WordPress themes menu
Created (and uploaded) child theme will be shown in the WordPress themes menu
Picture 1


3. Examples of WordPress child themes

Before explaining the way appearance and functionality is altered using child themes, I’ll first give a few examples of “blank” (prepared for additions and changes) child themes for a few popular WordPress themes. Showing the contents of the style.css and functions.php files of the child themes.

For a concrete child theme, it is good to visit the theme author’s website and check whether there are some particular requirements for the functions.php contents.


3.1. Storefront child theme

Storefront is a theme by the WordPress authors (Automattic). Primarily aimed at web-shops using WordPress and WooCommerce. How to make a child theme for it?

style.css:

/* 
Theme Name: Storefront Child 
Version: 1.0 
Description: Child theme for Storefront. 
Author: Woo
Author URI: http://woocommerce.com 
Template: storefront 
*/

functions.php:

<?php

// Storefront Child theme - add your code after this (new line), before the "?>"

?>


3.2. Astra child theme

Astra is a very fast and well written WordPress theme. With good integration with Elementor and WooCommerce.

style.css:

/**
Theme Name: Astra-child
Author: BikeGremlin
Author URI: https://io.bikegremlin.com
Description: Astra child theme
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: astra-child
Template: astra
*/

functions.php:

<?php
/**
 * Astra-child Theme functions and definitions
 *
 * @link https://developer.wordpress.org/themes/basics/theme-functions/
 *
 * @package Astra-child
 * @since 1.0.0
 */

/**
 * Define Constants
 */
define( 'CHILD_THEME_ASTRA_CHILD_VERSION', '1.0.0' );

/**
 * Enqueue styles
 */
function child_enqueue_styles() {

	wp_enqueue_style( 'astra-child-theme-css', get_stylesheet_directory_uri() . '/style.css', array('astra-theme-css'), CHILD_THEME_ASTRA_CHILD_VERSION, 'all' );

}

add_action( 'wp_enqueue_scripts', 'child_enqueue_styles', 15 );

// changes and additions go after this line


?>


3.3. GeneratePress child theme

GeneratePress is one of my favourite themes. With excellent documentation and support. Fast, well written, with a great Elementor integration.


style.css:

/*
 Theme Name:   GeneratePress child
 Theme URI:    https://io.bikegremlin.com/11334/child-theme/#3.3
 Description:  GeneratePress child theme
 Author:       BikeGremlin
 Author URI:   https://www.bikegremlin.com/about/
 Template:     generatepress
 Version:      1.0.0
 License:      GNU General Public License v2 or later
 License URI:  http://www.gnu.org/licenses/gpl-2.0.html
 Tags:         light, dark, two-columns, right-sidebar, responsive-layout, accessibility-ready, woo commerce ready
 Text Domain:  generatepress-child
*/

/*
 Enter your custom code below
*/


functions.php:

<?php
/**
 * GeneratePress child theme functions and definitions.
 *
 * Add your custom PHP in this file. 
 * Only edit this file if you have direct access to it on your server (to fix errors if they happen).
 */

/*
 * BEGIN removing mostly needless code like RTL language check
 * Arab language sites should include this code 

function generatepress_child_enqueue_scripts() {
    if ( is_rtl() ) {
        wp_enqueue_style( 'generatepress-rtl', trailingslashit( get_template_directory_uri() ) . 'rtl.css' );
    }
}
add_action( 'wp_enqueue_scripts', 'generatepress_child_enqueue_scripts', 100 );

 * END removing mostly needless code like RTL language check
*/

// add your functions and code below this, above the "?>"

?>


4. Appearance changes using child theme style.css

You can look for the code of your WordPress webiste by pressing F12 in most browsers while viewing the site and choosing “Elements” option.

A concrete example. When viewing picture galleries, I didn’t like social share icons to be visible. First thing to do was to find what style defines their position. It was done like shown in picture 2:

Finding the styling for share buttons
Finding the styling for share buttons
Picture 2

The picture is rather small, but you’ll have to take my word for it that next to number 4 in picture 2 it is written: “.heateor_sss_vertical_sharing”. So I edited it’s “vertical” position to “push it” further back so it doesn’t show when a Lightbox gallery image is shown, editing style.css of the child theme:

/* moving shared icons to the background  */

.heateor_sss_vertical_sharing {
	z-index:100000 !important;
}

/* end of moving shared icons to the background  */

Now, since “.heateor_sss_vertical_sharing” is defined in the child theme (as well), it takes precedence over that style definition in the main theme. This is how style and function alterations are done using a child theme: whatever you define in the child theme takes precedence.

The result of such changes can be seen on the website of a makeup studio in Novi Sad, for example.

Another example is the change of the background image and it’s styling. Style definition was “hunted down” using the method shown in picture 2, with a few more changes than in previous example:

/* changing background image and styling - don't forget to "close" the comment */

body {
background-image: url( 'https://io.bikegremlin.com/wp-content/uploads/2019/03/io_bikegremlin_background2.jpg' );
background-position: center center;
background-size: cover;
background-repeat: no-repeat;
background-attachment: fixed;
}

/* end of changing background image and styling */


5. Appearance and function changes using child theme functions.php

When changing functions.php file, it is possible to add a completely new function. For example, listing child pages of a higher hierarchy page, using a pre-defined shortcode:

// BEGIN list child pages

function relja_list_child_pages() {
  global $post;
  if ( is_page() )
  $childpages = wp_list_pages( 'sort_column=menu_order&title_li=&child_of=' . $post->ID . '&echo=0' );
  if ( isset($childpages )) {
    $string = '<ul>' . $childpages . '</ul>';
  }
 else{
     $string = '';
 }
  return $string;
}
add_shortcode('relja_child_pages', 'relja_list_child_pages');

// END list child pages


A function “relja_list_child_pages” was added. Now, shortcode “relja_child_pages” will list all the child pages.

Here’s an edited version that will list both the children, and the pages on the same level as the page where the shortcode is added:

// BEGIN list child and same-level pages

function relja_list_child_pages() { 
 
global $post; 
 
if ( is_page() && $post->post_parent )
 
    $childpages = wp_list_pages( 'sort_column=menu_order&title_li=&child_of=' . $post->post_parent . '&echo=0' );
else
    $childpages = wp_list_pages( 'sort_column=menu_order&title_li=&child_of=' . $post->ID . '&echo=0' );
 
if ( $childpages ) {
 
    $string = '<ul>' . $childpages . '</ul>';
}
 
return $string;
 
}
 
add_shortcode('relja_child_pages', 'relja_list_child_pages');

// END list child and same-level pages

An example from this website would be clicking on “Services” from the main menu.

Likewise, existing theme functions can be re-defined. One of the ways to do it is using “hooks”. A picture explains it better, here is hooks cheat sheet for the GeneratePress theme.

Here is how that is used to show contact telephone and email at the top of the page on a website:

// thin blue line on top

add_action( 'generate_before_header', 'blue_top_menu' );
function blue_top_menu(){
     ?>
        <div class="blue-top-menu"><a title="[email protected]" alt="[email protected]" href="mailto:[email protected]" class="link-blue-top-menu">[email protected] <a title="+38163666999" alt="+38163666999" href="tel:+38163666999" class="link-blue-top-menu">+38163666999</a></div>
            
    <?php  
}

// end of thin blue line on top

A new function called “blue_top_menu” was defined, with the hook “generate_before_header” used as a place for showing it.

How it looks like in practice can be seen on this website for sailing in Greece.


5.1. Adding visual breadcrumbs navigation

Without much talk, here’s the code that works: 🙂

// BEGIN Breadcrumbs display
//  to include in functions.php
function the_breadcrumb()
{

	$lg_home_name ="Home";
	$lg_archive_by_category = "";
    $lg_search_results_for = "Search:";
    $lg_posts_tagged = "Keyword:";
    $lg_articles_posted_by  = "Author:";


    $showOnHome = 0; // 1 - show breadcrumbs on the homepage, 0 - don't show
    $delimiter = '&raquo;'; // delimiter between crumbs
    //$home = 'Home'; // text for the 'Home' link
    $home = $lg_home_name; // text for the 'Home' link
    $showCurrent = 0; // 1 - show current post/page title in breadcrumbs, 0 - don't show
    $before = '<span class="current">'; // tag before the current crumb
    $after = '</span>'; // tag after the current crumb

    global $post;
    $homeLink = get_bloginfo('url');
    if (is_home() || is_front_page()) {
        if ($showOnHome == 1) {
            echo '<div id="crumbs"><a href="' . $homeLink . '">' . $home . '</a></div>';
        }
    } else {
        echo '<div id="crumbs"><a href="' . $homeLink . '">' . $home . '</a> ' . $delimiter . ' ';
        if (is_category()) {
            $thisCat = get_category(get_query_var('cat'), false);
            if ($thisCat->parent != 0) {
                echo get_category_parents($thisCat->parent, true, ' ' . $delimiter . ' ');
            }
            //echo $before . 'Archive by category "' . single_cat_title('', false) . '"' . $after;           
            echo $before .$lg_archive_by_category. ' ' . single_cat_title('', false) . '' . $after;

        } elseif (is_search()) {
            //echo $before . 'Search results for "' . get_search_query() . '"' . $after;
            echo $before  .$lg_search_results_for.' "' . get_search_query() . '"' . $after;
        } elseif (is_day()) {
            echo '<a href="' . get_year_link(get_the_time('Y')) . '">' . get_the_time('Y') . '</a> ' . $delimiter . ' ';
            echo '<a href="' . get_month_link(get_the_time('Y'), get_the_time('m')) . '">' . get_the_time('F') . '</a> ' . $delimiter . ' ';
            echo $before . get_the_time('d') . $after;
        } elseif (is_month()) {
            echo '<a href="' . get_year_link(get_the_time('Y')) . '">' . get_the_time('Y') . '</a> ' . $delimiter . ' ';
            echo $before . get_the_time('F') . $after;
        } elseif (is_year()) {
            echo $before . get_the_time('Y') . $after;
        } elseif (is_single() && !is_attachment()) {
            if (get_post_type() != 'post') {
                $post_type = get_post_type_object(get_post_type());
                $slug = $post_type->rewrite;
                echo '<a href="' . $homeLink . '/' . $slug['slug'] . '/">' . $post_type->labels->singular_name . '</a>';
                if ($showCurrent == 1) {
                    echo ' ' . $delimiter . ' ' . $before . get_the_title() . $after;
                }
            } else {
                $cat = get_the_category();
                $cat = $cat[0];
                $cats = get_category_parents($cat, true, ' ' . $delimiter . ' ');
                if ($showCurrent == 0) {
                    $cats = preg_replace("#^(.+)\s$delimiter\s$#", "$1", $cats);
                }
                echo $cats;
                if ($showCurrent == 1) {
                    echo $before . get_the_title() . $after;
                }
            }
        } elseif (!is_single() && !is_page() && get_post_type() != 'post' && !is_404()) {
            $post_type = get_post_type_object(get_post_type());
            echo $before . $post_type->labels->singular_name . $after;
        } elseif (is_attachment()) {
            $parent = get_post($post->post_parent);
            $cat = get_the_category($parent->ID);
            $cat = $cat[0];
            echo get_category_parents($cat, true, ' ' . $delimiter . ' ');
            echo '<a href="' . get_permalink($parent) . '">' . $parent->post_title . '</a>';
            if ($showCurrent == 1) {
                echo ' ' . $delimiter . ' ' . $before . get_the_title() . $after;
            }
        } elseif (is_page() && !$post->post_parent) {
            if ($showCurrent == 1) {
                echo $before . get_the_title() . $after;
            }
        } elseif (is_page() && $post->post_parent) {
            $parent_id  = $post->post_parent;
            $breadcrumbs = array();
            while ($parent_id) {
                $page = get_page($parent_id);
                $breadcrumbs[] = '<a href="' . get_permalink($page->ID) . '">' . get_the_title($page->ID) . '</a>';
                $parent_id  = $page->post_parent;
            }
            $breadcrumbs = array_reverse($breadcrumbs);
            for ($i = 0; $i < count($breadcrumbs); $i++) {
                echo $breadcrumbs[$i];
                if ($i != count($breadcrumbs)-1) {
                    echo ' ' . $delimiter . ' ';
                }
            }
            if ($showCurrent == 1) {
                echo ' ' . $delimiter . ' ' . $before . get_the_title() . $after;
            }
        } elseif (is_tag()) {
            //echo $before . 'Posts tagged "' . single_tag_title('', false) . '"' . $after;
			echo $before . $lg_posts_tagged .' "' . single_tag_title('', false) . '"' . $after;
        } elseif (is_author()) {
            global $author;
            $userdata = get_userdata($author);
            //echo $before . 'Articles posted by ' . $userdata->display_name . $after;
            echo $before . $lg_articles_posted_by.' ' . $userdata->display_name . $after;
        } elseif (is_404()) {
            echo $before . 'Error 404' . $after;
        }
        if (get_query_var('paged')) {
            if (is_category() || is_day() || is_month() || is_year() || is_search() || is_tag() || is_author()) {
                echo ' (';
            }
            echo __('Page') . ' ' . get_query_var('paged');
            if (is_category() || is_day() || is_month() || is_year() || is_search() || is_tag() || is_author()) {
                echo ')';
            }
        }
        echo '</div>';
    }
}

add_action( 'generate_before_main_content', 'tabela_zaglavlje' );
function tabela_zaglavlje(){
  if (function_exists('the_breadcrumb')){
  the_breadcrumb();
  echo "<hr style='margin:10px 0 20px 0;'>";
  }
}
// END Breadcrumbs display


6. Why all this?

A perfectly logical question, although I left it for the end.

Basic desired look and functionality of a WordPress website can be achieved using an appropriately chosen theme. Still, it is not always possible to find a good quality theme that matches your needs 100%.

Of course, there are a lot of good quality plugins that enable a lot of needed changes and additions. Still, I think it is better to solve as much as possible without using plugins, especially for minor alterations that can relatively easily be implemented. Why?

  • Each plugin puts additional “weight” on the website, often coming with a lot more functions and code (that needs to be run on each page visit) than necessary.
  • Each plugin is a potential security risk.
  • Because of the previous two points: the fewer plugins, the better. Use only those that are really necessary. Make all the other “minor” changes using a child theme.

Desired changes can be made “directly” within the theme. But: in case of a theme update, all the changes will be lost. A child theme is a convenient way to make the desired alterations “permanent”.


7. Conclusion

When choosing a theme for your WordPress website, do it with care and caution. Obviously, a theme should provide (most of) the looks and the functionality you require. But also check how regularly it is updated, does the author provide good support and documentation (important for child theme editing), as well as how well written and “fast” it is. I wrote a series of posts on website optimization, explaining that aspect.

Create a child theme immediately and test how it works.


Please use the BikeGremlin.net forum for any comments or questions.

If you've found any errors or lacking information in the article(s) - please let me know by commenting on the BikeGremlin forum.
You can comment anonymously (by registering with any name/nickname), but I think it is good to publicly document all the article additions (and especially corrections) - even if their author chooses to remain anonymous.

Skip to content