tailwindcss, windicss, and @unocss/core represent three generations of utility-first CSS engines. tailwindcss is the industry standard, offering a robust, stable ecosystem with a JIT (Just-In-Time) compiler that scans files for class names. windicss was designed as a faster, feature-rich alternative to Tailwind v2, introducing features like variant groups and attribute mode, but has since been deprecated in favor of UnoCSS. @unocss/core is the engine behind UnoCSS, a next-generation, highly modular framework that reimagines the utility CSS concept with instant build times, deep customization via rules, and support for multiple presets beyond just Tailwind compatibility.
Utility-first CSS has transformed how we style web applications, moving from predefined component libraries to composable class names. tailwindcss, windicss, and @unocss/core (the engine of UnoCSS) are the key players in this space. While they share a similar goal—generating small CSS bundles from utility classes—their internal architectures, extensibility, and current maintenance statuses differ significantly.
Before diving into technical comparisons, it is vital to address the status of windicss. The project is officially deprecated. The creator has archived the repository and explicitly stated that development has ceased. All new features and improvements intended for Windi CSS have been merged into UnoCSS.
Recommendation: Do not start new projects with windicss. If you are currently using it, plan a migration to either tailwindcss for stability or unocss (powered by @unocss/core) for advanced features.
// windicss: DEPRECATED - No longer receiving updates or security patches
// import { defineConfig } from 'windicss/helpers'; // Do not use in new projects
The fundamental difference lies in how these tools find and generate styles.
tailwindcss uses a scanner-based JIT engine. It scans your content files (HTML, JS, TS) to identify class names, then generates the corresponding CSS. While highly optimized, this process involves parsing files, which can add latency in very large monorepos or during rapid development.
// tailwind.config.js
module.exports = {
content: ["./src/**/*.{html,js,ts,jsx,tsx}"],
theme: { extend: {} },
// Tailwind scans these files to find classes like 'bg-blue-500'
};
@unocss/core (UnoCSS) operates as an on-demand engine. Instead of a heavy scan, it often integrates directly with build tools (like Vite) to intercept code transformations. It generates styles instantly as you type, without needing to re-scan the entire file tree. It treats CSS generation as a compiler plugin rather than a post-processing step.
// uno.config.ts
import { defineConfig, presetUno } from 'unocss';
export default defineConfig({
presets: [presetUno()], // Uses @unocss/core under the hood
// No 'content' array needed in Vite plugin mode; it hooks into the transform pipeline
});
windicss introduced a faster scanner than early Tailwind versions and supported "attribute mode," but its architecture is now obsolete compared to UnoCSS's modular approach.
// windicss (Legacy): Supported attribute mode
// <div btn primary>Click</div>
// This feature is now better supported and extended in UnoCSS
tailwindcss relies on a configuration object. You extend the theme, add plugins, or modify variants using a structured JS/TS object. This is easy to read but can become limiting if you need to generate complex, dynamic CSS that doesn't fit the theme model.
// tailwind.config.js: Extending theme
module.exports = {
theme: {
extend: {
colors: {
brand: '#FF5733',
},
// Adding a custom utility requires a plugin
}
},
plugins: [
function({ addUtilities }) {
addUtilities({
'.text-shadow': { 'text-shadow': '0 2px 4px rgba(0,0,0,0.5)' },
});
}
]
};
@unocss/core uses a rules-based system. While it supports a Tailwind-compatible config for ease of migration, its true power lies in defining custom rules and shortcuts programmatically. This allows you to create dynamic utilities that respond to regex patterns or complex logic.
// uno.config.ts: Defining custom rules
import { defineConfig } from 'unocss';
export default defineConfig({
rules: [
// Regex rule: matches 'text-shadow-[color]' and generates CSS dynamically
[/^text-shadow-(.+)$/, ([_, color]) => ({ 'text-shadow': `0 2px 4px ${color}` })],
// Static rule
['btn', { 'padding': '0.5rem 1rem', 'border-radius': '0.25rem' }]
],
shortcuts: {
'primary-btn': 'btn bg-blue-600 text-white hover:bg-blue-700'
}
});
Both Windi CSS (historically) and UnoCSS support variant groups, a syntax sugar that reduces repetition when applying multiple states (hover, focus, dark mode) to a single element. Tailwind CSS recently added similar support, but UnoCSS's implementation is deeply integrated and highly customizable.
<!-- Tailwind CSS (Modern) -->
<div class="hover:[&>div]:text-red-500 focus:[&>div]:text-blue-500">
<!-- Verbose for complex nested variants -->
</div>
<!-- UnoCSS / Windi CSS (Variant Groups) -->
<div class="hover:[&>div]:(text-red-500 font-bold) focus:[&>div]:(text-blue-500)">
<!-- Cleaner grouping syntax -->
</div>
Windi CSS popularized using HTML attributes instead of classes. UnoCSS fully supports this via presets, allowing for cleaner HTML in certain frameworks (like Vue or Svelte). Tailwind CSS does not support attribute mode natively.
<!-- UnoCSS with attributes preset -->
<div bg="blue-500" text="white" p="4">Hello</div>
<!-- Tailwind CSS: Classes only -->
<div class="bg-blue-500 text-white p-4">Hello</div>
UnoCSS includes a dedicated @unocss/preset-icons that allows you to use any icon from Iconify directly as a utility class, compiling them into pure CSS/SVG. Tailwind requires separate plugins or manual SVG embedding.
// UnoCSS: Direct icon usage in class
<div class="i-carbon-logo-github"></div>
// Automatically compiles to the GitHub logo SVG
// Tailwind CSS: Requires external plugin or manual SVG
// <svg class="w-6 h-6">...</svg>
In small to medium projects, the build speed difference between Tailwind and UnoCSS is negligible. However, as the codebase grows, @unocss/core's architecture shines. Because it avoids full-file scanning in favor of transform hooks, it offers near-instant feedback in development mode, even in massive monorepos.
Tailwind's build times are still excellent for most use cases, but the "scanning" step can become a bottleneck in watch mode if the content glob matches thousands of large files.
# Conceptual comparison of build feedback
# Tailwind: Scans files -> Updates CSS (Fast, but dependent on file count)
# UnoCSS: Intercepts import/transform -> Updates CSS (Instant, independent of file count)
Despite their architectural differences, all three share the core philosophy of utility-first CSS.
All three use prefix-based breakpoints for responsive styles.
<!-- All Packages -->
<div class="w-full md:w-1/2 lg:w-1/3">Responsive Column</div>
Support for dark mode toggling is standard across the board.
<!-- All Packages -->
<div class="bg-white dark:bg-gray-900 text-black dark:text-white">
Dark Mode Support
</div>
Developers can break out of the design system when needed using square brackets.
<!-- All Packages -->
<div class="w-[357px] bg-[#1da1f2]">
Custom arbitrary values
</div>
| Feature | Tailwind CSS | Windi CSS | UnoCSS (@unocss/core) |
|---|---|---|---|
| Status | ✅ Active / Standard | ❌ Deprecated | ✅ Active / Next-Gen |
| Engine | JIT Scanner | JIT Scanner (Legacy) | On-Demand / Transform Hook |
| Config Style | Theme Object | Theme Object | Rules & Shortcuts + Presets |
| Attribute Mode | ❌ No | ✅ Yes | ✅ Yes (via Preset) |
| Icon Support | Via Plugin | Limited | ✅ Built-in (Iconify) |
| Extensibility | Plugins | Plugins | Rules, Shortcuts, Presets |
| Best For | Stability, Ecosystem | None (Legacy only) | Performance, Customization |
tailwindcss remains the industry standard. If you are building a typical SaaS product, marketing site, or enterprise app and want a tool that "just works" with endless tutorials, plugins, and team familiarity, choose Tailwind. It is the safe, pragmatic choice.
windicss is historical context. It pushed the boundaries of what utility CSS could do (attribute mode, variant groups), but its life cycle has ended. Its spirit lives on in UnoCSS.
@unocss/core (via UnoCSS) is the architect's choice. If you need to squeeze out every millisecond of build performance, want to define custom CSS logic that Tailwind's config can't handle, or desire a unified system for icons, typography, and utilities, UnoCSS is superior. It requires a slightly steeper learning curve to master its rules system but offers unmatched flexibility.
Final Thought: For most teams today, the choice is between the stability of Tailwind and the future-proof power of UnoCSS. Avoid Windi CSS for any new initiative.
Choose @unocss/core (via the unocss package) if you need maximum performance, deep customization, or a modular architecture. It is ideal for teams building design systems from scratch, requiring custom CSS rules that don't fit standard utility patterns, or those who want to combine Tailwind compatibility with other CSS strategies like icons or typography in a single engine.
Choose tailwindcss for production applications where long-term stability, extensive community support, and a vast plugin ecosystem are critical. It is the safest bet for teams needing a standard solution with guaranteed maintenance and clear upgrade paths. Opt for this if you want the 'batteries-included' experience without needing to configure low-level engine details.
Do NOT choose windicss for new projects. The project is officially deprecated and archived by its author, who recommends migrating to UnoCSS. Using it now introduces technical debt and security risks due to lack of updates. Only consider this if you are maintaining a legacy codebase that cannot be immediately refactored.
The core engine of UnoCSS without any presets. It can be used as the engine of your own atomic CSS framework.
Please refer to the documentation.
MIT License © 2021-PRESENT Anthony Fu