Skip to content

[@tailwindcss/vite - v4] Content scanning doesn't work with cross-package paths in monorepo #19313

@2njeong

Description

@2njeong

Issue: Tailwind CSS v4 doesn't scan cross-package content in Vite monorepo

Environment

  • Tailwind CSS: @tailwindcss/vite@4.1.5, tailwindcss@4.1.5
  • Build Tool: Vite 5.0.8
  • Package Manager: pnpm (workspace)
  • Setup: Monorepo with multiple packages

Monorepo Structure

inz-ui/
├── packages/
│   ├── ui/                    # Component library (NO Tailwind dependencies)
│   │   ├── src/
│   │   │   ├── components/
│   │   │   │   └── Button.tsx   # Uses Tailwind classes
│   │   │   └── index.ts
│   │   ├── package.json         # NO tailwindcss in dependencies
│   │   └── vite.config.ts       # NO tailwindcss plugin
│   └── docs/                  # Documentation site (HAS Tailwind)
│       ├── src/
│       │   ├── App.tsx          # Imports from @inz-ui/ui
│       │   └── index.css        # @import 'tailwindcss'
│       ├── package.json         # tailwindcss@4.1.5, @tailwindcss/vite@4.1.5
│       ├── vite.config.ts       # Has tailwindcss() plugin
│       └── tailwind.config.ts   # content: ['../ui/src/**/*.tsx']

Key Point

Only the docs package has Tailwind installed. The goal is for docs' Tailwind instance to scan and process classes from the UI package's source files.

Configuration

ui/package.json

{
  "dependencies": {
    "react": "^19.0.0"
  },
  "devDependencies": {
    // NO tailwindcss or @tailwindcss/vite
  }
}

ui/vite.config.ts

import react from '@vitejs/plugin-react';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [react()],  // NO tailwindcss() plugin
});

docs/package.json

{
  "devDependencies": {
    "@tailwindcss/vite": "^4.1.5",
    "tailwindcss": "^4.1.5"
  }
}

docs/vite.config.ts

import tailwindcss from '@tailwindcss/vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      '@inz-ui/ui': resolve(__dirname, '../ui/src/index.ts'),  // Direct source reference
    },
  },
});

docs/src/index.css

@import 'tailwindcss';

docs/tailwind.config.ts

import type { Config } from 'tailwindcss';

export default {
  content: [
    './index.html',
    './src/**/*.{js,ts,jsx,tsx}',
    '../ui/src/**/*.{js,ts,jsx,tsx}',  // ← UI package source (cross-package)
  ],
} satisfies Config;

ui/src/components/Button.tsx

export const Button = ({ variant = 'primary', children }) => {
  return (
    <button className="bg-blue-500 text-white px-5 py-2.5 rounded-md">
      {children}
    </button>
  );
};

docs/src/App.tsx

import { Button } from '@inz-ui/ui';  // Resolves to ../ui/src/index.ts

function App() {
  return <Button>Click Me</Button>;
}

Expected Behavior

When running vite in the docs package:

  1. Docs' Tailwind instance should scan content: ['../ui/src/**/*.{js,ts,jsx,tsx}']
  2. Find classes like bg-blue-500, text-white, px-5, etc. in UI's Button.tsx
  3. Generate corresponding CSS utilities in docs' bundle
  4. Button should be styled correctly

Actual Behavior

  • Tailwind only processes classes found in docs/src/
  • Classes used in ../ui/src/ files are completely ignored
  • Button renders with no styles (all computed styles are 0 or default)
  • Console/Network shows CSS is loaded, but utility classes are missing

Workaround That Works

Build UI separately with its own Tailwind instance

  1. Add Tailwind to UI package
  2. UI builds its own CSS → dist/style.css
  3. Docs imports the pre-built CSS:
// docs/src/App.tsx
import { Button } from '@inz-ui/ui';
import '@inz-ui/ui/style.css';  // Pre-built CSS from UI package

This works, but requires:

  • Maintaining two separate Tailwind instances
  • Building UI package before running docs
  • Running UI in watch mode during development (vite build --watch)
  • More complex development workflow

Attempts to Fix

❌ Attempt 1: Import UI's CSS in docs (even without Tailwind in UI)

/* docs/src/index.css */
@import 'tailwindcss';
@import '../../ui/src/index.css';  /* Just an empty file or regular CSS */

Result: Still doesn't scan UI components

❌ Attempt 2: Absolute path in content

import { resolve } from 'path';

export default {
  content: [resolve(__dirname, '../ui/src/**/*.{js,ts,jsx,tsx}')],
} satisfies Config;

Result: No change

❌ Attempt 3: Add custom Vite plugin to pre-process UI files

Result: Too complex, shouldn't be necessary

Question

Is cross-package content scanning supported in Tailwind CSS v4 with @tailwindcss/vite?

This is a fundamental monorepo pattern where:

  • A UI library package provides raw components (no build step needed during dev)
  • A consumer package (docs/app) imports those components directly
  • Only the consumer package runs Tailwind to process all classes

This pattern is common in:

  • Design system documentation sites
  • Internal component library development
  • Storybook-style component showcases

Additional Notes

  • Tailwind CSS v3 + PostCSS: Works perfectly with the same setup
  • Vite's HMR: Works fine for component changes, just no styles
  • Same-package scanning: Works (docs' own files are processed correctly)
  • Issue is specifically: Cross-package file scanning in v4

The problem appears to be with how @tailwindcss/vite resolves the content glob patterns. It seems to only scan files within the same package, not relative paths outside the package root.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions