Using TailwindCSS with CSS-in-JS
Update 2— Dec 3, 2017
Obviously this isn’t the native
<style></style> syntax that comes with Vue, but it’s an option if you want to pull in Tailwind in via CSS-in-JS.
Update — Dec 1, 2017
I’ve added an example of how to do this in the repo using react-emotion.
At the beginning of 2017 I put together a small list of technologies I wanted to explore and grow in. Topics like Performance, PWAs, Vue, AdonisJS, Webpack, CSS-in-JS, and many more.
One of the items on my list is to write a Babel plugin. I didn’t have any ideas during the year but it dawned on me when writing my previous post Using PurifyCSS to Remove Unused TailwindCSS Classes that I should try to convert TailwindCSS to something we can leverage with a CSS-in-JS library such as Glamorous.
If you haven’t heard some of the benefits of CSS-in-JS checkout this amazing writeup by Mark Dalgleish:
Earlier this year…
Neckbeard was my first experiment with trying to merge Atomic CSS and CSS-in-JS. I actually spent a lot of time on it upfront but it doesn’t get much of my attention anymore.
Using React it would look something like this:
The solution was pretty elegant IMO, but the approach I took with Neckbeard had the downside of a very heavy runtime. The library generates an object of around 10,000 selector properties on page load and has a function to reference each selector.
The problem is that this object has to be created by the browser and stored in memory to be referenced by the nb(classes) function. This defeats one of the many benefits of CSS-in-JS, which is to only load the styles used on the page.
It needed to be better if it was going to be usable so instead of trying to rewrite Neckbeard, I decided to take a different approach with TailwindCSS instead.
After digging through the babel-handbook and reviewing lots of awesome examples, I finally felt like I could grasp Babel and ASTs enough to convert a string of TailwindCSS classes to a corresponding style object at build time. In other words, we wouldn’t make the browser generate all the necessary styles properties, but we would do it upfront at build time with a Babel plugin.
Example using Glamor 💅
The babel plugin will look for the function tw(classes), reference the corresponding TailwindCSS style properties, and replace the whole expression with an object to serve to the user. In this example I use Glamor but you could easily use Glamorous or other CSS-in-JS library that utilizes the same object syntax for styles.
Example using Glamorous 💄
Here’s a slightly more complicated component I borrowed from the TailwindCSS docs and built using Glamorous and babel-plugin-tailwind.
I provided some brief instructions on how to get started with the babel-plugin-tailwind over at https://github.com/andrewdelprete/babel-plugin-tailwind. There are a few undocumented features I still need to write about and will be sure to let everyone know once they’re fleshed out.
There are a few technical hurdles to figure out:
The babel plugin converts a string of TailwindCSS classes to an object. However, libraries like *emotion (See update above) and styled components require a string of traditional CSS properties to work properly.
I also haven’t figured out how to make this work with Vue yet. Vue has it’s own way of doing CSS-in-JS which I’m not sure if it can be leveraged or not.
Server-side Rendering is another item on the list. I believe this shouldn’t be too difficult as long as the source is ran through babel first.
If you’d like to contribute to the project, please say hello and open a PR!
I believe the ability to inject Tailwind’s predefined classes in CSS-in-JS can be a huge win. Having consistent font sizes, grid widths, spacing, colors, and breakpoints can help make your codebase more uniformed. Plus, the added bonus of these classes being replaced with an object at build time makes things nice and snappy for the end-user.
Also, If you’re interested in learning more about some easy performance wins, I’ve written a few articles on www.learnperf.com.
Thanks for reading!
-Andrew Del Prete