Safe area não funciona? Por que env(safe-area-inset-bottom) dá 0
Um diagnóstico de 30 segundos e cada motivo para o inset vir zerado (ou com o valor errado), com a correção e um código para ler os valores no aparelho.
Atualizado em ·5 min de leitura
Safe areaiPhone 17 Pro · 402 × 874
6234
Diagnóstico em 30 segundos
Falta viewport-fit=cover? Todos os insets ficam em 0. Corrija a meta viewport primeiro.
Testando no computador ou no modo celular do DevTools? Tela retangular não tem safe area: 0 é a resposta certa ali.
Top, left e right dão 0 em retrato, no Safari? É o esperado. Em retrato, a barra e a status bar do próprio Safari cuidam da ilha.
O bottom dá 0 ou pouco no Safari, mas 34 no app instalado? Também é o esperado. No navegador, a barra do Safari fica entre a página e o indicador de início.
O valor está certo e o elemento continua atrás do indicador de início? O inset está no elemento errado, ou o position: fixed está com bottom: 0 sem padding.
Ler pelo JavaScript dá 0? Leia um padding calculado, não uma variável CSS (código no fim).
1. Falta viewport-fit=cover (na maioria das vezes, é isso)
No padrão (viewport-fit=auto), o iOS mantém o layout dentro da área segura e pinta as faixas em volta com a cor de fundo. A página nunca encosta na ilha, então todo env(safe-area-inset-*) vale 0. Para ligar:
Depois confira se existe uma meta viewport só. Frameworks colocam a deles, e uma segunda meta sem viewport-fit=cover pode ganhar. No console da página:
[...document.querySelectorAll('meta[name=viewport]')].map((m) => m.content);
// tem de voltar uma entrada só, com viewport-fit=cover
No Vite, no Astro ou em HTML puro, edite a meta no index.html ou no layout. Com o cover ligado, a página passa mesmo por baixo da ilha, e o cabeçalho e a barra de baixo precisam do padding.
2. A barra do Safari está no meio
No modo navegador do iPhone, enquanto a barra do Safari aparece, a página não encosta no fim da tela. Por isso o inset de baixo pode dar 0 ou menos que os 34 px do aparelho, e pode mudar quando a barra abre e recolhe. Fixar 34px no código erra para os dois lados.
O iOS 26 muda um detalhe: no layout Compacto, a barra de abas flutua sobre a página e, com viewport-fit=cover, o conteúdo rola por baixo dela. Elementos fixos no rodapé precisam do inset e de um fundo sólido (o Safari 26 pinta a barra com a cor dos elementos fixos perto da borda de baixo). O guia 100vh no Safari do iPhone explica a barra em detalhe.
Dê sempre um mínimo com max(), para a barra ter respiro quando o inset for 0:
.barra-de-abas {
position: fixed;
inset-inline: 0;
bottom: 0;
padding-bottom: max(env(safe-area-inset-bottom), 12px);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
background: #111;
}
main { padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px)); } /* o conteúdo não fica sob a barra */
3. Retrato e paisagem
Em retrato, os insets que contam são o de cima (status bar e ilha) e o de baixo (indicador de início). Em paisagem a ilha vai para uma lateral: safe-area-inset-leftou-right passa a valer de 50 a 62 px, conforme o modelo, e o de baixo cai para uns 21 px. Layout que só cuida de cima e de baixo fica bom em retrato e põe texto sob a ilha quando o celular gira.
Aberto pela tela de início, não há barra do Safari: o inset de baixo é a altura inteira do indicador de início, e o de cima depende do estilo da status bar:
apple-mobile-web-app-status-bar-styledefault: a status bar é sólida e a página começa abaixo dela, inset de cima 0.
black-translucent: a página passa por baixo da status bar e o safe-area-inset-top vale a altura dela. Use no cabeçalho.
Há um relato antigo no WebKit (bug 236445) em que o modo standalone com viewport-fit=contain dá inset de baixo 0 enquanto o navegador dá um valor. Com cover, você foge dele.
5. No computador e no DevTools, sempre 0
O modo celular do Chrome e do Edge acerta o viewport, a densidade e o user agent, mas não a safe area: todo inset dá 0, em qualquer “iPhone”. O layout pode ficar perfeito ali e continuar embaixo da ilha num celular de verdade.
Para ver os valores reais sem o celular, é preciso uma ferramenta que reproduza as medidas. O Chromium tem um comando no protocolo do DevTools para isso, o Emulation.setSafeAreaInsetsOverride, que uma ferramenta pode chamar com os números de cada aparelho. O Simulador do iOS, no Mac, também informa os valores.
6. Chrome do Android e edge-to-edge
O Chrome do Android entende env(safe-area-inset-*). Desde o Chrome 135 no Android 15, a página pode ir de ponta a ponta, por baixo da barra de gestos:
Sem viewport-fit=cover, o Chrome reserva uma faixa embaixo e o inset fica em 0.
Com cover, a página passa por baixo da barra de gestos e o safe-area-inset-bottom informa a altura dela. O valor pode mudar quando a barra de baixo do Chrome se mexe.
O Chrome também criou o safe-area-max-inset-bottom, o maior valor que o inset vai ter. A recomendação do time do Chrome é usar o máximo no padding e compensar a diferença na posição, para o elemento não recalcular o layout a cada rolagem:
constant(safe-area-inset-bottom) só valeu no iOS 11.0 e 11.1. Pode apagar.
Erro de digitação não avisa.env(safe-area-inset-botom) sem reserva faz a declaração inteira voltar ao valor inicial da propriedade. Ao depurar, use uma reserva: env(safe-area-inset-bottom, 0px).
Não use env() em media query. A especificação permite, mas não conte com os navegadores.
Iframes: não conte com o inset dentro de um quadro incorporado. Coloque o padding na página que contém o iframe.
Ler os valores reais no aparelho
Desde o iOS 17.5, ler uma variável como --sab: env(safe-area-inset-bottom) com getPropertyValue pode dar 0. Leia um padding calculado:
Rode no Safari, depois no app instalado, depois em paisagem. Três contextos, três conjuntos de valores: é normal.
Testar com os insets reais enquanto programa
O Breakpane dá a cada iPhone e Android com moldura os valores reais de safe area, por esse mesmo comando do Chromium, no Windows e no Mac: ilha ou notch, indicador de início, laterais em paisagem, barra do Safari ou do Chrome no modo navegador e o modo App, com o estilo de status bar da sua página. Ele lê o viewport-fit como o iOS: sem cover, os insets ficam em 0 lá também. Os agentes de código podem rodar a auditoria de responsividade, que aponta conteúdo fora da área segura. O motor é o Chromium, não o WebKit: confira o resultado final num aparelho de verdade.