惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
博客园 - Franky
博客园 - 三生石上(FineUI控件)
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Apple Machine Learning Research
Apple Machine Learning Research
云风的 BLOG
云风的 BLOG
人人都是产品经理
人人都是产品经理
博客园 - 【当耐特】
L
LangChain Blog
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
爱范儿
爱范儿
罗磊的独立博客
博客园_首页
美团技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
月光博客
月光博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
量子位
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
V
Visual Studio Blog
T
Tailwind CSS Blog

Aaron Gustafson: Latest Posts

Can Your AI Pass the Accessibility Test? :: Aaron Gustafson Fixing Accessibility After the Fact Is Too Late :: Aaron Gustafson Easy Data-entry Verification with a Web Component :: Aaron Gustafson Visual Validation Feedback for Form Fields :: Aaron Gustafson Never Lose Form Progress Again :: Aaron Gustafson Accessibility Assistant for Figma v52 :: Aaron Gustafson Repeatable Form Fields Made Simple :: Aaron Gustafson A Production-Ready Web Component Starter Template :: Aaron Gustafson Fullscreen Video and Iframes Made Easy :: Aaron Gustafson Dynamic Datalist: Autocomplete from an API :: Aaron Gustafson Lazy Loading Images Based on Screen Size :: Aaron Gustafson A Web Component for Obfuscating Form Fields :: Aaron Gustafson Optimizing Your Codebase for AI Coding Agents :: Aaron Gustafson A Web Component for Conditionally Displaying Fields :: Aaron Gustafson Identifying Accessibility Data Gaps in CodeGen Models :: Aaron Gustafson Learning Web Design, 6th Edition is out! :: Aaron Gustafson Exploring AI’s Role in Accessibility :: Aaron Gustafson Complaining About Designers Fiddling with Figma Solves Nothing :: Aaron Gustafson On Diversity :: Aaron Gustafson A Web Component for Conditional Dependent Fields :: Aaron Gustafson On CrowdStrike, dependencies, and building robust products on the web :: Aaron Gustafson Requirement Rules for Checkboxes :: Aaron Gustafson Don’t Outsource Your Perspective to a LLM :: Aaron Gustafson One World, One Web, One Love :: Aaron Gustafson
Passing Your CSS Theme to `canvas` :: Aaron Gustafson
Aaron Gustafson · 2025-05-02 · via Aaron Gustafson: Latest Posts

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 preferences, so I thought I’d share (in brief) how I solved it.

The CSS Setup

The theming 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:

function importTheme() {
  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:

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:


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