PikoPong
  • Web Dev
  • Hack
  • Database
  • Big Data
  • AWS
  • Linux
No Result
View All Result
PikoPong
  • Web Dev
  • Hack
  • Database
  • Big Data
  • AWS
  • Linux
No Result
View All Result
PikoPong
No Result
View All Result
Home Web Dev

A DRY Approach to Color Themes in CSS

February 24, 2021
in Web Dev
289 3
A DRY Approach to Color Themes in CSS


The other day, Florens Verschelde asked about defining dark mode styles for both a class and a media query, without repeat CSS custom properties declarations. I had run into this issue in the past but hadn’t come up with a proper solution.

What we want is to avoid redefining—and thus repeating—custom properties when switching between light and dark modes. That’s the goal of DRY (Don’t Repeat Yourself) programming, but the typical pattern for switching themes is usually something like this:

:root {
  --background: #fff;
  --text-color: #0f1031;
  /* etc. */
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #0f1031;
    --text-color: #fff;
    /* etc. */
  }
}

See what I mean? Sure, it might not seem like a big deal in an abbreviated example like this, but imagine juggling dozens of custom properties at a time—that’s a lot of duplication!

Then I remembered Lea Verou’s trick using --var: ;, and while it didn’t hit me at first, I found a way to make it work: not with var(--light-value, var(--dark-value)) or some nested combination like that, but by using both side by side!

Certainly, someone smarter must have discovered this before me, but I haven‘t heard of leveraging (or rather abusing) CSS custom properties to achieve this. Without further ado, here’s the idea:

--color: var(--light, orchid) var(--dark, rebeccapurple);

If the --light value is set to initial, the fallback will be used (orchid), which means --dark should be set to a whitespace character (which is a valid value), making the final computed value look like this:

--color: orchid  ; /* Note the additional whitespace */

Conversely, if --light is set to a whitespace and --dark to initial, we end up with a computed value of:

--color:   rebeccapurple; /* Again, note the whitespace */

Now, this is great but we do need to define the --light and --dark custom properties, based on the context. The user can have a system preference in place (either light or dark), or can have toggled the website‘s theme with some UI element. Just like Florens‘s example, we’ll define these three cases, with some minor readability enhancement that Lea proposed using “on” and “off” constants to make it easier to understand at a glance:

:root { 
  /* Thanks Lea Verou! */
  --ON: initial;
  --OFF: ;
}

/* Light theme is on by default */
.theme-default,
.theme-light {
  --light: var(--ON);
  --dark: var(--OFF);
}

/* Dark theme is off by default */
.theme-dark {
  --light: var(--OFF);
  --dark: var(--ON);
}

/* If user prefers dark, then that's what they'll get */
@media (prefers-color-scheme: dark) {
  .theme-default {
    --light: var(--OFF);
    --dark: var(--ON);
  }
}

We can then set up all of our theme variables in a single declaration, without repetition. In this example, the theme-* classes are set to the html element, so we can use :root as a selector, as many people like to do, but you could set them on the body, if the cascading nature of the custom properties makes more sense that way:

:root {
  --text: var(--light, black) var(--dark, white);
  --bg: var(--light, orchid) var(--dark, rebeccapurple);
}

And to use them, we use var() with built-in fallbacks, because we like being careful:

body {
  color: var(--text, navy);
  background-color: var(--bg, lightgray);
}

Hopefully you’re already starting to see the benefit here. Instead of defining and switching armloads of custom properties, we’re dealing with two and setting all the others just once on :root. That’s a huge improvement from where we started.

Even DRYer with pre-processors

If you were to show me this following line of code out of context, I’d certainly be confused because a color is a single value, not two!

--text: var(--light, black) var(--dark, white);

That’s why I prefer to abstract things a bit. We can set up a function with our favorite pre-processor, which is Sass in my case. If we keep our code above defining our --light and --dark values in various contexts, we need to make a change only on the actual custom property declaration. Let’s create a light-dark function that returns the CSS syntax for us:

@function light-dark($light, $dark) {
  @return var(--light, #{ $light }) var(--dark, #{ $dark });
}

And we’d use it like this:

:root {
   --text: #{ light-dark(black, white) };
   --bg: #{ light-dark(orchid, rebeccapurple) };
   --accent: #{ light-dark(#6d386b, #b399cc) };
}

You’ll notice there are interpolation delimiters #{ … } around the function call. Without these, Sass would output the code as is (like a vanilla CSS function). You can play around with various implementations of this but the syntax complexity is up to your tastes.

How’s that for a much DRYer codebase?

More than one theme? No problem!

You could potentially do this with more than two modes. The more themes you add, the more complex it becomes to manage, but the point is that it is possible! We add another theme set of ON or OFF variables, and set an extra variable in the list of values.

.theme-pride {
  --light: var(--OFF);
  --dark: var(--OFF);
  --pride: var(--ON);
}

:root {
  --text:
    var(--light, black)
    var(--dark, white)
    var(--pride, #ff8c00)
  ; /* Line breaks are absolutely valid */

  /* Other variables to declare… */
}

Is this hacky? Yes, it absolutely is. Is this a great use case for potential, not-yet-existing CSS booleans? Well, that’s the dream.

How about you? Is this something you’ve figured out with a different approach? Share it in the comments!





Source link

Share219Tweet137Share55Pin49

Related Posts

Say Hello To CSS Container Queries
Web Dev

Say Hello To CSS Container Queries

I haven’t been more excited for a CSS feature like I’m now in the past six years I spent...

April 20, 2021
Keep Logs Tidy With the Log Cleaner Package for Laravel
Web Dev

Keep Logs Tidy With the Log Cleaner Package for Laravel

Laravel Log Cleaner is a package by Joost van Veen for keeping your log files small and tidy. While...

April 20, 2021
Making GraphQL Work In WordPress — Smashing Magazine
Web Dev

Making GraphQL Work In WordPress — Smashing Magazine

About The AuthorLeonardo Losoviz is a freelance developer and writer, with an ongoing quest to integrate innovative paradigms (Serverless...

April 20, 2021
How to Improve CSS Performance
Web Dev

How to Improve CSS Performance

Combined with the complexity of modern websites and the way browsers process CSS, even a moderate amount of CSS...

April 19, 2021
Next Post
Teaching Web Dev for Free is Good Business

Teaching Web Dev for Free is Good Business

Building A Discord Bot Using Discord.js — Smashing Magazine

Building A Discord Bot Using Discord.js — Smashing Magazine

Leave a Reply Cancel reply

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

Recommended

SmashingConf Live! Is A Wrap — Smashing Magazine

SmashingConf Live! Is A Wrap — Smashing Magazine

August 27, 2020
Give Your GNOME Desktop a Tiling Makeover With Material Shell

Give Your GNOME Desktop a Tiling Makeover With Material Shell

September 21, 2020
How To Make Performance Visible With GitLab CI And Hoodoo Of GitLab Artifacts — Smashing Magazine

A Book Release For Click! And A Chance To Rethink Our Routines — Smashing Magazine

July 10, 2020
SmashingConf Live! Is A Wrap — Smashing Magazine

SmashingConf Live! Is A Wrap — Smashing Magazine

September 2, 2020

Categories

  • AWS
  • Big Data
  • Database
  • DevOps
  • IoT
  • Linux
  • Web Dev
No Result
View All Result
  • Web Dev
  • Hack
  • Database
  • Big Data
  • AWS
  • Linux

Welcome Back!

Login to your account below

Forgotten Password?

Create New Account!

Fill the forms bellow to register

All fields are required. Log In

Retrieve your password

Please enter your username or email address to reset your password.

Log In