I have already made my love for styled-components
pretty clear on many different posts in this blog. This blog is about a new absolute favorite - twin.macro
.
Before we talk about twin.macro
, we have to talk about TailwindCSS.
Tailwind is a atomic CSS framework that has taken the world by storm. Everywhere you look, there would be a company/website using it. But atomic CSS has existed way before Tailwind - Tachyons is an early example.
One of my first exposures to this writing style was from Adam Wathan (who is also the author of Tailwind), when he wrote this (now) iconic blog post. It's super long, but he very well talks about what atomic CSS is and what we can achieve through it.
every line of new CSS is still an opportunity for new complexity
When Adam said he is releasing a new framework called Tailwind which enforces atomic CSS, I immediately wanted to check it out. I found the experience rather underwhelming, The tokens from CSS was already difficult enough, now I have to learn these short form tokens for them? Uggh.. this is hard.
From the website for Tailwind:
<figure class="rounded-xl bg-gray-100 p-8 md:flex md:p-0">
<img
class="mx-auto h-32 w-32 rounded-full md:h-auto md:w-48 md:rounded-none"
src="/sarah-dayan.jpg"
alt=""
width="384"
height="512"
/>
<div class="space-y-4 pt-6 text-center md:p-8 md:text-left">
<blockquote>
<p class="text-lg font-semibold">
“Tailwind CSS is the only framework that I've seen scale on large teams.
It’s easy to customize, adapts to any design, and the build size is
tiny.”
</p>
</blockquote>
<figcaption class="font-medium">
<div class="text-cyan-600">Sarah Dayan</div>
<div class="text-gray-500">Staff Engineer, Algolia</div>
</figcaption>
</div>
</figure>
This is not how any of us think of colors or spacing and they are new tokens that you have to learn. In commercial projects, designers who were not aware of our use of Tailwind created different colors, different spacings which crowded the theme file. I could not copy already written CSS from codepen projects and have it work (This seems to be the problem Tailwind UI solves). And despite all that, Tailwind succeeded.
I think the reason for Tailwind's success is better explained when talking about their second founder - Steve Schoger. Their first project together was a book named Refactoring UI which is still one of the best books that a developer can get to teach themselves some design. The book is derived from tips that Steve used to provide on Twitter for developers. Both Steve and Adam have given enough thought about making a developer successful in design and received feedback on it. They have applied several of these principles into the framework of Tailwind itself and that's where the magic numbers and color suites come from.
Make your ideas look awesome, without relying on a designer. - Refactoring UI
Twin blends the magic of Tailwind with the flexibility of css-in-js
twin
lets you write tailwind classes and converts them to css-in-js counterparts - currently supporting popular libraries like styled-components
and goober
. This means that instead of purging classes from HTML on build, twin can maintain a cache of classes that are being used and include them in the final bundle.
If you have something working with Tailwind before, using twin.macro
you can change className
to tw
:
function Cover() {
return (
<div className="flex-none w-48 relative">
<img
src="/classic-utility-jacket.jpg"
alt=""
className="absolute inset-0 w-full h-full object-cover"
/>
</div>
);
}
On twin
:
import "twin.macro";
function Cover() {
return (
<div tw="flex-none w-48 relative">
<img
src="/classic-utility-jacket.jpg"
alt=""
tw="absolute inset-0 w-full h-full object-cover"
/>
</div>
);
}
<div tw="flex-none w-48 relative">
<img
src="/classic-utility-jacket.jpg"
alt=""
tw="absolute inset-0 w-full object-cover"
{ /* Using `css` prop from `styled-components`, `emotion`. */ }
css="max-height: calc(100vh - 48px)"
/>
</div>
import tw, { styled } from 'twin.macro';
const Button = styled.button(({ isPrimary }) => [
// Group related classes
tw`px-2 py-4 m-2`,
tw`border border-gray-600 border-dashed rounded-sm`
// Custom CSS
`box-shadow: 2px 2px 5px 0px rgba(50, 50, 50, 0.75);`
// conditional styling
isPrimary && tw`bg-indigo-400`
// variant grouping
tw`hover:(text-black underline)`
]);
From twin.macro
examples:
import 'twin.macro'
const interactionStyles = () => (
<div tw="hover:(text-black underline) focus:(text-blue-500 underline)" />
)
const mediaStyles = () => <div tw="sm:(w-4 mt-3) lg:(w-8 mt-6)" />
const pseudoElementStyles = () => (
<div tw="before:(content block w-10 h-10 bg-black)" />
)
const stackedVariants = () => <div tw="sm:hover:(bg-black text-white)" />
const variantsInGroups = () => <div tw="sm:(bg-black hover:bg-white)">
If you don't want to learn all the freaking classes, you can now sponsor Sid for his UI Devtools.
I had mentioned that twin.macro
transpiles to styled-components
or emotion
, but you can also set it to transpile the output down to goober
.
Goober is a 1kB alternative to CSS-in-JS libraries with the same functionalities. You aren't going to be using a whole lot of features because tailwind anyway, so overload the size limits.
On installing twin.macro
and goober
, add a setting in package.json
to set it up to use goober:
npm i goober twin.macro
{
"babelMacros": {
"twin": {
"preset": "goober"
}
}
}
There is a lot more convienience features that you get to add in for babel
, here's few examples:
Happy Tailwinding