This PR changes some decisions about the config file structure that we never would have made had we known that eventually we would be adding the modules key and the options key.
TL;DR: Some sections have moved or been renamed, and we are going to start encouraging people to keep their config files much simpler and inherit our defaults unless they explicitly want to override or extend them.
Many thanks to @reinink, @samselikoff, @taylorotwell, @derrickreimer, @davidhemphill, and @jasonmccreary for putting up with my incessant badgering, whining, desperate requests for feedback and border-line depression trying to make this not suck over the last three weeks. You don't even want to see all of the iterations this went through ๐ฉ
-
Options are now top-level.
The
optionskey as been removed and theseparator,prefix, andimportantoptions are now top-level keys in the config file:module.exports = { prefix: '', important: false, separator: ':', // ... }
This is part of a larger vision where the config file is truly more of a config file, and details about your design system are just a specific part of that rather than the top-level focus. Think of it more like a
postcss.config.jsor ababel.config.jsfile โ a bunch of options for configuring Tailwind the tool, where your styles are just one part of that. -
Style-related options have moved to a new
themesection.All of the top-level options related to your design system (
screens,colors,fonts,backgroundColors,padding,margin, etc) have been nested inside a newthemesection.After introducing the
modules,options, andpluginssections, it felt very off to have all of the style options be direct siblings of options that were much more configuration-ish.It also made things look a bit weird if you (like many) kept your style-related configuration in other files, because you need to spread your styles into the configuration at the top-level:
module.exports = { ...styles, modules: [ // ... ], plugins: [ // ... ], options: { prefix: '', important: false, separator: ':', } }
With everything nested underneath the new
themesection, the example above would look like this:module.exports = { prefix: '', important: false, separator: ':', theme: styles, modules: [ // ... ], plugins: [ // ... ], }
Not a huge difference, but conceptually I think it makes a lot more sense for those options to be grouped together instead of all sitting at the top-level alongside things like
optionsandmodules. -
Your
themesection is merged with the defaultthemevalues.Tailwind already works this way in 0.x, but it's worth noting here for clarity.
You can leave your
themesection completely empty and your build will just use Tailwind's default values.If you provide any keys, those keys will override the default values.
module.exports = { theme: { // This will completely replace the default tracking values tracking: { '1': '-0.05em', '2': '0', '3': '0.05em', }, }, }
To extend any of Tailwind's defaults, you can grab the default values from
tailwindcss/defaultThemeand spread them in:const defaultTheme = require('tailwindcss/defaultTheme')() module.exports = { theme: { // This will extend the default tracking values tracking: { ...defaultTheme.tracking, wider: '0.1em', widest: '0.15em', }, }, }
Note that the
tailwindcss/defaultThememodule exports a function that returns the default theme (to avoid accidental mutation).Based on a lot of user interviews I've done over the last few weeks and a poll I conducted on Twitter, I learned that a lot of people really value having Tailwind provide well-considered default values.
So for v1.0, we are going to take a bit of a more opinionated stance and encourage partial customization of the default config by just overriding and extending the parts you want to change instead of publishing the entire default config by default for you to own and manipulate.
Everything of course will still be as customizable as ever, but the hope is that most people's config files will end up being much shorter/simpler, and serve just as a reference for the user's customizations, rather than having all of the defaults and their customizations combined into one giant file, where it's hard to tell what came with Tailwind out of the box vs. what was added by the user.
-
The
modulessection has been renamed tovariants."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 want to deprecate this terminology entirely, and make this section of the config purely about configuring variants for core plugins.
module.exports = { // ... - modules: { + variants: { appearance: ['responsive'], backgroundAttachment: ['responsive'], backgroundColors: ['responsive', 'hover', 'focus'], // ... zIndex: ['responsive'], }, // ... } -
Your
variantssection is merged with the defaultvariantsvalues.Just like the
themesection, you only need to provide values for thevariantsyou'd like to override.If you are fine with the defaults, you don't even need to provide the key.
To add
hovervariants toopacityfor example, you'd just add theopacitykey and list the variants you want to generate:module.exports = { // ... variants: { opacity: ['responsive', 'hover'], }, // ... }
It's important to note that whatever you provide overrides the default value, it's not merged. So to add a
hovervariant toopacity, you need to re-specify theresponsivevariant too, unless of course you don't want to generate it.We may make it easier to simply extend this configuration later, but I don't think this will be a real pain point for anyone in practice.
-
Modules are disabled in the
corePluginssection, not thevariantssection.Instead of setting a
modulecore plugin tofalsein thevariantssection of your config to disable it, you now set that plugin tofalsein a newcorePluginssection of the config:variants: { // ... negativeMargin: ['responsive'], - objectFit: false, - objectPosition: false, opacity: ['responsive'], // ... }, + corePlugins: { + objectFit: false, + objectPosition: false, + },The motivation behind this is that in the near future, I plan to rewrite our
preflightstyles as a core plugin as well, and if users want to disable preflight, doing so in thevariantssection of the config makes no sense because there's no way to add "responsive" or "hover" versions of preflight.We may also move the
containerplugin back to being a true "core plugin" instead of just a third-party plugin that happens to be in the same repository, so this change is also important in order for people to disable that plugin if we make that change.Overall, it just makes sense that disabling core plugins is divorced from variant configuration, because the idea of variants isn't relevant for every core plugin.
-
Core plugins can be configured directly through the
corePluginssection.In Convert built-in utility modules to private plugins #620, all of Tailwind's built-in modules were rewritten to use the plugin system, so there is no difference between the code we write to add new core functionality to Tailwind and the code that someone might write when authoring a third-party plugin.
Part of this included each plugin becoming directly configurable (for Tailwind's benefit internally), much like a third-party plugin would be.
Essentially, there's no reason you couldn't disable a core plugin, then re-introduce it by adding it your
pluginskey and passing it the appropriate configuration:corePlugins: { backgroundSize: false, }, plugins: [ require('tailwindcss/plugins/backgroundSize')({ variants: ['responsive'], values: { auto: 'auto', cover: 'cover', contain: 'contain', }, }) ]
In addition to allowing you to disable core plugins, the
corePluginskey also allows you to completely override a core plugin's configuration, by providing a configuration object for that plugin.This example does exactly the same thing as the previous example:
corePlugins: { backgroundSize: { variants: ['responsive'], values: { auto: 'auto', cover: 'cover', contain: 'contain', }, }, },
There's essentially no reason at all to do this currently (it's much simpler to configure a core plugin through the
variantsandthemekeys), but designing the configuration file this way means it could be possible to pass advanced configuration options to core plugins in the future that might not be exposed through thethemesection.For example, perhaps the
marginplugin could expose an additional option to let you customize the class names tomargin-{side}-{size}instead of the defaultm{size}-{size}syntax:corePlugins: { margin: { variants: ['responsive'], values: { auto: 'auto', // ... '32': '8rem', }, classPrefix: 'margin-', }, },
I don't have concrete plans to introduce features like this to the core plugins, but designing the config file this way at least makes it possible.
-
Every section is optional, as is the entire config file.
As I alluded to in point 3, one of the main themes for Tailwind v1.0 is going to be doubling down on the defaults and shouldering more of the burden for making sure the default values are awesome.
Until now, it's been easy to tell people "just customize it in your config" if they weren't happy with a default value. Going forward, I'd like to try even harder to make the defaults as widely usable as possible so that customization because more of an escape hatch than a mandatory step in using Tailwind.
As I mentioned before, there are no plans to make it harder or impossible to customize any of the things you can currently customize โ being able to customize things is the whole reason I created the framework! But rather than sending the message of "you own every value the framework uses, customize the hell out of it", I want to start saying "we've worked really hard to give you the best possible starting point we could create, but if you need to change or extend the framework in any way, it's easy to do".
Certain things are going to be very commonly completely replaced, like colors and fonts. But we should be able to make most of the other stuff suitable for just about any project right out of the box. Sure you might need to add an extra margin or padding value here and there, but in general you should be able to rely on our defaults and get pretty far.
All that to say, in this config file, every single key is optional. You don't even need to provide a config file at all if you don't want:
// postcss.config.js module.exports = { plugins: [ // Pass a custom config file if you have one: require('tailwindcss')('./tailwind.js'), // Or pass nothing and just use the defaults: require('tailwindcss')(), ] }
All of this is already true in Tailwind 0.x for what it's worth, but most people don't use Tailwind this way because it's not the path we encourage.
So in a future PR, the
tailwind initcommand will be changing as well, to create a much more minimal config file by default, with perhaps an extra flag you can pass if you want to generate the entire thing (like atailwind init --ejector something) and not inherit the default values at all.