Skip to content
ssntl/ui

Elevation

Five layers from the system's shadow tokens, from the base surface to modals, plus the pop-in and pop-out motion for floating layers.

  1. elevation-0Base surfaceCards and sections
  2. elevation-1Resting controlA selected tab, a badge
  3. elevation-2Dropdown layerMenus, popovers, toolbars
  4. elevation-3Raised cardFeatured or dragged cards
  5. elevation-4OverlayToasts and modals

Installation

$ pnpm dlx shadcn@latest add @ssntl/elevation

Usage

css
/* app/globals.css */
@import "../styles/ssntl/shadow-smooth.css";
@import "../styles/ssntl/elevation.css";

<section class="bg-muted elevation-0">…</section>
<div role="menu" class="elevation-2 data-[state=open]:pop-in">…</div>
<div role="dialog" class="elevation-4">…</div>

Rules

What this component expects from you, as it ships in styles/ssntl/elevation.md next to the code. The full file adds when to use it, keyboard, the UI foundations it applies and every shared rule.

styles/ssntl/elevation.md
## Rules for Elevation

- **Base surface.** Static content sits at `elevation-0` and separates by tone, never by shadow.
- **Dropdown layer.** Menus, popovers and toolbars take `elevation-2` (`shadow-float`).
- **Raised card.** A featured or dragged card takes `elevation-3` (float plus lift), above dropdowns.
- **Floating notification and modal depth.** Toasts and dialogs take `elevation-4`, the top layer; a modal adds a scrim.
- `pop-in` grows a layer in from its origin over `--duration-menu` (set `--pop-duration` for modals); `pop-out` leaves faster. Reduced motion keeps the fade only.
Open the full rules file (opens in a new tab)

Code

styles/ssntl/elevation.css
/* Elevation: how far a layer sits above the page, from the system's own
   shadow tokens. Static content stays on the base surface; only what floats
   gets a shadow, and the higher it floats, the more it casts.

     elevation-0      base surface: cards, sections (tone separates them)
     elevation-1      controls resting on a surface (bezel)
     elevation-2      dropdowns, popovers, menus, toolbars (float)
     elevation-3      raised or dragged cards (float + lift)
     elevation-4      toasts and modals, above everything (smooth stack × 3)

     pop-in, pop-out  enter and exit for floating layers; set --pop-duration
                      to var(--duration-panel) for modals */

@utility elevation-0 {
  box-shadow: none;
}

@utility elevation-1 {
  box-shadow: var(--shadow-bezel);
}

@utility elevation-2 {
  box-shadow: var(--shadow-float);
}

@utility elevation-3 {
  box-shadow: var(--shadow-float), var(--shadow-lift);
}

@utility elevation-4 {
  @apply shadow-smooth shadow-intensity-200 shadow-size-3;
}

/* Floating layers grow in from their origin and fade out faster than they
   came in. With reduced motion only the opacity changes. */
@utility pop-in {
  animation: pop-in var(--pop-duration, var(--duration-menu)) var(--ease-out);
  @media (prefers-reduced-motion: reduce) {
    animation-name: fade-in;
  }
}

@utility pop-out {
  animation: pop-out var(--duration-fast) var(--ease-out) forwards;
  @media (prefers-reduced-motion: reduce) {
    animation-name: fade-out;
  }
}

@keyframes pop-in {
  from {
    opacity: 0;
    scale: var(--es-press);
  }
}

@keyframes pop-out {
  to {
    opacity: 0;
    scale: 0.98;
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes fade-out {
  to {
    opacity: 0;
  }
}