Friday, May 27, 2022
  • Home
  • Event
  • Technology
  • Program
  • Education
No Result
View All Result
E-Capitals
No Result
View All Result
Home Program

Honor prefers-color-scheme in the CSS Paint API with Custom Properties

admin by admin
December 27, 2021
in Program
0
0
SHARES
0
VIEWS
Share on FacebookShare on Twitter

One of the coolest things I’ve been messing with in the last couple years is the CSS Paint API. I love it. I did a talk on it, and made a little gallery of my own paint worklets. The other cool thing is the prefers-color-scheme media query and how you can use it to adapt to a user’s preference for light or dark modes.

Recently, I found out that I can combine both of these really cool things with CSS custom properties in such a way that a paint worklet’s appearance can be tailored to fit the user’s preferred color scheme!

Setting the stage

I’ve been overdue for a website overhaul, and I decided to go with a Final Fantasy II theme. My first order of business was to make a paint worklet that was a randomly generated Final Fantasy-style landscape I named overworld.js:

A randomly generated 8-bit style landscape, made possible by the CSS Paint API!

It could use a bit more dressing up—and that’s certainly on the agenda—but this here is a damn good start!

After I finished the paint worklet, I went on to work on other parts of the website, such as a theme switcher for light and dark modes. It was then that I realized that the paint worklet wasn’t adapting to these preferences. This might normally be a huge pain, but with CSS custom properties, I realized I could adapt the paint worklet’s rendering logic to a user’s preferred color scheme with relative ease!

Setting up the custom properties for the paint worklet

The state of CSS these days is pretty dope, and CSS custom properties are one such example of aforementioned dopeness. To make sure both the Paint API and custom properties features are supported, you do a little feature check like this:

