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.
Table Of Contents (T.O.C.):
- What is a WordPress child theme?
- How to create a WordPress child theme
- Examples of WordPress child themes
3.1. Storefront child theme
3.2. Astra child theme
3.3. GeneratePress child theme
3.4. Virtue Premium child theme - Appearance changes using child theme style.css
- Appearance and function changes using child theme functions.php
5.1. Adding visual breadcrumbs navigation - Why all this?
- 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. 🙂
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.
- Here you can download the created child theme which’s code is shown below:
https://files.bikegremlin.com/wordpress/themes/generatepress-child.zip - Here is the theme edited by BikeGremlin (just edit, or remove the “YOUR-TRACKING-CODE” and “YOUR-PUB-CODE” sections for Google Analytics and AdSense:
https://files.bikegremlin.com/wordpress/themes/generatepress-bikegremlin.zip
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 "?>"
?>
3.4. Virtue Premium child theme
style.css:
/*
Theme Name: Virtue Premium Child
Theme URI: https://io.bikegremlin.com/11334/child-theme/#3.3
Description: Child theme for Virtue Premium theme.
Author: Relja Novović
Author URI: https://www.bikegremlin.com
Template: virtue_premium
Version: 1.0.0
*/
/* Custom CSS for child theme goes below */
functions.php:
<?php
function virtue_premium_child_enqueue_styles() {
// Enqueue the parent theme's stylesheet
wp_enqueue_style( 'parent-style', get_template_directory_uri() . '/style.css' );
// Enqueue the child theme's stylesheet
wp_enqueue_style( 'child-style', get_stylesheet_directory_uri() . '/style.css', array('parent-style') );
}
add_action( 'wp_enqueue_scripts', 'virtue_premium_child_enqueue_styles' );
?>
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:
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 its “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 NEW2 BREADCRUMBS
=============================================*/
function the_breadcrumb() {
// Set up language text variables (could be expanded or localized using __())
$lg_home_name = "Start";
$lg_archive_by_category = "Category:";
$lg_search_results_for = "Search:";
$lg_posts_tagged = "Keyword:";
$lg_articles_posted_by = "Author:";
$showOnHome = false; // Use booleans for clarity
$delimiter = '»'; // delimiter between crumbs
$home = $lg_home_name; // text for the 'Home' link
$showCurrent = false; // true - show current post/page title in breadcrumbs, false - don't show
$before = '<span class="current">'; // tag before the current crumb
$after = '</span>'; // tag after the current crumb
$homeLink = home_url();
if (is_home() || is_front_page()) {
if ($showOnHome) {
echo '<div id="crumbs"><a href="' . esc_url($homeLink) . '">' . esc_html($home) . '</a></div>';
}
} else {
echo '<div id="crumbs"><a href="' . esc_url($homeLink) . '">' . esc_html($home) . '</a> ' . $delimiter . ' ';
if (is_category()) {
$thisCat = get_category(get_query_var('cat'), false);
if ($thisCat && $thisCat->parent != 0) {
echo get_category_parents($thisCat->parent, true, ' ' . $delimiter . ' ');
}
echo $before . esc_html($lg_archive_by_category) . ' ' . esc_html(single_cat_title('', false)) . $after;
} elseif (is_search()) {
echo $before . esc_html($lg_search_results_for) . ' "' . esc_html(get_search_query()) . '"' . $after;
} elseif (is_day()) {
echo '<a href="' . esc_url(get_year_link(get_the_time('Y'))) . '">' . esc_html(get_the_time('Y')) . '</a> ' . $delimiter . ' ';
echo '<a href="' . esc_url(get_month_link(get_the_time('Y'), get_the_time('m'))) . '">' . esc_html(get_the_time('F')) . '</a> ' . $delimiter . ' ';
echo $before . esc_html(get_the_time('d')) . $after;
} elseif (is_month()) {
echo '<a href="' . esc_url(get_year_link(get_the_time('Y'))) . '">' . esc_html(get_the_time('Y')) . '</a> ' . $delimiter . ' ';
echo $before . esc_html(get_the_time('F')) . $after;
} elseif (is_year()) {
echo $before . esc_html(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="' . esc_url($homeLink . '/' . $slug['slug']) . '/">' . esc_html($post_type->labels->singular_name) . '</a>';
if ($showCurrent) {
echo ' ' . $delimiter . ' ' . $before . esc_html(get_the_title()) . $after;
}
} else {
$cat = get_the_category();
if ($cat) {
$cat = $cat[0];
$cats = get_category_parents($cat, true, ' ' . $delimiter . ' ');
if (!$showCurrent) {
$cats = preg_replace("#^(.+)\s$delimiter\s$#", "$1", $cats);
}
echo $cats;
if ($showCurrent) {
echo $before . esc_html(get_the_title()) . $after;
}
}
}
} elseif (is_tag()) {
echo $before . esc_html($lg_posts_tagged) . ' "' . esc_html(single_tag_title('', false)) . '"' . $after;
} elseif (is_author()) {
$userdata = get_userdata(get_queried_object_id());
echo $before . esc_html($lg_articles_posted_by) . ' ' . esc_html($userdata->display_name) . $after;
} elseif (is_404()) {
echo $before . 'Error 404' . $after;
}
// Handle pagination
if (get_query_var('paged')) {
echo ' (' . esc_html__('Page', 'text-domain') . ' ' . esc_html(get_query_var('paged')) . ')';
}
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 NEW2 BREADCRUMBS
=============================================*/
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.
Last updated:
Originally published: