---
title: "Por qué env(safe-area-inset) te devuelve 0"
excerpt: "Copias la receta del notch, la pegas, y no pasa nada. No está rota: le falta una pieza. Y cuando entiendas cuál, vas a descubrir que probablemente no la necesitas."
date: "2026-07-28T11:00:00.000Z"
category: "Web"
seo_title: "env(safe-area-inset) devuelve 0: cuándo usar viewport-fit=cover"
seo_description: "Las variables safe-area-inset valen 0 salvo que actives viewport-fit=cover. Te explico por qué, qué rompe activarlo y cómo decidir si tu sitio lo necesita."
keywords:
  - "safe-area-inset"
  - "viewport-fit cover"
  - "env safe area inset 0"
  - "notch CSS"
  - "safe area iPhone web"
  - "viewport-fit"
author:
  name: "angel cruz"
  picture: "https://angelcruzdevcdn.nyc3.cdn.digitaloceanspaces.com/images/me/angel-cruz.png"
ogImage:
  url: "/images/open-graph/og-image.png"
---

Tienes una barra fija abajo. En un iPhone con indicador de home queda medio tapada, así que buscas la solución, encuentras la receta de siempre y la pegas:

```css
.bottom-bar {
  padding-bottom: max(1rem, env(safe-area-inset-bottom));
}
```

Recargas. No cambió nada.

No está rota. Le falta una pieza, y esa pieza tiene efectos secundarios que casi nadie menciona. Esto va de entender el mecanismo completo para poder decidir si de verdad lo quieres, porque en muchos sitios la respuesta correcta es no tocar nada.

## Qué es la safe area

Los teléfonos dejaron de ser rectángulos limpios. Hay tres cosas que se comen pantalla:

- El notch o la isla dinámica arriba
- Las esquinas redondeadas
- La barra indicadora de home abajo

La **safe area** es el rectángulo donde puedes poner contenido sin que nada lo tape. Todo lo de afuera es zona de riesgo.

## Lo que el navegador hace sin que se lo pidas

Aquí está la parte que explica el misterio. Por defecto el navegador ya te protege: te achica el viewport para que quepa entero dentro de la zona segura.

