Pro Tailwind

01. Extracting common classes

🐇 TL;DR

  • We have a bunch of class repetition between the various “hardcoded” buttons
  • Let’s start by extracting the common classes to all buttons!

⬇️ Skip to the challenge


🐢 Background

In this challenge, we’re going to work with a series of buttons. These buttons have multiple size, shape and impact style variants.

Here’s what they look like:


Impact

Size

Shape


They’re currently implemented as “hardcoded” <button> elements, for which a lot of Tailwind classes are repeated over and over.

Here’s what the markup looks like:

<!-- prettier-ignore -->
<button class="rounded-md bg-indigo-500 px-5 py-2 font-semibold text-white shadow-md hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none">Bold</button>
<!-- prettier-ignore -->
<button class="rounded-md bg-indigo-100 px-5 py-2 font-semibold text-indigo-700 hover:bg-indigo-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50">Light</button>
<!-- prettier-ignore -->
<button class="rounded-md bg-transparent px-5 py-2 font-semibold text-indigo-700 hover:bg-indigo-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50">None</button>
<!-- prettier-ignore -->
<button class="rounded bg-indigo-500 px-7 py-2.5 text-lg font-semibold text-white shadow-md hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none">Large</button>
<!-- prettier-ignore -->
<button class="rounded bg-indigo-500 px-5 py-2 font-semibold text-white shadow-md hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none">Medium</button>
<!-- prettier-ignore -->
<button class="rounded bg-indigo-500 px-3 py-1 text-sm font-semibold text-white shadow-md hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none">Small</button>
<!-- prettier-ignore -->
<button class="bg-indigo-500 px-5 py-2 font-semibold text-white shadow-md hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none">Square</button>
<!-- prettier-ignore -->
<button class="rounded-md bg-indigo-500 px-5 py-2 font-semibold text-white shadow-md hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none">Rounded</button>
<!-- prettier-ignore -->
<button class="rounded-full bg-indigo-500 px-5 py-2 font-semibold text-white shadow-md hover:bg-indigo-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 active:translate-y-px disabled:pointer-events-none disabled:opacity-50 disabled:shadow-none">Pill button</button>

That’s obviously not ideal

Instead of repeating the Tailwind classes on each button, we want to dynamically generate the right composition of Tailwind classes for any given variant combination.

Here’s how the Button component will be used, to generate the same buttons as the example above:

// Impact
<Button impact="bold">Bold</Button>
<Button impact="light">Light</Button>
<Button impact="none">None</Button>

// Size
<Button size="large">Large</Button>
<Button size="medium">Medium</Button>
<Button size="small">Small</Button>

// Shape
<Button shape="square">Square</Button>
<Button shape="rounded">Rounded</Button>
<Button shape="pill">Pill button</Button>

A Just-in-Time friendly implementation

There are a few rules and gotchas around what you should and shouldn’t do when working with Tailwind’s Just-in-Time Engine, to make sure things work properly.

For example, you cannot construct dynamic classes like so:

<div class={`bg-${props.color}`}>

This will not work.

This page from the Tailwind CSS documentation website is very helpful to understand how the Just-in-Time engine works, and why certain rules apply.


Extracting common styles first

A good first step towards organising our multi-style component styling is to identify what classes are common to every single button variant, and store those in a variable.

Let’s do just that in our fist challenge!


🏆 Your first challenge 🏆

In the Gitpod workspace below:

  1. Take a look at the src/partials/hardcoded-buttons.astro file

  2. Identify the set of Tailwind classes that is common to all button variants

  3. In src/components/button.tsx, collect those common classes inside the baseClasses variable.

  4. Add the baseClasses to the className attribute of the Button component.

You’ll find more hints within the project.


✌️ Good luck!

Open in Gitpod →