Skip to content
WovenUI

WovenUI vs UnoCSS

The utility-first framework for Sass teams. WovenUI gives you classes with readable names, the same utilities as apply() mixins for any custom value, and semantic theme tokens that cover light, dark and high contrast from one markup. Mistakes stop the build with the fix in the message, and Dart Sass is the only build tool you need: no engine, no config file in JavaScript and no transformer to enable.

Side by side

TopicWovenUIUnoCSS
Class namesNames you can read: between, x-center, bg-surface, z-modal. Every class is also a mixin for apply(), so one vocabulary covers your HTML and your SCSS. Spacing is direction-aware for RTL: px, mx and space-x are logical, alongside ms, me, ps, pe and text-start.With the Wind presets, Tailwind-style names with prefix variants [7]. The wind4 preset is described as the Tailwind4 compact preset [6].
Custom valuesAny value, with clean markup: apply(top(117px), bg(#bada55)) in SCSS, checked by the compiler. Class names stay on your configured scale. A unitless number is a scale key; a value with a unit is used as written. Reusable combinations are ordinary classes written with apply().Bracket values in class names, plus custom rules and shortcuts in the config. A dynamic rule matches a RegExp and returns CSS from a function [10]; a shortcut combines utilities under one name [11].
Catching mistakesMistakes stop the build with the fix in the message. An unknown key, color, token or modifier in a mixin, the configuration or token() fails the compile and lists the valid options: apply(p(7)) gives p: 7 is not a configured key. Valid keys: 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 16, 20, 24, 32, 40, 48, 56, 64. Write a unit for a custom value (7px, 7rem). In markup, the VS Code extension flags conflicting classes such as p-4 p-2, and unknown classes when you turn that rule on.In the @apply transformer source, a name that does not resolve is dropped without a warning or an error [9].
Dark mode and themesOne markup for every theme, with no dark: classes. Semantic tokens switch with data-theme="light", "dark" or "inverse" on any element; without data-theme the operating system decides. data-contrast="high" applies your $high-contrast-light and $high-contrast-dark maps. Text on a brand or status fill (text-on-brand) is picked for contrast at compile time, with a warning below 4.5:1.The dark: variant. In the wind3 preset it is class-based by default, with a media-query option [7]. Dark styles are written per utility unless you define your own semantic values.
Design tokensSemantic roles out of the box: --wu-* custom properties for surfaces, text, borders, focus, brand and status roles, plus the palette and every scale. Classes and mixins output var(--wu-*), and token(name) raises an error for an unknown name, so a typo never reaches the browser. Colors are generated from $brand and the role maps, with shades 50 to 950.A theme object in the config, deep-merged into the default theme [12]; theme('colors.blue.500') compiles to a value in CSS [8]. In wind4, theme variables are emitted in a theme layer, on demand by default [6].
apply() in your stylesBuilt in: @include apply(...) is a Sass mixin, available in any .scss file with nothing to enable. Each item takes a custom value, a state and a breakpoint: apply(p(13px), bg(#bada55), shadow(md) hover, p(6) md), emitted in a fixed order, so item order never changes the result.@apply is provided by the directives transformer, which you enable in transformers; @apply text-center my-0 font-medium; is replaced by the equivalent declarations [8].
Component stylesMade for component stylesheets. @wovenui/core/mixins has the full API and outputs no CSS: configure once in a partial, and each CSS module or <style lang="scss"> block compiles to its own rules only. A card module with apply(p(4), bg(surface), rounded(md), shadow(md) hover, p(6) md) compiles to 206 bytes minified.The Vite plugin has modes for different layouts: global (default), vue-scoped, per-module (experimental), dist-chunk (experimental) and shadow-dom; Svelte scoped styles are a separate package [3].
Responsive and statesThe utility comes first and the breakpoint follows it: p-8@lg, hover:shadow-md. In SCSS every modifier follows the utility: apply(p(8) lg, shadow(md) hover).Variants are prefixes. Breakpoints include xl:, lt-sm: and at-lg: [7].
ConfigurationOne Sass @use ... with (...): $brand, scale maps such as $spacings and $z-index-levels, theme override maps and feature flags. Everything is validated at compile time, and a new color is one key in a theme map that gets classes and mixins.A uno.config.ts file with presets [5] and rules: a matcher (a string or a RegExp) plus a body, where later rules have higher priority [10]. A preset is a partial configuration that can be shared [13].
SetupOnly Sass: install the sass package (Dart Sass 1.79 or later, below 2.0) and @use "@wovenui/core". Vite, Next.js (Turbopack and webpack) and Astro resolve it with no extra configuration; Svelte adds vitePreprocess(). No framework plugin, no config file in JavaScript, no PostCSS plugin and no runtime JavaScript.A Vite plugin from unocss/vite, a uno.config.ts file and an import of virtual:uno.css [3]. A PostCSS plugin exists, documented as experimental [4]. Integrations also exist for Webpack, Rollup, Nuxt, Next, Astro, a CLI and the runtime [14].
CSS you shipOnly what you use. By default you ship only the rules you write: tokens and base styles are 5.8 KB gzipped, and each component adds only its own rules, with no purge step. Feature flags switch whole groups off. Using the prebuilt file with every class? See Ship only the classes you use.Searches your codebase for utility usage and generates the matching CSS on demand, plus preset preflights such as the reset and theme variables [2][6]. Only statically written class names are found: p-${size} is not detected, so the docs point to a safelist or a lookup object [2].
Ready-made patternsFree, step-by-step recipes for a card, navbar, form field, dark mode toggle, dashboard layout and pricing table. Core styles plain HTML buttons and form fields, includes CSS-only tooltips, and works with any component library you already use.Presets for pure CSS icons from Iconify, web font loading, typography, attributify mode and tagify mode [5].
Framework guidesSetup guides for React, Next.js, Vue, Svelte, Astro and plain HTML, plus a playground to try SCSS and markup in the browser.Integrations for Vite, Webpack, Rollup, Nuxt, Next, Astro, a CLI and the runtime [14].
Editor supportThe WovenUI extension for VS Code: completion, hover docs, color swatches and one-click fixes for apply(), classes and tokens. It rewrites Tailwind-style classes too, as used by the Wind presets: lg:p-4 becomes p-4@lg.An official VS Code extension (decorations, tooltips, autocomplete), a JetBrains plugin, Zed and LSP integrations, an ESLint plugin, an Inspector, an online Playground and Interactive Docs [1][15][16].
AI assistantsA complete guide ships in the package at docs/llms-full.txt and on this site at /llms-full.txt, with a short index at /llms.txt, so an assistant writes valid WovenUI code.UnoCSS publishes unocss.dev/llms.txt and unocss.dev/llms-full.txt.
LicenseMIT.MIT.

The same component in both

One card, written both ways: a title, text and an action, a hover shadow and more padding from the large breakpoint. The WovenUI version uses theme token classes (bg-surface, text-muted, border), so the same markup follows the theme: it is rendered below in a light and a dark section. The UnoCSS version uses palette classes, so dark mode needs a dark: class for each color. The apply() and @apply pair shows one custom value, one state and one breakpoint in each.

WovenUI: HTML
<div class="flex col gap-3 p-6 p-8@lg bg-surface border rounded-lg
            shadow-sm hover:shadow-md">
  <h3 class="text-lg font-semibold">Card title</h3>
  <p class="text-muted">Some quick example text.</p>
  <a href="#" class="self-start px-4 py-2 rounded-md bg-brand text-on-brand">Go somewhere</a>
</div>
UnoCSS: HTML (wind4 preset)
<div class="flex flex-col gap-3 rounded-lg border border-gray-200 bg-white p-6
            shadow-sm hover:shadow-md lg:p-8 dark:border-gray-700 dark:bg-gray-800">
  <h3 class="text-lg font-semibold text-gray-900 dark:text-white">Card title</h3>
  <p class="text-gray-600 dark:text-gray-400">Some quick example text.</p>
  <a href="#" class="self-start rounded-md bg-blue-600 px-4 py-2 text-white">Go somewhere</a>
</div>
WovenUI: SCSS with apply()
@use "@wovenui/core" as *;

.card {
  @include apply(
    flex, col, gap(3), p(13px), bg(#bada55),
    shadow(md) hover, p(6) md
  );
}

.card--themed {
  @include apply(p(4), bg(surface), text-color(muted), border(1), rounded(lg));
}
UnoCSS: @apply with a custom value, a state and a breakpoint
.card {
  @apply flex flex-col gap-3 p-[13px] bg-[#bada55] hover:shadow-md md:p-6;
}
WovenUI: configuration in SCSS
@use "@wovenui/core" as * with (
  $brand: #4361ee,
  $generate-utility-classes: true
);
UnoCSS: uno.config.ts
import { defineConfig, presetWind4, transformerDirectives } from 'unocss'

export default defineConfig({
  presets: [presetWind4()],
  transformers: [transformerDirectives()],
})

Moving from UnoCSS

  1. Rename layout classes: flex-col is col, justify-between is between and items-center is x-center, each on a .flex element. See Flex.
  2. Move breakpoint prefixes to suffixes: md:p-8 becomes p-8@md. See Spacing and Media queries.
  3. Turn @apply lines into @include apply(...) in your SCSS, with the same names written as mixins. Bracket values become values with a unit: p-[13px] becomes p(13px). See apply().
  4. Replace custom rules, shortcuts and the theme object with Sass configuration, theme override maps, and your own classes written with apply(). See Configuration and Theming.
  5. Replace dark: class pairs with semantic token classes such as bg-surface, text-muted and border. See Tokens.
  6. Remove the UnoCSS plugin, uno.config.ts and the virtual:uno.css import once nothing uses them, and install sass. Keep a separate package for icons or web fonts if you used those presets.
  7. Look up any mixin or function in the A to Z reference.