env()

Gabriel Shoyombo on

Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.

Experimental: Check browser support before using this in production.

The env() function replaces environment variables into CSS. To be more precise, it is a way to insert user-agent-defined variables into your stylesheet. It works similarly to the CSS var() function and custom properties, but rather than being scoped to the element it’s declared on, the CSS env() function is globally scoped to the document.

footer {
  position: fixed;
  bottom: env(safe-area-inset-bottom, 0px);
  left: 0;
  width: calc(100% - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px));
}

The env() function is defined in the CSS Environment Variables Module Level 1 specification.

Syntax

The env() function takes an environment variable name (e.g., safe-area-inset-top) and an optional fallback value (e.g., 0px).

env() = env(<custom-ident>, <declaration-value>?)

You can use it anywhere a length, color, or other value fits — think background-colorwidth, or padding as in the example below:

padding-top: env(safe-area-inset-top, 10px); /* Adds notch padding or 10px */

Common variables include:

  • safe-area-inset-topsafe-area-inset-rightsafe-area-inset-bottomsafe-area-inset-left: Offsets for device-safe areas (e.g., notches, status bars).

The safe-area-inset-* variables are the only universally supported environment variables defined in the CSS Environment Variables Module Level 1. They were introduced to tackle the iPhone X’s notch, and are now implemented across all major browsers (Chrome, Firefox, Safari, Edge) for iOS, Android, and other platforms. They offset content from device-specific obstructions, which makes them the primary use case for env(), as they solve a widespread problem without requiring custom setup.

env() in CSS?

Initially introduced in iOS 11 (September 2017) for the iPhone X’s notch, the CSS env() function is a part of CSS Environment Variables Module Level 1, which pulls dynamic values from the browser or OS. It debuted as constant() before being standardized in the CSS Working Group’s Environment Variables Module draft by 2018. It helped address the chaos of notched displays, giving developers a native way to style around hardware quirks.

The env() function accesses environment variables in CSS. It retrieves dynamic values like safe-area-inset-top-right-bottom-left (in pixels), reflecting device or app settings. If a variable is undefined, a fallback (e.g., 0px) kicks in. Unlike var(), which uses developer-defined properties, env() uses browser-provided data, updating as conditions change (e.g., rotation).

Basic usage

Let’s start with a classic scenario. You’re designing a full-screen hero section and want it to look polished on phones with notches or curved edges. You can choose to guess padding or risk clipped content, or bring in env() to handle it effortlessly. Here’s how env() helps:

