@adamwathan adamwathan released this Nov 17, 2017 · 587 commits to master since this release

New Features

Add a custom prefix to all utilities

One of the most common questions we've received since releasing v0.1.0 is "can I use Tailwind with {my existing CSS|another CSS framework}?"

While there was nothing stopping you from layering Tailwind on top of existing CSS, Tailwind has a lot of class names in common with other frameworks (.container, .mb-2, etc.) so you could run into problematic naming collisions if you weren't careful.

To fix this, you can now specify a custom prefix for all of the classes Tailwind generates under the new options key in your Tailwind config file:

{
  // ...
  options: {
    prefix: 'tw-',
    // ...
  },
}

Now all of Tailwind's utilities will include that prefix:

<!-- This... -->
<div class="bg-white hover:bg-blue md:bg-red"></div>

<!-- ... becomes this: -->
<div class="tw-bg-white hover:tw-bg-blue md:tw-bg-red"></div>

To learn more, read the full documentation.

Optionally make all utilities !important

Another common obstacle when trying to use Tailwind with existing CSS is dealing with specificity.

By default, Tailwind utilities are not marked as !important, which means that if your existing CSS has high specificity selectors, trying to override what those selectors are doing with a Tailwind utility just won't work.

To fix this, we've added another option to the options section of the Tailwind config file:

{
  // ...
  options: {
    // ...
    important: true,
    // ...
  },
}

If you set important to true, all of Tailwind's declarations will get marked as !important, so they can easily be used to override existing CSS.

To learn more, read the full documentation.

Round element corners independently

Up until now, you could only apply a border radius to pairs of corners, like the top two corners, left two corners, etc.

Now you can round corners independently too:

<!-- Round the top left corner: -->
<div class="rounded-tl"></div>

<!-- Round the top right corner: -->
<div class="rounded-tr"></div>

<!-- Round the bottom right corner: -->
<div class="rounded-br"></div>

<!-- Round the bottom left corner: -->
<div class="rounded-bl"></div>

See more examples in the border radius documentation.

Cascading border colors and styles

In v0.1.x, our border width utilities used the border shorthand property, which meant that setting a border width also set a style and color:

.border-2 {
  border: 2px solid config('colors.grey-lighter');
}

This meant that if you wanted to change the border style or color of an element and then change the border width at a larger breakpoint, you'd have to re-specify the style/color:

<div class="border-2 border-red border-dashed md:border-4 md:border-red md:border-dashed"></div>

Now our border width utilities only specify the width, so any color or style modifications will properly cascade to larger breakpoints without having to be re-specified:

<div class="border-2 border-red border-dashed md:border-4"></div>

This is technically a breaking change, so check out the relevant section in the upgrade guide to understand how this might affect your site.

Upgrade Guide / Breaking Changes

auto is no longer a hard-coded margin value

Impact: Low

Instead of hard-coding mx-auto, ml-auto, etc. into Tailwind itself, we've moved those values into the customizable margin scale in the config file.

So if you're using a custom config file, add auto to your margin scale:

  {
    // ...
    margin: {
+     'auto': 'auto',
      'px': '1px',
      '0': '0',
      '1': '0.25rem',
      '2': '0.5rem',
      '3': '0.75rem',
      '4': '1rem',
      '6': '1.5rem',
      '8': '2rem',
    },
  }

The defaultConfig function is now a separate module

Impact: Low

In the generated Tailwind config file, we include a line of code showing you how to reference Tailwind's default config values in case you'd like to reference them in your own config file.

For technical reasons, the way this works has changed:

// The old way:
var defaultConfig = require('tailwindcss').defaultConfig()

// The new way:
var defaultConfig = require('tailwindcss/defaultConfig')()

The good news is that this change makes it possible for you to import your custom config file into your front-end JavaScript if you'd like; making it easy to re-use the same color palette with libraries like D3.js or Chart.js for example.

Rounded utilities now combine position and radius size

Impact: High

Previously, border radius position and radius size were specified with two utilities. Now, size and position are combined into the same utility:

<!-- The old way: -->
<div class="rounded-lg rounded-t"></div>

<!-- The new way: -->
<div class="rounded-t-lg"></div>

We made this change because it makes working with border radius generally more predictable and much more flexible.

For example, previously, if you wanted to round 3 corners of an element, you could try this, but it wouldn't work:

<!-- Doesn't work: -->
<div class="rounded-lg rounded-t rounded-l"></div>

Instead, you'd see that only the left side was rounded. This is because the previous implementation of rounded-l worked by unrounding the right-side corners.

Now you can round 3 corners of an element two ways:

<!-- Option 1: Round two sides -->
<div class="rounded-t-lg rounded-l-lg"></div>

<!-- Option 3: Round the corners separately -->
<div class="rounded-tl-lg rounded-tr-lg rounded-bl-lg"></div>

Upgrade steps

  1. If you have a custom config file, make sure your 0 value border radius utility appears first in your border radius scale:

      {
        // ...
        borderRadius: {
    +     'none': '0',
          'sm': '.125rem',
          default: '.25rem',
          'lg': '.5rem',
          'full': '9999px',
    -     'none': '0',
        },
      }

    This is important if you ever need to reset the border radius of a side at a breakpoint and add a border radius to another side that shares a corner.

  2. Look for any time you round one side of an element in your codebase and collapse the separate position and size utilities into the new corresponding compound utility:

    <!-- Change this: -->
    <div class="rounded-lg rounded-t"></div>
    
    <!-- To this: -->
    <div class="rounded-t-lg"></div>
    
    
    <!-- Change this: -->
    <div class="rounded rounded-l"></div>
    
    <!-- To this: -->
    <div class="rounded-l"></div>
  3. If you were changing which side of an element was rounded responsively, now you'll need to explicitly unround one side when you round the other:

    <!-- Change this: -->
    <div class="rounded-lg rounded-t lg:rounded-lg lg:rounded-l"></div>
    
    <!-- To this: -->
    <div class="rounded-t-lg lg:rounded-t-none lg:rounded-l-lg"></div>

Border width utilities no longer affect border color/style

Impact: Medium

Previously, applying a border width utility like .border-2 would not only set the border width; it would also override the border color and border style.

This is no longer the case, so if you were ever depending on that behavior (for example when overriding things responsively), you'll need to update your code to explicitly change the color and style:

<!-- Change this: -->
<div class="border border-dashed border-red md:border-2"></div>

<!-- To this: -->
<div class="border border-dashed border-red md:border-2 md:border-solid md:border-grey-lighter"></div>

It's very unlikely that you were depending on this behavior so chances are you won't need to make this sort of change.

Instead, you'll probably notice this change from the opposite side, where you had to define your border color twice if you changed the size responsively.

Now you only need to define the color or style once, so although you don't have to remove the double definitions, they are now redundant and safe to remove:

<!-- Change this: -->
<div class="border border-dashed border-red md:border-2 md:border-dashed md:border-red"></div>

<!-- To this: -->
<div class="border border-dashed border-red md:border-2"></div>

@apply is now very strict about what classes can be applied

Impact: Low

Previously, @apply would only fail if it couldn't find a matching class to mixin. This led to unexpected behavior for a lot of people when trying to @apply complex classes.

So now instead of silently applying a class in a way that results in unexpected behavior, @apply behaves much more strictly and will throw an explicit error if trying to @apply something other than simple, un-nested, single definition classes.

