February 25, 2019

If It Ain't Broke...

I feel like I've spent the last two weeks wrestling with some really hard and annoying decisions for Tailwind 1.0, chasing my tail and not really making great progress, but looking back I actually managed to get quite a few things out the door.

Renaming core plugins and theme sections

Up until now, keys in the config file have been all over the place:

  • Some are plural (backgroundColors, borderColors, fonts), while others are singular (width, opacity, backgroundSize).
  • Some are named after the CSS property (height, backgroundAttachment), while others are named closer to the classes they generate (textSizes, leading).

This is because originally the keys in the config were only used to specify the values that should be generated, and we wanted the keys to feel like they were appropriately labeling the values they contained. But after adding the ability to customize variants, it started to become clear to me that the keys we use should really represent the module (now "core plugin") name.

Put another way, the key should sound correct when used in the form "the [core plugin] plugin".

Something I really wanted to fix for Tailwind 1.0 was making the configuration keys as intuitive and consistent as possible, so I made the decision to always apply two rules:

  1. Use singular terminology, not plural. It should be "the border color plugin", not "the border colors plugin".

  2. Match the CSS terminology unless there's a really good reason not to. The "text sizes" plugin should be the "font size" plugin, but the "text colors" plugin should still be the "text color" plugin since "color" could be confusing alongside the existing "colors" key that defines your entire color palette.

I opened and merged a big PR (#656) that made all of these changes and includes more details about the impact.

The TL;DR is that a bunch of key names will need to change in your config file, but it will only be a few minutes of work and I might even put together a tool to automate it.

The impossible class renaming dilemma

This was the hard part of the last two weeks.

The goal with Tailwind 1.0 is primarily to re-evaluate previous decisions, and make any breaking changes that would make the experience better based on what I've learned having the framework out in the wild for the last year and a half.

One of the areas I've been looking at is the class names. In particular I don't love the .leading-*, .tracking-* and .rounded-* classes.

Leading and tracking are terms that come from typography, and translate to line-height and letter-spacing respectively in CSS. Jonathan and I originally chose these names because classes like .line-height-normal or .letter-spacing-tight just felt way too long, and we were also hell-bent on avoiding two-word class prefixes if they could at all be avoided.

But even though the words leading and tracking are accurate and expressive, they aren't commonly known to developers without some background in typography. In fact one of the very first issues in the repo questioned these class names and suggested we change them to something more intuitive.

Since I already wanted to rename the leading and tracking keys in the config to lineHeight and letterSpacing (as part of the core plugin renaming I discussed earlier), I decided to revisit this, and see if I could come up with something better.

I proposed two PRs for changing these classes:

...as well as a PR for changing the rounded-* classes:

The community was pretty divided on it, and eventually I opened another issue to discuss my motivations and see if anyone had other ideas.

Ultimately I decided to close all of those PRs, and leave everything exactly the way that it is.

I realized that I have been trying too hard to make things perfect when the truth is there is no perfect option, they all have pros and cons. People have been building awesome sites with Tailwind exactly as it is for a year and a half and no one has run into any real problems with the class names. Yes, you might need to learn a few terms here and there, but once you know the names, they don't matter.

So at the end of the day I decided it makes no sense to introduce a really annoying breaking change and mess with peoples' muscle memory. The existing names are good enough and already past the point of diminishing returns.

Making the default theme extendable

Another bigger project I worked on was adding first-class support for extending the default theme, through special configuration options in Tailwind.

You can read all about it in the pull request (#655), but the TL;DR is that you'll be able to do stuff like this in your config file:

module.exports = {
  theme: {

    // Completely replaced values:
    colors: {
      // User's custom color palette
    fonts: {
      // User's custom font stack

    // Values that extend the defaults with a few extras:
    extend: {
      shadows: {
        xl: '0 25px 40px 0 rgba(0,0,0,0.11), 0 10px 20px 0 rgba(0,0,0,0.05)',
      opacity: {
        '60': '.6',

Updating the default breakpoints

Over the weekend I changed Tailwind's default breakpoints from being based on Bootstrap, to instead being derived from common device resolutions.

Here's the diff:

  screens: {
-   sm: '576px',
+   sm: '568px',

    md: '768px',

-   lg: '992px',
+   lg: '1024px',

-   xl: '1200px',
+   xl: '1280px',

Read more about it in the pull request (#683).

Small wins

In addition to what I talked about above, I shipped a bunch of smaller things in the last two weeks as well.

  • Added normalize.css as a real dependency in Tailwind (#652)

    Prior to this, we maintained a copy and pasted version of Normalize in our codebase by hand, because couldn't figure out how to reliably determine the path to the Normalize CSS file in the node_modules directory at the eleventh hour before shipping v0.1.0.

    The secret sauce ended up being require.resolve, which gives you the resolved path of a module instead of the module itself.

  • Allow users to customize where default variants are output relative to state variants (#657)

    When you specify a list of variants (like ['hover', 'focus', 'active']) in your Tailwind config (or using the @variants directive), Tailwind renders those variants in the same order you specify.

    The issue with that was that the original unaltered CSS would always be rendered first, and sometimes you might want a custom variant to be rendered before the original CSS so that the unaltered class overrides the custom variant.

    Check out this comment for an example of where it could be useful.

  • Create a shared spacing key in the default theme (#661)

    Previously if you wanted to edit your padding/margin values, you'd have to edit them in all three of the padding, margin, and negativeMargin sections.

    Now Tailwind uses a spacing key by default that controls all three of those at once, and is also inherited by the width and height sections so that it's easy to maintain a consistent spacing/sizing scale in one place.

    You can of course still override all of those sections independently if you like.

  • Rename .roman to .not-italic (#663)

    Since initial release, Tailwind has had a .roman class for setting font-style: normal. This class only really exists to undo .italic at higher breakpoints, for example:

    <span class="italic md:roman>...</span>

    It's a stupid name for a class, but we were sort of forced to use it becase of a bug in postcss-selector-not that caused the :not in classes like .md:not-italic to be interpreted as a :not() pseudo-selector, which caused problems for anyone using cssnext (now superceded by postcss-preset-env).

    That bug has since been fixed, so now seemed like the right time to switch to .not-italic, which is the class name we wanted to use from the beginning.


I have a few more chores to work on for Tailwind 1.0 from a code perspective, but these are the significant projects:

  • Convert the docs site to VuePress. I love Jigsaw but I want to make the docs a lot more interactive for 1.0, and VuePress will make that a lot easier.
  • Write new documentation for the 1.0 changes. At a minimum I need to document what's changed and make sure none of the docs are stale, but I also want to write a lot of new sections and flesh out some of the stuff that's already there. I don't want this to hold me back too much from releasing 1.0 so I'll probably have to settle in some areas, but I'd really like to release 1.0 with a splash and some really great updated docs if possible.
  • Continue refining the default theme. Steve and I have been working on improving the default color palette, shadows, max-width scale, and a few other things for 1.0. Need to make sure that we are really happy with it before I actually release it, especially now that we are encouraging people more than ever to depend on our default values.