The video uses a specific AI coding tool to demonstrate the setup, but the configuration works 1:1 with Cursor, Claude Code, or any other tool. Despia is web framework and tooling agnostic, so the only thing that matters is the CSS pattern you apply to your fixed and sticky elements.
--safe-area-top, --safe-area-bottom, --safe-area-left, --safe-area-right. Each one measures the inset for the matching edge, accounting for the status bar, notch, Dynamic Island, home indicator, and gesture bar on iOS, and the equivalent system chrome on Android. The variables are available before your JavaScript runs and update in real time when the device rotates.
There is no SDK call. The variables are always present in the runtime, and you reference them from CSS exactly like any custom property.
Installation
No package installation is required for the CSS variables themselves. The runtime injects them automatically into every WebView. Installdespia-native only if your page also calls SDK schemes for other features.
- Bundle
- CDN
The canonical pattern
Safe areas are insets that stack on top of your existing padding, never replacements for it. Always combine them with your base padding usingcalc(), and always pair the runtime variable with env(safe-area-inset-*) as a fallback. The full pattern is:
calc(BASE + ...)because safe areas are insets layered on top of your existing padding, not replacements for it. Withoutcalc(), your element loses its base padding on the safe-area edge.var(--safe-area-X, ...)first because the Despia runtime can override the native value. This is the source of truth inside the native container.env(safe-area-inset-X, ...)second because that is the native WebKit value, used during web preview and before the runtime injects its variables.0pxlast because thecalc()becomes invalid if both lookups resolve to nothing. The0pxcollapses the safe area cleanly to zero and leaves the base padding intact.
var(--safe-area-top), the bare env(safe-area-inset-top), or any form without calc() and a base padding.
Fixed header below the status bar
A fixed header at the top of the screen needs to sit below the status bar and notch, not under them. Apply the inset topadding-top so the header’s background extends up to the top of the screen while its content stays in the safe region.
Fixed bottom navigation above the home indicator
A bottom tab bar or navigation needs to clear the home indicator on iOS and the gesture bar on Android. Apply the inset topadding-bottom.
Scrollable content between fixed bars
When you have both a fixed header and a fixed bottom bar, your scrollable content needspadding-top and padding-bottom large enough to clear both. Combine the bar height with the safe-area inset in a single calc().
padding-top already accounts for the safe area inside the header, but the content needs to clear the entire visible header (its base height plus the inset), so the inset shows up in both places. This is correct, not a duplication.
Why pair var() with env()
The runtime variable and the native env value are not redundant. They cover different cases.| Source | Available when |
|---|---|
var(--safe-area-top) | Inside the Despia runtime, after the variables are injected. Source of truth |
env(safe-area-inset-top) | In any WebKit-based browser (Safari, iOS WKWebView), including web preview before the runtime initializes |
0px fallback | Anywhere the calc() would otherwise be invalid, e.g. desktop browsers without WebKit |
var() wins because it comes first. In Safari preview, var() resolves to undefined and the chain falls through to env(). On desktop Chrome or Firefox, neither resolves and the chain ends at 0px. The pattern is correct in every environment, which is why this is the only form the docs recommend.
Resources
NPM Package
despia-native