NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
npm · #907 most downloaded on npm
A utility-first CSS framework for rapidly building custom user interfaces.
Last release 15 days ago
08 Sep 2026
Ships on a steady schedule
a new release about every 8 days
Nearly every release is documented
notes for 60 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
9 years old
2657 releases · first in 2017
No release notes
No release notes
Add new plugin and plugin.withOptions APIs for plugin developers
plugin and plugin.withOptions APIs for plugin developers (#1268)One column per quarter.
Don't watch node_modules files for changes, fixed significant build performance regression in v1.2.0-canary.0
node_modules files for changes, fixed significant build performance regression in v1.2.0-canary.0 (#1179)node_modules files for changesv1.2.0-canary.0Automatically watch all config file dependencies (plugins, design tokens imported from other files, etc.) for changes when build watcher is running
justify-evenly utility (#1083)justify-evenly utilityFixes a bug where the .group class was not receiving the user's configured prefix when using the prefix option (#1216).
Fixes a bug where the .group class was not receiving the user's configured prefix when using the prefix option (#1216).
Note: Although this is a bugfix it could affect your site if you were working around the bug in your own code by not prefixing the .group class. I'm sorry 😞
Fixes an issue where in some cases function properties in the user's theme config didn't receive the second utils argument
theme config didn't receive the second utils argument (#1180)Fixes a bug with horizontal rules where they were displayed with a 2px border instead of a 1px border
Fixes issue where values like auto would fail to make it through the default negative margin config
auto would fail to make it through the default negative margin config (#1070)This is technically a very minor breaking change in the event that you were actually depending on hr elements not having a default border. You can rem…
The first new feature release since v1.0 has arrived! Tailwind v1.1 includes a bunch of new stuff, but I think the things you'll probably be most excited about are:
first-child, last-child, nth-child(odd), and nth-child(even)Important note — although this is a minor release, it includes two bug fixes that may have a superficial impact on how your site looks if you are using horizontal rules in your site or are relying on the default placeholder color defined in Tailwind's base styles.
Be sure to read through the fixes section before upgrading to understand the impact.
!importantborder-double utility<a name="new-features"></a>
<a name="added-utilities-for-screenreader-visibility"></a>
Tailwind now includes a new accessibility core plugin that adds sr-only and not-sr-only utilities for controlling whether an element is visually hidden but still accessible to screen readers.
Use sr-only to hide an element visually without hiding it from screen readers:
<a href="#">
<svg><!-- ... --></svg>
<span class="sr-only">Settings</span>
</a>
Use not-sr-only to undo sr-only, making an element visible to sighted users as well as screen readers. This can be useful when you want to visually hide something on small screens but show it on larger screens for example:
<a href="#">
<svg><!-- ... --></svg>
<span class="sr-only sm:not-sr-only">Settings</span>
</a>
By default, responsive and focus variants are generated for these utilities. You can use focus:not-sr-only to make an element visually hidden by default but visible when the user tabs to it — useful for "skip to content" links:
<a href="#" class="sr-only focus:not-sr-only">
Skip to content
</a>
You can customize which variants are generated by adding an accessibility key to the variants section of your config file:
// tailwind.config.js
module.exports = {
// ...
variants: {
accessibility: ['responsive', 'hover', 'focus', 'active']
}
}
<a name="added-utilities-for-placeholder-color"></a>
Tailwind now includes placeholder-{color} utilities for setting the placeholder color of form elements:
<input class="text-gray-900 placeholder-gray-500 ...">
By default, responsive and focus variants are generated for these utilities. You can customize which variants are generated by adding a placeholderColor key to the variants section of your config file:
// tailwind.config.js
module.exports = {
// ...
variants: {
placeholderColor: ['responsive', 'hover', 'focus', 'active']
}
}
<a name="first-last-even-and-odd-child-variants"></a>
Tailwind now includes variants for targeting the first-child, last-child, nth-child(odd), and nth-child(even) pseudo-classes.
These allow you to apply a utility to an element only when it is the first, last, odd, or even child of its parent. Very useful for things like "put a border between all of these items that are generated in a loop" for example:
<ul>
<li v-for="item in items" class="border-t first:border-t-0">{{ item }}</li>
</ul>
...or to add zebra striping to a table:
<table>
<tr v-for="row in rows">
<td class="odd:bg-white even:bg-gray-200">...</td>
<td class="odd:bg-white even:bg-gray-200">...</td>
</tr>
</table>
The pseudo-classes map to variants as follows:
| Pseudo-class | Variant |
|---|---|
:first-child |
first:{utility} |
:last-child |
last:{utility} |
:nth-child(odd) |
odd:{utility} |
:nth-child(even) |
even:{utility} |
Something worth emphasizing is that these variants apply to the child element itself, not to the children of the element with the utility. This is consistent with how other pseudo-class variants in Tailwind work, and how the :first/last-child pseudo selectors work in CSS.
Said again in code:
<!-- This is *not* how these variants are meant to be used -->
<ul class="first:border-t-0">
<li v-for="item in items" class="border-t">{{ item }}</li>
</ul>
<!-- The utilities should be used on the child itself, not the parent -->
<ul>
<li v-for="item in items" class="border-t first:border-t-0">{{ item }}</li>
</ul>
These variants are disabled by default for all utilities, but can be enabled for each core plugin in the variants section of your config file:
// tailwind.config.js
module.exports = {
// ...
variants: {
- backgroundColor: ['responsive', 'hover', 'focus']
+ backgroundColor: ['responsive', 'first', 'last', 'even', 'odd', 'hover', 'focus']
}
}
<a name="disabled-variant"></a>
Tailwind now includes a disabled variant for styling elements when they are disabled:
<input class="disabled:opacity-50 ...">
This variant is disabled by default for all utilities, but can be enabled for each core plugin in the variants section of your config file:
// tailwind.config.js
module.exports = {
// ...
variants: {
- opacity: ['responsive', 'hover', 'focus']
+ opacity: ['responsive', 'hover', 'focus', 'disabled']
}
}
<a name="visited-variant"></a>
Tailwind now includes a visited variant for styling visited links:
<a href="#" class="text-blue-500 visited:text-purple-500 ...">
This variant is disabled by default for all utilities, but can be enabled for each core plugin in the variants section of your config file:
// tailwind.config.js
module.exports = {
// ...
variants: {
- textColor: ['responsive', 'hover', 'focus']
+ textColor: ['responsive', 'hover', 'focus', 'visited']
}
}
<a name="increase-utility-specificity-using-a-scope-instead-of-important"></a>
!important (#1020)Prior to Tailwind v1.1, you may have used the important option to make sure that no matter what, your utilities always took precedence over any other styles applied to an element:
// tailwind.config.js
module.exports = {
important: true,
// ...
}
This is a totally reasonable thing to do but it can introduce some issues when incorporating third-party JS libraries that add inline styles to your elements, because Tailwind's important utilities would defeat the inline styles. This is really common with animation libraries for example.
Tailwind v1.1 adds the ability to make utilities "important" in a less aggressive manner by providing a selector instead of a boolean to the important option:
// tailwind.config.js
module.exports = {
important: '#app',
// ...
}
What this will do is prefix all of your utilities with that selector, increasing their specificity without actually making them !important.
By using an ID for this selector and adding that ID to the root element of your site, all of Tailwind's utilities will have a high enough specificity to defeat all other classes used in your project without interfering with inline styles:
<html>
<!-- ... -->
<style>
.high-specificity .nested .selector {
color: blue;
}
</style>
<body id="app">
<!-- Will be #bada55 -->
<div class="high-specificity">
<div class="nested">
<!-- Will be red-500 -->
<div class="selector text-red-500"><!-- ... --></div>
</div>
</div>
<!-- Will be #bada55 -->
<div class="text-red-500" style="color: #bada55;"><!-- ... --></div>
</body>
</html>
If this seems weird or complicated to you, chances are you haven't run into this situation before and can just ignore this feature. If you've been bitten by this problem in the past though, you'll understand exactly why this feature was added.
<a name="add-hover-focus-variants-for-opacity-by-default"></a>
Opacity utilities now have hover and focus variants enabled by default:
<div class="opacity-50 hover:opacity-100"><!-- ... --></div>
You can disable these if needed by overriding the default opacity variants in the variants section of your config file:
// tailwind.config.js
module.exports = {
// ...
variants: {
+ opacity: ['responsive']
}
}
<a name="added-border-double-utility"></a>
border-double utility (#1040)Tailwind now includes a border-double utility for, well, giving elements a double border.
<div class="border border-double"><!-- ... --></div>
<a name="support-negative-prefix-for-box-shadow-and-letter-spacing-plugins"></a>
The boxShadow and letterSpacing plugins now support the negative modifier prefix like zIndex, margin, and inset utilities do:
// tailwind.config.js
module.exports = {
theme: {
letterSpacing: {
'-1': '-.05em',
},
boxShadow: {
'-sm': 'inset 0 2px 4px rgba(0,0,0.1)',
},
}
}
This would generate classes like -tracking-1 and -shadow-sm, rather than tracking--1 and shadow--sm like you might expect.
<a name="support-passing-config-path-via-object"></a>
When adding Tailwind to your PostCSS config, you can now specify the config file path using an object syntax instead of only a string:
// postcss.config.js
module.exports = {
plugins: [
// Existing syntax:
require('tailwindcss')('custom-config.js'),
// Added syntax:
require('tailwindcss')({ config: 'custom-config.js' }),
]
}
This makes Tailwind compatible with PostCSS's object configuration syntax, which wasn't previously possible:
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: { config: 'custom-config.js' },
}
}
See the pull request for all of the details on how this works.
<a name="fixes"></a>
<a name="placeholders-no-longer-have-a-default-opacity"></a>
Prior to v1.1, Tailwind included the following base styles for form elements:
input::placeholder, textarea::placeholder {
color: inherit;
opacity: 0.5;
}
Due to a bug in IE11, this mistakenly caused the input and textarea elements themselves to be rendered at 50% opacity, not just the placeholders.
We've fixed this in v1.1 by changing the base styles to this:
input::placeholder, textarea::placeholder {
color: #a0aec0;
}
This sets the default placeholder to Tailwind's gray-500 color instead of inheriting the current color and changing the opacity.
This means that if you weren't assigning a custom placeholder color to your form elements, they will now look a bit different than they did before.
This will be most apparent in situations where you have changed the text color of an input and were relying on the inherit behavior — for example an input with red text where you also want the placeholder to be red.
Now that Tailwind includes placeholder color utilities, you can correct these superficial visual differences by adding a placeholder utility:
<input class="text-red-500 placeholder-red-300">
<a name="make-horizontal-rules-visible-by-default"></a>
Prior to v1.1, horizontal rules were mistakenly invisible in Tailwind because they had no border-width assigned.
Now hr elements have a default border-width of 1px so they actually show up when you create one.
This is technically a very minor breaking change in the event that you were actually depending on hr elements not having a default border. You can remove the border by adding border-0:
<hr class="border-0">
<a name="generate-correct-negative-margins-when-using-calc"></a>
Negative margin values were calculated fairly naively in previous versions of Tailwind by simply prefixing the positive value with a -. This of course didn't work if you were using more complex values like calc(100vw - 10rem) or var(--spacing-sm).
Tailwind v1.1 fixes this issue by using calc and the reduce-css-calc package to calculate the correct value to use.
Read more about it in the pull request.
Fixes issue where modifiers would mutate nested rules
Support built-in variants for utilities that include pseudo-elements
!important directly in Tailwind utility plugins!important directly in Tailwind utility pluginsIncrease precision of percentage width values to avoid 1px rounding issues in grid layouts
Throws an error when someone tries to use @tailwind preflight instead of @tailwind base, this is the source of many support requests
@tailwind preflight instead of @tailwind base, this is the source of many support requestsFixes a bug where @screen rules weren't bubbled properly when nested in plugins
@screen rules weren't bubbled properly when nested in plugins (#941)Fixes a bug where global variants weren't properly merged
No release notes
No release notes
Use 9999 and -9999 for order-last and order-first utilities respectively
9999 and -9999 for order-last and order-first utilities respectively (#906)Add bg-repeat-round and bg-repeat-space utilities
bg-repeat-round and bg-repeat-space utilities (#879)select-all and select-auto utilities (#885, https://github.com/tailwindcss/tailwindcss/commit/0fac54f8f11ddeb97ace42cd018d8bec603246b3)bg-repeat-round and bg-repeat-space utilitiesselect-all and select-auto utilitiesAdds responsive variants for the new order utilities by default, should have been there all along
responsive variants for the new order utilities by default, should have been there all alongFixes a bug where you couldn't extend the margin config
Removed negativeMargin plugin, now the regular margin plugin supports generating negative classes (like -mx-6) by using negative keys in the config, l
negativeMargin plugin, now the regular margin plugin supports generating negative classes (like -mx-6) by using negative keys in the config, like -6 (#865, upgrade guide)-top-6, -right-4) and z-index (-z-10) utilities, using the same negative key syntax supported by the margin plugin (#867, #875)order utilities (#693)cursor-text class by default (#795)-top-6, -right-4) and z-index (-z-10) utilities, using the same negative key syntax supported by the margin pluginorder utilitiescursor-text class by defaultnegativeMargin plugin, now the regular margin plugin supports generating negative classes (like -mx-6) by using negative keys in the config, like -6Fix a bug where stroke and fill plugins didn't properly handle the next object syntax for color definitions
@apply directives (#847)corePlugins: false (#849)corePlugins: false@apply directivesAdd the container key to the scaffolded config file when generated with --full
container key to the scaffolded config file when generated with --full (#792)extend (#803)SFMono-Regular from the beginning of the default monospace font stack, it has no italic support and Menlo looks better anyways (#805)container key to the scaffolded config file when generated with --fullSFMono-Regular from the beginning of the default monospace font stack, it has no italic support and Menlo looks better anywaysextendSupport lazy evaluation in theme.extend: #775
theme.extend: #775bolder for strong tags by default instead of fontWeight.bold: #782theme.extendbolder for strong tags by default instead of fontWeight.boldFix issue where @screen didn't work at all 🙃
@screen didn't work at all 🙃(#773)theme section of the config file are now passed a theme function instead of an object (#774)theme section of the config file are now passed a theme function instead of an object@screen didn't work at all 🙃Now that all of Tailwind's internal "modules" are actually just core plugins, I've decided to deprecate this terminology entirely, and make this secti…
It's here! 🎉
This release of Tailwind focuses mostly on changing things from 0.x that I would have done differently had I known where the feature set would be at today in advance.
So while there's not a ton of exciting new features, you can at least be excited about the fact that we now have a really stable base to build on, and that very soon we'll be out of the unpredictable pre-1.0 phase so you can feel comfortable using Tailwind in production if the 0.x label gave you pause.
maxWidth scale: https://github.com/tailwindcss/tailwindcss/pull/701object-position utilities are now customizable under theme.objectPosition: https://github.com/tailwindcss/tailwindcss/pull/676cursor utilities are now customizable under theme.cursors: https://github.com/tailwindcss/tailwindcss/pull/679flex-grow/shrink utilities are now customizable under theme.flexGrow/flexShrink: https://github.com/tailwindcss/tailwindcss/pull/690list-style-type and list-style-position: https://github.com/tailwindcss/tailwindcss/pull/761break-all utility: https://github.com/tailwindcss/tailwindcss/pull/763The documentation is still very much a work-in-progress (half of it is probably broken), but you can see the v1.0 documentation in its current state here:
https://next.tailwindcss.com/docs/what-is-tailwind/
If you notice any stale content, a pull request would be awesome:
https://github.com/tailwindcss/docs
Make sure you target the next branch.
Note: Some things have changed in later beta releases. If you are upgrading to the latest beta and not specifically to beta.1, follow the upgrade guide that's in the documentation: https://next.tailwindcss.com/docs/upgrading-to-v1
Steps that impact all users:
tailwind.js to tailwind.config.js@tailwind preflight with @tailwind baseconfig() with theme().list-reset.pin-{side} with .{top|left|bottom|right|inset}-{value}.roman with .not-italic.flex-no-grow/shrink with .flex-grow/shrink-0inline to any replaced elements (img, video, etc.) that should not be display: blockAdditional steps for CDN users, or anyone that has a true dependency on our default configuration either by omitting sections from their config file, referencing our config file, or not using a config file at all:
text/bg/border-{color} classestracking-tight/wide with tracking-tighter/widershadow-{size} utilitiesmax-w-{size} utilitiesAdditional steps for plugin authors:
<a name="update-tailwind"></a>
While v1.0 is still in a pre-release state, you can pull it in to your project using npm:
npm install tailwindcss@next --save-dev
Or using Yarn:
yarn add -D tailwindcss@next
<a name="update-your-config-file"></a>
Impact: All users, Effort: Moderate
This is really the big change in v1.0 — you can read all about the new config file format and motivation behind it in the initial pull request.
The new general config structure looks like this:
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: {
colors: { ... },
// ...
zIndex: { ... },
},
variants: {
appearance: ['responsive'],
// ...
zIndex: ['responsive'],
},
plugins: [
// ...
],
}
See the new default config file for a complete example.
There are a lot of changes here but they are all fairly cosmetic and entirely localized to this one file, so while it may look intimidating it's actually only 10-15 minutes of work.
Move all design-related top-level keys into a new section called theme.
Every key except options, modules, and plugins should be nested under a new theme key.
Your config file should look generally like this at this point:
let defaultConfig = require('tailwindcss/defaultConfig')()
let colors = {
// ...
}
module.exports = {
- colors: colors,
- screens: {
- // ...
- },
- // ...
- zIndex: {
- // ...
- },
+ theme: {
+ colors: colors,
+ screens: {
+ // ...
+ },
+ // ...
+ zIndex: {
+ // ...
+ },
+ },
modules: {
appearance: ['responsive'],
// ...
zIndex: ['responsive'],
},
plugins: [
require('tailwindcss/plugins/container')({
// ...
}),
],
options: {
prefix: '',
important: false,
separator: ':',
}
}
Rename modules to variants.
"Modules" was a word we just kinda grabbed because we needed something, and we wanted to use that section of the config to both specify variants and disable modules if necessary.
Now that all of Tailwind's internal "modules" are actually just core plugins, I've decided to deprecate this terminology entirely, and make this section of the config purely about configuring variants for core plugins.
After making this change, your config file should look like this:
let defaultConfig = require('tailwindcss/defaultConfig')()
let colors = {
// ...
}
module.exports = {
theme: {
// ...
},
- modules: {
+ variants: {
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
backgroundColors: ['responsive', 'hover', 'focus'],
// ...
zIndex: ['responsive'],
},
plugins: [
require('tailwindcss/plugins/container')({
// ...
}),
],
options: {
prefix: '',
important: false,
separator: ':',
}
}
Move your options settings to the top-level.
The advanced options have been moved to the top-level of the config file instead of being nested under the redundant options key.
After making this change, your config file should look like this:
let defaultConfig = require('tailwindcss/defaultConfig')()
let colors = {
// ...
}
module.exports = {
+ prefix: '',
+ important: false,
+ separator: ':',
theme: {
// ...
},
variants: {
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
backgroundColors: ['responsive', 'hover', 'focus'],
// ...
zIndex: ['responsive'],
},
plugins: [
require('tailwindcss/plugins/container')({
// ...
}),
],
- options: {
- prefix: '',
- important: false,
- separator: ':',
- }
}
Update the sections under theme to their new names.
As part of an effort to make the naming in the config file more consistent, many of the sections under theme have been renamed.
These are the sections that need to be updated:
| Old | New |
|---|---|
fonts |
fontFamily |
textSizes |
fontSize |
fontWeights |
fontWeight |
leading |
lineHeight |
tracking |
letterSpacing |
textColors |
textColor |
backgroundColors |
backgroundColor |
borderWidths |
borderWidth |
borderColors |
borderColor |
shadows |
boxShadow |
svgFill |
fill |
svgStroke |
stroke |
These names need to change in the variants section as well, so feel free to do a find and replace across the whole file.
Update the sections under variants to their new names.
As alluded to in the previous step, many of the sections under variants have been renamed as well.
These are the sections that need to be renamed (it is the same as the list above):
| Old | New |
|---|---|
fonts |
fontFamily |
textSizes |
fontSize |
fontWeights |
fontWeight |
leading |
lineHeight |
tracking |
letterSpacing |
textColors |
textColor |
backgroundColors |
backgroundColor |
borderWidths |
borderWidth |
borderColors |
borderColor |
shadows |
boxShadow |
svgFill |
fill |
svgStroke |
stroke |
Several sections under variants have also been split into multiple sections, for example lists has been split into listStylePosition and listStyleType:
// ...
module.exports = {
// ...
variants: {
// ...
- lists: ['responsive'],
+ listStylePosition: ['responsive'],
+ listStyleType: ['responsive'],
}
}
Here is a complete list of the sections that been split into multiple sections:
| Old | New |
|---|---|
flexbox |
flexDirection, flexWrap, alignItems, alignSelf, justifyContent, alignContent, flex, flexGrow, flexShrink |
lists |
listStylePosition, listStyleType |
position |
position, inset |
textStyle |
fontStyle, fontSmoothing, textDecoration, textTransform |
whitespace |
whitespace, wordBreak |
Note that in some cases (position, whitespace) the original section still exists, while in others (flexbox, textStyle), the original section has been completely removed.
You should reference the new default config file if you are ever unsure if you are making the right changes.
The simplest way to make these changes is to just copy the value you were using for the old section (something like ['responsive']) to all of the new sections that replace that section, but if you choose you can also use this as an opportunity to cull generated utilities you don't actually need.
For example, if you never use the responsive variants of antialiased or subpixel-antialiased, you could set fontSmoothing to [] while still using ['responsive'] for fontStyle, textDecoration, and textTransform.
Add any disabled modules core plugins to corePlugins.
In v0.x, you could disable a module core plugin by setting it to false in what is now the variants section.
In v1.0, to disable a plugin you need to set it to false in the corePlugins section instead:
let defaultConfig = require('tailwindcss/defaultConfig')()
let colors = {
// ...
}
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: {
// ...
},
variants: {
// ...
- float: false,
// ...
},
+ corePlugins: {
+ float: false,
+ },
plugins: [
require('tailwindcss/plugins/container')({
// ...
}),
],
}
This change was made to make it possible to disable other core plugins where variants are irrelevant, like preflight or container (more on this later).
Remove the container plugin from plugins and move any configuration to theme.
In v1.0, the container plugin is a core plugin just like padding, margin, etc. and should not be listed in your plugins section:
let defaultConfig = require('tailwindcss/defaultConfig')()
let colors = {
// ...
}
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: {
// ...
},
variants: {
// ...
},
plugins: [
- require('tailwindcss/plugins/container')({
- center: true,
- padding: '1rem',
- }),
],
}
If you had already removed the container plugin because you don't want those classes in your project, you should explicitly disable it using the corePlugins option:
let defaultConfig = require('tailwindcss/defaultConfig')()
let colors = {
// ...
}
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: {
// ...
},
variants: {
// ...
},
+ corePlugins: {
+ container: false
+ },
}
If you are taking advantage the center or padding options exposed by the container plugin, you should specify those options under theme.container instead of as direct arguments to the plugin.
let defaultConfig = require('tailwindcss/defaultConfig')()
let colors = {
// ...
}
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: {
// ...
+ container: {
+ center: true,
+ padding: '1rem',
+ }
},
variants: {
// ...
},
plugins: [
- require('tailwindcss/plugins/container')({
- center: true,
- padding: '1rem',
- }),
- ],
}
Inline your colors variable into theme.colors.
In v1.0, it's possible to specify that parts of your theme depend on other parts of your theme, and because of that it's no longer necessary to hold your colors in a separate variable.
Start by inlining your colors variable directly into theme.colors:
let defaultConfig = require('tailwindcss/defaultConfig')()
- let colors = {
- 'transparent': 'transparent',
- 'black': '#22292f',
- // ...
- 'pink-lightest': '#ffebef','
- }
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: {
- colors: colors,
+ colors: {
+ 'transparent': 'transparent',
+ 'black': '#22292f',
+ // ...
+ 'pink-lightest': '#ffebef','
+ },
// ...
},
variants: {
// ...
},
plugins: [],
}
Next, update any sections that were referencing the colors variable using the new closure syntax:
let defaultConfig = require('tailwindcss/defaultConfig')()
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: {
colors; {
'transparent': 'transparent',
'black': '#22292f',
// ...
'pink-lightest': '#ffebef','
},
// ...
- backgroundColor: colors,
+ backgroundColor: theme => theme('colors'),
// ...
- textColor: colors,
+ textColor: theme => theme('colors'),
// ...
- borderColor: global.Object.assign({ default: colors['grey-light'] }, colors),
+ borderColor: theme => ({
+ default: theme('colors.grey-light'),
+ ...theme('colors'),
+ }),
// ...
},
variants: {
// ...
},
plugins: [],
}
Don't invoke the default config as a function.
In v0.x, require('tailwindcss/defaultConfig') returned a function that returned the default config when invoked.
In v1.0, it simply returns the object:
- let defaultConfig = require('tailwindcss/defaultConfig')()
+ let defaultConfig = require('tailwindcss/defaultConfig')
module.exports = {
prefix: '',
important: false,
separator: ':',
theme: {
// ...
},
variants: {
// ...
},
plugins: [],
}
Remove any configuration you haven't customized.
One of the philosophical changes in v1.0 is that we are encouraging people to use their configuration files solely for specifying changes from the default config, rather than including the entire default config plus their changes.
Every single key in the config file is optional (in fact the file itself is optional too), so if there are things you've never customized, you're encouraged to remove them entirely.
For example, if you aren't specifying a custom separator or prefix or enabling the important option, you can remove them entirely:
let defaultConfig = require('tailwindcss/defaultConfig')
module.exports = {
- prefix: '',
- important: false,
- separator: ':',
theme: {
// ...
},
variants: {
// ...
},
plugins: [],
}
Similarly, if you aren't referencing the defaultConfig variable anywhere, remove that too:
- let defaultConfig = require('tailwindcss/defaultConfig')
module.exports = {
theme: {
// ...
},
variants: {
// ...
},
plugins: [],
}
If you haven't customized the opacity values, remove them:
module.exports = {
theme: {
// ...
- opacity: {
- '0': '0',
- '25': '.25',
- '50': '.5',
- '75': '.75',
- '100': '1',
- },
// ...
},
variants: {
// ...
},
plugins: [],
}
We will not change any of this configuration outside of a major version bump, so you are totally safe to depend on inheriting the default values.
The way your configuration is merged with the defaults is designed to be very intuitive and mostly just work, but for the curious:
prefix is replacedseparator is replacedimportant is replacedtheme is merged one level deep, so if you provide an object for theme.opacity it replaces the default theme.opacity objectvariants is merged one level deep, so if you provide an array for variants.opacity it replaces the default variants.opacity objectplugins is merged, but the default is an empty array so it's really the same as replacingIt's worth noting that you are not required to remove any redundant configuration, so if you'd prefer to own the entire system and be able to see it all in one place, you're absolutely welcome to keep everything in your config file.
It's very important to realize that many of the theme values have changed from v0.7.4 to v1.0, so just because you never customized a value that shipped by default in v0.x, that doesn't guarantee that you are safe to remove it from your config file.
A perfect example of this is colors. The default color palette is completely new in v1.0 with a new naming scheme, so even if you were using the default color palette in v0.x, you're actually using a custom color palette in v1.0.
Always double check that anything you want to remove is identical to the new default config file values before you remove it.
<a name="rename-tailwind-js-to-tailwind-config-js"></a>
tailwind.js to tailwind.config.jsImpact: N/A, Effort: Trivial
This is entirely optional but recommended — if you are using the old default config file name (tailwind.js), rename it to tailwind.config.js.
If you use that file name and keep the file in the root of your project, Tailwind will pick up your config file by default without having to specify the path in your build scripts/configuration.
Here's an example of what I mean using Laravel Mix:
mix.postCss('resources/css/app.css', 'public/css/app.css', [
- require('tailwindcss')('./tailwind.js'),
+ require('tailwindcss'),
])
If you keep your config file in a different folder, you'll still need to provide the path:
mix.postCss('resources/css/app.css', 'public/css/app.css', [
- require('tailwindcss')('./resources/tailwind.js'),
+ require('tailwindcss')('./resources/tailwind.config.js'),
])
<a name="replace-preflight-with-base"></a>
@tailwind preflight with @tailwind baseImpact: All users, Effort: Trivial
One of the new features in v1.0 is the ability for plugins to register base styles. As a result, our preflight styles are actually just another core plugin now, and the general "bucket" for base styles has been renamed from preflight to base.
Replace any instance of @tailwind preflight in your CSS files with @tailwind base:
- @tailwind preflight;
+ @tailwind base;
@tailwind components;
@tailwind utilities;
If you are using postcss-import and relying on our imports instead of the @tailwind directive, replace @import "tailwindcss/preflight" with @import "tailwindcss/base":
- @import "tailwindcss/preflight";
+ @import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
<a name="replace-config-with-theme"></a>
config() with theme()Impact: Moderate, Effort: Low
The config() helper function that Tailwind makes available to your CSS files has been replaced with a new theme() function that is automatically scoped to the theme section of your config file and should work as a drop-in replacement:
.btn {
- padding: config('padding.3');
+ padding: theme('padding.3');
// ...
}
A simple find and replace across your CSS files that switches config( to theme( should do it.
<a name="explicitly-style-any-headings"></a>
Impact: Moderate, Effort: Moderate
If you are using our preflight styles, all h1-h6 elements are unstyled by default in v1.0.
That means that out of the box, they all have a font-size of 1em (whatever the parent font size is) and a font-weight of inherit, so they look exactly like a p tag.
This might sound dumb at first, but in web application development it's very common for some piece of text to be a heading semantically, but actually be styled in a much less "in your face" way because it's meant to look more like a subtle label on a section of UI.
By using the user agent styles for headings, we also made it far too easy to accidentally deviate from your own design system. If the browser says that an h1 should be 2em, it could compute to a size that isn't part of your fontSize scale.
By unstyling headings by default, we make it a lot easier to avoid this pitfall by ensuring that any size or weight you set is explicit and intentional.
This change might not affect you at all if you are already specifying a font-weight and font-size on all your headings, but if you aren't, you just need to assign an explicit size and weight wherever it's missing:
- <h1>Manage Account<h1>
+ <h1 class="text-xl font-semibold">Manage Account<h1>
The exact changes you need to make will be highly specific to what you want to accomplish with your design, so you'll have to assess each situation independently.
This is a bit of an annoying change, but if it breaks your site, you could argue that it's actually revealing bugs in your markup.
<a name="explicitly-style-any-lists"></a>
Impact: Moderate, Effort: Moderate
If you are using our preflight styles, all ul and ol elements are unstyled by default in v1.0.
That means if you have any lists that depend on the default browser styling (bullets/numbers and a bit of left padding), you need to explicitly style those lists using the new .list-disc/decimal utilities and the existing padding utilities:
- <ul>
+ <ul class="list-disc pl-4">
<!-- ... -->
</ul>
If you really don't want to do this manually and would prefer that lists be styled by default, you can override our base styles with your own custom CSS by adding a couple of rules like this:
@tailwind base;
ul {
list-style-type: disc;
padding-left: theme('padding.4');
}
ol {
list-style-type: decimal;
padding-left: theme('padding.4');
}
@tailwind components;
@tailwind utilities;
<a name="remove-any-usage-of-list-reset"></a>
.list-resetImpact: Moderate, Effort: Low
Since lists are now unstyled by default, .list-reset has been removed. You technically don't need to change anything, but you're encouraged to remove any usage of it as it's now just dead code:
- <ul class="list-reset"><!-- ... --></ul>
+ <ul><!-- ... --></ul>
If you chose override our base styles and give lists a default style, you can use the new .list-none utility as well as .p-0 as a replacement for .list-reset to remove that base styling as needed:
- <ul class="list-reset"><!-- ... --></ul>
+ <ul class="list-none p-0"><!-- ... --></ul>
Again, if you are using our preflight styles unmodified (you probably are), you can simply remove list-reset from your markup and nothing will change.
This change only really affects you if you are not using our preflight styles, or overriding our global list reset.
<a name="replace-pin-with-inset"></a>
.pin-{side} with .{top|left|bottom|right|inset}-{value}Impact: High, Effort: Moderate
Utilities like .pin, .pin-x, and .pin-t have been removed in favor of less cleverly named classes like .top-0, .right-0, etc.
See the pull request for more details on the motivation behind this change.
Here is a complete list of the changes:
| Old | New |
|---|---|
.pin-none |
.inset-auto |
.pin |
.inset-0 |
.pin-y |
.inset-y-0 |
.pin-x |
.inset-x-0 |
.pin-t |
.top-0 |
.pin-r |
.right-0 |
.pin-b |
.bottom-0 |
.pin-l |
.left-0 |
Six new classes have been added as well:
| Class |
|---|
.inset-y-auto |
.inset-x-auto |
.top-auto |
.right-auto |
.bottom-auto |
.left-auto |
These are all now customizable in theme.inset too, whereas the pin-{side} utilities were not.
This is an annoying change, sorry.
<a name="replace-roman-with-not-italic"></a>
.roman with .not-italicImpact: Low, Effort: Low
Previously we used the name .roman for font-style: normal because of a bug in postcss-selector-not that prevented us from using .not-italic. That bug has been fixed, so this name has been changed.
- <div class="roman">
+ <div class="not-italic">
<!-- ... -->
</div>
I would be surprised if more than 5 people are even affected by this, I've never used this class once myself.
<a name="replace-flex-no-grow-shrink-with-flex-grow-shrink-0"></a>
.flex-no-grow/shrink with .flex-grow/shrink-0Impact: High, Effort: Low
In order to make these utilities more easily customizable, their names have changed to match our existing conventions.
- <div class="flex-no-grow">
+ <div class="flex-grow-0">
<!-- ... -->
</div>
- <div class="flex-no-shrink">
+ <div class="flex-shrink-0">
<!-- ... -->
</div>
These utilities are also now customizable in the theme.flexGrow and theme.flexShrink sections of your config file.
<a name="explicitly-add-color-and-underline-styles-to-links"></a>
Impact: High, Effort: Moderate
In v1.0, a tags automatically inherit the parent color and text-decoration styles which means that by default links are no longer blue and do not have an underline.
You are likely already adding a text color class like text-green-dark or similar to your links because you probably didn't want the default browser-blue color, but if not you'll need to add a color explicitly:
- <a href="#">
+ <a href="#" class="text-blue">
<!-- ... -->
</a
Similarly, if you have any links that need underlines, you'll have to add them manually:
- <a href="#">
+ <a href="#" class="underline">
<!-- ... -->
</a
On the flip side, if you are using no-underline in a million places across your project just to unstyle links, you can now safely remove that class:
- <a href="#" class="no-underline">
+ <a href="#">
<!-- ... -->
</a
If you really don't like these new defaults, you can add your own base link styles after @tailwind base:
@tailwind base;
a {
color: theme('colors.blue');
text-decoration: underline;
}
@tailwind components;
@tailwind utilities;
<a name="add-inline-to-any-replaced-elements-img-video-etc-that-should-not-be-display-block"></a>
inline to any replaced elements (img, video, etc.) that should not be display: blockImpact: Moderate, Effort: Moderate
In v1.0, all replaced elements (like img, svg, video, canvas, iframe, etc.) are set to display: block by default. This is counter to the browser default which is inline.
If you have any instances in your project where you actually want these elements to be inline, you'll need to add that class:
<span>
- <img src="..." class="h-4 w-4">
+ <img src="..." class="h-4 w-4 inline">
Manage
</span>
I don't think this will actually affect many people or projects, as you almost always want these elements to be block or you have them nested inside a flex container where it doesn't matter.
<a name="adjust-the-line-height-and-padding-on-your-form-elements"></a>
Impact: High, Effort: Moderate
If you are already setting an explicit line-height on form elements, this change will not affect you.
In v0.x, we used a line-height of 1.15 for form elements by default, sort of incidentally by depending on normalize.css.
This made it very easy to forget to add an explicit line-height like leading-tight or leading-normal to form elements, introducing a new line-height (1.15) into your project that doesn't match any of the leading-{size} utilities.
In v1.0, all form elements use a value of inherit for their line-height, so the line-height will match the parent element by default.
That means if you had some markup like this:
<div class="leading-normal ...">
<!-- ... -->
<input type="text" class="px-4 py-3">
</div>
...your input element will be slightly taller in v1.0 because the line-height has increased from 1.15 to 1.5.
You can fix this by adjusting any vertical padding to account for the new line-height, and optionally adding an explicit leading-{size} class if you don't want to match the line-height of the parent:
<div class="leading-normal ...">
<!-- ... -->
- <input type="text" class="px-4 py-3">
+ <input type="text" class="px-4 py-2 leading-tight">
</div>
You might not get the exact same height you had before, but that's likely because the old height was some weird fractional number like 42.4px (line-height of 1.15 * font-size of 16px + 24px of padding). With the new system you are much more likely to land on reasonable whole numbers, like 40px of 44px, depending on your chosen line-height and padding values.
If you really want to use 1.15 as your default line-height for form elements (I would recommend against it), you can add a rule like this to your own base styles:
@tailwind base;
button,
input,
optgroup,
select,
textarea {
line-height: 1.15;
}
@tailwind components;
@tailwind utilities;
<a name="adjust-the-text-color-on-your-form-elements"></a>
Impact: Low, Effort: Moderate
If you are already setting an explicit text color on form elements, this change will not affect you.
In v0.x, form elements used black text by default, even though true black was not part of the default color palette.
In v1.0, form elements inherit their text color from the parent, which means if you have any markup like this:
<div class="text-red">
<input type="text">
</div>
...your input would have red text instead of black text.
You can fix this by setting a text color on form elements explicitly:
<div class="text-red">
- <input type="text">
+ <input type="text" class="text-grey-darkest">
</div>
<a name="double-check-your-default-font-family"></a>
Impact: Low, Effort: Trivial
If you are already setting a default font family on your project (either with a class on html/body or using custom CSS), this change will not affect you.
In v1.0, the default font family has changed from sans-serif to our system font stack.
It's very unlikely that you weren't already overriding this with your own font, but if not you'll notice your site looks a bit different, and honestly probably better.
You don't really have to change anything unless for some unexplainable reason you want to use sans-serif as your default font family, in which case you can add a rule to your base styles:
@tailwind base;
html {
font-family: sans-serif;
}
@tailwind components;
@tailwind utilities;
<a name="double-check-your-default-line-height"></a>
Impact: Moderate, Effort: Moderate
If you are already setting a default line-height on your project (either with a class on html/body or using custom CSS), this change will not affect you.
In v0.x, the default line-height was 1.15 (inherited from normalize.css). Since that value isn't part of Tailwind's default theme, we've opted to change it to 1.5 for v1.0 so the default line-height matches a value in the line-height scale.
This means that if you are not setting a line-height either using a leading-{size} class on the html or body tags or by adding some base styles to your CSS, most things on your site are going to appear a little bit taller.
The easiest solution is to reset the line-height to 1.15 by default:
@tailwind base;
html {
line-height: 1.15;
}
@tailwind components;
@tailwind utilities;
However, a better long-term solution would be to pick a default line-height that matches a value in your line-height scale, and audit your site to find situations where it makes the design look worse and tweak those one at a time.
<a name="update-any-usage-of-text-bg-border-color-classes"></a>
text/bg/border-{color} classesImpact: Low, Effort: High
This change only affects you if you don't have a color palette defined in your config file or you are using Tailwind through a CDN.
Tailwind v1.0 comes with an entirely new color palette that provides 9 shades for each color instead of 7 (#737).
The naming scheme has changed from using words like darkest and lighter to a numeric scaled inspired by Material Design that starts at 100 for the lightest shade and ends at 900 for the darkest shade.
There is no way to map the old colors to the new colors 1:1 because the new palette includes more shades, so if you are using the v0.x default color palette and would like to upgrade to the new color palette, you are in for some fun (you're not).
I would recommend starting with the following substitutions and then adjusting colors up or down a shade on a case-by-case basis as you feel is needed.
For greys (note that grey has changed to gray 🇺🇸):
| Old | New |
|---|---|
| black | gray-900 |
| grey-darkest | gray-800 |
| grey-darker | gray-700 |
| grey-dark | gray-600 |
| grey | gray-500 |
| grey-light | gray-400 |
| grey-lighter | gray-200 |
| grey-lightest | gray-100 |
For other colors:
| Old | New |
|---|---|
| {color}-darkest | {color}-900 |
| {color}-darker | {color}-800 |
| {color}-dark | {color}-600 |
| {color} | {color}-500 |
| {color}-light | {color}-400 |
| {color}-lighter | {color}-200 |
| {color}-lightest | {color}-100 |
Again, this change only affects you if you do not have your own color palette specified in your config file, or you are using the default Tailwind build through a CDN. If you are using the v0.x color palette in your project, you can absolutely keep using it. You do not need to make these changes unless you have a hard dependency on our default color palette in some way.
<a name="replace-tracking-tight-wide-with-tracking-tighter-wider"></a>
tracking-tight/wide with tracking-tighter/widerImpact: Low, Effort: Low
This change only affects you if you don't have a tracking/letter-spacing scale defined in your config file or you are using Tailwind through a CDN.
In v1.0, the default letter-spacing scale has changed:
letterSpacing: {
+ tighter: -.05em,
- tight: -.05em,
+ tight: -.025em,
normal: 0,
- wide: .05em,
+ wide: .025em,
+ wider: .05em,
+ widest: .1em,
}
That means that if you want your project to look the same, you'll want to replace any existing occurrences of tracking-tight with tracking-tighter, and tracking-wide with tracking-wider.
Again, this only applies if you do not have a letter-spacing scale defined in your config file or if you are using the default Tailwind build through a CDN.
If you started with a complete config file, your old scale will continue to work the same way in v1.0 and you don't need to make any changes.
<a name="check-your-design-against-the-updated-default-breakpoints"></a>
Impact: Low, Effort: Low
This change only affects you if you don't have screens defined in your config file or you are using Tailwind through a CDN.
The default breakpoints have changed a bit in v1.0:
| Screen | Old | New |
|---|---|---|
| sm | 576px | 640px |
| md | 768px | 768px (unchanged) |
| lg | 992px | 1024px |
| xl | 1200px | 1280px |
If your config file doesn't have any screens defined or you are using the default Tailwind build through a CDN, you'll want to audit your design and make sure that nothing breaks because of these changes. No breakpoints got smaller so you are very unlikely to run into any issues, but it's worth checking either way.
Again, this only applies if you do not have any screens defined in your config file or if you are using the default Tailwind build through a CDN.
If you started with a complete config file, your old screens values will continue to work the same way in v1.0 and you don't need to make any changes.
<a name="double-check-any-usage-of-the-default-shadow-size-utilities"></a>
shadow-{size} utilitiesImpact: Low, Effort: Low
This change only affects you if you don't have a box-shadow scale defined in your config file or you are using Tailwind through a CDN.
Tailwind v1.0 introduces two new box-shadow sizes (xl, and 2xl) and the rest of the shadows have been adjusted as well (#691).
If your config file doesn't have a box-shadow scale defined or you are using the default Tailwind build through a CDN, you should double check that you are still happy with how your shadows look. You may want to replace some instances of lg with xl or 2xl, as the new lg shadow is a bit tighter than the old one.
Again, this only applies if you do not have a box-shadow defined in your config file or if you are using the default Tailwind build through a CDN.
If you started with a complete config file, your old box-shadow values will continue to work the same way in v1.0 and you don't need to make any changes.
<a name="update-any-usage-of-the-default-max-w-size-utilities"></a>
max-w-{size} utilitiesImpact: Low, Effort: Low
This change only affects you if you don't have a max-width scale defined in your config file or you are using Tailwind through a CDN.
Tailwind v1.0 introduces an all-new max-width scale that is much more usable than the previous default max-width scale (#701).
If your config file doesn't have a box-shadow scale defined or you are using the default Tailwind build through a CDN, you should audit your project for any usage of the existing max-w-{size} utilities and change the sizes as needed. In general, the new values are smaller than the old ones, so max-w-md for example may need to be max-w-xl or max-w-2xl in the new scale
Again, this only applies if you do not have a max-width defined in your config file or if you are using the default Tailwind build through a CDN.
If you started with a complete config file, your old max-width values will continue to work the same way in v1.0 and you don't need to make any changes.
<a name="escape-the-class-portion-of-any-custom-variants-you-have-created"></a>
Impact: Low, Effort: Low
In v1.0, you are required to manually escape the class name portion of any selectors you create when adding a new variant using a plugin.
For example:
- function({ addVariant }) {
+ function({ addVariant, e }) {
addVariant('first-child', ({ modifySelectors, separator }) => {
modifySelectors(({ className }) => {
- return `.first-child${separator}${className}:first-child`
+ return `.${e(`first-child${separator}${className}`)}:first-child`
})
})
},
This is just like what you need to do when adding utilities or components that may include user-provided strings.
Unfortunately there is no super simple way to support both v0.x and v1.0 at the same time without checking which version of Tailwind the user has installed and conditionally applying the escape function.
maxWidth scalelist-style-type and list-style-positionbreak-all utilityobject-position utilities are now customizable under theme.objectPositioncursor utilities are now customizable under theme.cursorsflex-grow/shrink utilities are now customizable under theme.flexGrow/flexShrinkUpdate our PostCSS related dependencies
. character had the responsive prefix added in the wrong place (#613).character had the responsive prefix added in the wrong place- Update Normalize to v8.0.1
Add --no-autoprefixer option to CLI build command
--no-autoprefixer option to CLI build command (#584)Update autoprefixer dependency (fixes #583)
Registering new variants from plugins
focus-within variant</a>@apply</a><a name="new-features"></a>
<a name="registering-new-variants-from-plugins"></a>
(Introduced as an experiment in v0.6.2, now promoted to an official feature)
Plugins can now add their own variants (like hover, focus, group-hover, etc.) to Tailwind.
To get started, destructure the new addVariant function from the object passed to your plugin, and call it with the name of the variant you'd like to add and a callback that can be used to manipulate the PostCSS nodes where the variant is being applied:
module.exports = {
plugins: [
function({ addVariant }) {
addVariant('important', ({ container }) => {
container.walkRules(rule => {
rule.selector = `.\\!${rule.selector.slice(1)}`
rule.walkDecls(decl => {
decl.important = true
})
})
})
}
]
}
Documentation is coming soon, but for now learn more in the pull request.
<a name="variant-order-can-be-customized-per-module"></a>
(Introduced as an experiment in v0.6.2, now promoted to an official feature)
Variants are now generated in the order that they are specified in the modules section of your config file, rather than in a hard-coded static order like in previous versions of Tailwind.
That means that if you want focus variants to defeat hover variants for background colors, but you want the opposite behavior for border colors, you can actually do that now by specifying the order in your config:
modules.exports = {
// ...
modules: {
// ...
backgroundColors: ['responsive', 'hover', 'focus'],
// ...
borderColors: ['responsive', 'focus', 'hover'],
// ...
}
}
Note that this doesn't affect responsive variants — those are a special case since responsive versions are also generated for other variants, and we group responsive declarations to optimize the resulting CSS.
<a name="added-focus-within-variant"></a>
focus-within variant (#463)Tailwind now includes a focus-within variant that you can use to change how an element is styled if an element inside of it has focus.
<div class="focus-within:shadow-lg">
<label>
<span>Email</span>
<input type="email">
</label>
</div>
Learn about the :focus-within pseudo-class on MDN
By default we don't generate focus-within variants for any utilities, but you can change this in the modules section your Tailwind configuration file:
modules.exports = {
// ...
modules: {
// ...
- backgroundColors: ['responsive', 'hover', 'focus'],
+ backgroundColors: ['responsive', 'focus-within', 'hover', focus'],
// ...
}
}
<a name="fancy-cli-updates"></a>
Tailwind 0.7.0 includes a completely rewritten CLI tool with nicer output and a better user experience.
All of the existing functionality is still there with the same API, it just looks better.
<a name="option-to-generate-config-without-comments"></a>
You can now use the --no-comments option when running tailwind init to generate a config file that excludes all of the inline documentation comments.
This is a great way to make your config file easier to skim if you're an experienced Tailwind user who doesn't need the comments.
<a name="make-configured-prefix-optional-when-using-apply"></a>
@apply (#553)If you're prefixing your generated utilities, including that prefix when using @apply is now optional.
/* Before */
.my-component {
@apply tw-bg-blue tw-text-white tw-font-bold;
}
/* Now */
.my-component {
@apply bg-blue text-white font-bold;
}
You can continue to use the prefix if you like, or drop it if you prefer a terser syntax.
<a name="improve-flexbox-behavior-in-ie"></a>
IE 10 and 11 interpret the shorthand flex property differently than other browsers.
Tailwind now specifies explicit grow, shrink, and basis values for the flex-1, flex-auto, and flex-initial utilities for a more consistent cross-browser experience.
Learn more at the flexbugs repo (bugs #4 and #6 specifically)
<a name="changes"></a>
<a name="variant-order-in-modules-config-is-now-significant"></a>
Impact: Low, Effort: Low
Prior to 0.7.0, variants were always generated in the same order, regardless of the order specified in the modules section of your config file.
Now, variants are generated in the they are specified. That means that if your config file currently lists variants in a different order than the <=0.6.6 default variant order, those variants will appear in a different order in your CSS.
To preserve the <=0.6.6 behavior, simply edit the modules section of your config file to make sure your variants are listed in the following order:
modules.exports = {
// ...
modules: {
// ...
[anyModule]: ['group-hover', 'hover', 'focus-within', 'focus', 'active']
// ...
}
}
<a name="normalize-updated-to-8"></a>
Impact: Low, Effort: Low
We've updated our dependency on Normalize.css from 7.0.0 to 8.0.0.
This drops support for very old browsers like IE9, Android 4.3, Safari 8, and iOS Safari 7-8.
If you still need to support those browsers, remove @tailwind preflight from your CSS, add Normalize.css 7.0.0 to your project, and manually add our additional preflight base styles.
<a name="removed-css-fix-for-chrome-62-button-border-radius-change"></a>
Impact: Low, Effort: Low
When Chrome 62 was released, it introduced a user agent stylesheet change that added a default border radius to all buttons.
This messed up styles for like half of the internet (including sites like GitHub itself), so Chrome reverted the change in Chrome 63.
We included a fix for this in Tailwind with the intention to remove it when Chrome 62 was no longer in common use. Now that usage has dropped to 0.09%, we've removed our fix.
If this is a problem for you (it isn't), you can add the removed styles back to your project right after @tailwind preflight.
Promote `shadowLookup` from experiment to official feature
shadowLookup from experiment to official featureFixes an issue where units were stripped from zero value properties
Fixes an issue where changes to your configuration file were ignored when using webpack --watch
webpack --watch (#520)Fixes an issue where @tailwind utilities generated no output
@tailwind utilities generated no output (#518)Added table layout utilities for styling tables
@apply-ing classes that aren't defined but would be generated</a><a name="new-features"></a>
<a name="added-table-layout-utilities-for-styling-tables"></a>
Tailwind now includes .table-auto and .table-fixed utilities for controlling the table-layout property.
By default we only generate responsive variants for these utilities but you can change this through the tableLayout module your Tailwind configuration file.
We've also updated Preflight to set border-collapse: collapse by default on all tables.
<a name="configuration-can-now-be-passed-as-an-object"></a>
Normally you pass your configuration to Tailwind by giving it a path:
// .postcssrc.js or similar
module.exports = {
// ...
plugins: [
// ...
require('tailwindcss')('./tailwind.js'),
]
}
Now you can also pass an object directly:
// .postcssrc.js or similar
const tailwindConfig = {
// ...
}
module.exports = {
// ...
plugins: [
// ...
require('tailwindcss')(tailwindConfig),
]
}
Note that we still recommend passing a path instead of an object, because Tailwind can't rebuild when the config changes if it doesn't have a config file to watch.
<a name="changes"></a>
<a name="default-config-file-changes"></a>
Impact: Low, Effort: Low
The default config file now includes a new tableLayout entry in the modules section.
Simply add this to your config file to sync it with this change, or leave it out if you just want to inherit the default configuration for the new module:
module.exports = {
// ...
modules: {
// ...
svgStroke: [],
+ tableLayout: ['responsive'],
textAlign: ['responsive'],
// ...
}
}
<a name="experiments"></a>
Tailwind 0.6.2 includes two new major features that are disabled by default behind flags.
These features may be changed or removed at any time without any regard for semantic versioning, so please do not depend on them in production just yet.
<a name="registering-new-variants-from-plugins"></a>
Plugins can now add their own variants (like hover, focus, group-hover, etc.) to Tailwind.
To get started, destructure the new addVariant function from the object passed to your plugin, and call it with the name of the variant you'd like to add and a callback that can be used to manipulate the PostCSS nodes where the variant is being applied:
module.exports = {
plugins: [
function({ addVariant }) {
addVariant('important', ({ container }) => {
container.walkRules(rule => {
rule.selector = `.\\!${rule.selector.slice(1)}`
rule.walkDecls(decl => {
decl.important = true
})
})
})
}
]
}
Proper documentation will be provided when this feature is stable and official, but in the mean time you can learn more by reading this comment from the pull request.
To enable this experiment, add pluginVariants: true under an experiments key in your Tailwind config:
module.exports = {
// ...
experiments: {
pluginVariants: true
}
}
<a name="allow-applying-classes-that-arent-defined-but-would-be-generated"></a>
@apply-ing classes that aren't defined but would be generated (#516)You can now use @apply to apply classes that aren't defined but would exist if @tailwind utilities was included in the same CSS file. This is mostly useful on projects that are setup to process multiple styles independently, for example a Vue.js project where you are using the <style> block of your single file components.
To enable this experiment, add shadowLookup: true under an experiments key in your Tailwind config:
module.exports = {
// ...
experiments: {
shadowLookup: true
}
}
@apply-ing classes that aren't defined but would be generated (experimental)Fix incorrect box-shadow syntax for the .shadow-outline utility 🤦♂️ : #503
Fix incorrect box-shadow syntax for the .shadow-outline utility 🤦♂️ : #503
If you generated a config file using v0.6.0, you'll want to make this same change in your own config file.
.shadow-outline utility 🤦♂️…will work the same in 0.6.0 aside from the two breaking changes mentioned earlier in this changelog.
.outline-none utility for suppressing focus styles</a>.shadow-outline utility as an alternative to default browser focus styles</a>outline: none !important styles from focusable but keyboard-inaccessible elements</a>group-hover variants</a><a name="new-features"></a>
<a name="added-border-collapse-utilities-for-styling-tables"></a>
Tailwind now includes .border-collapse and .border-separate utilities for controlling the border-collapse property.
By default we don't generate any variants for these utilities (not even responsive variants) but you can change this through the borderCollapse module your Tailwind configuration file.
We've also updated Preflight to set border-collapse: collapse by default on all tables.
<a name="added-more-axis-specific-overflow-utilities"></a>
In addition to .overflow-hidden and .overflow-visible, Tailwind now includes .overflow-x-hidden, .overflow-y-hidden, .overflow-x-visible and .overflow-y-visible for controlling overflow along a specific axis.
<a name="added-outline-none-utility-for-suppressing-focus-styles"></a>
.outline-none utility for suppressing focus styles (#491)Tailwind now includes a .outline-none utility for setting outline: 0 on an element to prevent the default browser focus ring.
By default, we also generate a focus variant (focus:outline-none) but no responsive variants.
<a name="added-shadow-outline-utility-as-an-alternative-to-default-browser-focus-styles"></a>
.shadow-outline utility as an alternative to default browser focus styles (#491)Outlines don't follow an element's border radius in most browsers, so a common practice is disable the browser's default focus outline and use a colored box-shadow to highlight focused elements.
Tailwind now includes a blue .shadow-outline utility that can be used for this purpose.
We've also enabled focus variants for box shadows by default, so you can add an outline shadow on focus by doing something like this:
<button class="focus:outline-none focus:shadow-outline ..."></button>
<a name="extended-default-padding-margin-negative-margin-width-and-height-scales"></a>
Tailwind's default configuration now includes more padding, margin, and negative margin sizes:
padding/margin/negativeMargin: {
'px': '1px',
'0': '0',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
+ '5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
+ '10': '2.5rem',
+ '12': '3rem',
+ '16': '4rem',
+ '20': '5rem',
+ '24': '6rem',
+ '32': '8rem',
}
We've also added 5 to the height and width scales to avoid any holes when compared with the spacing scales:
width/height: {
'auto': 'auto',
'px': '1px',
'1': '0.25rem',
'2': '0.5rem',
'3': '0.75rem',
'4': '1rem',
+ '5': '1.25rem',
'6': '1.5rem',
'8': '2rem',
'10': '2.5rem',
'12': '3rem',
'16': '4rem',
'24': '6rem',
'32': '8rem',
'48': '12rem',
'64': '16rem',
// ...
}
<a name="enable-focus-and-hover-variants-on-more-modules-by-default"></a>
Tailwind now includes focus and hover variants for more utilities out of the box.
We've added:
That means you do things like style an input differently based on whether it currently has focus:
<input class="border border-transparent bg-grey-lighter focus:bg-white focus:border-blue-light">
This was always possible if you had focus variants enabled in your own configuration, but Tailwind 0.6.0 sets these up for you out of the box so you don't need to make this common configuration change yourself. It also makes our CDN builds a little more powerful.
<a name="changes"></a>
<a name="removed-default-outline-none-important-styles-from-focusable-but-keyboard-inaccessible-elements"></a>
outline: none !important styles from focusable but keyboard-inaccessible elements (#491)Impact: Low, Effort: Low
Prior to 0.6.0, our Preflight base styles included this rule (borrowed from suitcss/base):
/**
* Suppress the focus outline on elements that cannot be accessed via keyboard.
* This prevents an unwanted focus outline from appearing around elements that
* might still respond to pointer events.
*/
[tabindex="-1"]:focus {
outline: none !important;
}
This is a useful default for many projects, but in the odd case that it's problematic for you it is really annoying to work around.
With the addition of the .outline-none helper, we think it makes sense to remove this default style and encourage people to simply add focus:outline-none to any focusable but keyboard-inaccessible elements:
- <div tabindex="-1" class="...">...</div>
+ <div tabindex="-1" class="focus:outline-none ...">...</div>
Of course, you can also reintroduce this rule into your own base styles after Preflight:
@tailwind preflight;
+ [tabindex="-1"]:focus {
+ outline: none !important;
+ }
@tailwind components;
@tailwind utilities;
<a name="moved-screen-prefix-for-responsive-group-hover-variants"></a>
group-hover variants (#497)Impact: Low, Effort: Medium
Prior to 0.6.0, if you had responsive and group-hover variants enabled for a module, the resulting CSS rule for a responsive group-hover variant would look something like this:
.sm\:group .group-hover\:bg-red { ... }
This was just a consequence of the responsive variants implementation and wasn't something we intentionally designed. It allowed you to do stuff like this:
<!-- Parent only behaves like a group on large screens and up, so the child -->
<!-- remains blue on small screens even when the parent is hovered. -->
<div class="lg:group">
<div class="bg-blue group-hover:bg-red">...</div>
</div>
This is not very useful in practice, and actually prevented you from changing how an element itself responded to group-hover on different screen sizes:
<div class="group">
<!-- Element was always red, even on small screens and up -->
<div class="group-hover:bg-red sm:group-hover:bg-blue">...</div>
</div>
In 0.6.0, the group-hover part of the selector adopts the screen prefix instead of the group part, so the code snippet from above will now work.
I would bet $100 that zero Tailwind users were depending on the pre-0.6.0 behavior, but if you were, the best solution is to write your own CSS for those parts of your project.
<a name="default-config-file-changes"></a>
Impact: Low, Effort: Low
The default config file now includes more padding, margin, negative margin, height, and width sizes; a new borderCollapse entry in the modules section; and enables more variants for more modules by default.
All the changes are purely additive, so you don't actually have to change any existing config files — all of your existing projects will work the same in 0.6.0 aside from the two breaking changes mentioned earlier in this changelog.
If you'd like to upgrade your config file to match the current default config file, you can view a diff of the changes here.
Improve sourcemaps for replaced styles like preflight
preflightpreflightFixes an issue with a dependency that had a security vulnerability: #438
Reverts a change that renamed the .roman class to .not-italic due to the fact that it breaks compatibility with cssnext: https://github.com/postcss/po
Reverts a change that renamed the .roman class to .not-italic due to the fact that it breaks compatibility with cssnext: https://github.com/postcss/postcss-selector-not/issues/10
We'll stick with .roman for now with a plan to switch to .not-italic in another breaking version should that issue get resolved in postcss-selector-not.
.roman class to .not-italic due to the fact that it breaks compatibility with cssnext: postcss/postcss-selector-not#10. We'll stick with .roman for now with a plan to switch to .not-italic in another breaking version should that issue get resolved in postcss-selector-not.Added .cursor-wait and .cursor-move utilities
.sticky position utility</a>.cursor-wait and .cursor-move utilities</a>.bg-auto background size utility</a>active variants</a>postcss-import support</a>.container component</a>.container component is now a built-in plugin</a>.overflow-x/y-scroll now set overflow: scroll instead of overflow: auto</a>.roman renamed to .not-italic</strike></a><a name="new-features"></a>
<a name="plugin-system"></a>
Tailwind now includes a feature-rich plugin system that allows people to create reusable third-party packages that can hook into Tailwind's compilation process to add new styles.
// ...
module.exports = {
// ...
plugins: [
function({ addUtilities, addComponents, config, prefix, e }) {
addComponents(
{
['.btn-blue']: {
backgroundColor: 'blue',
},
},
{ respectPrefix: true }
)
},
],
// ...
}
Documentation is coming very shortly, but in the mean time you can learn more through these two pull requests:
Update: Documentation is now available: https://tailwindcss.com/docs/plugins
<a name="added-sticky-position-utility"></a>
.sticky position utilityTailwind now includes a .sticky utility for setting an element to position: sticky. This isn't supported by IE 11, but falls back fairly gracefully with no effort so we decided to include it out of the box.
Learn more about sticky positioning at MDN
<a name="added-cursor-wait-and-cursor-move"></a>
.cursor-wait and .cursor-move utilitiesIn addition to .cursor-auto, .cursor-default, .cursor-pointer, and .cursor-not-allowed, Tailwind now includes .cursor-wait to indicate when the application is busy, and .cursor-move to indicate that an element can be moved.
<a name="added-bg-auto-background-size-utility"></a>
.bg-auto background size utilityTo allow resetting an element's background size at other breakpoints, Tailwind now includes a .bg-auto utility:
<div class="bg-cover md:bg-auto">...</div>
<a name="background-sizes-are-now-customizable"></a>
If you'd like to customize the available background size utilities in your project, you can now do so by adding a backgroundSize key to your Tailwind config:
module.exports = {
// ...
backgroundSize: {
'auto': 'auto',
'cover': 'cover',
'contain': 'contain',
},
}
<a name="support-for-active-variants"></a>
active variantsIn addition to hover, focus, and group-hover, Tailwind now includes support for active variants of each utility:
module.exports = {
// ...
modules: {
// ...
backgroundColors: ['hover', 'active'],
// ...
}
}
<a name="better-postcss-import-support"></a>
postcss-import supportIf you're using postcss-import to inline your imports, you can't use @tailwind preflight or @tailwind utilities directly in a file that contains other imports, due to postcss-import staying strict to the CSS spec for import statements.
Previously, the workaround for this was to create a new file just for @tailwind preflight and another new file just for @tailwind utilities, and then @import those files into your main stylesheet.
It turns out postcss-import can import files from node_modules, so as of v0.5.0, you can now import these files directly from Tailwind itself:
@import "tailwindcss/preflight";
@import "tailwindcss/utilities";
<a name="configuration-options-for-the-container-component"></a>
.container componentNow that the .container component is provided as a built-in plugin, it exposes optional configuration for centering the container by default as well as adding default horizontal padding:
// ...
module.exports = {
// ...
plugins: [
require('tailwindcss/plugins/container')({
center: true,
padding: '2rem',
}),
],
}
Containers are still not centered with no padding by default, and the configuration object is not required:
// ...
module.exports = {
// ...
plugins: [
require('tailwindcss/plugins/container')(),
],
}
You can also disable the container component entirely now by removing the plugin from the plugins list:
// ...
module.exports = {
// ...
plugins: [
- require('tailwindcss/plugins/container')(),
],
}
<a name="changes"></a>
<a name="the-container-component-is-now-a-built-in-plugin"></a>
.container component is now a built-in pluginImpact: Large, Effort: Low
The .container component has long been a bit of an oddball in the Tailwind codebase; it's the only set of styles that can't be used with state variants and apply different styles at different breakpoints.
With the addition of the new plugin system, it made sense to move the container component out of same bucket of code that holds all of our utility classes and into its own plugin with its own options.
If you are using the container in your projects, you will need to add the following section to your existing Tailwind config file:
// ...
module.exports = {
// ...
+ plugins: [
+ require('tailwindcss/plugins/container')(),
+ ],
}
You'll also need to add the new @tailwind components directive to your CSS:
@tailwind preflight;
+ @tailwind components;
@tailwind utilities;
<a name="state-variant-precedence-changes"></a>
Impact: Small, Effort: High
Prior to 0.5.0, state variants had the following precedence (lowest to highest):
That means that if an element had both focus:bg-blue and hover:bg-green applied, when the element was both focused and hovered, the hover styles would take precedence, so the element would be green.
It also meant that if an element had group-hover:bg-blue and hover:bg-green applied, hovering the element would make it blue because the group styles would take precedence over the individual element styles.
In 0.5.0, state variants have the following precedence (lowest to highest):
Now hover styles will defeat group-hover styles, and focus styles will defeat hover styles.
If this sounds counter-intuitive, see #417 for more information on the motivation behind this change.
It is extremely unlikely that this change affects you; the odds that you were changing the same property on both hover and focus is extremely low, and if you were, I'm willing to bet it was on an input field where the new behavior would actually feel like an improvement.
If this change does break the expected behavior in your project, the best solution is to create your own component classes for the places where you were doing complex interaction like this so you can control the precedence manually.
<a name="new-config-file-keys"></a>
Impact: Small, Effort: Low
plugins keyIf you'd like to use the new plugin system in an existing project, you'll need to add the plugins key to your config, and include the container component plugin if you need it:
// ...
module.exports = {
// ...
+ plugins: [
+ require('tailwindcss/plugins/container')(),
+ ],
}
This is optional, your project will build fine without this change and will just fall back to the plugins configuration from the default config file.
backgroundSize keyIf you'd like to customize the available background size utilities, add the backgroundSize key to your config
module.exports = {
// ...
+ backgroundSize: {
+ 'auto': 'auto',
+ 'cover': 'cover',
+ 'contain': 'contain',
+ },
}
This is optional, your project will build fine without this change and will just fall back to the backgroundSize configuration from the default config file.
<a name="overflow-x-y-scroll-now-set-overflow-scroll-instead-of-overflow-auto"></a>
.overflow-x/y-scroll now set overflow: scroll instead of overflow: autoImpact: Large, Effort: Medium
The .overflow-x-scroll and .overflow-y-scroll utilities now set overflow to scroll instead of auto.
New .overflow-x-auto and .overflow-y-auto utilities have been added to get the auto behavior with more consistent naming.
This change won't break any sites but will cause scrollbars to appear on Windows in places where they might not be actually needed, so if you don't want them visible you should switch instances of .overflow-x/y-scroll to .overflow-x/y-auto.
We've also removed the -ms-overflow-style: -ms-autohiding-scrollbar styles from the overflow utilities, which means scrollbars will now render with their default styling in IE/Edge instead of being forced to render as autohiding, which is not the browsers normal behavior.
<a name="roman-renamed-to-not-italic"></a>
.roman renamed to .not-italicImpact: Large, Effort: Medium
The .roman utility for undoing italic font styles has been renamed to .not-italic since .roman is a terrible name.
This was immediately reverted in v0.5.1 because it breaks compatibility with cssnext; ignore this change.
Hoping to change this in a future breaking release if/once the issue with postcss-selector-not is resolved.
Use global.Object to avoid issues with polyfills when importing the Tailwind config into other JS
global.Object to avoid issues with polyfills when importing the Tailwind config into other JS (#402)Fix an issue where borders couldn't be applied to img tags without specifying a border style (#362, #363)
img tags without specifying a border style (#362, #363)@apply by using a lookup table instead of searching (#401)img tags without specifying a border styleMake default sans-serif font stack more future proof and safe to use with CSS font shorthand
font shorthand (https://github.com/tailwindcss/tailwindcss/pull/353)@apply'd classes can now be made !important explicitly
@apply'd classes can now be made !important explicitly</a>@apply now strips !important from any mixed in classes</a><a name="changes"></a>
<a name="apply-d-classes-can-now-be-made-important-explicitly"></a>
@apply'd classes can now be made !important explicitlyIf you need to @apply a class and make it's declarations !important, you can now add !important to the @apply declaration itself. This will make the applied declarations !important even if they aren't marked as important in the class being applied:
// Input:
.bar {
@apply .foo !important;
}
.foo {
color: blue;
}
// Output:
.bar {
color: blue !important;
}
.foo {
color: blue;
}
<a name="changes"></a>
<a name="apply-now-strips-important-from-any-mixed-in-classes"></a>
@apply now strips !important from any mixed in classesImpact: Low
Prior to 0.4, if you had a class that contained !important declarations and you @apply'd that class to another class, the declarations would preserve their !important value:
// Input:
.bar {
@apply .foo;
}
.foo {
color: blue !important;
}
// Output:
.bar {
color: blue !important;
}
.foo {
color: blue !important;
}
This turned out to be problematic if you have Tailwind configured to make utilities !important by default, and you wanted to compose components from those utilities that contained descendant selectors, for example:
// Input:
.custom-table td {
@apply .text-grey-dark;
}
// Output:
.custom-table td {
color: #8795a1 !important;
}
The problem was that rules like this would have a higher specificity than the utilities themselves due to the compound selector, so you couldn't override those styles with utilities:
<table class="custom-table">
<tr>
<td class="text-red">Will still be grey</td>
</tr>
</table>
In 0.4, @apply will always strip !important to avoid specificity issues like this:
// Input:
.bar {
@apply .foo;
}
.foo {
color: blue !important;
}
// Output:
.bar {
color: blue;
}
.foo {
color: blue !important;
}
Odds of this affecting your existing codebase is quite low; if anything this will let you clean up code you might have had to write to work around this annoying behavior.
<a name=""></a>
Impact: Low
Some of the values in the default color palette have been tweaked with the aim of making it more useful in more situations.
The dark end of the grey scale has been spread out more, making grey closer to grey-light than it was previously. See the PR.
The darker/darkest variants of most colors have been desaturated slightly so they work better as background colors. See the PR.
These changes will only affect you if you are dynamically referencing the default color palette in your own config file. If you'd like to keep using the old colors, they can be found here:
@apply'd classes can now be made !important explicitly@apply now strips !important from any mixed in classesUpgrade Guide / Breaking Changes
@variants at-rule</a>.pin no longer sets width and height to 100%</a>fill no longer defaults to currentColor</a><a name="new-features"></a>
<a name="enable-disable-modules-and-control-which-variants-are-generated-for-each"></a>
The Tailwind config file now contains a new modules key where you can control which modules should be responsive, or have hover or focus variants generated:
// ...
module.exports = {
// ...
modules: {
// Generate base appearance utilities + responsive versions
appearance: ['responsive'],
// Generate base, responsive, hover, and focus versions
backgroundAttachment: ['responsive', 'hover', 'focus'],
// Only generate base utilities
backgroundPosition: [],
// ...
},
// ...
}
If you don't need a certain module at all, you can disable it by setting it to false:
// ...
module.exports = {
// ...
modules: {
// ...
flexbox: false,
// ...
},
// ...
}
This gives you better control over the generated file size and also lets you add hover/focus variants to utilities that don't have them by default, like shadows for example.
If you're a PurgeCSS user who doesn't care about the pre-PurgeCSS file size, you can even set modules to all to generate every variant for every utility :feelsgood:
// ...
module.exports = {
// ...
modules: 'all',
// ...
}
Learn more about in the configuration modules documentation.
<a name="focus-variants"></a>
As alluded to earlier, Tailwind now lets you generate focus: variants of each utility that are only active on focus.
Focus variants are currently not enabled on any modules by default, but you can enable them for a specific module in your own project by adding 'focus' to the variants list in the modules section of your config file:
// ...
module.exports = {
// ...
modules: {
// ...
backgroundColors: ['responsive', 'hover', 'focus'],
// ...
},
// ...
}
Focus variants work just like the hover variants that you're used to:
<input class="bg-grey-light focus:bg-white border border-grey">
<a name="group-hover-variants"></a>
Sometimes you need to change the style of an element when some parent element is hovered rather than the element itself.
To handle these situations, Tailwind 0.3 adds a new group-hover variant.
Group hover variants are currently not enabled on any modules by default, but you can enable them for a specific module in your own project by adding 'group-hover' to the variants list in the modules section of your config file:
// ...
module.exports = {
// ...
modules: {
// ...
textColors: ['responsive', 'hover', 'group-hover'],
// ...
},
// ...
}
To use a group-hover: utility variant, you need to mark the target parent element with the .group class:
<div class="group ...">
<svg class="text-grey-light group-hover:text-grey-dark"><!-- ... --></svg>
<a class="text-blue group-hover:underline" href="#">Click me</a>
</div>
Check out this CodePen to see it in action.
<a name="new-variants-at-rule"></a>
@variants at-ruleTo make it easy to generate hover, focus, and group-hover versions of your own utilities, Tailwind 0.3 adds a new @variants at-rule that lets you specify which variants to generate for a given list of classes:
@variants hover, focus {
.banana { color: yellow; }
.chocolate { color: brown; }
}
This will generate the following CSS:
.banana { color: yellow; }
.chocolate { color: brown; }
.focus\:banana:focus { color: yellow; }
.focus\:chocolate:focus { color: brown; }
.hover\:banana:hover { color: yellow; }
.hover\:chocolate:hover { color: brown; }
The @variants at-rule supports all of the values that are supported in the modules section of your config file:
responsivehoverfocusgroup-hoverNote: In previous versions, Tailwind included undocumented @hoverable and @focusable directives. These were fundamentally flawed in how they worked, and have been removed in favor of the new @variants directive.
The @responsive directive however has not been removed, and we fully intend to continue to support it as a shortcut for @variants responsive {}.
<a name="customize-the-separator-character"></a>
By default, Tailwind uses a colon (:) as a separator between variants and utility names:
<div class="hover:bg-blue">...</div>
Some templating languages (like Pug) don't play nicely with this, so Tailwind 0.3 adds a new configuration option that lets you change this if needed:
// ...
module.exports = {
// ...
options: {
// ...
separator: '_',
},
}
<a name="missing-config-keys-now-fallback-to-their-default-values"></a>
Prior to Tailwind 0.3, excluding a key (like backgroundColors) from your config file was undefined behavior.
To make upgrades smooth as new options are added to the config file, missing keys now fallback to their default values.
This has the added benefit of allowing you to completely omit keys from your config file if you don't intend to change the default values.
The exact behavior is as follows:
modules key is merged with the default modules key. This means that if you exclude a module from your config, it will be generated using the default settings. It will not be disabled unless you include the key and set it to false.options key is merged with the default options key. This means if you only want to change one option, you only need to provide that one key.<a name="new-utilities"></a>
.pin-none has been added to undo existing .pin-{side} utilities at different breakpoints.resize-both has been added to allow resizing an element both horizontally and vertically<a name="upgrade-guide-breaking-changes"></a>
<a name="lists-now-have-no-margins-by-default"></a>
Impact: Medium
Until 0.3, Tailwind's Preflight base styles left ul and ol elements generally untouched, relying on the list-reset utility to remove default browser styling if you wanted to use a list for navigation or similar.
In 0.3, Tailwind still doesn't change list-style-type or padding on lists in our base styles, but we do remove margins:
ul, ol {
margin: 0;
}
Tailwind already did this for all headings, block quotes, paragraph tags, etc., so removing margins on lists feels much more consistent.
It's unlikely this will impact your project as you were most likely overriding the browser's default margins with existing margin utilities.
If you were relying on the browser's default list margins, simply add margin utilities to your lists to make up for the now missing default margin.
<a name="pin-no-longer-sets-width-and-height-to-100"></a>
.pin no longer sets width and height to 100%Impact: Low
In an effort to make .pin{-side?} utilities easier to undo at different breakpoints, the all-sides .pin utility no longer sets width and height to 100%.
This will only affect you if you were using .pin on iframe, textarea, input, or button elements, and is easily remedied by adding the w-full and h-full utilities to those elements.
<a name="svg-fill-no-longer-defaults-to-current-color"></a>
fill no longer defaults to currentColorPrior to 0.3, Tailwind's Preflight styles set all SVG fills to currentColor:
svg {
fill: currentColor;
}
This made it harder to use icon sets like Feather that are drawn entirely with strokes with Tailwind, because they'd now be filled with the current text color by default instead of having no fill.
Tailwind 0.3 removes this base style entirely, and adds the fill-current class to make up for it, allowing you to be explicit when you want an SVG element to be filled with the current text color.
There's two ways you can update your project for this change:
fill-current class to any SVG elements that should be filled with the current text color.Fix issue with dist files not being published due to bug in latest npm
Fix overly specific border-radius reset for Chrome 62 button styles: https://github.com/tailwindcss/tailwindcss/pull/216
[Upgrade Guide / Breaking Changes](#upgrade-guide-breaking-changes)
auto is no longer a hard-coded margin valuedefaultConfig function is now a separate module@apply is now very strict about what classes can be appliedoptions key to your config<a name="new-features"></a>
<a name="add-a-custom-prefix-to-all-utilities"></a>
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.
<a name="optionally-make-all-utilities-important"></a>
!importantAnother 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.
<a name="round-element-corners-independently"></a>
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.
<a name="cascading-border-colors-and-styles"></a>
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.
<a name="upgrade-guide-breaking-changes"></a>
<a name="auto-is-no-longer-a-hard-coded-margin-value"></a>
auto is no longer a hard-coded margin valueImpact: 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',
},
}
<a name="the-default-config-function-is-now-a-separate-module"></a>
defaultConfig function is now a separate moduleImpact: 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.
<a name="rounded-utilities-now-combine-position-and-radius-size"></a>
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>
<a name="upgrade-steps"></a>
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.
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>
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>
<a name="border-width-utilities-no-longer-affect-border-color-style"></a>
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>
<a name="apply-is-now-very-strict-about-what-classes-can-be-applied"></a>
@apply is now very strict about what classes can be appliedImpact: 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:
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.
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.
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.
<a name="add-options-key-to-your-config"></a>
options key to your configImpact: 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.
<a name="spacing-radius-and-border-width-utility-declaration-order-changes"></a>
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>
Fix CDN files not being published to npm
Apply the same default placeholder styling that's applied to inputs to textareas
Autoprefix dist assets for quick hacking and prototyping
my-auto, mt-auto, and mb-auto margin utilitiessans-serif to end of default sans font stacktailwind init [filename], automatically append .js to filename if not presentconfig(...) function, ie. config('colors.blue', #0000ff)Add new .scrolling-touch and .scrolling-auto utilities for controlling inertial scroll behavior on WebKit touch devices
.scrolling-touch and .scrolling-auto utilities for controlling inertial scroll behavior on WebKit touch devicesTarget Node 6.9.0 explicitly (instead of 8.6 implicitly) to support more users
tailwind buildFix tailwind build CLI command not writing output files
tailwind build CLI command not writing output files[unreleased]: https://github.com/tailwindlabs/tailwindcss/compare/v4.3.3...HEAD [4.3.3]: https://github.com/tailwindlabs/tailwindcss/compare/v4.3.2...
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →