Skip to content
NuvoUI

NuvoUI vs Tailwind CSS

Both are utility-first. Tailwind is a CSS build tool that generates classes from your markup, including arbitrary values. NuvoUI is an SCSS framework: readable class names, the same utilities as apply() mixins, and theme tokens, compiled by Dart Sass alone.

Side by side

TopicTailwind CSSNuvoUI
PhilosophyUtility-first: style elements by combining single-purpose utility classes directly in markup [1].Utility-first SCSS with human-readable names. Every utility class is also a mixin for apply(), so the same vocabulary works in HTML and in component stylesheets.
SyntaxVariants are prefixes: hover:bg-sky-700, md:uppercase (mobile-first, applies from the breakpoint up) [1][2].Breakpoints are suffixes, states are prefixes: p-8@lg, hover:shadow-md. In SCSS, modifiers follow the utility: apply(p(8) lg, shadow(md) hover).
Custom valuesArbitrary values in square brackets in markup, generated on the fly: top-[117px], bg-[#bada55] [3].No arbitrary values in class names. Custom values go through mixins: apply(top(117px), bg(#bada55)). A unitless number is a scale key; a value with a unit is used as written.
ErrorsScans source files as plain text and throws away any token that does not map to a utility it knows, so a class name it does not recognize generates no CSS [11].Mixins, configuration and token() are checked at compile time. An unknown key, color or unit stops the compile and lists the valid options. apply(p(7)) fails with: 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). A misspelled class name in markup is not checked; it matches no rule.
CustomizationCSS-first configuration with the @theme directive; theme variables decide which utility classes exist [4].Sass configuration in the first @use ... with (...): $brand, scale maps, theme override maps and feature flags, validated at compile time.
Theming and dark modedark: variant, based on prefers-color-scheme by default; a manual toggle is set up with @custom-variant [5]. Dark styles are written per utility unless you define your own semantic theme variables.Semantic tokens switch with data-theme="light", "dark" or "inverse" on any element, with an operating system fallback. A [data-contrast="high"] hook applies $high-contrast-light and $high-contrast-dark override maps; both maps are empty by default.
RTLrtl: and ltr: variants [6], and logical utilities such as ms-*, me-*, ps-*, pe-* [7][8].Logical utilities and mixins (ms, me, ps, pe, start-*, end-*, border-s, rounded-s-*, text-start) alongside physical ones. No rtl: variant; direction-specific overrides are written in SCSS.
Design tokensTheme variables are also emitted as regular CSS variables, for example --color-mint-500 [4].--nu-* custom properties for surfaces, text, borders, focus, brand and status roles, the palette and every scale. Classes and mixins output var(--nu-*); token(name) raises an error for an unknown name.
Build requirementsInstalled as a Vite plugin, a PostCSS plugin or the Tailwind CLI [9]. v4 targets Chrome 111, Safari 16.4 and Firefox 128 and later [10].Dart Sass 1.79 or later (below 2.0), the sass package. Vite, Next.js (Turbopack and webpack) and Astro compile SCSS once it is installed, and @use "@nuvoui/core" resolves with no extra configuration; Svelte adds vitePreprocess(). No framework plugin, no JavaScript, no runtime, no CLI and no PostCSS plugin.
Bundle approachScans source files as plain text and generates only the classes it finds. Class names built by string concatenation are not detected [11].No scanning of its own. With $generate-utility-classes: true every configured class is emitted (about 1 MB minified, 93 KB gzipped with the defaults). PurgeCSS with a NuvoUI extractor removes the classes a project does not use: in our React and Vite sample the stylesheet went from 990 KB to 37 KB minified and from 92 KB to 5.6 KB gzipped, with identical computed styles. See Ship only the classes you use. Feature flags switch whole groups off. Mixin-only use emits what you write plus about 43 KB of base CSS (tokens, reset, base elements, tooltips), once, from the global stylesheet. apply() writes its declarations into each selector that uses it, so a style shared by many components is repeated in each of their rules.
Where styles liveUtility classes directly in markup [1]. Repeated styles are reused through a component in React, Svelte or Vue, or a template partial [1].Utility classes go in markup exactly as in Tailwind, in class or className. Vue, Svelte and Astro components can also keep a <style lang="scss"> block with apply() in the same file; React uses a CSS module next to the component. See the React, Vue, Svelte and Astro guides.
Component stylesEach CSS module and each Vue, Svelte or Astro <style> block is processed separately, so @apply needs @reference to the global stylesheet. The docs recommend utility classes in markup instead [10].@nuvoui/core/mixins has the same API as @nuvoui/core and outputs no CSS. Configure once in a partial and load the global stylesheet once; each CSS module or <style lang="scss"> block adds only its own rules, about 0.25 KB per component in our builds.
JS componentsNone in the framework. Tailwind Plus is a paid, one-time purchase with components, templates and the Catalyst UI kit [12]; Headless UI provides unstyled, accessible React and Vue components, MIT licensed [13].None. Core is a foundation layer with no component library. It styles plain HTML buttons and form fields, and includes CSS-only tooltips.
EcosystemOfficial installation guides for Vite, PostCSS, the CLI and many frameworks [9], plus Tailwind Plus and Headless UI.Small: the core package and its documentation, with setup guides for React, Next.js, Vue, Svelte, Astro and plain HTML. No plugin ecosystem or community templates.
LicenseMIT [14].MIT.

