Skip to content

Tokens

A design token is a named value that the whole interface shares: a colour, a spacing step, a font size, a duration. NuvoUI emits every token as a CSS custom property that starts with --nu-, and every utility class and mixin outputs those properties instead of raw values. Change a token and everything that uses it changes; switch the theme and the tokens that belong to the theme change with it.

.card {
padding: var(--nu-space-4);
background-color: var(--nu-bg-surface);
border-radius: var(--nu-radius-lg);
}
  • SCSS is the source. Tokens are generated from the configuration maps and colour variables when core is compiled. There is no separate token file to maintain.
  • Emitted on :root. Fixed tokens (palette, basic colours, scales, font family) sit in a :root-only block. Theme tokens sit in the light block (:root, [data-theme="light"]), and the dark theme redefines only those. Because fixed tokens are declared once on :root, a runtime :root override of one also applies inside [data-theme="light"] sections. See Theming.
  • Emitted without utility classes. Tokens are part of the theme feature, not of the utility classes, so a project that uses only mixins ($generate-utility-classes: false) still gets every token.
  • Decimal keys. A decimal key uses _ in the token name: spacing key 0.5 is --nu-space-0_5.

Tokens fall into tiers by whether they change with the theme.

Tier Examples Changes with the theme
Palette --nu-red-100, --nu-brand-700 No. Fixed shades, stops 50, 100 to 900, 950
Basic --nu-black, --nu-white, --nu-transparent No
Scale --nu-space-4, --nu-radius-md, --nu-z-modal, --nu-duration-200 No
Role --nu-brand, --nu-danger-subtle, --nu-text-red Yes
Theme --nu-bg-surface, --nu-text-muted, --nu-border-regular Yes

The palette never changes. Dark mode works by pointing role and theme tokens at different palette shades: in the default configuration --nu-brand is var(--nu-brand-700) in the light theme and var(--nu-brand-400) in the dark theme, and --nu-brand-700 itself stays the same colour in both.

:root {
--nu-brand-700: #0058bb;
--nu-brand-400: #5ca0ff;
}
:root,
[data-theme="light"] {
--nu-brand: var(--nu-brand-700);
}
[data-theme="dark"] {
--nu-brand: var(--nu-brand-400);
}

Palette hues (red, blue) are plain colours with no meaning. Roles (brand, danger) carry a meaning and point at a palette shade. Brand is a role, not an extra hue.

Family Tokens
Surfaces bg-base (the page), bg-surface (cards, drawers), bg-alternate (table headers, sidebars), bg-raised (menus, popovers, toasts), bg-hover, bg-selected, bg-disabled
Text text-regular, text-muted, text-subtle, text-inverted, text-link, text-link-hover
Borders border-regular, border-strong, border-control, border-control-hover
Focus border-focus (the ring colour, applied as outline-color), border-focus-width, border-focus-offset
Brand brand, brand-hover, brand-active, brand-subtle, brand-border, text-brand, text-on-brand
Statuses The same set for success, danger, warning and info
Error aliases error, error-*, text-error, text-on-error, each pointing at the matching danger token
Hue roles {hue}-subtle, {hue}-border, text-{hue} for gray, red, blue, green, yellow, purple, pink
Palette {name}-{stop} for every role and hue
Basic black, white, transparent
Scales space-*, radius-*, border-width-*, font-family, font-size-*, font-weight-*, line-height-*, shadow-*, opacity-*, z-*, duration-*, ease-*, control-height-*, control-padding-inline-*

There are no delay tokens and no disabled text token: disabled text is expressed with opacity.

  • Prefix and hyphens. Every core token is --nu- followed by words joined with single hyphens: --nu-text-muted, --nu-brand, --nu-space-4.
  • Component variables. Variables that belong to a component always contain a double underscore or a double hyphen after the component name: --nu-btn--BackgroundColor, --nu-sidebar__link--Color. They can never collide with a core token. Core’s tooltips follow this grammar (--nu-tooltip--Background); component packages built on core use it for their own variables.
  • Internal variables. A few utilities use internal custom properties that also start with --nu-, such as --nu-flex-gap, --nu-border-style and --nu-translate-x. They are not tokens: token() rejects them, and they are not part of the theme.
  • Short class names. Only the variables carry the prefix. Classes stay short: text-muted, bg-danger, p-4.

