You can read the interactive version of this blog post!
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:
<div class="bg-gradient-to-br
from-primary-300 via-accent-500 to-secondary-700
shadow-accent-500"
/>
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!
<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.
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. -
calc
, which allows to calculate values directly into CSS, is supported in 98.19% of all browsers. Stay tuned to learn how it works — otherwise you can already use the free online tool.
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.
Top comments (1)
This is fantastic! It would be even better if you could provide instructions on how to integrate this functionality into a SvelteKit project, both for new projects and existing ones. Ideally, the instructions would follow established Svelte practices. A React guide for our React-loving friends would be amazing too!