O que um vídeo de fundo realmente custa em 2026
Um hero com movimento vende atmosfera. Também compete pelo mesmo orçamento de rede, CPU e paint que o título, o CTA e a imagem que o Lighthouse trata como Largest Contentful Paint. Em sites WordPress geridos a partir de Portugal e servidos a clientes em PT, ES e DE, o padrão que mais aparece nas auditorias é o mesmo: o vídeo entra no HTML inicial, o poster é irrelevante ou inexistente, e o autoplay falha em Safari porque faltou muted ou playsinline.
Este tutorial trata o vídeo de fundo como um recurso de performance, não como um embelezamento. A pergunta útil não é “como faço o ficheiro tocar”, e sim “em que condições o movimento merece bytes no caminho crítico”.
Nos browsers modernos, autoplay sem gesto do utilizador só é fiável quando o áudio está silenciado. A documentação da MDN sobre autoplay e o elemento video descrevem exatamente esse contrato. O guia da web.dev sobre otimizar LCP mostra porque um vídeo mal colocado empurra o maior elemento da viewport para fora do orçamento de 2,5 s.
Como fazer autoplay de vídeo com muted e playsinline
Chrome, Firefox, Edge e Safari permitem autoplay de media quando o elemento está muted (ou o volume efetivo é zero) e, no iOS Safari, quando playsinline está presente. Sem estes atributos, o play() devolve uma Promise rejeitada. O hero fica com um frame preto ou com o poster congelado, e o visitante assume que a página está partida.
Atributos mínimos para um fundo decorativo:
<video
class="hero-video"
autoplay
muted
loop
playsinline
preload="none"
poster="/media/hero-poster.avif"
>
<source src="/media/hero.webm" type="video/webm" />
<source src="/media/hero.mp4" type="video/mp4" />
</video>Notas práticas que evitam tickets de suporte:
mutedtem de estar no markup. Definirvideo.muted = truesó em JavaScript chega tarde para a primeira tentativa de autoplay em alguns WebViews.playsinlineé obrigatório no iOS; sem ele o Safari tenta ecrã completo e aborta o autoplay de fundo.loopsem faixa de áudio reduz surpresas quando o utilizador reativa o som por engano noutro separador.- Não dependa de
autoplaysozinho: chamevideo.play().catch(() => {})depois de anexar osrc, e trate a rejeição como estado normal em redes lentas ou com poupança de dados ativa.
Em Portugal, muitos formulários de contacto abrem em telemóveis com poupança de dados da operadora. Nesse cenário o browser pode adiar media mesmo com atributos corretos. O poster deixa de ser “nice to have” e passa a ser o único conteúdo estável do hero.
Política de autoplay do Chrome e Media Engagement Index
As regras do Chrome estão publicadas na página do Chrome for Developers sobre autoplay. A política chegou no Chrome 66 para elementos de áudio e vídeo, e a primeira regra resolve a questão do vídeo de fundo: o autoplay silenciado é sempre permitido. O autoplay com som só é permitido quando se verifica uma destas condições:
- o utilizador interagiu com o domínio (clique, toque ou semelhante),
- no desktop, o utilizador ultrapassou o limiar do Media Engagement Index,
- no telemóvel, o utilizador adicionou o site ao ecrã principal, ou no desktop instalou-o como PWA.
O Media Engagement Index (MEI) mede a propensão de uma pessoa para consumir media num determinado site. Uma reprodução só conta se durar mais de sete segundos, tiver áudio presente e não silenciado, o separador estiver ativo e o vídeo for maior do que 200x140 px. Um loop silencioso no hero nunca constrói MEI, nem precisa. Pode consultar os seus próprios valores em about://media-engagement.
Os iframes seguem a mesma lógica. Um iframe da mesma origem pode fazer autoplay por omissão; um iframe de outra origem (um player do YouTube ou do Vimeo) precisa que a permissão lhe seja delegada com allow="autoplay". Sem ela, play() é rejeitado com NotAllowedError e o atributo autoplay é ignorado.
Como verificar se o autoplay é permitido antes de chamar play
HTMLMediaElement.play() devolve uma Promise. O guia de autoplay da MDN mostra o padrão: se a Promise for rejeitada e o nome do erro for NotAllowedError, o browser recusou a reprodução por questões de permissão, por isso mostre um controlo de reprodução ou mantenha o poster. Qualquer outro erro aponta para um problema de carregamento ou descodificação que vale a pena registar.
const video = document.querySelector('.hero-video');
video.play().catch((error) => {
if (error.name === 'NotAllowedError') {
video.closest('.hero')?.classList.add('is-static'); // manter o poster
}
});Existe também navigator.getAutoplayPolicy('mediaelement'), que responde "allowed", "allowed-muted" ou "disallowed" antes de tentar seja o que for. Segundo os dados de compatibilidade de browsers da MDN, só está disponível no Firefox (a partir da versão 112), por isso trate-o como um atalho opcional atrás de uma verificação de suporte, nunca como o único caminho.
Do lado do servidor, o cabeçalho Permissions-Policy controla que origens podem fazer autoplay. O valor por omissão é self. Permissions-Policy: autoplay=() desativa o autoplay em todo o documento, um interruptor pouco subtil mas eficaz em páginas onde o movimento nunca deve arrancar sozinho.
O que o Safari no iOS faz com um vídeo em autoplay
A WebKit descreveu as regras do iOS em New video policies for iOS (julho de 2016, iOS 10), e continuam a explicar a maioria dos casos de “no desktop funciona, no iPhone fica parado”:
<video muted>pode fazer autoplay sem gesto do utilizador, tal como um vídeo cujo ficheiro não tem nenhuma faixa de áudio.<video playsinline>reproduz dentro da página no iPhone em vez de passar para ecrã inteiro.- Um vídeo com
autoplaypausa quando deixa de estar visível, por exemplo quando é deslocado para fora da viewport. - Se um vídeo ganhar uma faixa de áudio ou deixar de estar silenciado sem gesto do utilizador, a reprodução pausa.
Daqui saem duas consequências práticas. Remova a faixa de áudio ao codificar (-an no FFmpeg), porque um ficheiro sem áudio continua elegível mesmo que algum script altere muted mais tarde. E não escreva lógica que assuma que o loop continua a correr fora do ecrã no iOS: o browser já o pausa aí.
Imagem poster do vídeo como elemento LCP
Se o maior elemento da viewport inicial for o <video>, o LCP espera por frames descodificados. Em ligações 4G instáveis isso empurra o LCP para 4-6 s com facilidade. A correção padrão é tratar o poster como o candidato a LCP e só depois substituir por movimento.
Regras que funcionam em produção:
- Exporte o poster no mesmo enquadramento do primeiro frame do vídeo (evita CLS quando o vídeo arranca).
- Sirva o poster em AVIF ou WebP com dimensões explícitas no CSS ou nos atributos
width/height. - Faça preload do poster (
<link rel="preload" as="image" href="...">) se ele for o LCP declarado, não do ficheiro de vídeo. - Não coloque
preload="auto"no<video>do hero. Isso compete com CSS e fontes no caminho crítico.
A web.dev resume o princípio: otimize o recurso que realmente pinta o LCP. Num hero com vídeo, esse recurso é quase sempre a imagem estática, não o stream.
Numa landing de serviços WordPress dirigida ao mercado lusófono europeu, medimos o mesmo template duas vezes: com src no HTML inicial o LCP mediava acima de 3,8 s em lab mobile; com poster AVIF no HTML e src injetado após window.load o LCP caiu para a faixa do poster (~1,6-2,1 s), e o vídeo arrancava 1-2 s depois sem bloquear o CTA.
A definição de LCP da web.dev inclui <video> entre os elementos que podem ser o Largest Contentful Paint e indica que marca temporal é usada: o tempo de carregamento da imagem poster ou o tempo de apresentação do primeiro frame, o que acontecer primeiro. Um poster rápido é, portanto, a alavanca que controla: fixa o tempo de LCP mesmo quando o vídeo chega depois. Sem poster, a métrica espera pelo primeiro frame descodificado, que depende do download do vídeo.
O objetivo não muda num hero com vídeo: LCP de 2,5 segundos ou menos, medido no percentil 75 dos carregamentos de página e separado entre telemóvel e desktop. Um hero que passa no Wi-Fi do escritório mas falha no percentil 75 em telemóvel, falha.
Que valor de preload usar num vídeo de fundo
O atributo preload controla quanto o browser descarrega antes de um gesto explícito:
| Valor | Comportamento típico | Quando usar num fundo |
|---|---|---|
none | Quase nada até haver src ativo e pedido de play | Hero decorativo, mobile first |
metadata | Cabeçalhos e talvez um frame | Quando precisa da duração ou dimensões sem stream completo |
auto | O browser pode descarregar agressivamente | Quase nunca em fundos; reserva para players com intenção clara de ver |
Para fundos, o default sensato é preload="none" mais um src diferido. Se o markup já inclui <source>, alguns browsers ignoram parcialmente a intenção de none. A abordagem mais previsível é omitir src/source no HTML inicial e atribuir o URL só quando as condições de motion e viewport forem cumpridas.
<video class="hero-video" muted loop playsinline preload="none"
poster="/images/hero-poster.avif" data-src="/videos/hero.webm"></video>
<script>
const video = document.querySelector('.hero-video');
const allowMotion = !matchMedia('(prefers-reduced-motion: reduce)').matches;
if (video && allowMotion && matchMedia('(min-width: 768px)').matches) {
addEventListener('load', () => {
video.src = video.dataset.src;
video.play().catch(() => {});
}, { once: true });
}
</script>Este padrão mantém o HTML inicial leve, respeita motion reduzido e evita gastar dados móveis em movimento decorativo abaixo de 768 px.
Desativar vídeo de fundo com prefers-reduced-motion
A media query prefers-reduced-motion: reduce não é um detalhe de acessibilidade opcional. Visitantes com vestibulopatia, migrânea ou simplesmente com a opção ativada no sistema operativo pedem menos movimento. Um loop infinito atrás do texto falha esse pedido.
Implementação mínima e honesta:
@media (prefers-reduced-motion: reduce) {
.hero-video {
display: none;
}
.hero {
background-image: url("/images/hero-poster.avif");
background-size: cover;
}
}No JavaScript, a mesma condição deve impedir a atribuição de src. Esconder o elemento com CSS depois de o ficheiro já ter sido descarregado não recupera a largura de banda gasta.
Se o vídeo transporta informação (demonstração de produto, tutorial), não o use como fundo silencioso. Coloque-o num player com controlos, legendas e botão de play explícito. Fundo decorativo e conteúdo informativo são contratos diferentes com o utilizador.
WCAG 2.2.2 pausar, parar e ocultar o vídeo de fundo
Um vídeo de fundo em loop é conteúdo em movimento, e as WCAG cobrem-no no nível A. O critério de sucesso 2.2.2 diz que, para qualquer informação em movimento, intermitente ou em deslocamento que (1) começa automaticamente, (2) dura mais de cinco segundos e (3) é apresentada em paralelo com outro conteúdo, tem de existir um mecanismo para o utilizador a pausar, parar ou ocultar, a não ser que o movimento seja essencial. A página Understanding 2.2.2 explica que se escolheram cinco segundos porque é tempo suficiente para captar a atenção do utilizador, mas curto o bastante para que ele possa esperar que a distração passe.
Um loop no hero cumpre as três condições: arranca sozinho, repete-se para lá de cinco segundos e está por trás do título e do CTA. Por isso, é obrigatório um controlo de pausa real, e respeitar prefers-reduced-motion não o substitui, porque muitas pessoas que se distraem com movimento nunca alteram essa definição do sistema.
O que o controlo precisa na prática:
- um
<button>nativo acessível por teclado, colocado junto ao vídeo e visível tanto sobre o poster como sobre os frames em movimento, - um nome acessível que descreva a ação (“Pausar vídeo de fundo”), atualizado quando o estado muda,
- um estado de pausa que se mantenha pelo menos durante a visualização atual da página, para que o loop não recomece no scroll seguinte.
O áudio é outro critério. Se um clip alguma vez reproduzir som automaticamente, as WCAG remetem para o 1.4.2 Audio Control, mais uma razão para manter os loops de fundo silenciosos.
Vídeos de fundo na biblioteca de media do WordPress
O media library do WordPress foi desenhado para imagens e PDFs. Vídeos de hero expõem vários atalhos perigosos:
- Upload direto para
uploads/sem reencode. Um.movde 80 MB filmado no iPhone passa a URL pública. O tema faz<video src="...mov">e o primeiro visitante em 4G paga a fatura. - Plugins de “background video” que injetam YouTube no above-the-fold. O iframe puxa o player completo, cookies e scripts de recomendação antes do primeiro paint útil. O LCP e o INP sofrem mesmo com fachada mal configurada.
- Dependência de oEmbed no conteúdo do hero. O bloco Embed resolve o provider no servidor, mas o HTML final ainda é um iframe pesado. Para fundo, prefira ficheiro self-hosted curto ou um shortcode que só imprime
<video>com poster. - Falta de
loadingstrategy no tema. Muitos temas filhos copiam o markup do customizer sempreload="none"e sem teste em staging com throttling 4G. - CDN que cacheia o HTML com
srcabsoluto de staging. Depois do go-live o vídeo 404 e o poster fica sozinho - o que, ironicamente, é melhor para performance, mas parte a narrativa visual. - Áudio residual no ficheiro. Designers exportam “sem som” mas deixam uma faixa silenciosa. Alguns browsers tratam isso de forma inconsistente face a autoplay. Remova a faixa com FFmpeg (
-an) e volte a gerar WebM/MP4.
Fluxo de trabalho que reduz regressões:
- Reencode local (WebM + H.264 MP4), alvo tipicamente abaixo de alguns megabytes para loops de 6-12 s em 720p ou 1080p cropado.
- Carregue o poster otimizado e o vídeo via SFTP ou media library, mas referencie-os no tema com caminhos estáveis.
- Teste em Safari iOS real, Chrome Android e Firefox desktop com
prefers-reduced-motionligado. - Meça LCP no PageSpeed Insights e no campo (CrUX) depois de uma semana, não só no Lighthouse local.
Veja também os nossos serviços de otimização de velocidade WordPress quando o gargalo já não é só o markup do hero.
Alojar vídeo no próprio site ou usar YouTube e Vimeo
YouTube e Vimeo resolvem transcoding e CDN. Para um fundo full-bleed no primeiro ecrã, o custo de JavaScript do player raramente compensa.
- YouTube: precisa de
mute=1,controls=0, e o truque daplaylistcom o mesmo ID para loop. Continua a descarregar o player. - Vimeo:
background=1simplifica a UI, mas o iframe e a API continuam no caminho crítico se o embed estiver no HTML inicial. - Self-host: controlo total de
preload, poster e codecs. Exige disciplina de compressão e um fallback MP4 para Safari quando o primário é AV1/WebM.
A fachada (lite embed) faz sentido quando o vídeo é conteúdo a pedido. Para atmosfera de fundo, a fachada correta é o poster estático - sem player por baixo até haver condições de motion.
Verificar a descodificação de AV1 com Media Capabilities
Colocar AV1 como primeiro <source> só indica se o browser consegue reproduzir o codec, não se o dispositivo o descodifica em hardware. A descodificação de AV1 por software num telemóvel mais antigo pode engasgar e gastar bateria, o oposto do que um loop decorativo deve fazer.
A Media Capabilities API responde a essa pergunta. navigator.mediaCapabilities.decodingInfo() devolve três booleanos: supported (o conteúdo pode ser descodificado), smooth (reproduz à taxa de frames configurada sem perder frames) e powerEfficient. Para type: "file" a configuração precisa de contentType, width, height, bitrate e framerate; omitir um valor lança um TypeError. Os dados de compatibilidade da MDN indicam Chrome 66, Firefox 63 e Safari 13.
const av1 = {
type: 'file',
video: {
contentType: 'video/mp4; codecs="av01.0.05M.08"',
width: 1280,
height: 720,
bitrate: 1500000,
framerate: 30,
},
};
const pickSource = async () => {
if (!('mediaCapabilities' in navigator)) return '/media/hero.mp4';
const r = await navigator.mediaCapabilities.decodingInfo(av1);
return r.supported && r.smooth && r.powerEfficient
? '/media/hero.av1.mp4'
: '/media/hero.mp4';
};Use as mesmas dimensões e o mesmo bitrate com que codificou o ficheiro; caso contrário, a resposta descreve outro ficheiro.
Vídeo silenciado em vez de um GIF animado
Se o “fundo” for na verdade uma animação curta, um GIF é a forma mais pesada de a servir. O guia da web.dev para substituir GIFs por vídeo converte um GIF de 3,7 MB num MP4 de 551 KB e num WebM de 341 KB com estes comandos:
ffmpeg -i my-animation.gif -b:v 0 -crf 25 -f mp4 -vcodec libx264 -pix_fmt yuv420p my-animation.mp4
ffmpeg -i my-animation.gif -c vp9 -b:v 0 -crf 41 my-animation.webmO elemento de substituição usa os mesmos atributos que um loop de fundo: <video autoplay loop muted playsinline>. Tudo o resto deste guia (poster, carregamento diferido, controlo de pausa) aplica-se sem alterações.
Quando não usar vídeo de fundo
Salte o vídeo e fique no poster (ou num still bem composto) quando qualquer uma destas condições for verdadeira:
- A página depende de um LCP agressivo para campanhas pagas ou SEO local (ex.: landings de cidade com orçamento de anúncios limitado).
- Mais de metade do tráfego chega em redes móveis lentas ou com poupança de dados.
- O texto do hero já compete visualmente com o movimento (contraste fraco, CTA pequeno).
- O “vídeo” é um slideshow disfarçado: três fades lentos não precisam de um contentor
<video>. - Há obrigação legal ou de marca de legendas/áudio - isso pede player com controlos, não um loop muted.
- A equipa não tem processo para reencode e para testar
prefers-reduced-motionem cada release.
Um still AVIF de 80-120 KB, bem enquadrado, supera um WebM de 3 MB que falha o autoplay em iOS. Movimento só vale a pena quando sobrevive a throttling, a políticas de autoplay e a preferências de acessibilidade sem empurrar o LCP para fora do verde.
Como medir o impacto do vídeo de fundo no LCP
Checklist curto após cada alteração de hero:
- Lighthouse mobile e desktop: LCP, CLS, pedidos de vídeo no caminho crítico.
- Filmstrip: o CTA é legível antes do primeiro frame em movimento?
- Safari iOS com Low Power Mode: o autoplay falha com elegância para o poster?
prefers-reduced-motion: reducesimulado nas DevTools: zero downloads de vídeo?- WordPress staging com um editor sem perfil técnico a trocar o media: o markup ainda sai com
mutedeplaysinline?
Se o vídeo só “funciona” no MacBook do designer em Wi-Fi da empresa, ainda não está pronto para produção.
Resumo
Trate autoplay como um privilégio com regras: muted + playsinline + falha segura para poster. Proteja o LCP com uma imagem estática pré-carregada. Use preload="none" e atrase o src. Honre prefers-reduced-motion. No WordPress, não deixe o media library servir ficheiros de telemóvel sem reencode. E quando o movimento não sobrevive a estas restrições, publique o poster e siga em frente - a conversão raramente pede um loop de 8 segundos atrás do H1.
Fontes de referência usadas neste guia: optimize LCP (web.dev), LCP (web.dev), política de autoplay do Chrome, políticas de vídeo do iOS (WebKit), Understanding 2.2.2 (W3C), decodingInfo (MDN), substituir GIFs por vídeo (web.dev), elemento video (MDN), guia de autoplay (MDN).






