Tailwind CSS 54 🎨 CSS Preprocessing Rules, Nesting Support, and Vendor Prefixing
Tailwind is often described as a replacement for CSS, but it is more accurate to say that it sits on top of CSS. The framework generates plain CSS, and the projects that use it still write CSS for the parts that utilities do not cover. The question is how that hand-written CSS is authored and processed. Three concerns dominate: whether a preprocessor such as Sass or Less is worth adding, how native CSS nesting compares to preprocessor nesting, and how vendor prefixes are handled. Modern Tailwind and modern browsers have changed the answers to all three, and the correct choices today are different from the ones that were standard five years ago.
Key point: Tailwind does not require a preprocessor. Modern CSS nesting, PostCSS, and autoprefixer cover the needs that Sass and Less were once added for. Adding a preprocessor to a Tailwind project is a choice, not a default.
Why these topics matter
The legacy of preprocessors. Sass, Less, and Stylus predate native CSS variables, native nesting, and native color functions. They existed because CSS lacked features developers needed: variables, nesting, mixins, and functions. Those features now exist in CSS itself, and browsers have supported them for years. Adding a preprocessor to a modern project is no longer necessary for the features it was originally chosen for, though it may still be justified for organizational reasons.
The Tailwind and PostCSS relationship. Tailwind itself is a PostCSS plugin. When a project uses Tailwind, it already has a PostCSS pipeline. That pipeline processes every CSS file, applies Tailwind’s utilities, and can run additional plugins such as autoprefixer. Understanding the pipeline clarifies what a preprocessor would add on top.
The nesting question. CSS nesting became widely supported in 2023. Before that, nesting was the primary reason developers chose Sass or Less. Now that nesting is native, the question is whether the preprocessor’s version is worth its additional layer of processing.
The vendor prefix question. Browsers sometimes ship new features behind prefixes such as -webkit- or -moz-. Autoprefixer reads a project’s browser support targets and adds the prefixes that are actually needed. Writing prefixes by hand is error-prone and outdated; autoprefixer handles them automatically.
The configuration question. Tailwind’s PostCSS plugin runs alongside other PostCSS plugins in a defined order. The order matters: autoprefixer runs after Tailwind generates its utilities so that prefixes are added to the final output, not to the source.
The maintenance question. Every layer in the build pipeline is a dependency to maintain, upgrade, and debug. A preprocessor adds a layer. PostCSS adds plugins. Autoprefixer adds a plugin. The question is whether each layer earns its place.
a. Preprocessing in a Tailwind project
A Tailwind project already uses PostCSS. The question is whether to add a preprocessor such as Sass on top.
What PostCSS provides
PostCSS is a CSS transformer. It parses CSS into an abstract syntax tree, passes it through a series of plugins, and serializes the result back to CSS. Tailwind is a PostCSS plugin. Autoprefixer is a PostCSS plugin. Many other plugins exist for specific transformations. The pipeline is configured once and runs on every build.
// postcss.config.js
module.exports = {
plugins: {
'postcss-import': {},
tailwindcss: {},
autoprefixer: {},
},
};
The order matters. postcss-import resolves @import statements. tailwindcss generates the utility classes and applies the project’s configuration. autoprefixer adds vendor prefixes based on the project’s browser targets.
What Sass adds
Sass provides variables, nesting, mixins, functions, and modules. Variables are now native CSS. Nesting is now native CSS. Mixins and functions have no native equivalent, but most Tailwind projects do not need them, because Tailwind’s utilities and @apply cover the common cases.
Adding Sass means adding a build step. The sass package compiles .scss files to .css, and PostCSS processes the result. The pipeline grows by one layer.
// With Sass
$brand-color: #1da1f2;
.card {
border: 1px solid $brand-color;
&__header {
padding: 1rem;
}
}
What native CSS provides
Native CSS variables are supported in every modern browser. They can be scoped to selectors, changed at runtime, and read from JavaScript. They replace the variable feature that motivated Sass.
:root {
--brand-color: #1da1f2;
}
.card {
border: 1px solid var(--brand-color);
}
Native nesting is supported in current versions of Chrome, Edge, Firefox, and Safari. It replaces the nesting feature that motivated Sass.
.card {
border: 1px solid var(--brand-color);
& .header {
padding: 1rem;
}
}
The & character refers to the parent selector, same as in Sass. The syntax differs slightly: native nesting requires & for descendant combinators, while Sass allows implicit nesting.
When a preprocessor is still justified
There are cases where Sass remains the right choice. A legacy codebase already using Sass should not be rewritten for its own sake. A team with deep Sass expertise may prefer its mixins and functions. A project that needs programmatic color manipulation beyond color-mix() may find Sass functions convenient. But for a new Tailwind project, the default is no preprocessor.
The Tailwind recommendation
Tailwind’s own documentation no longer recommends a preprocessor. The framework assumes plain CSS, and its plugin works with the standard PostCSS pipeline. The recommended setup is postcss-import, tailwindcss, and autoprefixer, with no preprocessor in between.
b. Nesting support
CSS nesting became a native feature in 2023, and browsers have supported it since then. It allows selectors to be nested inside other selectors, producing CSS that is more readable and organized than flat rules.
The basic nesting syntax
A nested rule is written inside its parent. The & character refers to the parent selector.
.card {
border: 1px solid gray;
& .title {
font-weight: bold;
}
&:hover {
border-color: blue;
}
}
The first nested rule compiles to .card .title. The second compiles to .card:hover. The & is required for the second because :hover is a pseudo-class, and without & the browser would interpret it as a descendant selector.
Nesting versus preprocessor nesting
Preprocessor nesting and native nesting differ in a few details. Sass allows implicit descendant nesting: a nested rule without & is a descendant. Native CSS requires the & for descendant combinators in some cases and treats bare selectors as relative. Sass also allows nesting of declarations and properties, which native CSS does not.
// Sass: implicit descendant
.card {
.title { font-weight: bold; } // compiles to .card .title
}
/* Native: explicit parent reference */
.card {
& .title { font-weight: bold; } /* compiles to .card .title */
}
The native syntax is more explicit. It requires the & when the intent is a descendant selector, which reduces ambiguity.
Tailwind and nesting
Tailwind’s utilities are flat classes. They are not nested. Nesting applies to the hand-written CSS in the project, not to the generated utilities. A project that writes a small amount of custom CSS can use native nesting for that CSS.
/* app.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
.prose-custom {
color: var(--foreground);
& h2 {
margin-top: 2rem;
font-size: 1.5rem;
}
}
The custom rule sits alongside the Tailwind directives. It uses native nesting. No preprocessor is involved.
Browser support
Native nesting is supported in Chrome 120+, Firefox 117+, and Safari 17.2+, all released in late 2023. For projects that support older browsers, PostCSS’s postcss-nesting plugin compiles native nesting syntax to flat CSS that older browsers understand.
// postcss.config.js
module.exports = {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': 'postcss-nesting',
tailwindcss: {},
autoprefixer: {},
},
};
The tailwindcss/nesting plugin is a wrapper that applies the project’s chosen nesting plugin. It ensures that nesting is processed before Tailwind generates its utilities, so that @apply and other Tailwind features work correctly inside nested rules.
The @apply interaction
Tailwind’s @apply directive inlines utility classes into custom CSS. It works inside nested rules as long as the nesting is processed before Tailwind runs. The tailwindcss/nesting plugin handles this ordering.
.button {
@apply rounded-md px-4 py-2;
&:hover {
@apply bg-blue-700;
}
}
The custom rule uses @apply and nesting together. The pipeline processes nesting first, then Tailwind resolves the @apply directives inside the flattened selectors.
c. Vendor prefixing with autoprefixer
Vendor prefixes are browser-specific extensions to CSS properties. They were common in the past, when browsers implemented new features behind experimental flags. Today, most features are shipped unprefixed, but some properties still need prefixes for older browsers or specific engines.
What autoprefixer does
Autoprefixer is a PostCSS plugin that adds vendor prefixes to CSS declarations based on a project’s browser support targets. It reads the targets from browserslist, then decides which prefixes to add. The developer writes standard CSS and never writes prefixes by hand.
/* Input */
.user-select-none {
user-select: none;
}
/* Output (with appropriate targets) */
.user-select-none {
-webkit-user-select: none;
user-select: none;
}
The user-select property still needs -webkit- in some Safari versions. Autoprefixer adds it automatically.
The browserslist configuration
Autoprefixer reads its targets from browserslist. The configuration can live in package.json or in a .browserslistrc file.
// package.json
{
"browserslist": [
"> 0.5%",
"last 2 versions",
"not dead"
]
}
The query > 0.5% targets browsers with more than 0.5% market share. last 2 versions targets the two most recent versions of each browser. not dead excludes browsers without official support.
Tailwind’s default target
Tailwind does not set browserslist targets. The project does. Tailwind generates utilities using standard CSS properties, and autoprefixer handles the prefixes. If a project does not configure browserslist, autoprefixer uses a default that targets the last two versions of each browser and browsers with more than 1% market share.
Prefixing and Tailwind utilities
Tailwind’s generated utilities are standard CSS. They do not include prefixes in the source. Autoprefixer adds them after Tailwind generates the output. This means the prefixing is consistent across all utilities, including custom CSS and @apply-based rules.
/* Tailwind generates */
.flex { display: flex; }
/* Autoprefixer adds (for old IE targets) */
.flex {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
}
For modern targets, no prefixes are added to display: flex. The prefixing is target-driven.
The PostCSS order
Autoprefixer must run after Tailwind in the pipeline. If it runs before, it prefixes the source CSS but not the generated utilities. The correct order places tailwindcss before autoprefixer.
// Correct order
module.exports = {
plugins: {
'postcss-import': {},
tailwindcss: {},
autoprefixer: {},
},
};
The postcss-import plugin runs first to inline imports. tailwindcss runs next to generate utilities. autoprefixer runs last to add prefixes to the complete output.
@supports and modern syntax
Autoprefixer handles prefixes, but some modern features need fallbacks, not prefixes. For these, @supports blocks let the developer provide a fallback for browsers that do not support the feature.
.grid {
display: flex;
}
@supports (display: grid) {
.grid {
display: grid;
}
}
Autoprefixer does not rewrite @supports blocks. It is the developer’s responsibility to provide fallbacks for features that are not universally supported.
The decline of prefixes
Vendor prefixes are less common than they were. Browsers have committed to shipping features unprefixed once they are standardized. Autoprefixer still adds prefixes for the properties that need them, but the list shrinks with each passing year. A project with modern browser targets may produce almost no prefixed output.
Complete Example Session
/* ============================================
PART 1: THE POSTCSS PIPELINE
============================================ */
// postcss.config.js
module.exports = {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': 'postcss-nesting',
tailwindcss: {},
autoprefixer: {},
},
};
// Order: imports resolved, nesting flattened,
// utilities generated, prefixes added.
/* ============================================
PART 2: NATIVE CSS VARIABLES
============================================ */
/* app/globals.css */
:root {
--brand-color: #1da1f2;
--radius: 0.5rem;
}
.dark {
--brand-color: #60a5fa;
}
.card {
border: 1px solid var(--brand-color);
border-radius: var(--radius);
}
/* No preprocessor needed for variables. */
/* ============================================
PART 3: NATIVE CSS NESTING
============================================ */
.card {
border: 1px solid gray;
& .title {
font-weight: bold;
}
&:hover {
border-color: blue;
}
}
/* Compiles to:
.card { border: 1px solid gray; }
.card .title { font-weight: bold; }
.card:hover { border-color: blue; }
*/
/* ============================================
PART 4: NESTING WITH TAILWIND @apply
============================================ */
.button {
@apply rounded-md px-4 py-2 font-medium;
&:hover {
@apply bg-blue-700;
}
&:disabled {
@apply opacity-50 cursor-not-allowed;
}
}
/* Nesting is flattened before Tailwind resolves @apply. */
/* ============================================
PART 5: BROWSERSLIST CONFIGURATION
============================================ */
// package.json
{
"browserslist": [
"> 0.5%",
"last 2 versions",
"not dead"
]
}
/* Autoprefixer reads this and adds prefixes as needed. */
/* ============================================
PART 6: AUTOPREFIXER IN ACTION
============================================ */
/* Input */
.user-select-none {
user-select: none;
}
.backdrop-blur {
backdrop-filter: blur(8px);
}
/* Output (with appropriate targets) */
.user-select-none {
-webkit-user-select: none;
user-select: none;
}
.backdrop-blur {
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
/* The developer wrote standard CSS. Autoprefixer added prefixes. */
/* ============================================
PART 7: @supports FOR FALLBACKS
============================================ */
.grid {
display: flex; /* fallback */
}
@supports (display: grid) {
.grid {
display: grid;
}
}
/* Autoprefixer does not touch @supports. It is a developer tool. */
/* ============================================
PART 8: MIXING TAILWIND AND CUSTOM CSS
============================================ */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.prose-custom {
color: var(--foreground);
& h2 {
@apply mt-8 text-2xl font-semibold;
}
& p {
@apply mt-4 leading-relaxed;
}
}
}
/* Tailwind directives, custom CSS, native nesting, @apply.
One pipeline. No preprocessor. */
/* ============================================
PART 9: WHY NOT SASS
============================================ */
/* Sass was added for:
- Variables → now native CSS variables
- Nesting → now native CSS nesting
- Mixins → often replaced by @apply
- Functions → occasionally still useful
*/
/* Sass is still justified for:
- Legacy codebases already using it
- Teams with deep Sass expertise
- Advanced color manipulation beyond CSS
*/
/* For a new Tailwind project, the default is no preprocessor. */
/* ============================================
PART 10: THE RECOMMENDED PIPELINE
============================================ */
// postcss.config.js
module.exports = {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': 'postcss-nesting',
tailwindcss: {},
autoprefixer: {},
},
};
/* Four plugins. No preprocessor.
- postcss-import: inline @import statements
- tailwindcss/nesting: flatten native nesting
- tailwindcss: generate utilities
- autoprefixer: add vendor prefixes
*/
The ten parts progressed from the PostCSS pipeline through native variables, native nesting, @apply, browserslist, autoprefixer, @supports, mixing Tailwind with custom CSS, the Sass question, and the recommended pipeline. The same card and button scenarios ran through every part, showing how the layers fit together without a preprocessor.
Quick Reference
PostCSS Pipeline Order
| Order | Plugin | Purpose |
|---|---|---|
| 1 | postcss-import | Inline @import statements |
| 2 | tailwindcss/nesting | Flatten native nesting |
| 3 | tailwindcss | Generate utilities |
| 4 | autoprefixer | Add vendor prefixes |
Native vs Preprocessor
| Feature | Native CSS | Sass | Less |
|---|---|---|---|
| Variables | var(--x) | $x | @x |
| Nesting | & syntax | Implicit | Implicit |
| Mixins | No | @mixin | .mixin() |
| Functions | calc(), color-mix() | @function | Built-in |
| Browser support | Modern browsers | Compiled | Compiled |
| Build step | None | Required | Required |
Autoprefixer Configuration
| File | Purpose |
|---|---|
package.json browserslist | Targets for prefixing |
.browserslistrc | Alternative location |
postcss.config.js | Plugin order |
Browser Targets
| Query | Meaning |
|---|---|
> 0.5% | Browsers with >0.5% market share |
last 2 versions | Two most recent per browser |
not dead | Exclude unsupported browsers |
defaults | Autoprefixer’s default target |
Best Practices
✅ Do This:
/* Use native CSS variables. */
:root { --brand: #1da1f2; } /* ✅ */
/* Use native CSS nesting for custom CSS. */
.card { & .title { font-weight: bold; } } /* ✅ */
/* Let autoprefixer handle prefixes. */
user-select: none; /* ✅ */
/* Configure browserslist for the project's targets. */
"browserslist": ["> 0.5%", "last 2 versions", "not dead"] /* ✅ */
/* Place autoprefixer after tailwindcss in the pipeline. */
plugins: { tailwindcss: {}, autoprefixer: {} } /* ✅ */
/* Use @supports for fallbacks, not prefixes. */
@supports (display: grid) { ... } /* ✅ */
/* Keep the pipeline minimal. */
postcss-import, nesting, tailwindcss, autoprefixer /* ✅ */
❌ Don’t Do This:
/* Don't write vendor prefixes by hand. */
-webkit-user-select: none; /* ❌ Let autoprefixer add them */
/* Don't add Sass for features that are now native. */
$color: #1da1f2; /* ❌ Use var(--color) */
/* Don't place autoprefixer before tailwindcss. */
plugins: { autoprefixer: {}, tailwindcss: {} } /* ❌ Wrong order */
/* Don't skip browserslist configuration. */
// No browserslist in package.json /* ❌ Uses defaults */
/* Don't use Sass mixins where @apply suffices. */
@mixin button { ... } /* ❌ Use @apply */
/* Don't ignore @supports for modern features. */
display: grid; /* ❌ Provide a flex fallback */
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| Prefixes missing | Autoprefixer before Tailwind | Reorder: Tailwind first |
| Nesting not compiled | No nesting plugin | Add tailwindcss/nesting |
| @apply fails in nested rules | Nesting not processed first | Place nesting before Tailwind |
| Wrong prefixes added | Incorrect browserslist | Configure targets correctly |
| Build slowed by Sass | Preprocessor for native features | Remove Sass; use native CSS |
| Variables not themed | Sass variables used | Use CSS variables |
| @supports ignored | Autoprefixer does not handle fallbacks | Write fallbacks manually |
| Pipeline too complex | Too many PostCSS plugins | Keep only the necessary four |
Real-World Examples
1. The PostCSS Config
module.exports = {
plugins: {
'postcss-import': {},
'tailwindcss/nesting': 'postcss-nesting',
tailwindcss: {},
autoprefixer: {},
},
};
2. Native CSS Variables
:root { --brand: #1da1f2; }
.card { border: 1px solid var(--brand); }
3. Native Nesting
.card {
& .title { font-weight: bold; }
&:hover { border-color: blue; }
}
4. Nesting with @apply
.button {
@apply rounded-md px-4 py-2;
&:hover { @apply bg-blue-700; }
}
5. Browserslist
"browserslist": ["> 0.5%", "last 2 versions", "not dead"]
6. Autoprefixer Output
.user-select-none {
-webkit-user-select: none;
user-select: none;
}
7. @supports Fallback
.grid { display: flex; }
@supports (display: grid) {
.grid { display: grid; }
}
8. Custom Layer
@layer components {
.prose-custom {
& h2 { @apply mt-8 text-2xl font-semibold; }
}
}
9. Dart Sass Installation (if needed)
npm install -D sass
10. Pipeline Verification
npx postcss src/app.css -o dist/app.css
Visual
The PostCSS Pipeline
┌────────────────────────────────────────────────────────────┐
│ POSTCSS PIPELINE ORDER │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ app.css │ │
│ │ @import "./base.css"; │ │
│ │ @tailwind base; │ │
│ │ @tailwind components; │ │
│ │ @tailwind utilities; │ │
│ │ .card { & .title { ... } } │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 1. postcss-import │ │
│ │ Inlines @import statements. │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 2. tailwindcss/nesting │ │
│ │ Flattens & nesting to plain selectors. │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 3. tailwindcss │ │
│ │ Generates utilities, resolves @apply. │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 4. autoprefixer │ │
│ │ Adds vendor prefixes per browserslist. │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────┐ │
│ │ dist/app.css │ │
│ │ Plain CSS, utilities included, prefixed. │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────┘
What Preprocessors Were For
┌────────────────────────────────────────────────────────────┐
│ PREPROCESSOR FEATURES → NATIVE EQUIVALENTS │
│ │
│ ┌────────────────────┬────────────────────────────┐ │
│ │ Sass / Less │ Native CSS │ │
│ ├────────────────────┼────────────────────────────┤ │
│ │ $var │ var(--var) │ │
│ │ .parent .child {} │ .parent { & .child {} } │ │
│ │ @mixin / .mixin() │ @apply (Tailwind) │ │
│ │ @function │ calc(), color-mix() │ │
│ │ @extend │ Reuse via classes │ │
│ └────────────────────┴────────────────────────────┘ │
│ │
│ The features that motivated Sass and Less now exist │
│ natively. The remaining gaps are narrow and rarely │
│ justify a preprocessor in a new Tailwind project. │
│ │
└────────────────────────────────────────────────────────────┘
Autoprefixer Decision Flow
┌────────────────────────────────────────────────────────────┐
│ AUTOPREFIXER DECISION FLOW │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Source: user-select: none; │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Read browserslist targets: │ │
│ │ "> 0.5%", "last 2 versions", "not dead" │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ v │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Check which targets need -webkit- for this │ │
│ │ property. │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────┴─────────┐ │
│ │ │ │
│ v v │
│ TARGETS NEED TARGETS DO NOT │
│ PREFIX NEED PREFIX │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ -webkit-user- │ │ user-select: none│ │
│ │ select: none; │ │ (no prefix) │ │
│ │ user-select: none;│ │ │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
│ The developer writes standard CSS. │
│ Autoprefixer decides per project targets. │
│ │
└────────────────────────────────────────────────────────────┘
Pipeline Complexity
┌────────────────────────────────────────────────────────────┐
│ RECOMMENDED vs LEGACY PIPELINE │
│ │
│ RECOMMENDED (2025) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ postcss-import │ │
│ │ tailwindcss/nesting │ │
│ │ tailwindcss │ │
│ │ autoprefixer │ │
│ └──────────────────────────────────────────────────┘ │
│ Four plugins. No preprocessor. │
│ │
│ LEGACY (2018) │
│ ┌──────────────────────────────────────────────────┐ │
│ │ gulp-sass → compile SCSS │ │
│ │ gulp-autoprefixer → add prefixes │ │
│ │ gulp-cssnano → minify │ │
│ │ gulp-rename → rename output │ │
│ │ gulp-sourcemaps → source maps │ │
│ │ gulp-concat → concatenate files │ │
│ └──────────────────────────────────────────────────┘ │
│ Six tools. One preprocessor. More moving parts. │
│ │
│ The modern pipeline is smaller because the features │
│ those tools provided are now native or built into │
│ PostCSS and Tailwind. │
│ │
└────────────────────────────────────────────────────────────┘
Summary
| Item | Value |
|---|---|
| PostCSS | CSS transformer; runs plugins in order |
| Tailwind plugin | Generates utilities; is a PostCSS plugin |
postcss-import | Inlines @import statements |
tailwindcss/nesting | Flattens native nesting before Tailwind |
| Autoprefixer | Adds vendor prefixes per browserslist |
| Native variables | var(--x), supported everywhere |
| Native nesting | & syntax, supported in modern browsers |
| Browserslist | Target configuration for autoprefixer |
@supports | Fallbacks for features that need them |
| Sass | Optional; not required for modern Tailwind |
@apply | Inlines utilities into custom CSS |
Key takeaways:
- A Tailwind project already uses PostCSS. Tailwind is a PostCSS plugin. The pipeline is the standard place for
postcss-import,tailwindcss, andautoprefixer. Adding a preprocessor is a separate decision. - Native CSS now covers what preprocessors were for. Variables, nesting, and color functions exist in CSS. The features that motivated Sass and Less are no longer missing from the platform.
- Native nesting is supported in modern browsers. The
&syntax compiles correctly. For older browser targets,postcss-nestingflattens it during the build. - The nesting plugin runs before Tailwind.
tailwindcss/nestingensures that@applyworks inside nested rules by flattening nesting before Tailwind resolves the directives. - Autoprefixer adds prefixes automatically. The developer writes standard CSS. Autoprefixer reads browserslist and adds only the prefixes the project’s targets need.
- Pipeline order matters.
postcss-import, then nesting, thentailwindcss, thenautoprefixer. Placing autoprefixer before Tailwind leaves the generated utilities unprefixed. @supportshandles fallbacks, not prefixes. Autoprefixer does not rewrite@supports. Features that need a fallback require a manual block.- The default is no preprocessor. For a new Tailwind project, the four-plugin PostCSS pipeline is sufficient. Sass remains an option for legacy codebases or teams with specific needs, but it is not the default.
Remember: Tailwind sits on top of PostCSS, and PostCSS is the place where CSS transformations happen. The recommended pipeline runs postcss-import, then tailwindcss/nesting, then tailwindcss, then autoprefixer. Native CSS variables and native CSS nesting cover the features that preprocessors were once required for, so a new project does not need Sass or Less. The nesting plugin flattens native nesting before Tailwind runs, so @apply works inside nested rules. Autoprefixer reads browserslist and adds only the prefixes the project’s targets need, so developers never write prefixes by hand. The result is a pipeline of four plugins, no preprocessor, and plain CSS as the output. The layers that once required Sass now live in the platform, and the build stays smaller as a result.
Stop using slow, ad-bloated tool sites! 🤮
🔎 Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)
⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free
🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!