Elian Codes

Back to all posts

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.


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}`));

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!

Written by Elian Van Cutsem on 13/10/2021

Back to all posts