DEV Community

Cover image for Creating Peacekeeper - Part 1: Themable Background Images
Roland Taylor
Roland Taylor

Posted on • Updated on

Creating Peacekeeper - Part 1: Themable Background Images

Peace, friends!

In the first instalment of "Creating Peacekeeper", I would've mentioned the fact that this template comes with changeable themes, implemented via the use of "CSS Variables" Custom Properties. Now, I will explain how this was achieved.

Alternate variants of the Peacekeeper theme

For most of my templates and projects, "themes" change more than just the background image, and I will likely include some minor changes in the release version of Peacekeeper.

However, in its current state, Peacekeeper's themes only change the background image, but they do this without actually replacing the source image.

So how is this achieved?

Step 1: Create a background "layer".

First, I didn't apply a background image to the <body>.

Using another element allows for more flexibility. Should I choose to extend Peacekeeper with more "fancy" background transitions, using this method will make that possible.

Instead, I chose to use a <div> (which has no semantic value*)1, as my background element.

I've added the class backdrop (which will be explained shortly) and the id bg, which is used for referencing this element from JavaScript.2

<body class="">
    <div class="backdrop" id="bg">
Enter fullscreen mode Exit fullscreen mode

*Note 1: It is important to use an element with no semantic value for this purpose. You don't want this element showing up to those who rely on screen readers and other accessibility features, seeing as it's purely aesthetic.

*Note 2: Technically, I could use querySelector() for this, and just select for the class background.

Styling the background "layer"

The class backdrop is defined as follows:

.backdrop {
  background-attachment: fixed;
  background-image: url('../img/peacekeeper.png');
  background-position: center;
  background-size: cover;
  bottom: 0;
  filter: var(--bg-filter);
  left: 0;
  position: fixed;
  right: 0;
  top: 0;
  transition-timing-function: linear;
  transition-duration: 1s;
  z-index: -1;
Enter fullscreen mode Exit fullscreen mode

Now, there are some important things to note here:

  • position: is fixed;. This ensures that the background will not move even when the page is resized.
  • bottom:, left:, right:, and top:, are all set to 0;. This attaches the element to the edges of your screen at all times.
  • z-index: is set to -1;. Along with the <div> being at the top of the document, this property-value pair ensures the <div> is below everything else.
  • The background is filtered, using a CSS Variable (Custom Property).

The variable --bg-filter is defined as follows:

:root {
  --bg-filter: hue-rotate(0);
Enter fullscreen mode Exit fullscreen mode

We define an empty filter here because there can be issues with glitchy transitions and animations in some browsers if there is no default value to transition from. I've certainly experienced this in Firefox.

Step 2: Define variants

Now that we have our background, we need to define some variants as classes. These will serve as our themes, and can be assigned via the document, or applied on the fly, <div> via JavaScript.

Example Variants:

.variant0 {
  --bg-filter: hue-rotate(-50deg);

.variant1 {
  --bg-filter: hue-rotate(50deg);

.variant7 {
  --bg-filter: hue-rotate(0deg) blur(135px);
Enter fullscreen mode Exit fullscreen mode

Peacekeeper comes with 8 of these variants by default, starting at .variant0. As you can see, each variant changes the value of the variable --bg-filter, and nothing else is required.

We could, in theory, add even more complex behaviours, such as changing the position or opacity of the <div>, but I'll leave that to your imagination for now.

You may also note that I've added a blur to the last variant. This is perfectly fine, and doesn't introduce transition/animation glitches, because the property filter already had a default value. It's a little confusing, but suffice to say, so long as the default value is not none, the browser treats the property as it should when transitions are employed.

A note about scope:

Like all other variables (custom properties) used throughout Peacekeeper, --bg-filter was defined under the document root, using :root as the selector. This means, in theory, that we can change just about any property that references these variables, using any of the variants we defined before.

But just how far will these changes reach? Well, that depends on your intended scope. If you only want to change the value say, a button's background-color, you could assign the relevant variant class to this button, or to its parent. If you wanted to assign these changes to the entire document, you could add the relevant class to the body of the document.

Step 3: Applying a variant

Now that your variants (themes) have been defined, it's time to try them out.

There are three ways to do this:

  • Apply a variant class to the <div> bg in your HTML code.
  • Apply a variant class to the <body> in your HTML code.
  • Apply a variant class on the fly via JavaScript.

The first two are pretty simple:

<body class="variant0">
    <div class="backdrop" id="bg">
Enter fullscreen mode Exit fullscreen mode


<div class="backdrop variant0" id="bg">
Enter fullscreen mode Exit fullscreen mode

You'd do this if you're simply seeking to have Peacekeeper use this variant from the get-go. But what if you want to switch your theme after loading the page, for instance, when switching to the portfolio?

This is when you use the third method.

Using JavaScript to apply a class

Note: Before I explain how this works, let me just remind you that the really heavy lifting is done by the CSS code we wrote before. JavaScript is only required here to trigger the class change upon an event (such as a button being clicked).

Technically, we could achieve a similar effect using pure CSS, and say, a checkbox. But, that's outside the scope of this post. I only mentioned it so that you know, it's possible. Depending on the feedback I get, I may write a separate post demonstrating how to achieve this.


For this, I've used a function that looks as follows:

function applyVariant(num) {
  document.getElementById('bg').classList = ('backdrop ' + 'variant' + num);
Enter fullscreen mode Exit fullscreen mode

Surprisingly short, isn't it?

So what does it do?

Enter fullscreen mode Exit fullscreen mode
  • selects the <div>, bg.
.classList = ('backdrop ' + 'variant' + num);
Enter fullscreen mode Exit fullscreen mode
  • sets the class list of bg to backdrop variantnum, where, num is any value that you pass to the function. Of course, in this case, valid values include 1 to 7, but you can add your own variants of any name, and pass them to this function in the same manner, seeing as JavaScript uses duck typing for variables.

If you want to name your variants differently, you could modify the function by removing + 'variant'.

You can then call this function via any event, using onclick="applyVariant('value')" on any element that supports clicks, from within another function, or via an eventListener.

And that's it!

Now you know how to switch your theme on the fly, including modifying a background image in the same manner!

In future posts, I will demonstrate how you can use this method for more interesting animated backgrounds, switching between dark and light mode, etc.

If you've found this post useful, don't forget to share it with a friend! You can follow me here, or on Twitter, @rolandixor - for more posts just like this!

Top comments (0)