small icon of Elian

Elian.codes

🎉 Write less code by using CSS Houdini

Write less code by using CSS Houdini

Some time ago I started looking into CSS Houdini. It was awesome already, you could do a lot of cool things with it, but I didn’t really see a use of the available things at the time. Today was the day that I looked back at CSS Houdini and refactored some of my code to use CSS Houdini.

Writing the package

The thing I wanted to create in Houdini is my background gradient. This isn’t hard to do in CSS or anything, but 1. it was a good practice to learn CSS Houdini 2. required less code 3. now supports CSS custom properties out of the box.

Writing a package for Houdini isn’t that hard (at least, this was a simple one, I imagine some are way harder).

You start by defining what your package should do:

class ElianCodesBg {
  static get inputProperties() {
    return ["--color-for-bg"];
  }
  static get inputArguments() {
    return ["<color>"];
  }

  paint(ctx, size, props) {
    ctx.fillStyle = props.get("--color-for-bg");
    ctx.fillRect(size.width / 2, 0, size.width, size.height);
  }
}
registerPaint("eliancodes-bg", ElianCodesBg);

here we defined that the Worklet should use one input property called --color-for-bg, which is a color type. Next we define that it should draw a rectangle with half of the width of the element and the same height.

At last, the Paint get registered as eliancodes-bg so you can use that in your CSS like:

background-image: paint(eliancodes-bg);

That’s basically it!

The package is available on NPM, although it should be fine to load it over CDN.

Using the package

add this code to your html as a <script> or add it in an already linked JS-file.

CSS.paintWorklet.addModule("https://unpkg.com/eliancodes-bg@0.0.1/index.js");

using that javascript, we could just set a custom color in the CSS file

.bg {
  --color-for-bg: lightgreen;
  background-image: paint(eliancodes-bg);
}

depending on the element you set the class="bg" on, half of the background will be filled with the --color-for-bg color.

Check the codepen here: https://codepen.io/elianvancutsem/pen/wvqaXQV

Refactoring the old code

Now that the package was published I only needed to rewrite some of my older Astro & TypeScript code to use regular CSS and Houdini instead of a lot of TypeScript code.

The index background

If you look at my website, one of the first things people notice, is that the background on the homepage has a 50% white, 50% random color gradient. Prevously, the background was generated by a lot of shitty TypeScript code that looked at the class in the <html> element and would add style="background: linear-gradient(90deg, #FFF 50%, ${color.code} 50%)" to the element in the case that the dark (TailwindCSS darkmode) class wasn’t there. This was the code before:

const setBgColor = (color) => {
  if (
    document.querySelector("html").getAttribute("class") != undefined &&
    !document.querySelector("html").getAttribute("class").includes("dark") &&
    document.querySelector("html").getAttribute("class").includes("index-bg")
  ) {
    document
      .querySelector("html")
      .setAttribute(
        "style",
        `background: linear-gradient(90deg, #FFF 50%, ${color.code} 50%)`,
      );
  } else if (
    document.querySelector("html").getAttribute("class").includes("dark")
  ) {
    document
      .querySelector("html")
      .setAttribute(
        "style",
        `background: linear-gradient(90deg, #000 50%, #000 50%)`,
      );
  } else {
    document.querySelector("html").setAttribute("style", "");
  }
};

after upgrading to houdini, it got replaced by:

document.documentElement.style.setProperty(
  "--color-for-bg",
  document.documentElement.classList.contains("dark") ? "black" : color.code,
);

The --color-for-bg custom property gets used by the Houdini package and will make the background work without any other code!

Adding a polyfill

One of the things you notice when playing around with CSS Houdini, is that it’s not supported (yet) in all browsers. The Paint API already shipped in chrome, but is still under review in Firefox for example. Luckilly, the guys at Houdini created a polyfill that will make it work with all browsers and it’s very easy to add.

Just add this line in your body to use the polyfill on not-supported browsers:

<script src="https://unpkg.com/css-paint-polyfill"></script>

The use-color class

The use-color class is the class I custom set to change the textcolor of different elements to the active accent-color. Also this got extremely refactored! Before I would manually find all elements with the use-color class and add a tailwind text-color-${color.class} class, so that Tailwind would render it correctly.

To do this, I wrote some code:

const color = getNewColor();
document.querySelectorAll(".use-color").forEach((element) => {
  colors.forEach((color) => element.classList.remove(`text-${color.class}`));
  element.classList.add(`text-${color.class}`);
});

The getNewColor() in above example would return a random color from an array with the tailwind class and color code as values.

const color = { class: "primary-green", code: "#86EFAC" };

This got refactored to a very simple method where a CSS custom property would get changed from TypeScript:

:root {
  --random-color: lightgreen;
}

.use-color {
  color: var(--random-color);
}
const color = getNewColor();
document.documentElement.style.setProperty("--random-color", color.code);

This way a lot of code is replaced by way better lines!