Design Tokens

Z-Index

Create and manage z-index design tokens with CSS variables for consistent stacking order across your application.

Overview

The z-index composable helps you create a consistent stacking order system with minimal code. It generates z-index variables that can be easily referenced throughout your application, preventing the #1 source of CSS stacking bugs — conflicting arbitrary z-index values.

Why use z-index composables?

Z-index composables help you:

  • Prevent stacking conflicts: Use a predefined scale instead of inventing arbitrary values that inevitably collide.
  • Establish semantic layers: Name your z-index levels by purpose (dropdown, modal, toast) rather than magic numbers.
  • Enable flexible theming: Override z-index variables to adjust stacking order across your application.
  • Maintain consistency: Ensure every developer on the team uses the same stacking scale.
  • Pair with elevation: Align z-index levels with box-shadow elevation for a coherent visual hierarchy.

useZIndex

Styleframe provides a set of carefully crafted default z-index values that you can use out of the box:

styleframe.config.ts
import { styleframe } from 'styleframe';
import { useZIndex } from '@styleframe/theme';

const s = styleframe();

const {
    zIndex,
    zIndexHide,
    zIndexBase,
    zIndexDropdown,
    zIndexSticky,
    zIndexOverlay,
    zIndexModal,
    zIndexPopover,
    zIndexToast,
    zIndexMax,
    zIndexAuto,
} = useZIndex(s);

export default s;

The default values include:

  • hide: Behind base content (-1)
  • base (default): Default stacking level (0)
  • dropdown: Dropdowns and select menus (100)
  • sticky: Sticky headers and columns (200)
  • overlay: Overlays and backdrops (300)
  • modal: Modals and dialogs (400)
  • popover: Popovers and tooltips (500)
  • toast: Toasts and notifications (600)
  • max: Escape hatch for highest priority (9999)
  • auto: Browser default (auto)
Good to know: The scale uses increments of 100 between semantic levels, leaving room for intermediate values if needed. The max token (9999) acts as an escape hatch for edge cases.

Extending the Default Z-Index Values

You can customize which z-index is used as the default while keeping all other standard values. Use the @ prefix to reference another key in the values object:

styleframe.config.ts
import { styleframe } from 'styleframe';
import { useZIndex, zIndexValues } from '@styleframe/theme';

const s = styleframe();

const { zIndex } = useZIndex(s, {
    ...zIndexValues,
    default: '@dropdown'
});

export default s;

Creating Custom Z-Index Variables

The useZIndex() function creates a set of z-index variables for establishing stacking order in your interface.

styleframe.config.ts
import { styleframe } from 'styleframe';
import { useZIndex } from '@styleframe/theme';

const s = styleframe();

const {
    zIndex,
    zIndexBase,
    zIndexDropdown,
    zIndexModal,
    zIndexToast,
} = useZIndex(s, {
    default: '@base',
    base: '0',
    dropdown: '1000',
    modal: '2000',
    toast: '3000',
});

export default s;

The function creates variables for each stacking level you define. Each key in the object becomes a z-index variable with the prefix z-index--, and the export name is automatically converted to camelCase (e.g., dropdownzIndexDropdown).

Pairing Z-Index with Box Shadow

Z-index and box shadow should work together — higher elevation elements should have both higher z-index and more prominent shadows:

styleframe.config.ts
import { styleframe } from 'styleframe';
import { useZIndex, useBoxShadow } from '@styleframe/theme';

const s = styleframe();
const { ref, selector } = s;

// Create z-index scale
const {
    zIndexDropdown,
    zIndexOverlay,
    zIndexModal,
    zIndexToast,
} = useZIndex(s);

// Create matching shadow scale
const {
    boxShadowMd,
    boxShadowLg,
    boxShadowXl,
    boxShadow2xl,
} = useBoxShadow(s);

// Pair elevation with stacking order
selector('.dropdown', {
    zIndex: ref(zIndexDropdown),
    boxShadow: ref(boxShadowMd),
});

selector('.overlay', {
    zIndex: ref(zIndexOverlay),
});

selector('.modal', {
    zIndex: ref(zIndexModal),
    boxShadow: ref(boxShadowXl),
});

selector('.toast', {
    zIndex: ref(zIndexToast),
    boxShadow: ref(boxShadow2xl),
});

export default s;
Pro tip: Establish a convention where elevation and stacking always increase together. For example: dropdown (z-index 100, shadow md), modal (z-index 400, shadow xl), toast (z-index 600, shadow 2xl). This creates a consistent mental model for your team.

Examples

Application Shell

Here's how to create a complete stacking system for a typical application layout:

styleframe.config.ts
import { styleframe } from 'styleframe';
import { useZIndex } from '@styleframe/theme';

const s = styleframe();
const { ref, selector } = s;

const {
    zIndexSticky,
    zIndexDropdown,
    zIndexOverlay,
    zIndexModal,
    zIndexToast,
} = useZIndex(s);

// Sticky navigation
selector('.navbar', {
    position: 'sticky',
    top: '0',
    zIndex: ref(zIndexSticky),
});

// Dropdown menus sit above sticky elements
selector('.dropdown-menu', {
    position: 'absolute',
    zIndex: ref(zIndexDropdown),
});

// Full-screen overlay behind modals
selector('.backdrop', {
    position: 'fixed',
    inset: '0',
    zIndex: ref(zIndexOverlay),
});

// Modal sits above the overlay
selector('.modal', {
    position: 'fixed',
    zIndex: ref(zIndexModal),
});

// Toasts appear above everything
selector('.toast', {
    position: 'fixed',
    zIndex: ref(zIndexToast),
});

export default s;

Best Practices

  • Always use tokens: Never write raw z-index numbers. Use the token scale so the entire team stays consistent.
  • Match elevation with shadows: Higher z-index should correspond to higher box-shadow. This creates visual coherence.
  • Use semantic names: Name tokens by purpose (modal, toast) not by number. This makes intent clear when reading code.
  • Leave gaps between levels: The default 100-increment scale leaves room for intermediate values without reshuffling.
  • Use hide sparingly: Negative z-index (-1) can cause elements to become unreachable. Reserve it for decorative elements.
  • Mind stacking contexts: Remember that z-index only works within the same stacking context. A modal inside a z-index: 1 container can't escape it.
  • Keep the scale small: Most applications need 5-8 z-index levels. More than that usually signals a stacking context issue.
Pro tip: If you find yourself needing z-index values between levels, reconsider your DOM structure first. Flattening nested stacking contexts often eliminates the need for intermediate values.

FAQ