Passing Your CSS Theme to `canvas`

While working on a recent project I noticed an issue with a canvas-based audio visualization when I toggled between light and dark modes. When I���d originally set it up I was browsing in dark mode and the light visualization stroke showed up perfectly on the dark background, but it was invisible when viewed using the light theme (which I���d neglected to test). I searched around, but didn���t find any articles on easy ways to make canvas respond nicely to user preserences, so I thought I���d share (in brief) how I solved it.

# The CSS Setup

The themeing of this particular project uses CSS custom properties. For simplicty I���m going to set up two named colors and then use two theme-specific custom properties to apply them in the default light theme and the dark theme:

:root{���color-dark: #222;���color-light:rgba(255, 255, 255, 0.5);���color-background:var(���color-light);���color-foreground:var(���color-dark);}@media(prefers-color-scheme: dark){:root{���color-background:var(���color-dark);���color-foreground:var(���color-light);}}# Applying the Theme to Canvas

To get the theme into my canvas-related code, I set up a theme object to hold the values:

const theme ={};

Next, I wrote a function to pull in the theme colors using window.getComputedStyle(). After defining the function, I call it immediately to populate the theme object:

functionimportTheme(){theme.foreground =window.getComputedStyle(document.documentElement).getPropertyValue(������color-foreground���).trim()||���black���;theme.background =window.getComputedStyle(document.documentElement).getPropertyValue(������color-background���).trim()||���white���;}importTheme();

I set this up with just two theme colors, but you can import as many (or few) as you like. Be sure to set a sensible default or fallback for each color though, just in case your theme���s custom property names change.

With this in place, I can set my canvas animation���s colors by referencing them from the theme object. For example:

context.fillStyle = theme.foreground;# Keeping Things in Sync

The final bit of magic comes when you add an event listener to a MediaQueryList:

const mediaQuery = window.matchMedia(���(prefers-color-scheme: dark)���);mediaQuery.addEventListener(���change���, importTheme);

Here I���ve used matchMedia() to get a MediaQueryList object. Typically we use the matches property of this object to establish whether the media query currently matches or not. A lesser-known option, however, is that you can attach an event listener to it that will be triggered whenever the query���s status changes. So cool! With this in place, the canvas contents will update whenever the user���s theme changes. Here���s an example of that:

https://www.youtube.com/watch?v=pALIuO5uHUA

This video demonstrates how a canvas element rendering a dark sine wave against a light background can miraculously transform into a light sine wave against a dark background using CSS custom properties and a bit of JavaScript.

# Demo

I put together a quick demo of this in a fork of Alvin Shaw���s Canvas Sine Wave Experiment:

See the Pen

Hopefully this is helpful to someone out there. Happy themeing!

 •  0 comments  •  flag
Share on Twitter
Published on May 01, 2025 14:49
No comments have been added yet.