CSSenv() — safe areas & environment variables

env() — Safe Areas & Environment Variables

Modern phones have notches, camera cutouts, home-indicator bars, and rounded corners that eat into the usable screen area. The env() function exposes these device-specific safe measurements to CSS, so a fixed header or footer can avoid being cut off or overlapped by hardware and system UI.

The four safe-area-inset values

Variable

Protects against

env(safe-area-inset-top)

Notches, camera cutouts, and status bars at the top

env(safe-area-inset-right)

Rounded corners / notches in landscape orientation

env(safe-area-inset-bottom)

The home-indicator bar on notched iPhones

env(safe-area-inset-left)

Rounded corners / notches in landscape orientation

CSS
.app-footer {
  padding-bottom: env(safe-area-inset-bottom);
}

.app-header {
  padding-top: env(safe-area-inset-top);
}
Requires viewport-fit=cover
env(safe-area-inset-*) values are all zero unless the page opts in to actually drawing under the notch/home indicator in the first place, via the viewport meta tag.

HTML
<meta
  name="viewport"
  content="width=device-width, initial-scale=1, viewport-fit=cover"
/>
Why viewport-fit=cover is required
Without it, the browser keeps your content safely inside the "safe" rectangle automatically, and the insets stay zero because there is nothing to protect against. viewport-fit=cover tells the browser "let my content extend edge-to-edge," which is exactly when the insets become non-zero and meaningful.
Combining insets with existing spacing

A fixed footer usually already has its own padding for visual breathing room — the safe-area inset needs to be added on top of that, not replace it, otherwise content sits flush against the safe-area boundary with no spacing on devices that have zero inset (most Android phones, older iPhones).

CSS
.app-footer {
  /* calc() combines a fixed design spacing value with the dynamic inset */
  padding-bottom: calc(16px + env(safe-area-inset-bottom));
}

/* max() is often cleaner: takes whichever is larger */
.app-footer-alt {
  padding-bottom: max(16px, env(safe-area-inset-bottom));
}

Approach

Result on a notch-free device

Result on a notched device

calc(16px + env(...))

16px padding

16px + inset padding

max(16px, env(...))

16px padding

whichever is larger — inset if it exceeds 16px

Fallback values

env() accepts a second argument as a fallback for browsers or contexts where the environment variable is not defined at all (not just zero — truly absent).

CSS
.app-footer {
  padding-bottom: env(safe-area-inset-bottom, 16px);
}
titlebar-area for installed PWAs

Desktop PWAs that customize their own title bar (via the window-controls-overlay display mode) get additional env() variables describing the space the operating system reserves for window controls, so custom title bar content doesn't overlap the minimize/maximize/close buttons.

CSS
.custom-titlebar {
  position: fixed;
  top: env(titlebar-area-y, 0);
  left: env(titlebar-area-x, 0);
  width: env(titlebar-area-width, 100%);
  height: env(titlebar-area-height, 33px);
}
Full example: a notch-safe app shell

CSS
.app-shell {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}

.app-header {
  padding: max(12px, env(safe-area-inset-top))
           max(16px, env(safe-area-inset-right))
           12px
           max(16px, env(safe-area-inset-left));
}

.app-content {
  flex: 1;
  padding-inline: max(16px, env(safe-area-inset-left))
                  max(16px, env(safe-area-inset-right));
}

.app-footer {
  padding: 12px
           max(16px, env(safe-area-inset-right))
           max(12px, env(safe-area-inset-bottom))
           max(16px, env(safe-area-inset-left));
}
  • Add viewport-fit=cover to the viewport meta tag first — without it every inset stays zero

  • Combine insets with max() (design spacing vs device inset) rather than replacing your normal padding

  • Always provide a fallback value for browsers that do not support a given env() variable

  • Test on an actual notched device or the browser DevTools device toolbar with a notched preset — the insets are invisible on a desktop simulator without one

Testing without a physical notched device

Not every developer has an iPhone with a notch on hand. Chrome DevTools lets you simulate the safe-area insets directly, without a physical device, through its device toolbar sensor emulation.

  1. Open DevTools → toggle the device toolbar (Ctrl/Cmd+Shift+M)

  2. Choose a notched device preset from the dropdown (e.g. an iPhone model with a notch)

  3. Reload the page — the emulated device profile reports the expected safe-area-inset values

  4. Confirm your fixed header/footer padding grows appropriately in the emulated notch area

Emulation is a good first check, not a final one
Device emulation confirms your CSS logic is correct, but real devices sometimes report subtly different inset values depending on orientation, browser chrome visibility, and OS version. Verify on at least one real notched device before shipping anything safe-area critical.
Other env() use cases beyond safe areas

Safe-area insets are the most common use of env(), but the function itself is a general mechanism for exposing browser/OS environment values to CSS — more variables have been proposed and shipped for specific platform integrations over time.

Variable

Context

safe-area-inset-*

Notches, home indicators, rounded corners on mobile

titlebar-area-*

Custom title bar space in installed PWAs using window-controls-overlay

keyboard-inset-*

Proposed: space taken up by an on-screen virtual keyboard

Not every env() variable is broadly supported yet
Beyond the safe-area insets (which are well established), some environment variables are still evolving or limited to specific browsers/platforms. Always pair a newer env() variable with a sensible fallback value, and verify actual support before depending on it for anything critical.
Next
See how env() combines with fluid sizing functions in min(), max() & clamp().