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

tailwindcss-chain

v1.1.2

Published

A Vite plugin that lets you chain multiple Tailwind CSS utilities under a shared variant prefix using pipe syntax

Downloads

425

Readme

tailwindcss-chain

A Vite plugin that lets you chain multiple Tailwind CSS utilities under a shared variant prefix using pipe (|) syntax.

The Problem

Tailwind variant prefixes get repetitive fast. When you need several utilities under the same variant, you end up repeating yourself:

<div class="md:hover:bg-blue-600 md:hover:text-white md:hover:scale-105 md:hover:shadow-lg">

The Solution

With tailwindcss-chain, use a pipe to chain utilities under a single variant prefix:

<div class="md:hover:bg-blue-600|text-white|scale-105|shadow-lg">

At build time, the plugin expands this back into standard Tailwind classes. Your output is identical -- your source code is just cleaner.

Installation

npm install tailwindcss-chain
pnpm add tailwindcss-chain
yarn add tailwindcss-chain

Setup

Add the plugin to your vite.config.ts. It must come before the Tailwind CSS plugin, since it uses enforce: 'pre' to transform your source files before Tailwind scans them for classes.

import tailwindcssChain from 'tailwindcss-chain'
import tailwindCSS from '@tailwindcss/vite'

export default defineConfig({
  plugins: [tailwindcssChain(), tailwindCSS()]
})

Syntax

The pipe character | separates utilities that share the same variant prefix:

variant:util1|util2|util3

This expands to:

variant:util1 variant:util2 variant:util3

The variant prefix is everything up to and including the last colon before the first utility. This means stacked variants work naturally.

Examples

| Input | Expands To | |-------|------------| | md:hover:bg-blue-600\|text-white\|scale-105 | md:hover:bg-blue-600 md:hover:text-white md:hover:scale-105 | | md:max-lg:bg-red-500\|scale-110\|shadow-lg | md:max-lg:bg-red-500 md:max-lg:scale-110 md:max-lg:shadow-lg | | dark:bg-gray-900\|text-white\|border-gray-700 | dark:bg-gray-900 dark:text-white dark:border-gray-700 | | [&>svg]:w-5\|h-5\|text-current | [&>svg]:w-5 [&>svg]:h-5 [&>svg]:text-current | | group-hover:md:translate-y-0\|opacity-100\|scale-100 | group-hover:md:translate-y-0 group-hover:md:opacity-100 group-hover:md:scale-100 |

Practical usage

Responsive hover states:

<button class="md:hover:bg-blue-600|text-white|scale-105">
  Submit
</button>

Dark mode theming:

<div class="dark:bg-gray-900|text-white|border-gray-700">
  Content
</div>

Styling child elements with arbitrary variants:

<div class="[&>svg]:w-5|h-5|text-current">
  <svg>...</svg>
</div>

Range breakpoints:

<div class="md:max-lg:bg-red-500|scale-110|shadow-lg">
  Visible at md-lg only
</div>

Laravel Blade Setup

For Laravel Blade templates, the Vite plugin alone is not enough. Blade files are server-rendered by PHP and read from disk by Tailwind -- they never pass through Vite's transform pipeline. You need an additional Blade precompiler to expand chains at compile time.

Add this to your AppServiceProvider:

use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::precompiler(function (string $content) {
        return preg_replace_callback(
            '/((?:(?:[\w-]+|\[[^\]]*\]):)+)((?:[^\s\'"|]+\|)+[^\s\'"|]+)/',
            function ($matches) {
                $prefix = $matches[1];
                $utilities = explode('|', $matches[2]);

                return implode(' ', array_map(fn ($u) => $prefix . $u, $utilities));
            },
            $content
        );
    });
}

Then clear your compiled views so Blade picks up the precompiler:

php artisan view:clear

Why is this needed? Tailwind v4's Vite plugin reads template files directly from disk for class scanning, and Laravel renders Blade on the server. The Blade precompiler expands chains at compile time, so both the HTML output and Tailwind's scanner (via compiled views in storage/framework/views/) see the expanded classes.

Tip: Make sure your CSS includes a @source for compiled views so Tailwind picks up the expanded classes:

@source '../../storage/framework/views/*.php';

How It Works

tailwindcss-chain is a Vite plugin that transforms your source files at build time:

  1. It registers with enforce: 'pre', so it runs before Tailwind's Vite plugin scans for classes.
  2. It finds tokens containing a pipe | outside of brackets, splits on the pipe, and prepends the shared variant prefix to each utility.
  3. The expanded output is what Tailwind sees -- standard, fully-qualified class names.

This means there is zero runtime cost. All expansion happens during the build step, and your production bundle contains only standard Tailwind classes.

Supported file types (Vite transform): .js, .jsx, .ts, .tsx, .html, .vue, .svelte, .astro, .md, .mdx, .blade.php, .php

Server-rendered templates (Blade, etc.): Require the additional precompiler step described above, since these files are not part of Vite's module graph.

Limitations

  • Vite only. This is a Vite plugin. It does not work with webpack, Parcel, or other bundlers.
  • Server-rendered templates need extra setup. Laravel Blade (and other server-rendered templates) require a precompiler because Tailwind reads these files from disk, not through Vite's transform pipeline. See the Laravel Blade Setup section.
  • No pipes inside bracket expressions within a chain. The pipe character inside square brackets (e.g., arbitrary values like [color:red|blue]) is treated as bracket content, not a chain separator. This is by design to avoid ambiguity with CSS selectors and arbitrary values that may contain |.
  • JS/TS files: attribute patterns only. In .js, .jsx, .ts, and .tsx files, chains are only expanded inside attribute patterns like className="hover:a|b". Pipes in regular JS code (bitwise OR, classList.add(...), etc.) are left untouched. If you need to add classes at runtime via JS, use the expanded form: classList.add('hover:a', 'hover:b').

License

MIT