| |

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

OrderPluginPurpose
1postcss-importInline @import statements
2tailwindcss/nestingFlatten native nesting
3tailwindcssGenerate utilities
4autoprefixerAdd vendor prefixes

Native vs Preprocessor

FeatureNative CSSSassLess
Variablesvar(--x)$x@x
Nesting& syntaxImplicitImplicit
MixinsNo@mixin.mixin()
Functionscalc(), color-mix()@functionBuilt-in
Browser supportModern browsersCompiledCompiled
Build stepNoneRequiredRequired

Autoprefixer Configuration

FilePurpose
package.json browserslistTargets for prefixing
.browserslistrcAlternative location
postcss.config.jsPlugin order

Browser Targets

QueryMeaning
> 0.5%Browsers with >0.5% market share
last 2 versionsTwo most recent per browser
not deadExclude unsupported browsers
defaultsAutoprefixer’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

PitfallWhy It HappensFix
Prefixes missingAutoprefixer before TailwindReorder: Tailwind first
Nesting not compiledNo nesting pluginAdd tailwindcss/nesting
@apply fails in nested rulesNesting not processed firstPlace nesting before Tailwind
Wrong prefixes addedIncorrect browserslistConfigure targets correctly
Build slowed by SassPreprocessor for native featuresRemove Sass; use native CSS
Variables not themedSass variables usedUse CSS variables
@supports ignoredAutoprefixer does not handle fallbacksWrite fallbacks manually
Pipeline too complexToo many PostCSS pluginsKeep 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

ItemValue
PostCSSCSS transformer; runs plugins in order
Tailwind pluginGenerates utilities; is a PostCSS plugin
postcss-importInlines @import statements
tailwindcss/nestingFlattens native nesting before Tailwind
AutoprefixerAdds vendor prefixes per browserslist
Native variablesvar(--x), supported everywhere
Native nesting& syntax, supported in modern browsers
BrowserslistTarget configuration for autoprefixer
@supportsFallbacks for features that need them
SassOptional; not required for modern Tailwind
@applyInlines 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, and autoprefixer. 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-nesting flattens it during the build.
  • The nesting plugin runs before Tailwind. tailwindcss/nesting ensures that @apply works 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, then tailwindcss, then autoprefixer. Placing autoprefixer before Tailwind leaves the generated utilities unprefixed.
  • @supports handles 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!