Comment on page
Using Child Themes
Customize P4 using a child theme
Please talk to the P4 Team before doing custom development and theming. If we are ever going to reach the full potential of Greenpeace’s engagement platform it is absolutely essential that we work TOGETHER.
In order to follow WordPress guidelines and simplify our code, we are moving a lot of styling options to the theme.json file. If you want to override and/or add some of these settings for your site, you can create your own
theme.json
file with your customisations in the root folder of your child theme. For implementation help, or just to understand better how this works, here are some useful links:All css changes should happen inside
style.css
that lies on the root of the child theme and initially is empty. This css code is loaded after the master-theme’s css, so anything put in there will override the corresponding class or element.Keep in mind though, that in the master-theme many elements and classes are nested (using Sass). For that reason we have to be careful with precedence and take that fact into account when we write our css code for our custom child theme. The more specific and explicit a css declaration is, the bigger precedence it has.
So for instance, if we want to adjust the color of the post tags, we should add a css rule like this one:
.single-post .page-header .top-page-tags a {
color: #2980b9;
}
If we want to override an existing template the first step is to create a new folder named templates inside the root of our custom child theme.
Inside that folder, we create a twig template with the same name it has on the master-theme. The best approach would be to actually copy it from the master-theme so we can have its original code as a base for modifications.
So for instance, If we want to adjust the default page template we have to copy the one from the master-theme that exists at
templates/page.twig
and at the moment contains this code:{% extends "base.twig" %}
{% block content %}
<div class="page-content">
{{ post.content|raw }}
</div>
{% endblock %}
Assuming we want to display more metadata to the pages content, we would have to use the same code and do any additions (or deletions) we want.
{% extends "base.twig" %}
{% block content %}
<div class="page-content">
<div>{{ post.author.name }}</div>
{{ post.content|raw }}
</div>
{% endblock %}
Since we already have some production running websites that use their own customized child-theme we can use them as a guide for more advanced and specific tweaks. Besides screenshots and live examples provided here, you can also check the code on the relevant child theme of the NRO.
For the needs of the Greek website or the Thai website we needed to change the Lora serif font, that is used in the master-theme, to a Sans option. Noto Sans is the font we use for body copy if a language doesn’t support serif font. In order to avoid overriding every present or future occurrence of the Lora font, instead we used the same font-family name to load the new font. So every class or element that uses Lora will automatically use the new one.
As an example, here is a snippet of code that sets the Greek letterspace for normal font-face:
@font-face {
font-family: 'Lora';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/notosans/v7/o-0IIpQlx3QUlC5A4PNr5jRAW_0.woff2) format('woff2');
}
The full code of the Greek website customizations can be seen at the planet4-child-theme-greece repository.
Many NROs have decided to change their primary navigation toolbar, below a full list and some screenshots:



For the needs of this awesome Handbook website we needed to add a sidebar and move the navigation links to that. In order to achieve that we created two new templates in the child theme. One for the top navigation bar and one for the sidebar. Following master theme naming scheme, the first one is
navigation-bar.twig
, where we removed the code that generates the navigation links and left only the logo and the search form. The second one is the sidebar.twig
where we added the code for generating the navigation menu and through custom css code we style it to be vertical.If you need more space in the top bar navigation (for instance you have a multilingual site and need a language switcher at the top), you might need to move the GPI NRO selector from the top bar to the bottom of the page like on GPLux website.

To move the selector you need to:
- create in your child theme directory an empty directory called "templates" symmetrical to the templates directory of the P4 master theme (here)
- modify the template by deleting the following blocks.
<button
class="country-dropdown-toggle"
data-toggle="open"
data-target="#country-list"
aria-expanded="false"
aria-label="{{ data_nav_bar.country_dropdown_toggle }}"
>
<span class="screen-reader-text">{{ __( 'Selected', 'planet4-master-theme' ) }}:</span> {{ website_navbar_title }}
<span class="screen-reader-text">{{ __( 'Change Country', 'planet4-master-theme' ) }}</span>
</button>
{% include 'countries.twig' %}
- add the country selector to the footer where you want to print it for example below the footer-links-secondary.
<ul class="list-unstyled footer-links-country">
<button
class="country-dropdown-toggle"
data-toggle="open"
data-target="#country-list"
aria-expanded="false"
aria-label="{{ data_nav_bar.country_dropdown_toggle }}"
>
<span class="screen-reader-text">{{ __( 'Selected', 'planet4-master-theme' ) }}:</span> {{ website_navbar_title }}
<span class="screen-reader-text">{{ __( 'Change Country', 'planet4-master-theme' ) }}</span>
</button>
{% include 'countries.twig' %}
</ul>
- then apply the css.
.footer-links-country .country-dropdown-toggle {
color: #5d646b;
background-color: white;
}
.country-list {
position: absolute;
top: -600px;
height: 600px;
width: 100%;
background: white;
border: 1px solid #73be31;
a {
color: #5d646b;
&:hover {
color: #73be31;
}
}
}
@media (min-width: 992px) {
.country-list {
width: 80%;
}
}
If you want to change the little arrow which opens the selector, you can rewrite the “after” element with an image from your theme directory.
.country-dropdown-toggle:focus::after,
.country-dropdown-toggle:hover::after {
background-image: url(/wp-content/themes/planet4-child-theme/YOUR-IMAGE.svg);
}
Since our css code on child themes is located in just one file it can easily start getting difficult to maintain as it grows in size. To avoid that, we can create a new folder and break up our css code to blocks that make sense. Then we can just import them to the main
style.css
.@import 'css/variables.css';
@import 'css/navigation.css';
@import 'css/footer.css';
To make some of the custom css code more maintainable we can use css variables. Especially for the things that are re-used in many places inside our code. Our theme colors are a good example for that.
:root {
--black: black;
--white: white;
--light-grey: #f6f4e7;
--blue: #01223D;
--blue-60: #007799;
--blue-40: #03aad6;
--red: #e51538;
}
.nav-search-wrap .form-control {
background-color: var(--blue);
color: var(--white);
}

