npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vite-plugin-tailwind-legacy

v1.2.0

Published

Plugin Vite para gerar CSS Tailwind v4 compatível com navegadores antigos, utilizando tailwind v3

Readme

vite-plugin-tailwind-legacy

⚡ Vite plugin that generates Tailwind v4 CSS with legacy browser compatibility fallback using Tailwind CSS v3.

Read this in other languages: Português

✨ Why use it?

Problem:

❌ Tailwind v4 uses modern CSS features like oklch() that break in older browsers

❌ Conventional polyfills don't completely solve the problem

❌ Maintaining two CSS versions manually is cumbersome

Solution:

✅ Automatically generates a Tailwind v3 fallback for legacy browsers

✅ ZERO impact on Tailwind v4 for modern browsers

✅ Only legacy devices receive the fallback CSS

🎬 See the demos in the images below 👇

The plugin's goal is to maintain a consistent experience on legacy browsers without compromising Tailwind v4's advantages (for modern browsers). With careful development for both realities, your site will have very similar appearance and functionality in any browser - modern or old - while you continue leveraging all the advanced v4 features in your workflow.

✨ Features

This plugin executes an extra step after the build to:

  • Generate legacy CSS with tailwindcss@3 compatible with older browsers

  • Dynamically inject the legacy CSS in browsers that don't support Tailwind v4 (like Chrome < 111) through a browser-check.js script in the build HTML files

🚀 Installation

npm install --save-dev vite-plugin-tailwind-legacy
# or
yarn add vite-plugin-tailwind-legacy --dev

⚙️ Configuration

  • Add the vite-plugin-tailwind-legacy plugin to your Vite config.
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
import TailwindLegacyPlugin from 'vite-plugin-tailwind-legacy'; 

export default defineConfig(({ command }) => {
  const isBuild = command === 'build';

   return {
      plugins: [
        react(),
        tailwindcss(),
        TailwindLegacyPlugin({
          tailwindConfig: 'tailwind.config.legacy.js',
          assetsDir: 'dist/assets',
          publicPath: '/static/assets/',
          injectInHTML: true,
        })
      ],      
}
})

🛠 Important Information

⚠️ Special Attention - Before proceeding, pay attention to these two important pieces of information:

1 - The content field - it should point to your Vite's final build:

content: ["./dist/**/*.{html,js}"],  // 👈 Verify this path matches your files built by Vite

2 - Colors

