Loading...

Postulate is the best way to take and share notes for classes, research, and other learning.

More info

Using Tailwind Right: Atomic Components, Not Classes

Profile picture of Samson ZhangSamson Zhang
Feb 18, 2021Last updated Feb 19, 20213 min read

I'm a big fan of Tailwind. I was skeptical about it when I first learned of it, grouping it with Emotion, styled-components, and the bunch that were terrifyingly different from the CSS that I knew and loved; but my skepticism proved to be wrong. Tailwind felt like a dialect of CSS that was somehow even more natural to work with than the original.

At first, I used Tailwind with Miles McCain's a17t CSS framework. I used it like inline CSS, not thinking about how it could build into a larger design or styling system.

When I built Updately (the first daily updates version), I started by defining a few atomic styles. Instinctively, I did this in the globals.css file, through utility classes:

.up-button { @apply px-4 h-12 rounded-md transition font-semibold; } .up-button.small { @apply h-10; } a.up-button { @apply inline-block; line-height: 3rem; } a.up-button.small { line-height: 2.5rem; } .up-button:disabled { @apply opacity-25; cursor: not-allowed; } .up-button.primary { background-color: #0026FF; @apply text-white; } .up-button:not(.text):not(.primary) { @apply border-2 border-black; } .up-button:not(.text):not(:disabled):hover { @apply bg-black text-white; } .up-button.text:not(:disabled):hover{ @apply bg-gray-100; }

I used @applys to use Tailwind, and thought that this was how Tailwind was supposed to be used. It was easy enough to slap this class onto any buttons that I needed.

There was the minor issue that changes to globals.css took forever to compile, as Tailwind had to be compiled alongside it each time. In and of itself this wasn't too bad, but it was indicative of a larger mistake that I made: I was going against the pattern of how Tailwind is meant to be used.

I realized this when I had to explain Tailwind to some other devs I met. "Tailwind lets you style your app through classes," I would say. "It sounds weird, but if you think about it, it's actually much more aligned with how React works -- the markup itself is componetized, so it makes sense to put componetized styling in the markup rather than separately componetized CSS." I should have realized here that what I was saying was directly contradicting my approach, but somehow I didn't. Only later, when one dev asked me, "How do you go about re-using styling?" and I replied, "Any re-used styling should correlate with re-used components, so there's no need for any extra complication," did I connect theory to practice and realize, "oh, wait, I'm supposed to put my styling in components, not classes."

With EloLeague, I did Tailwind right. As I created styles I knew I would re-use, I threw them in components, as simple as ElH1, ElH2, and ElH3:

export default function ElH1({children} : {children: ReactNode}) { return ( <h1 className="text-4xl mt-12 mb-6"> {children} </h1> ) }

export default function ElH2(props: {children: ReactNode, className?: string}) { return ( <h2 className={"font-bold text-4xl el-font-display uppercase " + props.className}> {props.children} </h2> ) }

export default function ElH3(props: {children: ReactNode}) { return ( <h3 className="font-bold"> {props.children} </h3> ) }

Using them, then, is an absolute joy: you don't have to take that tiny extra step to add a class. You don't have to think at all. Want a header? Throw in an <ElH1>, and throw your worries away.


Comments (loading...)

Sign in to comment

EloLeague

Notes and learnings from building EloLeague.com