From 89cbfc7b2dfaafec38f2123fc9047b0d6a49f1de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Rouleau?= Date: Fri, 17 Oct 2025 11:00:25 -0400 Subject: [PATCH] Add `optimize` option to `@tailwindcss/vite` plugin (#19131) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an `optimize` option to the Vite plugin that matches the API and behavior of the PostCSS plugin. Supports three formats: - `optimize: false` - disable optimization - `optimize: true` - enable optimization with minification - `optimize: { minify: false }` - enable optimization without minification 🤖 Generated with [Claude Code](https://claude.ai/code) --------- Co-authored-by: Claude Co-authored-by: Jordan Pittman Co-authored-by: Robin Malfait --- integrations/vite/index.test.ts | 109 ++++++++++++++++++++++++ packages/@tailwindcss-vite/README.md | 40 +++++++++ packages/@tailwindcss-vite/src/index.ts | 42 +++++++-- 3 files changed, 182 insertions(+), 9 deletions(-) diff --git a/integrations/vite/index.test.ts b/integrations/vite/index.test.ts index 3afb057c5..ead71b472 100644 --- a/integrations/vite/index.test.ts +++ b/integrations/vite/index.test.ts @@ -914,3 +914,112 @@ test( function firstLine(str: string) { return str.split('\n')[0] } + +test( + 'optimize option: disabled', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss({ optimize: false })], + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + }, + }, + async ({ exec, expect, fs }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] + + // Should not be minified when optimize is disabled + let content = await fs.read(filename) + expect(content).toContain('.hover\\:flex {') + expect(content).toContain('&:hover {') + expect(content).toContain('@media (hover: hover) {') + expect(content).toContain('display: flex;') + }, +) + +test( + 'optimize option: enabled with minify disabled', + { + fs: { + 'package.json': json` + { + "type": "module", + "dependencies": { + "@tailwindcss/vite": "workspace:^", + "tailwindcss": "workspace:^" + }, + "devDependencies": { + "vite": "^7" + } + } + `, + 'vite.config.ts': ts` + import tailwindcss from '@tailwindcss/vite' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssMinify: false }, + plugins: [tailwindcss({ optimize: { minify: false } })], + }) + `, + 'index.html': html` + + + + +
Hello, world!
+ + `, + 'src/index.css': css` + @reference 'tailwindcss/theme'; + @import 'tailwindcss/utilities'; + `, + }, + }, + async ({ exec, expect, fs }) => { + await exec('pnpm vite build') + + let files = await fs.glob('dist/**/*.css') + expect(files).toHaveLength(1) + let [filename] = files[0] + + // Should be optimized but not minified + let content = await fs.read(filename) + expect(content).toContain('@media (hover: hover) {') + expect(content).toContain('.hover\\:flex:hover {') + expect(content).toContain('display: flex;') + }, +) diff --git a/packages/@tailwindcss-vite/README.md b/packages/@tailwindcss-vite/README.md index 7d21bd883..53e03ab47 100644 --- a/packages/@tailwindcss-vite/README.md +++ b/packages/@tailwindcss-vite/README.md @@ -34,3 +34,43 @@ For help, discussion about best practices, or feature ideas: ## Contributing If you're interested in contributing to Tailwind CSS, please read our [contributing docs](https://github.com/tailwindcss/tailwindcss/blob/next/.github/CONTRIBUTING.md) **before submitting a pull request**. + +--- + +## `@tailwindcss/vite` plugin API + +### Enabling or disabling Lightning CSS + +By default, this plugin detects whether or not the CSS is being built for production by checking the `NODE_ENV` environment variable. When building for production Lightning CSS will be enabled otherwise it is disabled. + +If you want to always enable or disable Lightning CSS the `optimize` option may be used: + +```js +import tailwindcss from '@tailwindcss/vite' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [ + tailwindcss({ + // Disable Lightning CSS optimization + optimize: false, + }), + ], +}) +``` + +It's also possible to keep Lightning CSS enabled but disable minification: + +```js +import tailwindcss from '@tailwindcss/vite' +import { defineConfig } from 'vite' + +export default defineConfig({ + plugins: [ + tailwindcss({ + // Enable Lightning CSS but disable minification + optimize: { minify: false }, + }), + ], +}) +``` diff --git a/packages/@tailwindcss-vite/src/index.ts b/packages/@tailwindcss-vite/src/index.ts index 00ba92db4..ab9635d10 100644 --- a/packages/@tailwindcss-vite/src/index.ts +++ b/packages/@tailwindcss-vite/src/index.ts @@ -18,12 +18,20 @@ const SPECIAL_QUERY_RE = /[?&](?:worker|sharedworker|raw|url)\b/ const COMMON_JS_PROXY_RE = /\?commonjs-proxy/ const INLINE_STYLE_ID_RE = /[?&]index\=\d+\.css$/ -export default function tailwindcss(): Plugin[] { +export type PluginOptions = { + /** + * Optimize and minify the output CSS. + */ + optimize?: boolean | { minify?: boolean } +} + +export default function tailwindcss(opts: PluginOptions = {}): Plugin[] { let servers: ViteDevServer[] = [] let config: ResolvedConfig | null = null let isSSR = false - let minify = false + let shouldOptimize = true + let minify = true let roots: DefaultMap = new DefaultMap((id) => { let cssResolver = config!.createResolver({ @@ -65,8 +73,22 @@ export default function tailwindcss(): Plugin[] { async configResolved(_config) { config = _config - minify = config.build.cssMinify !== false isSSR = config.build.ssr !== false && config.build.ssr !== undefined + + // By default we optimize CSS during the build phase but if the user + // provides explicit options we'll use those instead + if (opts.optimize !== undefined) { + shouldOptimize = opts.optimize !== false + } + + // Minification is also performed when optimizing as long as it's also + // enabled in Vite + minify = shouldOptimize && config.build.cssMinify !== false + + // But again, the user can override that choice explicitly + if (typeof opts.optimize === 'object') { + minify = opts.optimize.minify !== false + } }, }, @@ -116,12 +138,14 @@ export default function tailwindcss(): Plugin[] { } DEBUG && I.end('[@tailwindcss/vite] Generate CSS (build)') - DEBUG && I.start('[@tailwindcss/vite] Optimize CSS') - result = optimize(result.code, { - minify, - map: result.map, - }) - DEBUG && I.end('[@tailwindcss/vite] Optimize CSS') + if (shouldOptimize) { + DEBUG && I.start('[@tailwindcss/vite] Optimize CSS') + result = optimize(result.code, { + minify, + map: result.map, + }) + DEBUG && I.end('[@tailwindcss/vite] Optimize CSS') + } return result },