Creating Peacekeeper - Part 1: Themable Background Images
Tags: css, design, webdev, html
Published: 2021-09-17 22:23:54

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](Custom Properties Link Goes Here). Now, I will explain how this was achieved.

![Alternate variants of the Peacekeeper theme](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xpovexg5gha5rkji7gzq.png)

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\*)](https://www.freecodecamp.org/news/semantic-html5-elements/)<sup>1</sup>, 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.<sup>2</sup>

```html
<body class="">
    <div class="backdrop" id="bg">
    </div>
</body>
```

---

**\*Note 1:** *It is important to use an element with <em>no</em> 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:

```css
.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;
}
```
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:

```css
:root {
  --bg-filter: hue-rotate(0);
}
```

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:

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

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

.variant7 {
  --bg-filter: hue-rotate(0deg) blur(135px);
}
```

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:

```html
<body class="variant0">
    <div class="backdrop" id="bg">
    </div>
</body>
```

or

```html
<div class="backdrop variant0" id="bg">
</div>
```
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.

---

**AND NOW FOR THE FUN**

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

```javascript
function applyVariant(num) {
  document.getElementById('bg').classList = ('backdrop ' + 'variant' + num);
}
```

Surprisingly short, isn't it?

---

**So what does it do?**

```javascript
document.getElementById('bg')
```
- selects the `<div>`, `bg`.

```javascript
.classList = ('backdrop ' + 'variant' + num);
```

- 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](https://en.wikipedia.org/wiki/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!*