Tailwind v4 uses oklch() as the default color format, which doesn't work in older browsers. To ensure functionality:

  • Use hexadecimal colors (#RRGGBB) in your theme
  • Convert oklch values (https://oklch.com)

Basic example:

colors: {
  primary: '#1445e2',       // Compatible format
  secondary: '#4f46e5'      // Compatible format
}

⚙️ Tailwind Configuration (Legacy Version)

📁 Create a tailwind.config.legacy.js file in the same directory as your vite.config.ts with this base structure (Change colors to match your theme):

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./dist/**/*.{html,js}"],
  darkMode: ['class'],
  theme: {
    extend: {
      colors: {
        background: '#fefeff',
        foreground: '#333333',
        card: '#ffffff',
        "card-foreground": '#333333',
        popover: '#ffffff',
        "popover-foreground": '#333333',
        primary: '#1445e2',
        "primary-foreground": '#f9f9f9',
        secondary: '#0598f6',
        "secondary-foreground": '#f9f9f9',
        muted: '#f0f0ff',
        "muted-foreground": '#7a7a99',
        accent: '#b76b2c',
        "accent-foreground": '#ffffff',
        destructive: '#e74c3c',
        "destructive-foreground": '#fff2f2',
        border: '#e0e0f0',
        input: '#e0e0f0',
        ring: '#333333',
        chart: {
          1: '#c96d3f',
          2: '#47c1a9',
          3: '#4297e7',
          4: '#8a64d1',
          5: '#df5d8f',
        },
        base: {
          100: '#fefeff',
          200: '#f0f0ff',
          300: '#e3e3ff',
          content: '#333333',
        },
        
        neutral: '#6b6b8d',
        "neutral-content": '#fefeff',
        info: '#5bbef0',
        "info-content": '#f6faff',
        success: '#63d182',
        "success-content": '#f0fff5',
        warning: '#f6d365',
        "warning-content": '#fff9e3',
        error: '#e74c3c',
        "error-content": '#fff2f2',
        
        sidebar: '#fbfbff',
        "sidebar-foreground": '#252525',
        "sidebar-primary": '#252525',
        "sidebar-primary-foreground": '#fbfbff',
        "sidebar-accent": '#f0f0ff',
        "sidebar-accent-foreground": '#252525',
        "sidebar-border": '#e8e8e8',
        "sidebar-ring": '#737373',
      },

      borderRadius: {
        sm: 'calc(0.625rem - 4px)',
        md: 'calc(0.625rem - 2px)',
        lg: '0.625rem',
        xl: 'calc(0.625rem + 4px)',
      },

      height: {
           ...Array.from({ length: 1000 }, (_, i) => i + 1).reduce((acc, val) => {
          acc[val] = `${val * 0.25}rem`;
          return acc;
        }, {}),
      },
      width: {
           ...Array.from({ length: 1000 }, (_, i) => i + 1).reduce((acc, val) => {
          acc[val] = `${val * 0.25}rem`;
          return acc;
        }, {}),
      },
      padding: {
        ...Array.from({ length: 1000 }, (_, i) => i + 1).reduce((acc, val) => {
          acc[val] = `${val * 0.25}rem`;
          return acc;
        }, {}),
      },
      margin: {
        ...Array.from({ length: 1000 }, (_, i) => i + 1).reduce((acc, val) => {
          acc[val] = `${val * 0.25}rem`;
          return acc;
        }, {}),
      },
      gap: {
           ...Array.from({ length: 1000 }, (_, i) => i + 1).reduce((acc, val) => {
          acc[val] = `${val * 0.25}rem`;
          return acc;
        }, {}),
      },
      zIndex: {
        ...Array.from({ length: 1000 }, (_, i) => i + 1).reduce((acc, val) => {
          acc[val] = `${val}`;
          return acc;
        }, {}),
      },
    },
  },
  plugins: [
    require('tailwindcss-animate'), // Remove this if you don't use it
    function({ addBase }) {
          addBase({
            'button': {
              backgroundColor: 'transparent',
              backgroundImage: 'none',
              padding: 0,
              border: 'none',
              outline: 'none',
              cursor: 'pointer',
            },
          });
        },
  ],
};

📝 How to Use

  1. Install the plugin as shown above
  2. Configure your Vite config according to the example
  3. Create the Tailwind configuration file
  4. Run your Vite build normally [it might ask for authorization to install tailwind-v3 [this shouldn't interfere with your project as it won't install in your project's package.json]]

🛠 Specific Configurations

Default configs:

TailwindLegacyPlugin({
  tailwindConfig: 'tailwind.config.legacy.js', // Change the tailwindConfig directory
  inputCSS: 'input.css', // Change the input CSS filename
  assetsDir: 'dist/assets', // Change the output directory for generated files
  publicPath: '/static/assets/',  // Change the public path
  buildDir: 'dist', // Change the build output directory to scan for HTML files
  injectInHTML: true,   // Enable insertion of legacy browser check script
  deleteStyles: ['app', 'layout'],  // Select which stylesheet will be deleted, if empty deletes all
  outputCSSName: 'tailwind-v3-legacy.css', // Change the name of the generated css file, must end with .css
})

📂 tailwindConfig

Default: 'tailwind.config.legacy.js'

What it does: Specifies the path to the Tailwind v3 configuration file

When to change: If you want to use a different name for the configuration file or directory

Example:

tailwindConfig: './config/tailwind-legacy-config.js'

📄 inputCSS

Default: 'input.css'

What it does: Specifies the temporary input CSS file name that will be created during the build process

When to change: If you need to avoid conflicts with existing files in your project

Example:

inputCSS: 'legacy-input.css'
// or
inputCSS: 'src/test/legacy-input.css'

📁 assetsDir

Default: 'dist/assets'

What it does: Defines where the generated CSS and JS files will be saved

When to change: If your build uses a different directory structure

Example:

assetsDir: 'build/static'

🌐 publicPath

Default: '/static/assets/'

What it does: Controls the public path used to load assets in the HTML

When to change: When assets are served from a different path

Example:

publicPath: '/static/' // relative path
// or
publicPath: 'https://cdn.example.com/assets/' // full URL with host

📂 buildDir

Default: 'dist'

What it does: Specifies the build output directory where HTML files are located for script injection

When to change: When using different frameworks (e.g., Nuxt uses .output/public, Next.js uses out, etc.)

Example:

buildDir: '.output/public'  // For Nuxt
// or
buildDir: 'out'  // For Next.js static export

✨ injectInHTML

Default: true

What it does: Automatically injects the legacy browser check script

When to disable (false): For SSG (like Next.js, Gatsby), you can use the backend (Django, for example) to check the client's browser before serving the HTML, avoiding misconfiguration, and you can already inject the output.css generated by the plugin.

For server-side implementations (Django, Rails, etc), see:

BACKEND-INTEGRATION.md
(Includes examples of browser detection and controlled fallback)

🎯 deleteStyles

Default: [] (empty array)

What it does: Defines which CSS files will be removed in legacy browsers. If empty, removes ALL styles. If filled, removes only the specified ones.

When to use: When you want to preserve some styles (e.g., component CSS, critical styles) and only replace Tailwind.

Example:

deleteStyles: ['app', 'layout'] // removes app.css, app-abc123.css, layout.css, layout-min.css, etc.
// or 
deleteStyles: ['app.css', 'layout.css'] // removes app.css, layout.css, etc.

📄 outputCSSName

Default: 'tailwind-v3-legacy.css'

What it does: Defines the name of the CSS file generated by Tailwind v3 for legacy browsers.

When to change: To customize the fallback file name or avoid conflicts.

Example:

outputCSSName: 'legacy-styles.css' // generates legacy-styles.css instead of default.

🔄 Browser Compatibility

🚨 Legacy Browser Limitations

The Flexbox/Grid gap feature doesn't have full support in:

  • Chrome < 84 (released in 2020)
  • Firefox < 80 (released in 2020)
  • Safari < 14.1 (released in 2021)
  • Edge < 84 (released in 2020)

You can work around this by using other CSS classes in your project, keeping in mind compatibility with very old browsers.

❓ How does it work?

  1. The vite-plugin-tailwind-legacy creates a v3-based CSS file for legacy browsers

  2. It adds a script to HTML files that checks if it's a legacy browser, and if so, uses output.css as a fallback for legacy browsers.

🔄 vite-plugin-tailwind-legacy Flow

graph TD
    A[Browser Accesses Page] --> B[Loads Default Tailwind v4]
    B --> C{Is Legacy Browser?}
    C -->|NO - Modern Browser| D[Do Nothing]
    C -->|YES - Legacy Browser| E[Remove Tailwind v4 Dynamically]
    E --> F[Inject Tailwind v3 CSS]

    style D fill:#005a1c,stroke:#166534
    style F fill:#005a1c,stroke:#166534
    style E fill:#c67000,stroke:#92400e

🔍 Examples

Before and after the plugin on Chrome 85.0.4183.102 mobile (just generated output.css and applied it to the site via Developer Tools)

Shadcn https://ui.shadcn.com/docs/components/avatar

Sites in development with Tailwind v4 =>

Before and after the plugin on Chrome 85.0.4183.102 mobile

📊 Performance

  • Zero impact on modern browsers (no extra CSS loaded)
  • Minimal impact on legacy browsers (only one additional HTTP request)
  • Generated CSS is automatically minified