
CSS Has Environment Variables Now
Here's What You Need to Know About env()
CSS is developing faster than most of us can keep up. One feature I recently discovered: the env() CSS function. Yes, there are environment variables in CSS, similar to what you'd use in Node.js.
TL;DR
CSS env() gives you access to browser-provided environment variables — safe area insets for notches and rounded screens, titlebar dimensions for desktop PWAs, keyboard insets for virtual keyboards, and viewport segments for foldable devices. But it doesn't stop there. With PostCSS, you can define your own environment variables in a single shared module and use them in both CSS and TypeScript — eliminating the "I updated it in one place but forgot the other" class of bugs entirely. The safe area insets have universal browser support today, the other browser-provided variables are landing in Chromium, and custom env variables work anywhere PostCSS does. If you're building for the modern web, env() should be in your toolkit.
The Syntax
Simple. Clean. Two arguments — the variable name and an optional fallback value for when the variable isn't defined (or equals zero).
All Available Environment Variables
There are more env() variables than you might expect. Here's the full list from the spec.
Safe Area Insets (widely supported)
safe-area-inset-topsafe-area-inset-rightsafe-area-inset-bottomsafe-area-inset-left
These are the OGs — the reason env() exists. They represent the space between the edge of the viewport and the area of the screen that's actually safe to render content in. They ensure your web content isn't obscured by device-specific UI elements like display notches (hello, iPhone), rounded screen corners, and system navigation bars (gesture bars on Android, the Dynamic Island on newer iPhones, etc.).
safe-area-inset Demo
There are also static max counterparts in the spec:
safe-area-max-inset-topsafe-area-max-inset-rightsafe-area-max-inset-bottomsafe-area-max-inset-left
These represent the maximum value the dynamic insets could ever reach — useful when you want to reserve space for a retractable system bar even when it's currently hidden. Limited browser support for now.
Titlebar Area (Chromium, for desktop PWAs)
titlebar-area-xtitlebar-area-ytitlebar-area-widthtitlebar-area-height
These kick in when your PWA uses the window-controls-overlay display override in its manifest. They let you position content where the title bar would normally be, while keeping it clear of the minimize/maximize/close buttons:
titlebar-area Demo
Keyboard Insets (Chromium, VirtualKeyboard API)
keyboard-inset-topkeyboard-inset-rightkeyboard-inset-bottomkeyboard-inset-leftkeyboard-inset-widthkeyboard-inset-height
These provide the position and dimensions of the on-screen virtual keyboard relative to the viewport. If you've ever built a chat input that gets buried behind the keyboard on mobile, these are your fix:
keyboard-inset Demo
Note: these require opting into the VirtualKeyboard API via JavaScript (navigator.virtualKeyboard.overlaysContent = true).
Viewport Segments (experimental, for foldables)
viewport-segment-topviewport-segment-rightviewport-segment-bottomviewport-segment-leftviewport-segment-widthviewport-segment-height
These are for multi-screen and foldable devices (think Samsung Galaxy Fold, Microsoft Surface Duo). They're indexed — you pass two integers after the name to specify which segment you're targeting:
The first integer is the horizontal index, the second is vertical. Very limited browser support.
viewport-segment Demo
Preferred Text Scale (emerging)
preferred-text-scale
Represents the user's preferred text scale factor from their OS or browser settings. If text-size-adjust: auto would double the text size, this resolves to 2. Could be useful for responsive typography that truly respects user preferences.
A Real Problem This Solves
Imagine you're building a PWA with a fixed bottom navigation bar. On an iPhone with a home indicator gesture bar, your nav sits underneath the system UI. Your users are tapping the bottom of your nav and accidentally going home instead.
With env(), you fix this in one line:
The browser knows the exact dimensions of the device's safe area and injects the correct value. No JavaScript measurement hacks, no device-specific media queries, no maintaining a lookup table of device dimensions.
Don't Forget the Viewport Meta Tag
None of this works without opting in. You need to tell the browser you want to render into the full viewport, including behind system UI:
The key part is viewport-fit=cover. Without it, the browser letterboxes your content into the safe area by default and the env() values will all be zero. You're essentially telling the browser: "I'll handle the safe areas myself, thanks."
Using It in Practice
Here's a more complete example — a full-bleed layout that respects all safe areas:
You can also use env() inside calc(), which is where it gets interesting:
This gives you a 60px nav bar that extends into the unsafe area with padding, so the background color fills the space but your actual content stays visible.
Tailwind CSS
If you're working in Tailwind, you can use arbitrary values:
Or define them as CSS variables and reference them in your Tailwind config for something more reusable:
Why the Fallback Matters
In regular desktop browsers (or devices without notches/rounded corners), these safe area variables typically resolve to zero. That's fine for padding — zero padding is usually what you want on a rectangular screen.
But if you're using env() in a calc() to define a height or position, a zero value might break your layout. That's where the fallback earns its keep:
Browser Support
env() has solid support across modern browsers. Safari led the charge (Apple needed it for the iPhone X notch), and Chrome, Firefox, and Edge all followed. You're safe to use it in production today.

