惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Vercel News
Vercel News
博客园_首页
Y
Y Combinator Blog
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
阮一峰的网络日志
阮一峰的网络日志
aimingoo的专栏
aimingoo的专栏
H
Hackread – Cybersecurity News, Data Breaches, AI and More
MyScale Blog
MyScale Blog
GbyAI
GbyAI
人人都是产品经理
人人都是产品经理
T
Tailwind CSS Blog
MongoDB | Blog
MongoDB | Blog
D
DataBreaches.Net
博客园 - Franky
Engineering at Meta
Engineering at Meta
量子位
The GitHub Blog
The GitHub Blog
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
酷 壳 – CoolShell
酷 壳 – CoolShell
N
Netflix TechBlog - Medium

Nx Blog

Sharing Tailwind CSS Styles Across Apps in a Monorepo | Nx Blog How SiriusXM Stays Competitive by Iterating and Getting to Market Fast | Nx Blog Agentic Experience Is the New Developer Experience | Nx Blog Nx Joins the Linux Foundation and the Agentic AI Foundation | Nx Blog A Monorepo Is NOT a Monolith | Nx Blog Why we deleted (most of) our MCP tools | Nx Blog Teach Your AI Agent How to Work in a Monorepo | Nx Blog How Broadcom stays efficient and nimble with monorepos | Nx Blog Why Monorepos are King in the Age of AI | Nx Blog Nx 2026 Roadmap: Expanding Agent Autonomy, Improving Performance, Better Polyglot and More | Nx Blog End to End Autonomous AI Agent Workflows with Nx | Nx Blog Autonomous Agents at Scale | Nx Blog Scaling 700+ Projects: How Nx Became a 'No-Brainer' for Caseware | Nx Blog The Missing Multiplier for AI Agent Productivity | Nx Blog A Year of Nx Webinars | Nx Blog Wrapping Up 2025 | Nx Blog Nx 22.3 Release: Angular 21 Support, tsgo Compiler, and Prettier v3 | Nx Blog Nx Cloud Release: Agent Resource Usage | Nx Blog Nx Platform Outperforms DIY Cache by 5x | Nx Blog An Nx Carol: Past, Present, and Future of Your Monorepo | Nx Blog Nx 22.1 Release: Terminal UI on Windows, Storybook 10, Vitest 4, and more! | Nx Blog The Compounding Effect: How Nx Features Multiply Performance Gains | Nx Blog 10 Monorepo Myths Debunked: Separating Fact from Fiction | Nx Blog Nx Cloud Release: Enterprise Task Analytics | Nx Blog Watch and Rebuild Storybook Dependencies with Nx | Nx Blog Book - React for Enterprise: Timeless Architecture for Enterprise Apps | Nx Blog Beyond Remote Cache: Unlock 70% More CI Performance | Nx Blog Nx 22 Release: Expanding the build platform | Nx Blog What's the Point of Generating All This Code If You Can't Merge It? | Nx Blog What's New in Nx Self-Healing CI | Nx Blog
Configure Tailwind v4 with Angular in an Nx Monorepo | Nx...
Juri Strumpflohner · 2026-01-15 · via Nx Blog

Tailwind CSS v4 brought significant simplifications: no more tailwind.config.js, minimal dependencies, and a simple CSS import to get started. But if you're using Angular with Tailwind v4 in an Nx monorepo, there's a catch: your production CSS might be larger than it needs to be.

This article explains how Tailwind v4's scanning works with Angular's PostCSS setup, why it can lead to bloated CSS output, and how to optimize it using @source directives and Nx Sync Generators.

Using Vite in an NPM Workspace?

Setting Up Tailwind v4 with Angular

Setting up Tailwind v4 with Angular is straightforward. Both the Angular docs and Tailwind docs have guides for this. But they miss one important aspect when working in a monorepo, which is what we're digging into today.

Since Angular uses PostCSS for CSS processing, you need the PostCSS plugin.

Install the required packages:

{
  "dependencies": {
    "@tailwindcss/postcss": "^4.1.13",
    "tailwindcss": "^4.1.13"
  }
}

Configure PostCSS in your application:

apps/demoapp/.postcssrc.json

{
  "plugins": {
    "@tailwindcss/postcss": {}
  }
}

Import Tailwind in your styles entry point:

apps/demoapp/src/styles.css

@import 'tailwindcss';

That's it. Your Angular app now has Tailwind v4 configured. But here's where things get interesting in a monorepo setup.

The Problem: Over-Scanning in Monorepos

Consider a typical Nx workspace structure:

apps/
  demoapp/           # Your Angular application
libs/
  ui-design-system/  # Shared UI components (used by demoapp)
  another-ui/        # Another library (NOT used by demoapp)

When you run nx build demoapp, you'd expect only the Tailwind classes from demoapp and its dependency ui-design-system to end up in the final CSS. But that's not what happens by default.

Let's say another-ui has a component with this template:

libs/another-ui/src/lib/another-ui/another-ui.html

<p class="text-red-500">AnotherUi works!</p>