Here are the rules @apply now enforces:

  1. You cannot @apply a class that is part of any ruleset which is nested within an at-rule.

    This means you can't @apply classes that are nested within media queries:

    @media (min-width: 300px) {
      .a { color: blue; }
    }
    
    .b {
      /* This will throw an error */
      @apply .a;
    }

    This never worked before anyways, but now you'll get an error instead of wondering why your class didn't inherit the responsive behavior of the class you tried to apply.

  2. You cannot @apply a class that contains a pseudo-selector.

    .a:hover {
      color: red;
    }
    
    .b {
      /* This will throw an error */
      @apply .a;
    }

    This never worked before either, but now the error message you get will be more helpful.

  3. You cannot @apply a class that is included in multiple rulesets.

    .a {
      color: red;
    }
    
    .b {
      /* This will throw an error */
      @apply .a;
    }
    
    .a {
      color: blue;
    }

    This is what caused the confusion in #112. While supporting this wouldn't have negative consequences 95% of the time, it can be really confusing when it does cause problems.

    @apply is meant for single stand-alone rulesets, so we don't see a reason to support this. If a class appears in multiple rulesets, it's a sign that something complex is happening, and @apply is not intended to be used to inherit complex behavior.

If your build fails because of @apply in v0.2.0 but built successfully in v0.1.0, it's very likely something on your site wasn't actually working the way you expect.

Add options key to your config

Impact: Low

If you'd like to use the new prefix or important options, you'll want to add the options key to the bottom of your config file.

Here's what it looks like with the default values:

{
  // ...
  options: {
    prefix: '',
    important: false,
  },
}

This key is optional, so nothing will break if you don't add it.

Spacing, radius, and border width utility declaration order changes

Impact: Low

Previously, these utilities were declared purely based on the order in your scale, and with more specific declarations declared first.

For example, padding looked something like this:

.pt-0 { /* ... */ }
.pr-0 { /* ... */ }
.pb-0 { /* ... */ }
.pl-0 { /* ... */ }
.px-0 { /* ... */ }
.py-0 { /* ... */ }
.p-0 { /* ... */ }

.pt-1 { /* ... */ }
.pr-1 { /* ... */ }
.pb-1 { /* ... */ }
.pl-1 { /* ... */ }
.px-1 { /* ... */ }
.py-1 { /* ... */ }
.p-1 { /* ... */ }

.pt-2 { /* ... */ }
.pr-2 { /* ... */ }
.pb-2 { /* ... */ }
.pl-2 { /* ... */ }
.px-2 { /* ... */ }
.py-2 { /* ... */ }
.p-2 { /* ... */ }

This meant that more general utilities like p-2 would override side-specific utilities like pl-1 if the scale value in the general utility was higher:

<!-- This markup: -->
<div id="padded" class="p-4 px-2 pl-1"></div>

<!-- ...is equivalent to this CSS: -->
<style>
#padded {
  padding-top: config('padding.4');
  padding-bottom: config('padding.4');
  padding-right: config('padding.4');
  padding-left: config('padding.4');
}
</style>

Now, spacing, radius, and border width utilities are declared from most general to most specific, sorted by position first, then by the scale:

.p-0 { /* ... */ }
.p-1 { /* ... */ }
.p-2 { /* ... */ }

.py-0 { /* ... */ }
.px-0 { /* ... */ }
.py-1 { /* ... */ }
.px-1 { /* ... */ }
.py-2 { /* ... */ }
.px-2 { /* ... */ }

.pt-0 { /* ... */ }
.pr-0 { /* ... */ }
.pb-0 { /* ... */ }
.pl-0 { /* ... */ }
.pt-1 { /* ... */ }
.pr-1 { /* ... */ }
.pb-1 { /* ... */ }
.pl-1 { /* ... */ }
.pt-2 { /* ... */ }
.pr-2 { /* ... */ }
.pb-2 { /* ... */ }
.pl-2 { /* ... */ }

This means that setting a left padding will override an x padding which will override an "all sides" padding:

<!-- This markup: -->
<div id="padded" class="p-4 px-2 pl-1"></div>

<!-- ...is equivalent to this CSS: -->
<style>
#padded {
  padding-top: config('padding.4');
  padding-bottom: config('padding.4');
  padding-right: config('padding.2');
  padding-left: config('padding.1');
}
</style>

It's very unlikely that this change will break your layout; it's more likely that you were working around this annoying behavior:

<!-- What you've probably written: -->
<div class="pt-4 pr-4 pb-4 pl-2"></div>

<!-- What you can write now: -->
<div class="p-4 pl-2"></div>
Assets 2