How to have Perfect Contrast of Text Color on Any Background in TailwindCSS
Tags: tailwindcss, webdev, tooling, themes
Published: 2024-05-17 10:11:40

> You can read the [interactive version of this blog post](https://easypagego.com/blog/perfect-contrast-text-color-on-any-background/)!

## The Problem

In recent years I have developed a good number of websites. The basic structure is usually similar among them, but there are some tasks that always require a lot of effort. And, coincidentally, they are precisely the **most tedious** and complicated ones. Especially **styling and choosing the colors of the website**.

There are quite a few palette generators around the web, but all of them leave you with just a bunch of *hexs* and *rgbs* — it’s up to you to join those into your system.

As if managing a theme was not enough, you were perhaps required to add the **dark mode**! The **TailwindCSS** team proposes to do this by wandering around the template, adding classes prefixed with `dark:` to instruct each element about its appearance when the selected theme is dark.

This is imho not optimal since, in addition to bloating the template even more, it would require manual work should I be asked to change some colors. And what if I want a third or even fourth theme?

### The Real Problem

I’ve been living with this problem peacefully until, after spending too many days hunting for html tags to slightly change their shades, I realized that, alas, *the text was no longer readable* - **it lacked contrast**.

Lighthouse accessibility check, unconcerned about my emotional state, lowered the score.

> I don’t want to have to think about this problem anymore.

## The Solution

I have come up with a system that allows for an unlimited number of themes, each with an unlimited amount of colors. Most importantly, **that ensures that the text is always readable on any color**.

Basic usage involves configuring at **least two themes**, *light mode* and *dark mode*.

Each theme requires at *least five colors*. They are `background`, `neutral` (useful for card backgrounds), `primary`, `secondary` and `accent`.

No one is stopping you from adding others like `success`, `warning`, `error`… you name it — it is completely configurable!

For each of them, the TailwindCSS utilities and components are produced and available. It means you can not only add a `bg-secondary-300` here and a `border-t-neutral-500` there, but you get fancy stuff for free — IDE autocomplete included:

```html
<div class="bg-gradient-to-br
  from-primary-300 via-accent-500 to-secondary-700
  shadow-accent-500"
/>
```

![A square with a fancy colored background and shadow](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbpgntv8j2phxz3rzzro.png)

### Text perfect contrast

Thanks to a simple plugin (no need to install anything), some contrast dedicated classes are generated (and available in the IDE autocomplete). They follow the pattern `text-wacg-<color>-<shade>`.

> You can try real-time widgets on my [blog post](https://easypagego.com/blog/perfect-contrast-text-color-on-any-background/#text-perfect-contrast)!

![The widget the helps appreciate the power of this strategy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vyl9ccyl0f2vxgprup99.png)

```html
<div class="bg-primary-500 text-wacg-primary-500">
	This text will always be readable
</div>
```

This is easily obtainable with my [free online tool](https://easypagego.com/tools/tailwindcss-accessible-colors/).

***

### Benefits

Read more about the other convenient benefits:

#### Framework agnostic

It is **adoptable in any framework**: React, Next.js, Vue, Nuxt, Angular, Svelte, SvelteKit, Sapper, Laravel, Spring, Rails, Django, Express, Hugo, Solid.js, Astro.js, Preact, Ember.js, Alpine.js, LitElement, JQuery and any other I may have omitted.

#### Zero dependencies

I have opted for a **dependency-free solution**. As a developer, I do not like to install packages unless it is strictly necessary. Mainly because I know how dangerous it can be — supply chain attack gettin’ real!

This solution is **not a black box**, and a few paragraph below you can appreciate how simple yet powerful it is.

#### CSS Only

This solution minimizes JavaScript usage at runtime, **leveraging CSS for the majority of heavy lifting**.

#### IDE autocomplete

All the palette colors are utilized to generate all the **TailwindCSS utilities** (`bg-primary-500`, `border-l-neutral-300`, etc…), complete with *IDE autocompletion*. Additionally, any unused elements are purged by TailwindCSS, ensuring they are not present in the final CSS output.

#### Browser support

The system is based on two *CSS features*:

- `var`, which allows the declaration and usage of cascading variables in stylesheets, has [support on 97.32% of browsers](https://caniuse.com/?search=CSS%20Variable%20(Custom%20Properties)).
- `calc`, which allows to calculate values directly into CSS, is [supported in 98.19% of all browsers](https://caniuse.com/?search=calc%20as%20CSS%20unit%20value).
Stay tuned to learn how it works — otherwise you can already use the [free online tool](https://easypagego.com/tools/tailwindcss-accessible-colors/).

---

### FaQ

> Q: Why do I have to explicitly set the `text-wacg` class? Couldn’t it be implicit in the background one?
>
> **A: It could, but I learned to appreciate explicitness.**