Luxembourg child theme implements a short css section to underline titles with a green gradient line. We had an unexpected good feedback on this effect thus we share the code here:
.page-header-title{
width: auto;
background-image: linear-gradient(10deg, #73be31 0%, rgba(115,190,49,0.3) 100%);
background-repeat: repeat-x;
display: inline;
padding: 0 10px 20px 0px;
background-position: 0 0.8em;
background-size: 10px 9px;
}
If you don’t use postcss prefix you can use this code:
.page-header-title{
width: auto;
background-image: -webkit-linear-gradient(80deg, #73be31 0%, rgba(115,190,49,0.3) 100%);
background-image: -o-linear-gradient(80deg, #73be31 0%, rgba(115,190,49,0.3) 100%);
background-image: linear-gradient(10deg, #73be31 0%, rgba(115,190,49,0.3) 100%);
background-repeat: repeat-x;
display: inline;
padding: 0 10px 20px 0px;
background-position: 0 0.8em;
background-size: 10px 9px;
}
You will find on GP Luxembourg a custom contact form. On this form, changing the input Objet de votre message will open many different use cases for the user (like changing his bank account). An e-mail is sent to the admin on submit.

If you want to reproduce this form you can simply grab the code here:
- and optionally the css. (please note scss is compiled with a webpack script to minified scripts and scss in the /dist folder)
Form validation, masking, animation, 3rd party popups. See Aotearoa child theme for examples / source code.
This will show Donate button fixed on top of screen on mobile devices

Always visible Donate button on mobile
.btn-donate-top {
position: fixed;
left: 0;
z-index: 10;
width: 100%;
line-height: 46px;
top: 46px;
transition: top .2s;
}
.btn-donate-top-hide {
top: -46px;
}
.admin-bar .btn-donate-top {
top: 114px;
}
.admin-bar .btn-donate-top-hide {
top: -114px;
}
body.with-donate-on-top {
padding-top: 46px;
}
const setupEnhancedDonateButton = () => {
const maxWidth = '576px';
const enhancedButtonClass = 'btn-donate-top';
const isMobile = () => window.matchMedia(`(max-width: ${maxWidth})`).matches;
const setupEnhancedButton = () => {
let originalDonateBtn = document.querySelector('.nav-donate > .btn-donate');
let enhancedDonateBtn = document.querySelector(`.${enhancedButtonClass}`);
if (!originalDonateBtn) {
return;
}
isMobile()
? addDonateButtonOnTop(originalDonateBtn, enhancedDonateBtn)
: removeDonateButtonOnTop(enhancedDonateBtn);
};
const addDonateButtonOnTop = (originalDonateBtn, enhancedDonateBtn) => {
if (enhancedDonateBtn) {
return;
}
let enhanced = originalDonateBtn.cloneNode(true);
enhanced.classList.add(enhancedButtonClass);
document.querySelector('body').appendChild(enhanced);
document.querySelector('body').classList.add('with-donate-on-top');
}
const removeDonateButtonOnTop = (enhancedDonateBtn) => {
if (enhancedDonateBtn) {
enhancedDonateBtn.parentNode.removeChild(enhancedDonateBtn);
}
document.querySelector('body').classList.remove('with-donate-on-top');
}
const ready = (fn) => {
if ( document.readyState !== 'loading' ) {
return fn();
}
document.addEventListener('DOMContentLoaded', fn);
};
ready(() => {
setupEnhancedButton();
window.addEventListener('resize', () => setupEnhancedButton());
const windowHeight = window.innerHeight;
window.addEventListener('scroll', () => {
const enhancedDonateBtn = document.querySelector(`.${enhancedButtonClass}`);
if (!enhancedDonateBtn) {
return;
}
enhancedDonateBtn.classList.toggle(
'btn-donate-top-hide',
isMobile() ? window.scrollY > windowHeight : false
);
});
});
};
setupEnhancedDonateButton();
Last modified 11mo ago