Even though demoapp never imports another-ui, if you check your production CSS output, you'll find text-red-500 in there. This happens for every unused class in every library in your workspace.

Why Does This Happen?

The @tailwindcss/postcss plugin uses process.cwd() as its default scanning base:

// @tailwindcss/postcss source
let base = opts.base ?? process.cwd();

When you run nx build demoapp, process.cwd() is your workspace root. Tailwind scans from there, finding all .html, .ts, and other template files across your entire monorepo.

This is different from the @tailwindcss/vite plugin, which uses the Vite config root (typically the app directory) as its base. With Vite you have the opposite problem: not enough is included by default, so you must explicitly add library sources. If you're using Vite with React or other frameworks, check out the Tailwind v4 with Vite in an NPM Workspace guide.

You could restrict the scanning by providing the base property in your PostCSS config:

apps/demoapp/.postcssrc.json

{
  "plugins": {
    "@tailwindcss/postcss": {
      "base": "./apps/demoapp/src"
    }
  }
}

But then you'd need to manually add @source directives for all your library dependencies. We'll look at a more automated approach.

Comparing PostCSS vs Vite Plugin Behavior

PluginDefault BaseMonorepo BehaviorNeeds @source for libs?
@tailwindcss/postcssprocess.cwd()Scans entire workspaceOnly if you restrict with source()
@tailwindcss/viteconfig.rootScans app directory onlyYes, always

With the PostCSS plugin, you get all classes from all libraries. With the Vite plugin, you get only classes from the app and must explicitly add library sources. Both approaches benefit from automation to manage @source directives.

Solution: Restricting Scanning with @source Directives

Tailwind v4 provides the source() function and @source directive to control which directories get scanned.

Step 1: Restrict Automatic Scanning

Update your styles entry point to limit automatic scanning to just your app:

apps/demoapp/src/styles.css

@import 'tailwindcss' source('./app');

The source("./app") modifier tells Tailwind to only auto-scan the app subdirectory relative to this CSS file.

Step 2: Add Library Dependencies

Now you need to explicitly add your library dependencies:

apps/demoapp/src/styles.css

@import 'tailwindcss' source('./app');

@source "../../../libs/ui-design-system/src";

With this configuration:

  • Classes from apps/demoapp/src/app/** are included (via source("./app"))
  • Classes from libs/ui-design-system/src/** are included (via @source)
  • Classes from libs/another-ui/** are NOT included

Your production CSS now contains only what you actually use.

Automating @source with Nx Sync Generators

Manually maintaining @source directives works, but introduces maintenance burden:

  • Easy to forget when adding new dependencies
  • Missing styles don't break builds (just cause visual bugs)
  • Every team member needs to remember to update paths

Nx Sync Generators solve this by automatically keeping your @source directives in sync with your project dependencies.

Using @juristr/nx-tailwind-sync

In the Tailwind v4 with Vite in an NPM Workspace article, I explained how to build a custom sync generator to automate @source directive management. I've since packaged this into @juristr/nx-tailwind-sync so you can use it directly.

Install it:

npm install @juristr/nx-tailwind-sync

Register the sync generator in your application's project.json:

apps/demoapp/project.json

{
  "name": "demoapp",
  "targets": {
    "build": {
      "executor": "@angular/build:application",
      "syncGenerators": ["@juristr/nx-tailwind-sync:source-directives"],
      ...
    },
    "serve": {
      "executor": "@angular/build:dev-server",
      "syncGenerators": ["@juristr/nx-tailwind-sync:source-directives"],
      ...
    }
  }
}

Now when you run nx build demoapp or nx serve demoapp, the sync generator:

  1. Analyzes your application's dependency graph
  2. Generates @source directives for each dependency
  3. Updates your styles.css automatically

Your styles file gets managed markers:

apps/demoapp/src/styles.css

@import 'tailwindcss' source('./app');

/* nx-tailwind-sources:start */
@source "../../../libs/ui-design-system";
/* nx-tailwind-sources:end */

When you add a new library dependency, the next build or serve detects the change and updates the directives automatically.

How Sync Generators Work

Sync generators run before certain targets (like build or serve) and check if generated files are in sync with your source code. If something is out of sync, Nx prompts you to apply the changes.

This is the same mechanism Nx uses to keep TypeScript project references in sync with your dependency graph. Learn more in the Sync Generators documentation.

Alternative: PostCSS Plugin Approach

Community member Poul Hansen created a PostCSS plugin that achieves similar automation at build time rather than through sync generators.

This approach uses the createGlobPatternsForDependencies function from @nx/angular/tailwind to dynamically inject sources during the PostCSS processing phase. Check out the gist for implementation details.

Conclusion

Tailwind v4's simplified setup is great for standalone projects, but requires attention in monorepos. With Angular's PostCSS-based setup, the default behavior scans your entire workspace, potentially bloating your production CSS with unused classes.

By combining source() restrictions with @source directives and automating their maintenance through Nx Sync Generators, you get optimal CSS output without manual upkeep.

Learn More