åå«Tailwindļ¼å¦ä¹ å¦ä½ē»ē»ęēCSS
Source: Julia Evans
Hello! 8 years ago, I wrote excitedly about discovering Tailwind.
At that time I really had no idea how to structure my CSS code and given the choice between a pile of complete chaos and Tailwind, I was really happy to choose Tailwind. It helped me make a lot of tiny sites!
I spent the last week or so migrating a couple of sites away from Tailwind and towards more semantic HTML + vanilla CSS, and it was SO fun and SO interesting, so here are some things I learned!
As usual Iām not a full-time frontend developer and so all of my CSS learning has happened in fits and starts over many years.
it turns out Tailwind taught me a lot
When I started thinking about structuring CSS, I was intimidated at first: Iām not very good at structuring my CSS! But then I started reading blog posts talking about how to structure CSS (like A whole cascade of layers or How I write CSS in 2024) and I realized a couple of things:
- Every CSS code base has a bunch of different things going on (layouts! fonts! colours! common components!)
- Itās extremely useful to have systems or guidelines to manage each of those things, otherwise things descend into chaos
- Tailwind has systems for some of these, and I already know those systems! Maybe I can imitate the systems I like!
For example, Tailwind has:
- a reset stylesheet
- a colour palette
- a font scale
the systems Iām going to talk about
Iām going to talk about a few aspects of my CSS codebase and my thoughts so far what kind of rules I want to impose on the codebase for each one. Some of them are copied from Tailwind and some arenāt.
- reset
- components
- colours
- font sizes
- utility classes
- the base
- spacing
- responsive design
- the build system
1. reset
I just copied Tailwindās āpreflight stylesā
by going into tailwind.css and copying the first 200 lines or so.
I noticed that Iāve developed a relationship with Tailwindās CSS reset over time,
for example Tailwind sets box-sizing: border-box on every element (which means
that an elementās width includes its padding):
* { box-sizing: border-box; }
I think it would be a real adjustment for me to switch to writing CSS without
these, and Iām sure there are lots of other things in the Tailwind reset (like
html {line-height: 1.5;}) that Iām subconsciously used to and donāt even realize are
there.
2. components
This next part is the bulk of the CSS!
The idea here is to organize CSS by ācomponentsā, in a way thatās spiritually related to Vue or React components. (though there might not actually be any Javascript at all in the site)
Basically the idea is that:
- Each ācomponentā has a unique class
- The CSS for one component never overrides the CSS for any other component
- Each component has its own CSS file
So editing the CSS for one component wonāt mysteriously break something in another component. And probably like 80% of the CSS that I would actually want to change is in various component files, so if Iām editing a 100-line component, I just have to think about those 100 lines. Itās way easier for me to think about.
For example, this HTML might be the .zine ācomponentā.
<figure class="zine horizontal">
<img src="whatever.jpg">
</figure>
And the CSS looks something like this, using nested selectors:
.zine {
...
&.horizontal {
...
}
&.vertical {
...
}
&:hover {
...
}
}
I havenāt done anything programmatic (like web components or @scope) that ensures that components wonāt interfere with each other, but just having a convention and trying my best already feels like a big improvement.
Next: conventions to maintain some consistency across the site and keep these components in line with each other!
3. colours
colours.css has a bunch of variables like this which I can use as necessary.
Colour is really hard and I didnāt want to revisit my use of colour in this
refactor, so I left this alone.
The only guideline Iām trying to enforce here is that all colours used in the site are listed in this file.
:root {
--pink: #fea0c2;
--pink-light: #F9B9B9;
--red: #f91a55;
--orange: rgb(222, 117, 31);
...
}
4. font sizes
One thing I appreciated about Tailwind was that if I wanted to set a font
size, I could just think āhm, I want the text to be bigā, write text-lg, and
be done with it! And maybe if itās not big enough Iād use xl or 2xl instead.
No trying to remember whether Iām using em or px or rem.
So I defined a bunch of variables, taken from Tailwind, like this:
--size-xs: 0.75rem;
--line-height-xs: 1rem;
--size-sm: 0.875rem;
--line-height-sm: 1.25rem;
Then if I want to set a font size, I can do it like this. Itās a little more verbose than Tailwind but Iām happy with it for now.
h3 {
font-size: var(--size-lg);
line-height: var(--line-height-lg);
}
5. utilities
There are some things like buttons that appear in many different components. Iām calling these āutilitiesā.
I copied some utility classes from Tailwind (like .sr-only for things that
should only appear for screenreader users).
This section is pretty small and I try to be careful about making changes here.
6. the base
ābaseā styles are styles that apply across the whole site that I chose myself. I
have to keep this section really small because Iām not confident enough to
enforce a lot of styles across the whole site. These are the only two I feel
okay about right now, and I might change the <section> one:
/* put a 950px column in the middle of each <section> */
section {
--inner-width: 950px;
padding: 3rem max(1rem, (100% - var(--inner-width))/2);
}
a {
color: var(--orange);
}
I think for the base styles itās going to be easiest for me to work kind of bottom up ā first start with almost nothing in the base styles, and then move some styles from the components into base styles as I identify common things I want.
7. spacing
I havenāt completely worked out an approach to managing padding and margins yet. Iām definitely trying to be more principled than how I was doing it in Tailwind though, where I would just haphazardly put padding and margins everywhere until it looked the way I wanted.
Right now Iām working towards making the outer layout components in charge of
spacing as much as possible. For example if I have a <section> with a bunch of
children that I want to have space between them, I might use this to space the
children evenly:
section > *+* {
margin-top: 1rem;
}
Some inspiration blog posts:
8. responsive design: use more grid!
The way I was doing responsive design in Tailwind was to use a lot of media
queries. Tailwind has this md:text-xl syntax that means āapply the text-xl
style at sizes md or largerā.
Iām trying something pretty different now, which is to make more flexible CSS grid layouts that donāt need as many breakpoints. This is hard but itās really interesting to learn about whatās possible with grid, and itās a good example of something that I donāt think is possible with Tailwind.
For example, Iāve been learning about how to use auto-fit to automatically use
2 columns on a big screen and 1 column on a small screen like this:
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 400px), max-content));
justify-content: center;
I also used grid-template-areas a lot which is an amazing feature that I donāt think you can use with Tailwind.
Some inspiration:
- A responsive grid layout with no media queries from CSS Tricks
9. the build system: esbuild
In development, I donāt need a build system: CSS now has both built in import statements, like this:
@import "reset.css";
@import "typography.css";
@import "colors.css";
and built in nested selectors, like this:
.page {
h2 { ...}
}
If I want, I can use esbuild to bundle the CSS file for production. That looks something like this.
esbuild style.css --bundle --loader:.svg=dataurl --loader:.woff2=file --outfile=/tmp/out.css
Even though I usually avoid using CSS and JS build systems, I donāt mind using esbuild (which I wrote about in 2021 here) because itās based on web standards and because itās a static Go binary.
why migrate away from Tailwind?
A few people asked why I was migrating away from Tailwind. A few factors that contributed are:
- Tailwind has become much more reliant on a build system since 2018, I think itās impossible (?) to use newer versions of Tailwind without using a build system. So Iāve been using Tailwind v2 for years. (thereās also litewind apparently)
- Itās always been true that youāre supposed to use Tailwind with a build
system, but Iāve never really done that, so I have 2.8MB
tailwind.min.cssfiles (270K gzipped) in a lot of my projects and it feels a little silly. - Iām a lot better at CSS than I was when I started using Tailwind
- Ultimately Tailwind is limiting: if you want to do Weird Stuff in your CSS, itās not always possible with Tailwind. Those limits can be extremely useful (a lot of this post is about me reimplementing some of Tailwindās limits!) but at this point Iād like to be able to pick and choose.
- I ended up with sites that mixed both vanilla CSS and Tailwind in the same project and that was not fun to maintain
- I got curious about what writing more semantic HTML would feel like.
CSS features Iām curious about
While doing this I learned about a lot of CSS features that I didnāt use but am curious about learning about one day:
@layer(from A Whole Cascade of Layers)- @scope) (especially this example of how to use @scope in a ācomponentā CSS design from the specification!)
- container queries
- subgrid
one last reason I moved away from Tailwind
Iāve been talking a lot in this post about what I learned from using Tailwind, and thatās all true.
But I read this post 3 years ago called Tailwind and the Femininity of CSS that really stuck with me. I honestly probably started out with an attitude towards CSS a little like that post describes:
Theyāve heard itās simple, so they assume itās easy. But then when they try to use it, it doesnāt work. It must be the fault of the language, because they know that they are smart, and this is supposed to be easy.
But in the last 10 years Iāve learned to really love and respect CSS as a technology.
So I decided years ago that I wanted to react to āCSS is hardā by getting better at CSS and taking it seriously as a technology, instead of devaluing it. Doing that changed everything for me: I learned that so many of my frustrations (ācentering is impossibleā) had been addressed in CSS a long time ago, and that also what ācenteringā means is not always straightforward and it makes sense that there are many ways to do it. CSS is hard because itās solving a hard problem!
Iāve been so impressed by the new CSS features that have been built in the last 10-15 years (some of which Iāve talked about in this post!) and how they make it easier to use CSS, and spending the time to improve my CSS skills has been a really cool experience.
And that post made me feel like Tailwind contributes to the devaluing of CSS expertise, and like thatās not something I want to be a part of, even if Tailwind has been a useful tool for me personally. Especially in this time of LLMs where it feels more important than ever to value humansā expertise.
Another blog post criticizing Tailwind that influenced me:
thatās all for now!
Thanks to Melody Starling who originally designed and wrote the CSS for wizardzines.com, everything cool and fun about the site is thanks to Melody.
Also I read so many incredible blog posts about CSS while working on this (from CSS Tricks, Smashing Magazine, and more), Iāve tried to link some of them throughout this post and I really appreciate how much folks in the CSS community share their practices.