DEV Community

Kevin Rodríguez
Kevin Rodríguez

Posted on

Colors, themes and the User's ability to choose.

Over the last two years, "dark mode" 🌒 has become a really popular example of the freedom of choice and tweak how your user-interface looks, also, all major OS provide some sort of theming option; Let's take macOS appearance settings as an example for a second:

As you can see, the user is free to choose from a list of accent colors and one of two base colors.

This usually means that users are happier when you provide them options on how they want their UI to look, and that's why we've been able to theme our UIs since the days of Windows 95 or even earlier.

How can we bring a feature like this to javascript apps?

Well, there are several existing solutions for that like CSS variables, or Themes for CSS-In-JS (Most of the current CSS-In-JS libraries support this); while this can be easy to do, you might notice there's a little issue:

You need to create, maintain and handle every color combination and generate the right color scheme for it.

Also, in the previous attachment from the macOS appearance settings, you can notice there's a very, VERY limited color selection.


Some background

When I was working on an iOS project back in 2015 I stumbled upon an amazing color framework: Chameleon Framework. It provided everything I'd wanted as a user for an application: Color scheme generation and a nicely curated list of pre-selected and recommended colors. Then I moved to the web and even if there are some great tools for generating color schemes ahead of time, I wasn't able to find something as powerful as Chameleon.

This is why I decided to create Pigment 🌈, a pure Typescript alternative that can be used in Node, the Web and React-Native (And pretty much anything that can run Javascript).

It provides lots of useful features to automate the color-generation process, but you can read more about those features on Pigment's Main Repository, for now, I'd like to focus on user-freedom.

What are some use cases?

  • Web/mobile apps that want to provide the user some freedom on how the UI looks.
  • All kinds of color theming.
  • Multi-tenant and white-label apps can be easily themed according to the business.

This can be demonstrated with a tiny example:

Navy Blue Theme

Light Orange Theme

Live demo


Let's give the users more freedom!

More freedom on how their user interfaces look 🌈! This is really important because it makes the user feel more engaged with your product, it makes users feel the application that is being used is more "theirs"

A small note on dark-mode

Nowadays there are several ways to detect if dark mode is set on the OS, you can use CSS media queries for this.

How can we detect this on JS? What happens if we want to modify a theme based on the preferences using JS?

You can add an event listener on the (prefers-color-scheme: dark) media query. In React, you can even make this a Hook!

import { useEffect, useState } from 'react'

const useDarkModeDetection = () => {
  const darkModeMedia = window.matchMedia('(prefers-color-scheme: dark)')
  const [isDarkMode, setIsDarkMode] = useState(darkModeMedia.matches)
  useEffect(() => {
    const handleColorModeChange = (event: MediaQueryListEvent) => {
      setIsDarkMode(event.matches)
    }
    if (darkModeMedia.addEventListener) {
      darkModeMedia.addEventListener('change', handleColorModeChange)
    } else {
      if (darkModeMedia.addListener as any) {
        darkModeMedia.addListener(handleColorModeChange)
      }
    }
    return () => {
      if (darkModeMedia.removeEventListener) {
        darkModeMedia.removeEventListener('change', handleColorModeChange)
      } else {
        if (darkModeMedia.removeListener as any) {
          darkModeMedia.removeListener(handleColorModeChange)
        }
      }
    }
  }, [darkModeMedia])

  return { isDarkMode }
}

export default useDarkModeDetection

A little react and styled-components

Let's start by defining a small function to automate color generation!
You'll usually need a background color, a muted color, warning, danger, primary and secondary colors. You can tell pigment to generate color palettes for you in this scenario.

import { Color, Colors } from '@kevinrodriguez-io/pigment-core' 

type ColorSchemeKey =
  | 'analogousColorScheme'
  | 'analogousFlatColorScheme'
  | 'complementaryColorScheme'
  | 'complementaryFlatColorScheme'
  | 'triadicColorScheme'
  | 'triadicFlatColorScheme'

export const generateColorTheme = (
  color: Color,
  colorScheme: ColorSchemeKey = 'analogousColorScheme',
  warningColor: {
    light: string
    dark: string
  } = Colors.flatPurple,
  dangerColor: {
    light: string
    dark: string
  } = Colors.flatRed,
): ColorTheme => {
  // Every color scheme provides 5 colors, the third one (center) being the base color
  const background = color.contrastingFlatTextColor.hexString
  const primary = color.hexString
  // The second one usually works great.
  const secondary = color[colorScheme][1].hexString
  const text = color.contrastingFlatTextColor.hexString
  // The fourth one usually works great.
  const accent = color[colorScheme][3].hexString
  const muted = color.contrastingFlatTextColor.shade(25).hexString
  const invertedText =
    color.contrastingFlatTextColor.contrastingFlatTextColor.hexString
  return {
    primary,
    secondary,
    text,
    invertedText,
    accent,
    background,
    muted,
    warning: color.hsl.l < 50 ? warningColor.dark : warningColor.light,
    danger: color.hsl.l < 50 ? dangerColor.dark : dangerColor.light,
  }
}

After that, you can just use the function to modify your current theme every time dark-mode changes!

import React from 'react'
import { Color, Colors } from '@kevinrodriguez-io/pigment-core'
import { ThemeProvider } from 'styled-components'
import theme from './theme'
import useDarkModeDetection from './hooks/useDarkModeDetection'
import generateColorTheme from './utils/generateColorTheme'

// You can store the user selection in a database or localStorage too!
const DARK_MODE_COLOR = new Color(Colors.flatSkyBlue.light)
const LIGHT_MODE_COLOR = new Color(Colors.flatNavyBlue.dark)

const App: React.FC = () => {
  const { isDarkMode } = useDarkModeDetection()
  let appTheme: Theme
  appTheme = isDarkMode
    ? {
        ...theme,
        colors: generateColorTheme(DARK_MODE_COLOR, 'complementaryColorScheme'),
      }
    : {
        ...theme,
        colors: generateColorTheme(
          LIGHT_MODE_COLOR,
          'complementaryColorScheme',
        ),
      }
  return (
    <ThemeProvider theme={appTheme}>
      <SomeComponent />
    </ThemeProvider>
  )
}

There are great options out there to generate themes! But this is my little grain of sand to ease this process. So let's give our users options! Options on how their UI's can look.

Providing freedom of choice can make your product a good fit for a market full of options.

Alright. If you have any questions feel free to ask them! 🌈

Latest comments (0)