The same component in both

A card with a title, text and an action, a hover shadow and more padding from the large breakpoint. The Tailwind version uses palette classes, so dark mode needs a dark: class for each color. The NuvoUI version uses theme token classes (bg-surface, text-muted, border), so the same markup follows the theme.

Tailwind CSS: HTML
<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>
NuvoUI: 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>
Tailwind CSS: theme
@import "tailwindcss";

@theme {
  --color-mint-500: oklch(0.72 0.11 178);
}
NuvoUI: SCSS with apply()
@use "@nuvoui/core" as * with (
  $brand: #4361ee
);

.card {
  @include apply(
    flex, col, gap(3),
    p(6), bg(surface), rounded(lg), border(1),
    shadow(md) hover, p(8) lg
  );
}

.card__text {
  @include apply(text-color(muted), mb(1.25rem));
}

Moving from Tailwind CSS

  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. Replace arbitrary values in markup with a class from the configured scale, or with a mixin in SCSS: @include apply(top(117px)). See apply().
  4. Replace @theme variables with Sass configuration and theme override maps. See Configuration and Theming.
  5. Replace dark: class pairs with semantic token classes such as bg-surface, text-muted and border. See Tokens.
  6. Look up any mixin or function in the A to Z reference.

Sources

Every statement about Tailwind CSS on this page comes from its official documentation or repository. Statements about NuvoUI come from the core source, its changelog and the core documentation.

  1. [1] Tailwind CSS: Styling with utility classeshttps://tailwindcss.com/docs/styling-with-utility-classes
  2. [2] Tailwind CSS: Responsive designhttps://tailwindcss.com/docs/responsive-design
  3. [3] Tailwind CSS: Adding custom styleshttps://tailwindcss.com/docs/adding-custom-styles
  4. [4] Tailwind CSS: Theme variableshttps://tailwindcss.com/docs/theme
  5. [5] Tailwind CSS: Dark modehttps://tailwindcss.com/docs/dark-mode
  6. [6] Tailwind CSS: Hover, focus and other stateshttps://tailwindcss.com/docs/hover-focus-and-other-states
  7. [7] Tailwind CSS: Marginhttps://tailwindcss.com/docs/margin
  8. [8] Tailwind CSS: Paddinghttps://tailwindcss.com/docs/padding
  9. [9] Tailwind CSS: Installing with Vitehttps://tailwindcss.com/docs/installation/using-vite
  10. [10] Tailwind CSS: Compatibilityhttps://tailwindcss.com/docs/compatibility
  11. [11] Tailwind CSS: Detecting classes in source fileshttps://tailwindcss.com/docs/detecting-classes-in-source-files
  12. [12] Tailwind CSS blog: Tailwind UI is now Tailwind Plushttps://tailwindcss.com/blog/tailwind-plus
  13. [13] Headless UI repositoryhttps://github.com/tailwindlabs/headlessui
  14. [14] Tailwind CSS repository (MIT license)https://github.com/tailwindlabs/tailwindcss