El descriptor que controla esto es `viewport-fit`, definido en el [CSS Round Display Module Level 1](https://drafts.csswg.org/css-round-display/). Sus valores:

- **`auto`** (el default): "no afecta al viewport de layout inicial, y la página entera es visible"
- **`contain`**: "el viewport de layout inicial y el visual se fijan al rectángulo más grande inscrito en el display del dispositivo"
- **`cover`**: "el viewport de layout inicial y el visual se fijan al rectángulo circunscrito de la pantalla física"

Traducido: por defecto tu viewport queda **inscrito** dentro de la pantalla, esquivando el notch. Con `cover` queda **circunscrito**, o sea que abarca la pantalla física completa, notch incluido.

![Comparación de dos teléfonos. Por defecto, el viewport queda inscrito entre el notch y el indicador de home, sin tocarlos, y los safe area insets valen 0. Con viewport-fit=cover, el viewport cubre la pantalla física completa: las zonas del notch y del indicador quedan dentro de él y pasan a ser responsabilidad tuya, y los insets devuelven valores reales.](/images/posts/viewport-fit-safe-area.svg)

## Por qué tu env() vale 0

Ya se ve solo. [MDN define](https://developer.mozilla.org/en-US/docs/Web/CSS/env) que los valores de `safe-area-inset-*` son "0 si el viewport es un rectángulo y no hay features ocupando espacio del viewport".

Y ahí está: por defecto **el notch no ocupa espacio de tu viewport**, porque tu viewport empieza más abajo. No hay nada que compensar, así que los cuatro valores son `0`. Tu `max(1rem, 0)` resuelve a `1rem` y la línea no hace absolutamente nada.

No es una interpretación mía. Hay un [bug de WebKit](https://bugs.webkit.org/show_bug.cgi?id=272779) cuyo título lo dice literalmente: *safe-area-inset-left and safe-area-inset-right should be 0 [when] viewport-fit is not cover (but contain or auto)*.

La pieza que falta es esta:

```html
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
```

Con eso el viewport pasa a cubrir la pantalla entera, los insets devuelven píxeles de verdad, y tu padding funciona.

Pero acabas de cambiar el trato: **el navegador dejó de protegerte y ahora el responsable eres tú, en cada elemento del sitio.**

## Si tienes X, no hagas Y

Aquí es donde la mayoría de los tutoriales te deja tirado. Activar `cover` no es gratis. Estas son las reglas que uso para decidir.

### Si tu fondo es un color sólido, no actives cover

Es el caso más común y el más malentendido. La gente activa `cover` para que el fondo llegue al borde físico.

No hace falta. El [spec de backgrounds](https://www.w3.org/TR/css-backgrounds-3/#special-backgrounds) dice que "el fondo del elemento raíz se convierte en el fondo del canvas y su área de pintado se extiende para cubrir el canvas entero". En la práctica, en iOS la franja que queda fuera del viewport se pinta con el color de fondo de tu página. Si tu `body` tiene un color plano, **ya tienes el efecto edge-to-edge** sin ninguno de los riesgos.

Comprueba antes de asumir: si en tu teléfono no ves una banda de otro color, no tienes un problema que resolver.

### Si tienes elementos con position: fixed, cover te obliga a inset cada uno

Navbar arriba, barra de acciones abajo, un botón flotante, un menú a pantalla completa, un banner de cookies. Cada uno de esos elementos vive pegado a un borde, y con `cover` los bordes ahora incluyen zona insegura.

Activar `cover` sin tocarlos convierte un sitio que funcionaba en uno con el logo debajo del notch. No es una mejora incremental: o los arreglas todos en el mismo commit, o introdujiste una regresión.

### Si usas 100vw o full-bleed, revisa antes de activar

Con `cover` cambia lo que significa `100vw`: pasa a medir la pantalla física completa. Cualquier sección a sangre que hoy calza perfecto empieza a extenderse por debajo del notch.

Ojo con el padding, que es donde se cae mucha gente: si tu contenedor sangrado tiene `padding: 0 1rem`, esos 16px no alcanzan para librar un notch lateral de unos 47px. El fondo se extiende bien, pero el texto queda tapado.

### Si tienes imagen, video o degradado a sangre completa, ahí sí lo quieres

Este es el caso legítimo. Un hero con foto, un reproductor de video, un mapa, un lienzo de dibujo. Que el contenido visual toque el borde físico es una mejora real y perceptible, y justifica el trabajo de blindar el resto.

### Si es una PWA en modo standalone, casi siempre lo quieres

Sin chrome del navegador, tu contenido es toda la pantalla y la sensación de app depende de llegar a los bordes. Es el escenario donde `cover` más rinde. Y también donde más obligatorio es hacer bien la tarea, porque no hay barra del navegador que te tape los errores.

### Si tienes un bottom nav o un CTA sticky, cover sin env() es peor que nada

Estos dos se combinan mal por sí solos. Un bottom nav pegado a `bottom: 0` con `cover` activado queda directamente debajo del indicador de home, con targets táctiles que el sistema se come.

Si activas `cover`, el padding con `env()` en esos elementos no es opcional.

## La trampa: casi nadie prueba en horizontal

En vertical el notch está arriba y el indicador abajo, que es lo que todo el mundo revisa. **En horizontal el notch se va a un costado**, y ahí aparecen `safe-area-inset-left` y `safe-area-inset-right`, que en vertical valían 0.

Es el bug clásico de este tema: alguien activa `cover`, agrega padding arriba y abajo, prueba en vertical, y lo da por cerrado. Después llega un usuario en horizontal y tiene la primera palabra de cada título comida por el notch.

Si activas `cover`, gira el teléfono. No es opcional.

## Si decides hacerlo, hazlo completo

Va todo en el mismo commit o no va:

1. **El meta viewport** con `viewport-fit=cover`
2. **Padding horizontal** en cada contenedor a sangre: `padding-left: max(1rem, env(safe-area-inset-left))` y su espejo a la derecha
3. **Elementos fijos arriba**: `padding-top: max(…, env(safe-area-inset-top))`
4. **Elementos fijos abajo**: `padding-bottom: max(…, env(safe-area-inset-bottom))`
5. **Probar en vertical y en horizontal**, en un dispositivo con notch real

El patrón `max()` importa: te garantiza tu padding de diseño cuando el inset es 0 (un teléfono sin notch, un escritorio) y lo agranda solo cuando hace falta. Si escribes `padding-bottom: env(safe-area-inset-bottom)` a secas, en cualquier pantalla rectangular tu elemento se queda sin padding.

`env()` también acepta fallback como segundo argumento, útil para navegadores viejos que no conocen la variable:

```css
padding-bottom: max(1rem, env(safe-area-inset-bottom, 0px));
```

Y si te cruzas con `constant(safe-area-inset-bottom)` en algún snippet antiguo, ignóralo. Fue la sintaxis original de iOS 11.0 y [WebKit la reemplazó por `env()`](https://webkit.org/blog/7929/designing-websites-for-iphone-x/) en iOS 11.2. Está muerta.

## La regla mental

Antes de copiar la receta, pregúntate qué estás resolviendo:

- **¿Ves una franja de color distinto en el borde?** Probablemente no, y entonces no tienes un problema.
- **¿Tienes contenido visual que quieres que toque el borde físico?** Ahí sí, y vale el trabajo.
- **¿Estás copiando `env()` porque lo viste en un blog?** Comprueba primero si te devuelve algo distinto de 0.

El default del navegador no es una limitación que haya que vencer. Es una decisión de diseño sensata que te resuelve gratis el 90% de los casos. `viewport-fit=cover` es renunciar a esa red de seguridad a cambio de control, y solo conviene cuando de verdad vas a usar ese control.

La peor combinación posible es la intermedia: activarlo porque sonaba bien, blindar solo la mitad de los elementos, y probar solo en vertical.

---

## Sitemap

Índice completo del sitio: [/sitemap.md](https://www.angelcruz.dev/sitemap.md)

Canónico HTML: [https://www.angelcruz.dev/post/safe-area-inset-viewport-fit-cover](https://www.angelcruz.dev/post/safe-area-inset-viewport-fit-cover)
