Elevation
themes specs/themes/elevation.kmd
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).
When this spec applies
Primary triggers
- Apply elevation to any Koder surface
All triggers
- Add depth/shadow to a container or surface
- Promote/demote element on focus/press/drag
- Tune per-preset elevation feel (flat vs deep)
Specification body
Spec — Elevation
Facet Visual 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 |
|---|---|---|---|
| 0 | none | none (surface = bg) | Page background, flat elements |
| 1 | 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 |
| 2 | 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) |
| 3 | 0 4px 8px rgba(0,0,0,0.14), 0 8px 16px rgba(0,0,0,0.10) | 11% accent | FAB (default), elevated button |
| 4 | 0 6px 10px rgba(0,0,0,0.16), 0 12px 24px rgba(0,0,0,0.12) | 12% accent | FAB hover, contextual menu |
| 5 | 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); /* effectively none */
}
.card { box-shadow: var(--shadow-1); background: var(--surface); }
Dark mode: tonal-driven
Shadows are mostly invisible on dark backgrounds; tonal 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); /* 5% accent */
}
.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) |
Per-preset details in ui-style.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 border instead: 1px solid
borderfor 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
Cross-link
themes/color-roles.kmd— accent color used for tonal overlaythemes/light-dark.kmd— light/dark mode switches shadow/tonal balancethemes/ui-style.kmd— per-preset elevation defaultsinteraction/states.kmd— state-driven elevation changesfoundations/elements.kmd— family ↔ default elevation
R8 — Recesso / sunken (design-RFC-012)
Eixo ortogonal à elevação. A elevação responde "quão acima da página?"
(recursiva, tonal — no dark mais alto = mais claro). O sunken responde
"esta superfície é um recesso escavado na sua moldura?" — profundidade 1,
não-recursivo por contrato.
Token: --surface-sunken (verge.kmd §R4.1/§R4.3) — dark #0A0A0B,
light #ECEEF0.
- R8.1 —
--surface-sunkené sempre ≥1 passo mais escuro que a moldura no dark (cinza-inset no light); nunca mais claro que o container. - R8.2 — Não-recursivo: proibido como container de filhos elevados (cards/menus/dialogs). Quem precisa de filhos elevados usa a rampa 0–5.
- R8.3 — Recesso define-se por tom + borda (hairline
--border), não por sombra (um recesso não "flutua"); sombra inset opcional como cue. - R8.4 — a11y: texto sobre
--surface-sunkenmantém AA; separação tonal recesso↔moldura ≥1.15× (themes/light-dark.kmd). - Usar em: telas de auth emolduradas (
id#190), input wells, blocos de código, painéis inset. Não usar onde superfícies empilham acima da página → elevação.
❌ Anti-pattern: usar
--surface-sunkene depois pôr um card/menu elevado dentro dele (quebra o eixo; o recesso não é base de empilhamento).
References
specs/themes/ui-style.kmdspecs/themes/color-roles.kmdspecs/themes/light-dark.kmdspecs/foundations/elements.kmd