Two words that are easy to confuse each have one meaning.

Word Meaning Examples
regular The normal variant of text and border: neither light nor heavy --nu-text-regular, --nu-border-regular
base The bottom layer of the surface stack: the page --nu-bg-base

State names use past participles: inverted, disabled.

Every role has the same set of tokens. The bare role name is the solid fill:

Token Purpose
--nu-{role} Solid fill at rest
--nu-{role}-hover, --nu-{role}-active The fill when hovered and pressed
--nu-{role}-subtle Tinted background
--nu-{role}-border Soft edge
--nu-text-{role} Role-coloured text on a normal page
--nu-text-on-{role} Text placed on the {role} fill

The roles are brand, success, danger, warning and info; error is an alias of danger. The utility classes bg-{role}, bg-{role}-subtle, text-{role} and text-on-{role} cover the most used tokens; the hover, active and border tokens are used through mixins and token().

There are three ways to reach a token or a colour in SCSS.

Tool Returns Follows the theme Use for
token(name) var(--nu-name) Yes Any token in your own CSS. Checked at compile time
get-color(name, stop) A real colour, such as #0058bb No Sass colour maths and compile-time contrast
var(--nu-name) written by hand Itself Yes Allowed, but not checked: a typo fails silently in the browser
Mixins: bg(brand), p(4) var(--nu-...) Yes Normal styling through apply()
@use "sass:color";
@use "@nuvoui/core" as *;
.notice {
background-color: token(warning-subtle);
border-inline-start: token(border-width-4) solid token(warning);
padding: token(space-4);
}
.email-button {
// Fixed colour for email clients that do not support custom properties
background-color: get-color(brand, 700);
border-color: color.adjust(get-color(brand, 700), $lightness: -10%);
}
.notice {
background-color: var(--nu-warning-subtle);
border-inline-start: var(--nu-border-width-4) solid var(--nu-warning);
padding: var(--nu-space-4);
}
.email-button {
background-color: #0058bb;
border-color: #004088;
}

token() takes the token name without --nu- and returns var(--nu-{name}), with no fallback value. It accepts exactly the names that core emits, so an unknown name fails the build instead of the page.

Input Error
token(text-mutd) token(text-mutd): unknown token. Close matches: text-muted.
token(--nu-text-muted) token(--nu-text-muted): pass the name without the prefix, e.g. token(text-muted).
token(delay-150) token(delay-150): unknown token. Valid names are the --nu- custom properties in :root, without --nu-.
token(brand-default) token(brand-default): renamed in 2.0, use token(brand).
token(error-default) token(error-default): renamed in 2.0, use token(danger).

With the theme feature flag off no tokens are emitted, and token() raises token(name): no tokens are emitted because the theme feature flag is off.

