Surfaces explains Aspekt depth, contextual surface inheritance, overlay lift, and neutral state treatments.
Depth before decoration
Surfaces are Aspekt's neutral z-axis. The scale runs from surface-1 through surface-8, where each step describes a plane moving toward the viewer rather than a louder visual priority.
Components read the nearest surface context and can lift from it. That keeps popovers, dropdowns, dialogs, drawers, and toasts visibly separated even when they open inside another elevated component.
Tokens, classes, and API
Surface tokens and class names are styling tools. The React API is what calculates the current surface level and passes that level to nested components.
Tokens
--surface-1, --surface-current, --surface-hoverCSS custom properties that define colors, shadows, and contextual treatments. Tokens do not choose a level by themselves.
Class names
bg-surface-current, border-border, shadow-surface-3Tailwind utilities mapped to the tokens. Use these when styling markup inside an already resolved surface.
React API
SurfaceProvider value, Surface level, Surface liftComponents that resolve the current level, publish it to descendants, and update contextual tokens for the subtree.
Component props
surface, surfaceLift, surfaceShadowBuilt-in surface controls on Aspekt components. Most overlays already lift from their current context by default.
How the model works
A surface sets --surface-current for its subtree. Muted, hover, active, border, and ring treatments are then mixed from that current substrate, so neutral UI states keep the right contrast at every depth.
Choose a substrate
Use surface-1 for the page or application canvas, then reserve higher levels for content that visually moves forward.
Lift from context
Use lift when a component should sit above whatever surface contains it. Nested overlays stay separated without hard-coded colors.
Keep meaning separate
Use neutral surfaces for depth. Use action, success, warning, destructive, and info colors only when the UI needs semantic meaning.
Move through levels
Drag both knobs to choose which slice of the ladder to nest. Each layer lifts a single step off the one it sits in, whether the stack spans two levels or all eight.
One card, many substrates
Move the card through the ladder. The neutral buttons, muted regions, borders, and lifted menu all recompute from that card's current surface instead of using fixed colors.
Workspace review
Card substrate surface-3
surface-5Dashboard, drawer, popover
A common product flow: open a drawer from a dashboard, then open a popover inside that drawer. Each overlay lifts from the surface it actually sits in.
Revenue dashboard
Surface-1 application background
Pipeline
Conversion
Expansion
Segment drawer
Lifted to surface-4 from the dashboard
4Account owner
Status filter
Status popover
Lifted from the drawer context to surface-6.
API shape
The exported surface primitives are small by design. Most product code should set an explicit level at major app boundaries, then let relative lift calculate nested panels and overlays from the current context.
The calculation is automatic when you use Surface,ElevatedSurface, or a component with surfaceLift. A class like bg-surface-current only reads the already resolved token.
Sets the current surface context for descendants. Values are rounded and clamped from 1 to 8.
Creates a plane, resolves absolute or relative depth, applies the matching surface background and shadow, then publishes the resolved level.
A convenience wrapper around Surface for the common case where something should lift one step from its parent.
Surface controls built into overlays and framed primitives. Use them when the component should override its default depth.
Built-in component surfaces
Components that create a new plane already participate in the surface model. Override surface, surfaceLift, or surfaceShadow only when a product flow needs a different depth.
Small overlays lift two levels above the surface that opened them.
Prominent overlays move further forward from the application canvas or current overlay.
Framed content surfaces lift one level for outline variants and stay in context for softer variants.
Next.js placement
In the App Router, set the base context once near the root layout, then let pages and overlays choose absolute levels or relative lift based on the plane they introduce.
import type { ReactNode } from "react"; import { SurfaceProvider } from "@/components/aspekt/surface";import "@/app/globals.css"; export default function RootLayout({ children }: { children: ReactNode }) { return ( <html lang="en"> <body className="bg-surface-current text-primary"> <SurfaceProvider value={1}>{children}</SurfaceProvider> </body> </html> );}Surface levels
--surface-1The default app and site substrate.
--surface-2Cards, panels, tables, and persistent content surfaces.
--surface-3Small overlays, menus, and elevated nested panels.
--surface-4Dialogs, drawers, toasts, and prominent overlay surfaces.
--surface-5Dropdowns and popovers opened inside larger overlays.
--surface-6Second-order nested overlays that still need separation.
--surface-7Rare foreground planes above already elevated content.
--surface-8The clamped top of the surface ladder.
Surface treatments
--surface-currentThe current substrate inherited from the nearest Surface context.
--surface-mutedSoft fills, quiet regions, rails, code blocks, and inactive controls.
--surface-hoverHover and transient interaction fills inside a surface.
--surface-activePressed, selected, checked, and active neutral states.
--surface-borderDefault contextual borders and separators.
--surface-border-strongHigher contrast borders for selected or emphasized outlines.
--surface-ringContextual focus rings and active outlines.
Surface shadows
shadow-surface-1The resting shadow for the app canvas and base substrate.
shadow-surface-2A quiet elevation for cards, panels, and tables.
shadow-surface-3A visible lift for floating panels and compact overlays.
shadow-surface-4The default overlay shadow for dialogs, drawers, and toasts.
shadow-surface-5A stronger shadow for overlays opened inside overlays.
shadow-surface-6A high foreground shadow for second-order nested content.
shadow-surface-7A rare foreground shadow above already elevated planes.
shadow-surface-8The strongest clamped shadow in the surface scale.