Pro Tailwind

00. Workshop starting point

🐇 TL;DR

  • We start with 3 custom color shades: base, rainforest and candy
  • These are “normal” colors, they don’t change between themes
  • This is not a theming strategy

🦘 A jumping headstart

We’ll kick off the workshop with a little head start - three custom color themes:

50

100

200

300

400

500

600

700

800

900

50

100

200

300

400

500

600

700

800

900

50

100

200

300

400

500

600

700

800

900

Creating color palettes from scratch will send you down a deceptively large rabbit hole, so we’ll skip this bit.

There are many useful online tools to help you create color palettes. I used Huetone to create the three workshop color shades.

After the workshop, you should totally experiment with some of the great online tools!


Click on the toggle below to show the code for our three color themes.


You can see that we’ve followed Tailwind’s convention of *-50 to *-900 for naming our shades.

We could use totally different shade names, but since we’re going to implement multiple themes to an already existing Tailwind CSS project, matching the naming convention will make our lives much easier.


Current implementation

Right now, we’re just defining three new colors in our Tailwind config:

// tailwind.config.js
const themes = {...}

module.exports = {
  theme: {
    extend: {
      colors: {
        base: themes.base.colors,
        rainforest: themes.rainforest.colors,
        candy: themes.candy.colors,
      },
    },
  },
}

Hmmmm. Is this… theming?

butterfly meme about theming

Nope.

There is no theming happeneing at all here. We’ve just extended our theme with new colors.

Each color is generating new, different utility classes. Like bg-rainforest-500, or text-candy-800.

Those colors have a “hardcoded” value baked in their name. One specific value.


What we want is to be able to use the same color utility classe across all themes.

We should be able to redefine the value of that color based on the currently active theme.


We need a name that makes sesnse regardless of what color that utility class represents. A semantic color name.

To do this, let’s leverage the power of CSS custom properties, or CSS variables.


CSS variables for the win!

CSS variables are defined with a two-dash prefix --, and consumed with the var() function.

Here’s an example:

:root {
  /* CSS var definition */
  --primary-500: #6b70fc;
}

.button {
  /* CSS var consumption */
  background-color: var(--primary-500);
  ...;
}

A great feature of CSS variables is that they can be redefined within any CSS selector.

When doing so, the new value will only take effect within the scope of that selector:

:root {
  --primary-500: #6b70fc;
}

.button {
  background-color: var(--primary-500);
  ...;
}

.button--pink {
  /* Redefine the value of an existing CSS variable */
  --primary-500: deeppink;
}

With that in place, take the following markup:

<button class="button">Indigo button</button>
<button class="button button--pink">Pink button</button>

Here’s how these two buttons will look like:

The Indigo button is using the color value defined at the :root scope. The Pink button uses the valued scoped to the .button-pink class.

This is incredibly useful for our theming use case.

You can see a similar example in this Tailwind Play.


Nested CSS variable scopes

You can declare CSS variable at the top level - the root scope - in your CSS. These values will be available everywhere throughout the project.

You can then create “theme scope” selectors, where you redefine these same CSS variables with new values.

Any HTML element within a given theme scope will get those updated values when reading on of the CSS variables!


Ready for your first challenge?

Of course you are.

Let’s get started!