Skip to content
NuvoUI

The SCSS foundation for design systems.

Classes you can read, tokens for every theme, and plain CSS out.

npm install -D sass @nuvoui/coreGet started

Every theme. One markup.

Light, dark, inverse, high contrast and right to left. The classes stay the same; only three attributes change.

Read it once. Know what it does.

The first row of that inbox, and the markup that builds it.

HTML
<label class="group flex x-center gap-3 px-4 py-3 border-b-1 hover:bg-hover cursor-pointer">
  <input type="checkbox">
  <span class="w-2 h-2 rounded-full no-shrink bg-brand"></span>
  <span class="w-24 w-32@md no-shrink truncate font-semibold" dir="auto">Mara Lindqvist</span>
  <span class="grow min-w-0 truncate" dir="auto">
    <span class="font-semibold">Q4 roadmap review moved to Thursday</span>
    <span class="hide inline@md text-muted">Pushing it to 14:00 so finance can join. Agenda is unchanged.</span>
  </span>
  <span class="hide flex@lg pill px-2 text-xs bg-blue-subtle text-blue">Planning</span>
  <time class="w-16@md no-shrink text-end text-xs text-muted">09:42</time>
</label>

Any value. One class.

The spacing scale has 1rem and 1.25rem. This row wants 1.125rem, and apply() takes it as written.

SCSS
@use "@nuvoui/core" as *;

.mail-row {
  @include apply(
    flex, x-center, gap(3),
    px(4), py(1.125rem),
    border-bottom(1), cursor(pointer),
    bg(hover) hover
  );
}
Compiled CSS
.mail-row {
  display: flex;
  align-items: center;
  gap: var(--nu-space-3);
}
.mail-row > * {
  --nu-flex-gap: var(--nu-space-3);
}
.mail-row {
  padding-inline: var(--nu-space-4);
  padding-block: 1.125rem;
  border-bottom-width: var(--nu-border-width-1);
  border-bottom-style: var(--nu-border-style);
  border-bottom-color: var(--nu-border-regular);
  --nu-border-style: solid;
  cursor: pointer;
}
.mail-row:hover {
  background-color: var(--nu-bg-hover);
}

It refuses mistakes.

Drop the unit and the build stops, before the mistake can reach a browser.

SCSS
@use "@nuvoui/core" as *;

.mail-row {
  @include apply(
    flex, x-center, gap(3),
    px(4), py(1.125),
    border-bottom(1), cursor(pointer),
    bg(hover) hover
  );
}
Sass output
Error: "py: 1.125 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 (1.125px, 1.125rem)."

Specifications

NuvoUI core 2.0.1

Output
Plain CSS. Core ships no JavaScript and needs no runtime or PostCSS plugin.
Language
SCSS modules, loaded with @use. Requires Dart Sass 1.79.0 or later, below 2.0.
Classes and mixins
Every utility is also a mixin for apply(), with states and breakpoints. Utility classes are opt-in through $generate-utility-classes.
Tokens
Every scale is a --nu- custom property: colour, space, type, radius, shadow, motion.
Themes
Light, dark and inverse through data-theme, high-contrast overrides through data-contrast="high". Without data-theme, the operating system colour scheme decides.
Colour
Roles and palette generated in OKLCH, in stops 50, 100 to 900, and 950.
Direction
Logical utilities such as ps, me, start, border-s and text-start mirror under dir="rtl".
Errors
Unknown keys and colour names stop the compile and list the valid options. Text on a brand or status fill below 4.5:1 raises a warning.
Licence
MIT.

Start with one line.

npm install -D sass @nuvoui/coreRead the documentation