Z-index
Z-index utilities control which element paints on top when elements overlap. NuvoUI has two sets: named levels for page layers (dropdowns, modals, toasts), which are tokens, and numbered keys for ordering siblings inside one component, which are plain numbers.
How z-index works
Section titled “How z-index works”- Named levels.
z-dropdown,z-modaland the other levels outputvar(--nu-z-{level}). Use them for anything that must stack correctly against other parts of the page. - Numbered keys.
z-10,z-50and the other keys output the number itself (z-index: 10). Use them for local order inside a component, such as overlapping avatars. - Positioned elements only.
z-indexapplies to positioned elements (relative,absolute,fixed,sticky) and to flex and grid items.
Named levels
Section titled “Named levels”The levels and their default values, generated from the compiled core build:
| Key | Token | Value |
|---|---|---|
base | --nu-z-base | 0 |
hover | --nu-z-hover | 10 |
dropdown | --nu-z-dropdown | 100 |
sticky | --nu-z-sticky | 200 |
fixed | --nu-z-fixed | 300 |
overlay | --nu-z-overlay | 400 |
drawer | --nu-z-drawer | 500 |
modal | --nu-z-modal | 600 |
popover | --nu-z-popover | 700 |
tooltip | --nu-z-tooltip | 800 |
toast | --nu-z-toast | 900 |
max | --nu-z-max | 9999 |
| Level | Intended for |
|---|---|
base |
Content in normal flow |
hover |
An element lifted on hover, such as a card |
dropdown |
Dropdown menus and select lists |
sticky |
Sticky headers and table heads |
fixed |
Fixed headers and navigation bars |
overlay |
Backdrops behind drawers and modals |
drawer |
Side drawers |
modal |
Modal dialogs |
popover |
Popovers, which can open from inside a modal |
tooltip |
Tooltips; core tooltips use this level |
toast |
Toast notifications, above everything else in the app |
max |
Debugging tools and other elements that must be on top |
<div class="relative z-0 h-32"> <div class="absolute top-0 start-0 w-48 p-3 bg-surface border rounded shadow-md z-dropdown">z-dropdown (100)</div> <div class="absolute top-6 start-12 w-48 p-3 bg-brand text-on-brand rounded shadow-md z-modal">z-modal (600)</div> <div class="absolute top-12 start-24 w-48 p-3 bg-surface border rounded shadow-md z-sticky">z-sticky (200)</div></div>.menu { @include apply(absolute, z-index(dropdown));}.dialog { @include apply(fixed, z-modal);}The order in the source does not matter: the element with the higher level paints on top.
Numbered keys
Section titled “Numbered keys”z-auto, z-0, z-10 to z-100 in steps of ten, z-200, z-500 and z-1000. They are not tokens; changing them means changing the configuration.
<div class="relative z-0 h-12"> <div class="absolute start-0 z-30 w-12 h-12 pill bg-brand border-2 border-white"></div> <div class="absolute start-8 z-20 w-12 h-12 pill bg-purple border-2 border-white"></div> <div class="absolute start-16 z-10 w-12 h-12 pill bg-pink border-2 border-white"></div></div>Keep numbered keys inside a component that creates its own stacking context (for example with relative z-0), so they cannot compete with page layers.
Stacking contexts
Section titled “Stacking contexts”A z-index only competes with elements in the same stacking context. An element with a positioned z-index, opacity below 1, a transform, a filter or isolation: isolate starts a new context, and nothing inside it can paint above an element outside it with a higher level. If a modal opened from inside a card stays under a header, move the modal out of the card in the DOM instead of raising its level.
Responsive variants
Section titled “Responsive variants”Every z-index class has a responsive variant with an @{breakpoint} suffix: z-10@md, z-dropdown@lg. A variant applies from its breakpoint upwards.
<nav class="relative z-auto z-fixed@lg">Navigation</nav>| Suffix | Media query |
|---|---|
@xs | @media (min-width: 480px) |
@sm | @media (min-width: 640px) |
@md | @media (min-width: 768px) |
@lg | @media (min-width: 1024px) |
@xl | @media (min-width: 1280px) |
@2xl | @media (min-width: 1536px) |
Z-index has no state variants.
Interaction with other utilities
Section titled “Interaction with other utilities”sticky. The.stickyclass setsz-index: 999inside:where(), so any z-index class on the same element overrides it:sticky z-sticky. See Position.elevation-*. Elevation classes set a z-index from 1 to 5. Z-index classes come later in the stylesheet, soelevation-3 z-dropdownuses the dropdown level. See Shadows.
@use "@nuvoui/core" as *;
.dialog { @include apply(fixed, z-index(modal));}.avatar-stack__item { @include apply(relative, z-index(10));}.dialog { position: fixed; z-index: var(--nu-z-modal);}.avatar-stack__item { position: relative; z-index: 10;}z-index() accepts a level name (gives the token), a numbered key (gives the number), any integer, auto, calc() or var(). Each level also has a shortcut mixin of the same name as the class: z-base, z-hover, z-dropdown, z-sticky, z-fixed, z-overlay, z-drawer, z-modal, z-popover, z-tooltip, z-toast, z-max, and z-auto.
.toast { @include apply(fixed, z-toast);}.layer { @include apply(z-index(15));}Tokens in your own CSS
Section titled “Tokens in your own CSS”Only named levels are tokens.
.command-palette { z-index: token(z-popover);}Configuration
Section titled “Configuration”$z-index-levels holds the named levels and $z-indexes the numbered keys. Each replaces the default map; it is not merged. Core tooltips read the tooltip level, so keep it when you use tooltips.
@use "@nuvoui/core" with ( $generate-utility-classes: true, $z-index-levels: ("base": 0, "hover": 10, "dropdown": 1000, "sticky": 1100, "fixed": 1200, "overlay": 1300, "drawer": 1400, "modal": 1500, "popover": 1600, "tooltip": 1700, "toast": 1800, "max": 9999));A new key adds a class, a token and a valid z-index() argument: adding "banner": 1250 gives .z-banner, --nu-z-banner and z-index(banner). The shortcut mixins exist only for the default level names.
Errors
Section titled “Errors”| Input | Error |
|---|---|
z-index(huge) |
Unknown z-index: huge. Use a key (auto, 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 500, 1000), a level (base, hover, dropdown, sticky, fixed, overlay, drawer, modal, popover, tooltip, toast, max), an integer, auto, calc() or var(). |
z-index(1.5) |
Unknown z-index: 1.5. Use a key (...), a level (...), an integer, auto, calc() or var(). |
token(z-10) |
token(z-10): unknown token. Close matches: z-base, z-hover, z-dropdown, ... (numbered keys are not tokens) |
CSS variables
Section titled “CSS variables”| Variable | Description |
|---|---|
--nu-z-{level} |
One token per key of $z-index-levels. Fixed across themes. See Named levels. |
Numbered keys are emitted as plain numbers.
Class reference
Section titled “Class reference”Generated from the compiled core build. Responsive variants are not listed.
Named levels (12 rules)
| Selector | Declarations | Default value |
|---|---|---|
.z-base | z-index: var(--nu-z-base) | 0 |
.z-hover | z-index: var(--nu-z-hover) | 10 |
.z-dropdown | z-index: var(--nu-z-dropdown) | 100 |
.z-sticky | z-index: var(--nu-z-sticky) | 200 |
.z-fixed | z-index: var(--nu-z-fixed) | 300 |
.z-overlay | z-index: var(--nu-z-overlay) | 400 |
.z-drawer | z-index: var(--nu-z-drawer) | 500 |
.z-modal | z-index: var(--nu-z-modal) | 600 |
.z-popover | z-index: var(--nu-z-popover) | 700 |
.z-tooltip | z-index: var(--nu-z-tooltip) | 800 |
.z-toast | z-index: var(--nu-z-toast) | 900 |
.z-max | z-index: var(--nu-z-max) | 9999 |
Numbered keys (15 rules)
| Selector | Declarations | Default value |
|---|---|---|
.z-auto | z-index: auto | auto |
.z-0 | z-index: 0 | 0 |
.z-10 | z-index: 10 | 10 |
.z-20 | z-index: 20 | 20 |
.z-30 | z-index: 30 | 30 |
.z-40 | z-index: 40 | 40 |
.z-50 | z-index: 50 | 50 |
.z-60 | z-index: 60 | 60 |
.z-70 | z-index: 70 | 70 |
.z-80 | z-index: 80 | 80 |
.z-90 | z-index: 90 | 90 |
.z-100 | z-index: 100 | 100 |
.z-200 | z-index: 200 | 200 |
.z-500 | z-index: 500 | 500 |
.z-1000 | z-index: 1000 | 1000 |
Accessibility and RTL
Section titled “Accessibility and RTL”- Layers and focus. Raising a modal’s z-index makes it appear on top but does not stop keyboard focus from reaching content behind it. Use the
dialogelement withshowModal(), orinerton the rest of the page. - Do not cover focused content. Sticky and fixed layers can hide the element that has focus (WCAG 2.2, 2.4.11 Focus Not Obscured). Leave space with
scroll-padding-top, or keep such layers small. - Top layer. A dialog opened with
showModal()and an element shown with the Popover API render in the browser’s top layer, above every z-index. Use z-index levels for everything else. - RTL. Z-index has no direction.