Build a CSS Transition Tool for your editors
Tags: css, javascript, webdev, tutorial
Published: 2022-09-12 07:48:59

I love building small tools to help editors and marketeers create more engaging content.

Typically, these tools are super-simple. Often, it's just updating a few _CSS Custom Properties_, a list of `class`-names, `data`-attributes or similar.

I recently build a _CSS Transition Tool_, so editors can fine-tune how an element, or group of elements, enters the screen when scrolling.

A typical example of this is _fade up:_

![Fade up](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ffntr42hszr76ou5dgz.gif)

The tool also allows editors to fine-control the transition of each element in the group:

![Zoom in/out](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jwd4ose5al16w2rlo8a1.gif)

If you want to build a similar tool, read on.

---

## Creating the CSS

First things first. The tool needs a lot of CSS-classes, that'll control the individual `transform`-properties. To run the transitions (on scroll, using intersection observer), we'll just add a `trs`-class to the outermost (parent) element.

We'll be using easings from [Open Props](https://open-props.style/), so let's start, by importing those:

```css
@import 'https://unpkg.com/open-props@1.4.14/src/props.easing.css';
```

Next, we need some variables:

```css
:where([class*="trs-"]) {
  --dg: 100deg;
  --tx: 55px;
  --ty: 110px;
  --zi: 0.6;
  --zo: 1.2;
}
```

`--dg` is for transitions with `degrees`, `--tx` is `x`-offset, `--ty` is `y`-offset, and `zi` and `zo`is used for `zoom`-transitions.

For the transition-classes, we'll use all of these properties, example "fade-down":

```css
.trs-fdd { opacity: 0; transform: translate3d(0px, calc(-1 * var(--ty)), 0); }
```

... or "flip-down":

```css
.trs-fld { backface-visibility: hidden; transform: perspective(2500px) rotateX(var(--dg)); }
```

For delays, durations and easings, we'll add a bunch of micro-classes:

```css
.trsde-25 { --delay: 25ms; }
.trsde-50 { --delay: 50ms; }
/* .etc */
```

---

When one of the transition-classes has the `trs`-class, we'll _run a transition:_

```css
:is([class*="trs-"].trs) {
  opacity: 1;
  transform: translateZ(0) scale(1);
  transition: all var(--duration, 400ms) var(--delay, 0ms) var(--easing, var(--ease-1, ease));
}
```

For any child-element _within_ a `trs`-class, we'll use:

```css
:is(.trs [class*="trs-"]) {
  opacity: 1;
  transform: translateZ(0);
  transition-timing-function: var(--easing, ease-in-out);
  transition-delay: var(--delay, 0ms);
  transition-duration: var(--duration, 400ms);
}
```

> You can see all the necessary styles in the demo at the end of this tutorial. 

---

## HTML
Now for some simple markup for previewing the transitions:

```html
<div id="demo">
  <div id="demo1">
    <img src="image1.jpg" />
  </div>
  <div id="demo2">
    <img src="image2.jpg" />
  </div>
  <div id="demo3">
    <img src="image3.jpg" />
  </div>
</div>
```

For all the properties we want the editors to play around with, we'll add a `<select>`, with the CSS `class`-names as `<option value>`. 

Example:

```html
<select>
  <option value="trsde-0" selected>0ms</option>
  <option value="trsde-25">25ms</option>
  <option value="trsde-50">50ms</option>
  ...
</select>
```

We'll group the `<select>`-elements in `<fieldset>`s, one for each child-element in the preview-markup:

```html
<fieldset name="demo"> ...
```

Notice the `name="demo"`-part. We'll use that in JavaScript, so all the controls within this fieldset will target the element in the preview-markup with `id="demo"`.

Finally, wrap all the editor-markup within a `<form id="app">`.

---

## JavaScript
Since we have a `<form>`, we just need a single `input`-event:

```js
app.addEventListener('input', event => {
  const node = event.target;
  const group = node.closest('fieldset');
  document.getElementById(group.name).className = [...group.elements].map(input => input.value).join(' ').replace(/  +/g, ' ');
  playIt();
})
```

All there's left to add, is the code for `playIt`, a simple method that removes the `trs`-class, and adds it again after a timeout:

```js
const playIt = () => { demo.classList.remove('trs'); setTimeout(() => { demo.classList.add('trs'); }, 400); }
```

To help our editors re-trigger the animation, let's add a `<button id="play">`, that'll also run `playIt()`

```js
play.addEventListener('click', () => playIt());
```

And that's it. Very little code, but a great help for editors and marketeers!

---

## Demo
Here's the demo. I recommend you click on "Edit on Codepen", and then choose "Full Screen View":

{% codepen https://codepen.io/stoumann/pen/zYjxZqB %}

As soon as you change something in the editor, click on the "See code". 

_Example:_

![Code snippet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zsf56nr7v6lk3ln85z3y.png)

Here, you can see, that some of the classes we created in the beginning of this tutorial, has been added to the main wrapper.

Now, change the transitions of one or more of the child-elements, and see the code update.

---

Feel free to implement the editor in a CMS — or have the editors copy the code-snippet, if you want to implement the transitions manually.

Most of the transitions are inspired by [AOS](https://michalsnik.github.io/aos/).