get-color() resolves role and palette names before CSS named colours, so get-color(red) is palette red (#ef4444), not the CSS colour red. It takes the same stops as the palette and never returns var(). Because the result is fixed at compile time, it does not change with the theme and ignores theme overrides; use it only where a real colour value is needed.

Input Result
get-color(brand) The configured $brand
get-color(brand, 700) Shade 700 of the brand scale
get-color(red, 450) get-color(red, 450): invalid shade. Valid shades: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950.

The configuration maps stay available for compile-time maths: map.get($spacings, 4) returns 1rem, not a token.

@use "sass:map";
@use "@nuvoui/core" as *;
.icon {
width: calc(map.get($spacings, 4) * 1.5);
}

Generated from the compiled core build with the default configuration. The Dark column shows the value in the dark theme where it differs; “Same” means the token is defined once and does not change. A consumer configuration ($brand, the theme maps, the scale maps) changes these values.

Surfaces
TokenLightDark
--nu-bg-base#fbfafc#1a1a1a
--nu-bg-surface#fff#2e2e2e
--nu-bg-alternatevar(--nu-gray-100)#242424
--nu-bg-raised#fff#3a3a3a
--nu-bg-hovervar(--nu-gray-100)#3a3a3a
--nu-bg-selectedvar(--nu-brand-50)var(--nu-brand-950)
--nu-bg-disabledvar(--nu-gray-100)#333
Text
TokenLightDark
--nu-text-regular#1a1a1a#f1f1f1
--nu-text-muted#666#a8a8a8
--nu-text-subtle#949494#6e6e6e
--nu-text-inverted#f1f1f1#1a1a1a
--nu-text-linkvar(--nu-brand-700)var(--nu-brand-300)
--nu-text-link-hovervar(--nu-brand-800)var(--nu-brand-200)
Borders and focus
TokenLightDark
--nu-border-regular#d3d5d9#474747
--nu-border-strongvar(--nu-gray-400)var(--nu-gray-500)
--nu-border-controlvar(--nu-gray-500)var(--nu-gray-400)
--nu-border-control-hovervar(--nu-gray-600)var(--nu-gray-300)
--nu-border-focusvar(--nu-brand-600)var(--nu-brand-400)
--nu-border-focus-width2px2px
--nu-border-focus-offset2px2px
Brand and statuses
TokenLightDark
--nu-brandvar(--nu-brand-700)var(--nu-brand-400)
--nu-brand-hovervar(--nu-brand-800)var(--nu-brand-300)
--nu-brand-activevar(--nu-brand-900)var(--nu-brand-200)
--nu-brand-subtlevar(--nu-brand-50)var(--nu-brand-950)
--nu-brand-bordervar(--nu-brand-200)var(--nu-brand-800)
--nu-text-brandvar(--nu-brand-700)var(--nu-brand-300)
--nu-successvar(--nu-success-700)var(--nu-success-400)
--nu-success-hovervar(--nu-success-800)var(--nu-success-300)
--nu-success-activevar(--nu-success-900)var(--nu-success-200)
--nu-success-subtlevar(--nu-success-50)var(--nu-success-950)
--nu-success-bordervar(--nu-success-200)var(--nu-success-800)
--nu-text-successvar(--nu-success-800)var(--nu-success-300)
--nu-dangervar(--nu-danger-700)var(--nu-danger-400)
--nu-danger-hovervar(--nu-danger-800)var(--nu-danger-300)
--nu-danger-activevar(--nu-danger-900)var(--nu-danger-200)
--nu-danger-subtlevar(--nu-danger-50)var(--nu-danger-950)
--nu-danger-bordervar(--nu-danger-200)var(--nu-danger-800)
--nu-text-dangervar(--nu-danger-700)var(--nu-danger-300)
--nu-warningvar(--nu-warning-500)var(--nu-warning-400)
--nu-warning-hovervar(--nu-warning-600)var(--nu-warning-300)
--nu-warning-activevar(--nu-warning-700)var(--nu-warning-200)
--nu-warning-subtlevar(--nu-warning-50)var(--nu-warning-950)
--nu-warning-bordervar(--nu-warning-200)var(--nu-warning-800)
--nu-text-warningvar(--nu-warning-800)var(--nu-warning-300)
--nu-infovar(--nu-info-700)var(--nu-info-400)
--nu-info-hovervar(--nu-info-800)var(--nu-info-300)
--nu-info-activevar(--nu-info-900)var(--nu-info-200)
--nu-info-subtlevar(--nu-info-50)var(--nu-info-950)
--nu-info-bordervar(--nu-info-200)var(--nu-info-800)
--nu-text-infovar(--nu-info-800)var(--nu-info-300)
--nu-text-on-brand#f1f1f1#1a1a1a
--nu-text-on-success#f1f1f1#1a1a1a
--nu-text-on-danger#f1f1f1#1a1a1a
--nu-text-on-warning#1a1a1a#1a1a1a
--nu-text-on-info#f1f1f1#1a1a1a
Error aliases
TokenLightDark
--nu-errorvar(--nu-danger)var(--nu-danger)
--nu-error-hovervar(--nu-danger-hover)var(--nu-danger-hover)
--nu-error-activevar(--nu-danger-active)var(--nu-danger-active)
--nu-error-subtlevar(--nu-danger-subtle)var(--nu-danger-subtle)
--nu-error-bordervar(--nu-danger-border)var(--nu-danger-border)
--nu-text-errorvar(--nu-text-danger)var(--nu-text-danger)
--nu-text-on-errorvar(--nu-text-on-danger)var(--nu-text-on-danger)
Hue roles
TokenLightDark
--nu-gray-subtlevar(--nu-gray-50)var(--nu-gray-950)
--nu-gray-bordervar(--nu-gray-200)var(--nu-gray-800)
--nu-text-grayvar(--nu-gray-700)var(--nu-gray-300)
--nu-red-subtlevar(--nu-red-50)var(--nu-red-950)
--nu-red-bordervar(--nu-red-200)var(--nu-red-800)
--nu-text-redvar(--nu-red-700)var(--nu-red-300)
--nu-blue-subtlevar(--nu-blue-50)var(--nu-blue-950)
--nu-blue-bordervar(--nu-blue-200)var(--nu-blue-800)
--nu-text-bluevar(--nu-blue-700)var(--nu-blue-300)
--nu-green-subtlevar(--nu-green-50)var(--nu-green-950)
--nu-green-bordervar(--nu-green-200)var(--nu-green-800)
--nu-text-greenvar(--nu-green-800)var(--nu-green-300)
--nu-yellow-subtlevar(--nu-yellow-50)var(--nu-yellow-950)
--nu-yellow-bordervar(--nu-yellow-200)var(--nu-yellow-800)
--nu-text-yellowvar(--nu-yellow-800)var(--nu-yellow-300)
--nu-purple-subtlevar(--nu-purple-50)var(--nu-purple-950)
--nu-purple-bordervar(--nu-purple-200)var(--nu-purple-800)
--nu-text-purplevar(--nu-purple-700)var(--nu-purple-300)
--nu-pink-subtlevar(--nu-pink-50)var(--nu-pink-950)
--nu-pink-bordervar(--nu-pink-200)var(--nu-pink-800)
--nu-text-pinkvar(--nu-pink-700)var(--nu-pink-300)
Palette
TokenLightDark
--nu-brand-50#ebf3ffSame
--nu-brand-100#dbeaffSame
--nu-brand-200#bad7ffSame
--nu-brand-300#91beffSame
--nu-brand-400#5ca0ffSame
--nu-brand-500#007bffSame
--nu-brand-600#006be0Same
--nu-brand-700#0058bbSame
--nu-brand-800#004494Same
--nu-brand-900#003270Same
--nu-brand-950#002352Same
--nu-success-50#e5fae6Same
--nu-success-100#cef7d1Same
--nu-success-200#a8ecaeSame
--nu-success-300#7fdb89Same
--nu-success-400#54c365Same
--nu-success-500#28a745Same
--nu-success-600#059333Same
--nu-success-700#007a28Same
--nu-success-800#005e1dSame
--nu-success-900#004513Same
--nu-success-950#00300bSame
--nu-danger-50#ffeeedSame
--nu-danger-100#ffdfdeSame
--nu-danger-200#ffc1bfSame
--nu-danger-300#ff9997Same
--nu-danger-400#fd6066Same
--nu-danger-500#dc3545Same
--nu-danger-600#c72037Same
--nu-danger-700#ab0728Same
--nu-danger-800#88001cSame
--nu-danger-900#670013Same
--nu-danger-950#4c000bSame
--nu-warning-50#fff6e5Same
--nu-warning-100#fff2d8Same
--nu-warning-200#ffeabeSame
--nu-warning-300#ffdf9cSame
--nu-warning-400#ffd16cSame
--nu-warning-500#ffc107Same
--nu-warning-600#dca600Same
--nu-warning-700#b28600Same
--nu-warning-800#866300Same
--nu-warning-900#5e4500Same
--nu-warning-950#3d2b00Same
--nu-info-50#e4f8fcSame
--nu-info-100#ccf3fbSame
--nu-info-200#a4e6f4Same
--nu-info-300#79d4e6Same
--nu-info-400#4cbdd2Same
--nu-info-500#17a2b8Same
--nu-info-600#008da1Same
--nu-info-700#007485Same
--nu-info-800#005a67Same
--nu-info-900#00414cSame
--nu-info-950#002d35Same
--nu-gray-50#f0f1f5Same
--nu-gray-100#e3e6ecSame
--nu-gray-200#cacfd9Same
--nu-gray-300#adb4c0Same
--nu-gray-400#8d94a1Same
--nu-gray-500#6b7280Same
--nu-gray-600#5d6471Same
--nu-gray-700#4d535fSame
--nu-gray-800#3c414cSame
--nu-gray-900#2d3139Same
--nu-gray-950#20232aSame
--nu-red-50#ffefedSame
--nu-red-100#ffe2dfSame
--nu-red-200#ffc8c3Same
--nu-red-300#ffa69eSame
--nu-red-400#ff766fSame
--nu-red-500#ef4444Same
--nu-red-600#d72f33Same
--nu-red-700#b81822Same
--nu-red-800#930013Same
--nu-red-900#6e000cSame
--nu-red-950#4f0006Same
--nu-blue-50#ecf3ffSame
--nu-blue-100#ddeaffSame
--nu-blue-200#bfd8ffSame
--nu-blue-300#9ac0ffSame
--nu-blue-400#6ba3ffSame
--nu-blue-500#3b82f6Same
--nu-blue-600#2a70dfSame
--nu-blue-700#1a5ac1Same
--nu-blue-800#0b449cSame
--nu-blue-900#023179Same
--nu-blue-950#012158Same
--nu-green-50#e4fde7Same
--nu-green-100#ccfcd4Same
--nu-green-200#a6f7b6Same
--nu-green-300#7ceb96Same
--nu-green-400#50da78Same
--nu-green-500#22c55eSame
--nu-green-600#00ac4dSame
--nu-green-700#008d3dSame
--nu-green-800#006b2dSame
--nu-green-900#004d1eSame
--nu-green-950#003412Same
--nu-yellow-50#fff5e1Same
--nu-yellow-100#fff0cfSame
--nu-yellow-200#ffe4aaSame
--nu-yellow-300#ffd576Same
--nu-yellow-400#f8c43fSame
--nu-yellow-500#eab308Same
--nu-yellow-600#ca9a00Same
--nu-yellow-700#a57d00Same
--nu-yellow-800#7c5d00Same
--nu-yellow-900#584100Same
--nu-yellow-950#3a2a00Same
--nu-purple-50#f6f0ffSame
--nu-purple-100#efe4ffSame
--nu-purple-200#e1cbffSame
--nu-purple-300#d0acffSame
--nu-purple-400#bd83ffSame
--nu-purple-500#a855f7Same
--nu-purple-600#9542e0Same
--nu-purple-700#7e2ec1Same
--nu-purple-800#631b9cSame
--nu-purple-900#4b0d79Same
--nu-purple-950#350658Same
--nu-pink-50#ffeef4Same
--nu-pink-100#ffe2ecSame
--nu-pink-200#ffc8dcSame
--nu-pink-300#ffa5c9Same
--nu-pink-400#ff74b2Same
--nu-pink-500#ec4899Same
--nu-pink-600#d43386Same
--nu-pink-700#b41c6eSame
--nu-pink-800#900455Same
--nu-pink-900#6b003eSame
--nu-pink-950#4c002bSame
Basic colours
TokenLightDark
--nu-black#000Same
--nu-white#fffSame
--nu-transparenttransparentSame
Spacing, radius and border width
TokenLightDark
--nu-space-00Same
--nu-space-10.25remSame
--nu-space-20.5remSame
--nu-space-30.75remSame
--nu-space-41remSame
--nu-space-51.25remSame
--nu-space-61.5remSame
--nu-space-82remSame
--nu-space-102.5remSame
--nu-space-123remSame
--nu-space-164remSame
--nu-space-205remSame
--nu-space-246remSame
--nu-space-328remSame
--nu-space-4010remSame
--nu-space-4812remSame
--nu-space-5614remSame
--nu-space-6416remSame
--nu-radius-xs0.25remSame
--nu-radius-sm0.375remSame
--nu-radius-md0.5remSame
--nu-radius-lg0.75remSame
--nu-radius-xl1remSame
--nu-radius-2xl1.25remSame
--nu-radius-full99inSame
--nu-radius-none0Same
--nu-border-width-00pxSame
--nu-border-width-11pxSame
--nu-border-width-22pxSame
--nu-border-width-44pxSame
--nu-border-width-88pxSame
Typography
TokenLightDark
--nu-font-familysystem-ui, -apple-system, "BlinkMacSystemFont", "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", sans-serifSame
--nu-font-size-xs0.75remSame
--nu-font-size-sm0.875remSame
--nu-font-size-md1remSame
--nu-font-size-base1remSame
--nu-font-size-lg1.25remSame
--nu-font-size-xl1.5remSame
--nu-font-size-2xl1.75remSame
--nu-font-size-3xl2remSame
--nu-font-size-4xl2.5remSame
--nu-font-weight-thin100Same
--nu-font-weight-extralight200Same
--nu-font-weight-light300Same
--nu-font-weight-normal400Same
--nu-font-weight-medium500Same
--nu-font-weight-semibold600Same
--nu-font-weight-bold700Same
--nu-font-weight-extrabold800Same
--nu-font-weight-black900Same
--nu-line-height-none1Same
--nu-line-height-tight1.25Same
--nu-line-height-snug1.375Same
--nu-line-height-normal1.5Same
--nu-line-height-relaxed1.625Same
--nu-line-height-loose2Same
Shadow, opacity and z-index
TokenLightDark
--nu-shadow-sm0 1px 2px 0 rgba(0, 0, 0, 0.1)Same
--nu-shadow-md0 4px 6px -1px rgba(0, 0, 0, 0.1)Same
--nu-shadow-lg0 10px 15px -3px rgba(0, 0, 0, 0.1)Same
--nu-shadow-xl0 20px 25px -5px rgba(0, 0, 0, 0.1)Same
--nu-shadow-hero0 20px 25px 5px rgba(0, 0, 0, 0.1)Same
--nu-shadow-monster0 20px 55px 20px rgba(0, 0, 0, 0.1)Same
--nu-opacity-00Same
--nu-opacity-50.05Same
--nu-opacity-100.1Same
--nu-opacity-200.2Same
--nu-opacity-250.25Same
--nu-opacity-300.3Same
--nu-opacity-330.33Same
--nu-opacity-400.4Same
--nu-opacity-500.5Same
--nu-opacity-600.6Same
--nu-opacity-660.66Same
--nu-opacity-700.7Same
--nu-opacity-750.75Same
--nu-opacity-800.8Same
--nu-opacity-900.9Same
--nu-opacity-1001Same
--nu-z-base0Same
--nu-z-hover10Same
--nu-z-dropdown100Same
--nu-z-sticky200Same
--nu-z-fixed300Same
--nu-z-overlay400Same
--nu-z-drawer500Same
--nu-z-modal600Same
--nu-z-popover700Same
--nu-z-tooltip800Same
--nu-z-toast900Same
--nu-z-max9999Same
Motion
TokenLightDark
--nu-duration-00msSame
--nu-duration-7575msSame
--nu-duration-100100msSame
--nu-duration-150150msSame
--nu-duration-200200msSame
--nu-duration-300300msSame
--nu-duration-500500msSame
--nu-duration-700700msSame
--nu-duration-10001000msSame
--nu-ease-linearlinearSame
--nu-ease-incubic-bezier(0.4, 0, 1, 1)Same
--nu-ease-outcubic-bezier(0, 0, 0.2, 1)Same
--nu-ease-in-outcubic-bezier(0.4, 0, 0.2, 1)Same
Controls
TokenLightDark
--nu-control-height-sm2remSame
--nu-control-height-md2.5remSame
--nu-control-height-lg3remSame
--nu-control-padding-inline-sm0.75remSame
--nu-control-padding-inline-md1remSame
--nu-control-padding-inline-lg1.25remSame

The same list, with resolved values for both themes, is in docs/TOKENS.md in the core repository.