Elevation

mandatory

Elevation system — 6 levels (0-5) expressed as shadows + tonal surface colors. Material parity (`/styles/elevation`). Defines WHEN to use which level (per element family) and HOW to render it (shadow + tonal overlay per theme mode).

Spec — Elevation

Facet *isual*do Koder Design. Material parity: https://m3.material.io/styles/elevation.

The 6 elevation levels (0-5)

Level Shadow recipe Tonal overlay (dark mode) Use
** none none (surface = bg) Page background, flat elements
** 0 1px 2px rgba(0,0,0,0.10), 0 2px 6px rgba(0,0,0,0.06) 5% accent over surface Default card, list item
** 0 1px 3px rgba(0,0,0,0.12), 0 4px 8px rgba(0,0,0,0.08) 8% accent Raised card, app bar (scrolled)
** 0 4px 8px rgba(0,0,0,0.14), 0 8px 16px rgba(0,0,0,0.10) 11% accent FAB (default), elevated button
** 0 6px 10px rgba(0,0,0,0.16), 0 12px 24px rgba(0,0,0,0.12) 12% accent FAB hover, contextual menu
** 0 8px 16px rgba(0,0,0,0.18), 0 16px 32px rgba(0,0,0,0.14) 14% accent Modal sheet, dialog, dropdown

Token names: --shadow-1 through --shadow-5, --tonal-1 through --tonal-5.

R1 — Element family default elevation

Family Default level
Surface (page bg) 0
Container (card) 1
Container (sheet) 3
Control (button — filled) 0 (matches surface)
Control (button — elevated) 1
Control (button — outlined/text) 0
Control (FAB) 3
Modal/dialog 5
Snackbar 4
Tooltip 3
Menu 4
Dropdown 4
Drawer (overlay) 5
Bottom sheet (scrim attached) 5

R2 — Light vs dark elevation

Light mode: shadow-driven

Shadow visible because surfaces are bright; tonal overlay barely contributes (0-3% accent maximum).

:root {
  --shadow-1: 0 1px 2px rgba(0,0,0,0.10), 0 2px 6px rgba(0,0,0,0.06);
  --tonal-1: rgba(59, 91, 253, 0.00); 
}
.card { box-shadow: var(--shadow-1); background: var(--surface); }

Dark mode: tonal-driven

Shadows are mostly invisible on dark backgrounds; *onal overlay*of the accent color shifts the surface toward "lighter / closer" to communicate elevation. Material 3 default.

[data-theme="dark"] {
  --shadow-1: 0 1px 2px rgba(0,0,0,0.40), 0 4px 12px rgba(0,0,0,0.30);
  --tonal-1: rgba(126, 151, 255, 0.05); 
}
.card {
  box-shadow: var(--shadow-1);
  background:
    linear-gradient(var(--tonal-1), var(--tonal-1)),
    var(--surface);
}

The linear-gradient trick lets the tonal overlay compose on top of the surface color without nesting.

R3 — State-driven elevation changes

Per interaction/states.kmd:

State Elevation change
Hover (mouse) +1 level (card 1 → 2)
Focus (keyboard) No elevation change (focus uses ring)
Press (touch) -1 level (FAB 3 → 2, "pushed in")
Drag +2 levels (lifts)
Dropped/settled back to default

Animation: transition: box-shadow var(--motion-fast) var(--ease-standard);

R4 — Per-preset variation

Presets vary in "depth" feel:

Preset Shadow scale
verge (default) Standard (Adwaita-based v0)
material3 Standard
material2 Slightly stronger shadows
gnome Subtle shadows; tonal-heavy in dark
windows_11 Mica blur replaces shadow at level 3+
windows_95 NO shadows — flat 3D bevel borders instead
macos_sonoma Soft, longer shadows; iOS-like depth
flat_design (planned) Level 0 for everything
brutalist NO shadows
neumorphism INVERSE shadows (inset for "pressed in" feel)
glassmorphism Combines shadow + backdrop blur
material_expressive Larger shadows; +1 level baseline
terminal_classic NO shadows
carbon_ibm NO shadows (flat by design)

Perpreset details in `uistyle.kmd`.

R5 — Forbidden combinations

  • ❌ Level 5 element inside a level 3 element (parent must be ≥ child)
  • ❌ Animating elevation > 1 level on hover (jarring)
  • ❌ Using box-shadow for purely decorative reasons (use level 0)
  • ❌ Hardcoded shadow values in widget code (always token)
  • ❌ Different shadow colors per element (use the system — single color, varying opacity)

R6 — Forced colors / high contrast

When OS reports forced-colors: active or prefers-contrast: more:

  • All shadows → none (forced-colors mode strips shadows anyway)
  • Tonal overlay → none
  • Elevation indicated by *order*instead: 1px solid border for

    each level above 0

R7 — Accessibility

  • Shadow MUST NOT be the only indicator of elevation/state
  • Always pair shadow with: border color shift, position offset, or

    background tonal change

  • Color-blind users should perceive elevation via tonal lightness

    change, not shadow alone

  • themes/color-roles.kmd — accent color used for tonal overlay
  • themes/light-dark.kmd — lightdark mode switches shadowtonal balance
  • themes/ui-style.kmd — per-preset elevation defaults
  • interaction/states.kmd — state-driven elevation changes
  • foundations/elements.kmd — family ↔ default elevation

Source: ../home/koder/dev/koder/meta/docs/stack/specs/themes/elevation.kmd