.hero {
  padding-top: env(safe-area-inset-top, 20px);
  padding-bottom: env(safe-area-inset-bottom, 20px);
  height: calc(100vh - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
}

If you test this on an iPhone with a notch, you might discover that the safe-area-inset-top is 44px (covering the sensor area), so your hero’s padding-top becomes 44px, pushing content below the notch. On a notch-less Android, it falls back to 20px. The height calculation ensures the section fills the viewport without overflowing, subtracting insets dynamically.

You’re building a mobile app-like site with a navigation bar that sticks to the bottom. You want it to sit cleanly above the device’s home indicator (like iPhone’s swipe bar) without gaps or overlaps. env() makes this a breeze, and here’s how you can make it your own:

.nav {
  position: sticky;
  bottom: env(safe-area-inset-bottom, 0px);
  left: env(safe-area-inset-left, 0px);
  right: env(safe-area-inset-right, 0px);
}

In the demo above, the safe-area-inset-bottom (say, 34px for iPhone’s home indicator) ensures the nav floats just above it, avoiding any area where it’d be untappable. On a flat-edged device, it falls back to 0px, hugging the bottom perfectly. Also, the left and right insets keep it clear of curved corners.

Keyboard-aware inputs

You’re adding a chat input at the bottom of a messaging app. When the keyboard pops up on mobile, you don’t want it covering your input field. env() can adjust for the keyboard’s safe area, making the UI feel thoughtful:

.chat-input {
  position: fixed;
  bottom: calc(env(safe-area-inset-bottom, 0px) + 10px);
  left: env(safe-area-inset-left, 10px);
  right: env(safe-area-inset-right, 10px);
}

On an iPhone, safe-area-inset-bottom might jump to 34px when the keyboard appears, pushing the input up so it’s not hidden. The calc() adds a 10px buffer for breathing room. On a keyboard-less desktop, it falls back to 10px padding.

Non-standard environment variables

While the safe-area-inset-* environment variables are the only ones defined in the spec and are also supported everywhere, Chrome introduced two unofficial variables titlebar-area-* and keyboard-inset-*.

  • titlebar-area-*: It returns the safe area the title bar uses for the window’s button. It’s mainly used in progressive web apps that cover the full screen. Its suffix can be -x-y-width or -height.
  • keyboard-inset-*: It returns the safe area occupied by virtual keyboards in mobile. Its suffix can be -width-height-left-right-top or -bottom.

Besides those two, there are also two official environment variables defined in the spec but with no support in any browser: safe-area-max-inset-* and viewport-segment-*. You can check the demo below to see if your browser supports any of them:

Custom environment variables

For now, you can only define custom environment variables in your project using tools like PostCSS with the postcss-env-function plugin, which enables non-standard variables beyond browser-defined ones. First, install the plugin:

npm install postcss postcss-env-function --save-dev

In your PostCSS config (e.g., postcss.config.js), add the plugin and define custom variables:

module.exports = {
  plugins: [ require("postcss-env-function") ({variables: {"custom-theme": "#3498db", "custom-spacing": "20px"}})];
}

Then, in your CSS, use env() to access these variables:

.header {
  background: env(custom-theme, #ccc);
  padding: env(custom-spacing, 10px);
}

During the build, PostCSS replaces env(custom-theme) with #3498db and env(custom-spacing) with 20px, falling back to #ccc and 10px if undefined. This approach lets you manage project-specific variables centrally, though it requires a build step and is not native to browsers.

Future of env() variables

As PWAs evolve, I envision env() becoming a powerful tool for deeper integration with app settings, especially for branding and accessibility. Imagine a progressive web app where env() could directly pull values from the manifest.json file to style UI elements. For example, I suggest a future variable like env(manifest-background-color) to access the background_color (e.g., #ffffff) from the manifest, allowing the screen’s background to match:

.splash {
  background: env(manifest-background-color, #fff);
}

Or, picture yourself crafting a progressive web app with a custom theme color set in its manifest file (e.g., theme_color: #f1c40f). In the future, having env() pull that color directly from the manifest file would be a fun way to play with branding:

.header {
  background: env(theme-color, #f1c40f);
  padding: 15px;
  color: white;
  text-align: center;
}

With env(theme-color), your header inherits that exact shade in the manifest.json file without hardcoding it. If the app updates its theme, the CSS updates too.

env() vs. other techniques

The vw/vh, media queries and JavaScript tackle responsiveness but do nothing much regarding device context. Unlike env(), where it is its specialty. Here’s how they compare:

env()Proportional to the viewport.Media QueriesJavaScript
PurposeDevice/OS variable access.Viewport scaling.Breakpoint-based styles.Programmatic styling.
Syntaxenv(safe-area-inset-top, 0px)width: 100vw@media (max-width: 600px)element.style.padding = '20px'
BehaviorDynamic, context-specific offsets.Proportional to viewport.Discrete style changes.Any logic, any property.
PrecisionExact for safe areas, custom UI.Broad viewport sizing.Stepped, range-based.Ultimate control, complex setup.
Use CaseNotches, app themes, font scales.Fluid layouts.Screen-size layouts.Data-driven dynamics.
Examplepadding: env(safe-area-inset-left)height: 100vh@media (min-width: 600px)window.innerWidth checks

Demo

Here, safe-area-inset-bottom with env() helps to keep the footer above the home bar (e.g., 34px on iPhone), and width shrinks to avoid side insets.

Specification

The env() function is defined in the CSS Environment Variables Module Level 1 specification, which is currently in Editor’s Draft.

Browser support

You can always use the built-in fallback inside env() second argument, and also define the variable as usual before using env():

padding: 10px;
padding: env(safe-area-inset-top, 10px);