const paintAPISupported = "registerProperty" in window.CSS && "paintWorklet" in window.CSS`

The first step is to define your custom properties, which involves the CSS.registerProperty method. That looks something like this:

CSS.registerProperty({
  name,             // The name of the property
  syntax,           // The syntax (e.g., <number>, <color>, etc.)
  inherits,         // Whether the value can be inherited by other properties
  initialValue      // The default value
});

Custom properties are the best part of using the Paint API, as these values are specified in CSS, but readable in the paint worklet context. This gives developers a super convenient way to control how a paint worklet is rendered—entirely in CSS.

For the overworld.js paint worklet, the custom properties are used to define the colors for various parts of the randomly generated landscape—the grass and trees, the river, the river banks, and so on. Those color defaults are for the light mode color scheme.

The way I register these properties is to set up everything in an object that I call with Object.entries and then loop over the entries. In the case of my overworld.js paint worklet, that looked like this:

// Specify the paint worklet's custom properties
const properties = {
  "--overworld-grass-green-color": {
    syntax: "<color>",
    initialValue: "#58ab1d"
  },
  "--overworld-dark-rock-color": {
    syntax: "<color>",
    initialValue: "#a15d14"
  },
  "--overworld-light-rock-color": {
    syntax: "<color>",
    initialValue: "#eba640"
  },
  "--overworld-river-blue-color": {
    syntax: "<color>",
    initialValue: "#75b9fd"
  },
  "--overworld-light-river-blue-color": {
    syntax: "<color>",
    initialValue: "#c8e3fe"
  }
};

// Register the properties
Object.entries(properties).forEach(([name, { syntax, initialValue }]) => {
  CSS.registerProperty({
    name,
    syntax,
    inherits: false,
    initialValue
  });
});

// Register the paint worklet
CSS.paintWorklet.addModule("/worklets/overworld.js");

Because every property sets an initial value, you don’t have to specify any custom properties when you call the paint worklet later. However, because the default values for these properties can be overridden, they can be adjusted when users express a preference for a color scheme.

Adapting to a user’s preferred color scheme

The website refresh I’m working on has a settings menu that’s accessible from the site’s main navigation. From there, users can adjust a number of preferences, including their preferred color scheme:

Related Posts

Just How Long Should Alt Text Be? | CSS-Tricks

Beautify GitHub Profile | CSS-Tricks

Cool CSS Hover Effects That Use Background Clipping, Masks, and 3D | CSS-Tricks

Customizing Color Fonts on the Web | CSS-Tricks

The color scheme setting cycles through three options:

“System” defaults to whatever the user has specified in their operating system’s settings. The last two options override the user’s operating system-level setting by setting a light or dark class on the <html> element, but in the absence of an explicit, the “System” setting relies on whatever is specified in the prefers-color-scheme media queries.

The hinge for this override depends on CSS variables:

/* Kicks in if the user's site-level setting is dark mode */
html.dark { 
  /* (I'm so good at naming colors) */
  --pink: #cb86fc;
  --firion-red: #bb4135;
  --firion-blue: #5357fb;
  --grass-green: #3a6b1a;
  --light-rock: #ce9141;
  --dark-rock: #784517;
  --river-blue: #69a3dc;
  --light-river-blue: #b1c7dd;
  --menu-blue: #1c1f82;
  --black: #000;
  --white: #dedede;
  --true-black: #000;
  --grey: #959595;
}

/* Kicks in if the user's system setting is dark mode */
@media screen and (prefers-color-scheme: dark) {
  html {
    --pink: #cb86fc;
    --firion-red: #bb4135;
    --firion-blue: #5357fb;
    --grass-green: #3a6b1a;
    --light-rock: #ce9141;
    --dark-rock: #784517;
    --river-blue: #69a3dc;
    --light-river-blue: #b1c7dd;
    --menu-blue: #1c1f82;
    --black: #000;
    --white: #dedede;
    --true-black: #000;
    --grey: #959595;
  }
}

/* Kicks in if the user's site-level setting is light mode */
html.light {
  --pink: #fd7ed0;
  --firion-red: #bb4135;
  --firion-blue: #5357fb;
  --grass-green: #58ab1d;
  --dark-rock: #a15d14;
  --light-rock: #eba640;
  --river-blue: #75b9fd;
  --light-river-blue: #c8e3fe;
  --menu-blue: #252aad;
  --black: #0d1b2a;
  --white: #fff;
  --true-black: #000;
  --grey: #959595;
}

/* Kicks in if the user's system setting is light mode */
@media screen and (prefers-color-scheme: light) {
  html {
    --pink: #fd7ed0;
    --firion-red: #bb4135;
    --firion-blue: #5357fb;
    --grass-green: #58ab1d;
    --dark-rock: #a15d14;
    --light-rock: #eba640;
    --river-blue: #75b9fd;
    --light-river-blue: #c8e3fe;
    --menu-blue: #252aad;
    --black: #0d1b2a;
    --white: #fff;
    --true-black: #000;
    --grey: #959595;
  }
}

It’s repetitive—and I’m sure someone out there knows a better way—but it gets the job done. Regardless of the user’s explicit site-level preference, or their underlying system preference, the page ends up being reliably rendered in the appropriate color scheme.

Setting custom properties on the paint worklet

If the Paint API is supported, a tiny inline script in the document <head> applies a paint-api class to the <html> element.

/* The main content backdrop rendered at a max-width of 64rem.
   We don't want to waste CPU time if users can't see the
   background behind the content area, so we only allow it to
   render when the screen is 64rem (1024px) or wider. */
@media screen and (min-width: 64rem) {
  .paint-api .backdrop {
    background-image: paint(overworld);
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    z-index: -1;

    /* These oh-so-well-chosen property names refer to the
       theme-driven CSS variables that vary according to
       the user's preferred color scheme! */
    --overworld-grass-green-color: var(--grass-green);
    --overworld-dark-rock-color: var(--dark-rock);
    --overworld-light-rock-color: var(--light-rock);
    --overworld-river-blue-color: var(--river-blue);
    --overworld-light-river-blue-color: var(--light-river-blue);
  }
}

There’s some weirdness here for sure. For some reason, that may or may not be the case later on—but is at least the case as I write this—you can’t render a paint worklet’s output directly on the <body> element.

Plus, because some pages can be quite tall, I don’t want the entire page’s background to be filled with randomly generated (and thus potentially expensive) artwork. To get around this, I render the paint worklet in an element that uses fixed positioning that follows the user as they scroll down, and occupies the entire viewport.

All quirks aside, the magic here is that the custom properties for the paint worklet are based on the user’s system—or site-level—color scheme preference because the CSS variables align with that preference. In the case of the overworld paint worklet, that means I can adjust its output to align with the user’s preferred color scheme!

Not bad! But this isn’t even that inventive of a way to control how paint worklets render. If I wanted, I could add some extra details that would only appear in a specific color scheme, or do other things to radically change the rendering or add little easter eggs. While I learned a lot this year, I think this intersection of APIs was one of my favorites.

Next Post

The Top 10 Stories of 2021, Plus Our 2022 Futurecast – Event Marketer

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Popular Posts

Technology

Robotic exoskeleton startup wins Oregon life and biosciences angel competition – GeekWire

by admin
May 27, 2022
0

Biomotum’s SPARK device for rehabilitation of individuals with cerebral palsy, stroke, and other neurological conditions. (Biomotum Photo) Wearable robotics startup...

Read more

Robotic exoskeleton startup wins Oregon life and biosciences angel competition – GeekWire

Seattle startup JetClosing, which launched in 2016 to digitize home closing process, is shutting down – GeekWire

Seattle University aims to boost entrepreneurship – GeekWire

Fishery monitoring software wins $25K at Univ. of Washington student startup competition – GeekWire

Elizabeth Scallon departs Amazon for HP; Fauna hires marketing exec; and more – GeekWire

Just How Long Should Alt Text Be? | CSS-Tricks

Load More

Popular Posts

Best Winter Olympics Activities for Kids in the Classroom

by admin
January 19, 2022
0

What is Packet Sniffing Attack? – Types and How to Prevent It?

by admin
December 11, 2021
0

Zain Nadella, 1996-2022: Microsoft CEO’s son remembered for love of music, bright smile, profound impact on his dad

by admin
March 1, 2022
0

Copyright © 2021 - e-Capitals.com DESIGNED AND DEVELOPED BY TEAM WORDPRESS BLOGX

No Result
View All Result
  • Home
    • Home 1
    • Home 2
    • Home 3
  • Event
  • Technology
  • Program
  • Education

Copyright © 2021 - e-capitals.com - DESIGNED AND DEVELOPED BY TEAM WORDPRESS BLOGX