Sprites CSS: quando ainda ajudam e como criar um
A maioria dos tutoriais de sprites CSS ainda começa com a mesma frase usada em 2009: sprites reduzem solicitações HTTP, portanto deixam seu site mais rápido. Esse argumento foi construído sobre uma limitação do HTTP/1.1 que não existe mais em um servidor moderno. Veja a versão honesta — o que ainda realmente justifica um sprite, o que não justifica e o cálculo exato de background-position e os cálculos de Retina para criar um que funcione.
O que realmente é um sprite CSS
Um sprite CSS é um único arquivo de imagem que contém vários elementos gráficos separados, acompanhado de CSS que mostra apenas um deles por vez. Não há recorte nem JavaScript envolvido. O elemento recebe um valor fixo de width e height, a folha inteira é definida como seu background-image, e background-position move essa folha atrás do elemento para que o ícone desejado fique dentro da caixa visível. A caixa é uma janela; a folha de sprites é uma grande folha de papel arrastada atrás dela.
O detalhe que confunde as pessoas no primeiro contato é que os deslocamentos são negativos. Você não está movendo a janela em direção ao ícone; está movendo a folha para que o ícone chegue à janela. Para mostrar um ícone cujo canto superior esquerdo fica a 96 pixels da borda esquerda da folha, você desloca a folha 96 pixels para a esquerda, o que corresponde a background-position: -96px 0.
Um exemplo concreto. Suponha que você organize cinco ícones de 24×24 em uma única linha, com 8 pixels de separação entre eles, começando a 8 pixels da esquerda e a 8 pixels do topo. Isso resulta em uma folha de 160×32. A borda esquerda do terceiro ícone fica em 8 + 24 + 8 + 24 + 8 = 72 pixels, e sua borda superior fica em 8 pixels. A regra é:
| Declaração | O que faz |
|---|---|
| background-image: url("/sprite.png") | Carrega a folha inteira atrás do elemento |
| background-repeat: no-repeat | Impede que a folha se repita e mostre outros ícones nas bordas |
| background-position: -72px -8px | Desloca a folha 72px para a esquerda e 8px para cima, para que o terceiro ícone fique na origem |
| width: 24px; height: 24px | Limita a janela a exatamente um ícone — é isso que esconde os demais |
| display: inline-block | Permite que um elemento inline, como span, respeite largura e altura |
Altere apenas os dois números em background-position e você obtém outro ícone do mesmo arquivo. Esse é o mecanismo inteiro.

A parte honesta: HTTP/2 mudou as contas
Sprites CSS foram inventados para superar uma limitação específica. No HTTP/1.1, um navegador abria poucas conexões TCP por origem — normalmente seis —, e cada conexão podia transportar apenas uma solicitação por vez. Quarenta arquivos de ícones significavam quarenta solicitações na fila de seis canais, cada uma pagando o custo de estabelecer a conexão e da latência de ida e volta. Reuni-los em um arquivo transformava quarenta solicitações serializadas em uma. Em uma conexão de alta latência, isso não era uma micro-otimização: muitas vezes era o maior ganho possível em uma página.
O HTTP/2 usa multiplexação. Muitas solicitações compartilham uma conexão simultaneamente, sem bloqueio no início da fila na camada HTTP, e os cabeçalhos são comprimidos entre solicitações. O problema específico que os sprites foram inventados para resolver praticamente desapareceu. Se um tutorial diz em 2026 que sprites são mais rápidos porque reduzem a quantidade de solicitações e para por aí, foi escrito para uma web que não existe mais.
Então por que este artigo existe? Porque "o motivo original deixou de existir" não é o mesmo que "não há motivo". Várias vantagens reais permanecem com a multiplexação, e são elas que merecem orientar a decisão:
- O custo adicional por requisição não chega a zero. Solicitações multiplexadas são baratas, não gratuitas. Cada uma ainda contém cabeçalhos, uma consulta ao cache e uma posição no agendador de recursos do navegador. Com cinco ícones, isso é irrelevante. Com duzentos ícones pequenos, é mensurável, e o sprite reduz tudo a uma entrada.
- A gestão do cache fica mais simples. Um arquivo significa uma entrada de cache, uma Cache-Control política, um nome de arquivo com hash para invalidar. Duzentos arquivos de ícones versionados separadamente são uma superfície de compilação e implantação que precisa ser gerenciada e normalmente não é.
- Versionamento atômico. Todos os ícones são distribuídos juntos, então você nunca acaba com um conjunto parcialmente atualizado, com três ícones do estilo antigo vindos do cache e os demais novos. Na implantação de um design system, isso garante consistência de verdade, não é apenas organização.
- Sem piscada de ícone ausente. Depois que a folha é carregada, todos os ícones ficam disponíveis instantaneamente. Ícones carregados individualmente aparecem um por vez conforme as solicitações são concluídas, o que é mais visível justamente onde mais incomoda: uma barra de ferramentas ou grade de ícones na parte inicial da página.
- Mudanças de estado sem latência. Um estado de foco do mouse ou ativo localizado em outra parte da mesma folha já foi baixado. Com arquivos separados, a imagem do estado de foco do mouse muitas vezes só é solicitada quando ele acontece pela primeira vez, causando uma piscada em branco na primeira interação do usuário com o botão. Esse sempre foi um dos argumentos mais fortes a favor dos sprites, e o HTTP/2 não o enfraqueceu em nada.
Quando não usar um sprite CSS
Uma recomendação confiável precisa incluir os casos em que a resposta é não, e há vários deles para sprites CSS.
- Ícones monocromáticos e escaláveis devem usar SVG. Um elemento inline <svg> ou um sprite SVG criado com <symbol> e <use> redimensiona para qualquer tamanho sem cálculos de Retina e herda a cor por meio de currentColor. Para um conjunto típico de ícones de interface, essa ferramenta é simplesmente melhor, e é por isso que as folhas de sprites perderam discretamente a maior parte de seu espaço nos sistemas de ícones.
- Um único ícone nunca justifica um sprite. Se você tem uma única imagem, use uma única imagem. O sprite acrescenta gestão de coordenadas e uma etapa de build sem qualquer benefício.
- Ícones que mudam independentemente. O versionamento atômico tem dois lados. Se um ícone muda, a entrada de cache da folha inteira é invalidada, e cada usuário baixa tudo novamente. Um conjunto de ícones que muda com frequência não é um bom candidato a sprite.
- Qualquer recurso que precise de recoloração ou temas. Um sprite rasterizado tem cores fixas. Modo escuro, temas de marca e cores de estado exigem uma segunda folha ou truques frágeis com filtros. Essa é a vantagem estrutural mais clara do SVG.
- Ícones que precisam acompanhar o tamanho da fonte. Sprites ficam vinculados a dimensões em pixels. Se seus ícones precisam acompanhar a escala de em junto com o texto, uma abordagem vetorial resolve isso, enquanto um sprite dificulta.
Sprite, sprite SVG, SVG embutido ou fonte de ícones
| Abordagem | Redimensiona sem distorções | Recolorir via CSS | Solicitações | Cache | Acessibilidade |
|---|---|---|---|---|---|
| Sprite CSS (rasterizado) | Não — pixels fixos, precisa de uma folha 2x | Não, apenas truques com filtros | Um para todos os ícones | Excelente, um arquivo de longa duração | Imagem de fundo, invisível à tecnologia assistiva — precisa de um rótulo de texto |
| Sprite SVG (symbol + use) | Sim, qualquer tamanho | Sim, via currentColor | Um para todos os ícones | Excelente, um arquivo | No DOM, aceita title e ARIA |
| SVG inline por ícone | Sim, qualquer tamanho | Sim, controle completo por CSS | Zero, embutido no HTML | Nenhum — reenviado em cada página | Melhor, totalmente no DOM |
| Fonte de ícones | Sim, acompanha font-size | Sim, via color | Um arquivo de fonte | Bom | Ruim — os glifos são lidos em voz alta ou substituídos por fontes alternativas |
Leia essa tabela como uma decisão, não como um placar. Artes raster multicoloridas — bandeiras, logotipos, emblemas ilustrados, capturas de ícones de aplicativos — não podem virar sprites SVG de uma forma razoável. É nesse espaço que sprites CSS continuam sendo a resposta correta, não uma solução ultrapassada.
Criando um
O fluxo de trabalho tem quatro etapas, e apenas a terceira é trabalhosa de fazer à mão.
- Reúna os ícones. Exporte no tamanho final de exibição (ou no dobro — veja a seção sobre retina). Mantenha os tamanhos consistentes sempre que possível; uma grade uniforme simplifica o cálculo das coordenadas e deixa o CSS regular.
- Agrupe-os em uma folha. Uma linha simples ou uma grade fixa é mais fácil de entender; um empacotador aproveita melhor o espaço quando os tamanhos variam. Deixe um espaço de separação entre os ícones.
- Leia as coordenadas. Cada ícone precisa de x, y, largura e altura em pixels da folha. Fazer isso manualmente em um editor de imagem é onde o trabalho com sprites costuma dar errado, porque uma leitura incorreta de um pixel aparece como uma faixa do ícone vizinho sem indicar o motivo.
- Escreva o CSS. Uma regra base compartilhada e uma pequena regra por ícone.
A regra base compartilhada importa mais do que parece. Todo ícone precisa do mesmo background-image, background-repeat: no-repeat, e display: inline-block. Repetir o background-image A URL em cinquenta regras não é um problema de desempenho — o navegador a busca uma única vez de qualquer forma —, mas é um problema de manutenção: renomeie a folha para invalidar o cache e você terá cinquenta lugares para editar, em vez de um. Assim, o padrão é uma classe base com tudo que é compartilhado e classes por ícone com apenas posição e tamanho:
| Regra | Conteúdo |
|---|---|
| .sprite | background-image, background-repeat: no-repeat, display: inline-block |
| .sprite-search | background-position: -8px -8px; width: 24px; height: 24px |
| .sprite-settings | background-position: -40px -8px; width: 24px; height: 24px |
Na marcação, isso é <span class="sprite sprite-search"></span>. No Sass, a mesma ideia costuma ser expressa como um placeholder, %sprite-base, incluído em cada regra de ícone com @extend — que produz um seletor agrupado na saída compilada, em vez de repetir as declarações, e mantém a marcação com apenas uma classe por ícone.
Retina e HiDPI: o truque de background-size
Essa é a parte que a maioria dos guias pula ou explica errado. Um sprite raster desenhado em 1x fica pouco nítido em uma tela 2x, e a solução não é mudar os deslocamentos — é empacotar no dobro da resolução e depois informar ao CSS que a folha tem metade de seu tamanho real.
Pegue o exemplo anterior e dobre tudo na exportação: os ícones têm 48×48 pixels reais, o espaço entre eles é 16 e a folha sai com 320×64 pixels reais. Agora defina background-size: 160px 32px — exatamente metade das dimensões reais da folha. O navegador reduz a folha inteira para um espaço de coordenadas de 160×32 pixels CSS e mapeia os detalhes extras para os pixels físicos do dispositivo em uma tela HiDPI.
A vantagem é que todos os outros números do seu CSS permanecem no sistema original de coordenadas 1x. O terceiro ícone continua sendo background-position: -72px -8px com width: 24px; height: 24px, sem alteração, mesmo que seus pixels reais estejam em 144, 16 no arquivo. Você calcula os deslocamentos uma única vez, em pixels CSS, e o único background-size declaração na classe base trata o mapeamento de densidade de todos os ícones de uma vez.
Duas consequências disso merecem ser ditas explicitamente. Primeiro, background-size pertence à regra base compartilhada, não deve ser repetido por ícone. Segundo, não sirva duas folhas por media query sem um bom motivo: a folha 2x em metade do tamanho também fica correta em telas 1x (o navegador apenas reduz a resolução), então uma folha e uma regra atendem aos dois casos, ao custo de um arquivo maior. Como uma folha de ícones bem comprimida normalmente é pequena, essa troca costuma valer a pena — e evita o meio-termo complicado de proporções fracionárias de pixels do dispositivo, como 1,5 e 2,5, em que a troca por media query precisa escolher um lado, e um deles estará errado.
Variantes de foco do mouse e estados
O layout clássico de sprites para elementos interativos é uma grade em que cada coluna é um ícone e cada linha é um estado: padrão na primeira linha, ao passar o mouse na segunda, ativo ou desativado na terceira. Como as colunas não se movem, uma mudança de estado é apenas um deslocamento em Y, e o deslocamento em X permanece exatamente igual.
Com ícones de 24px e um espaço de 8px, a primeira linha fica em y = 8 e a segunda em y = 40. A regra padrão do terceiro ícone é background-position: -72px -8px e sua regra ao passar o mouse é background-position: -72px -40px. Se definir o passo vertical como uma variável — por exemplo, --sprite-row: 32px — você pode definir o estado ao passar o mouse uma vez na classe base usando calc() com uma variável X por ícone, em vez de escrever uma segunda regra para cada ícone.
É aqui que o sprite realmente ainda supera arquivos separados: os pixels do estado ao passar o mouse chegaram junto com os pixels padrão, então a primeira passagem do mouse é instantânea. É também por isso que você deve manter os estados na mesma folha, em vez de separá-los — uma folha separada para esse estado reintroduz exatamente a piscada causada pela requisição na primeira passagem do mouse que o layout pretendia evitar.
Espaçamento e por que o vazamento de cor é menor aqui do que nos jogos
Agrupar imagens lado a lado cria a possibilidade de um amostrador ultrapassar o limite de um ícone e capturar a cor de um vizinho. Nas engines de jogos, esse é um risco constante e bem conhecido, porque as texturas são reduzidas, recebem mipmaps, são filtradas e desenhadas com transformações arbitrárias de subpixel.
Sprites CSS vivem em um ambiente mais favorável. Não há mipmaps, os fundos normalmente são compostos em posições inteiras e, exatamente em 1:1 sem transformação, nenhuma amostragem atravessa o limite. Mas "mais ameno" não significa "nunca", e os problemas aparecem em situações previsíveis: proporções fracionárias de pixels do dispositivo (1.5, 2.25, 3), zoom da página em porcentagens incomuns, qualquer transform: scale() em um ancestral, e o background-size redução da seção Retina — todos eles colocam as bordas dos ícones em limites fracionários de pixels do dispositivo, onde o compositor precisa interpolar.
A solução é a mesma e custa pouco: deixe um pequeno espaço transparente, de 2 a 4 pixels em 1x (portanto de 4 a 8 em uma folha 2x), entre todos os ícones e ao redor da borda externa da folha. Isso acrescenta muito pouco ao tamanho do arquivo e elimina toda uma categoria de relatos de erro do tipo "há uma linha fraca à esquerda deste ícone, mas só no meu notebook". Para a versão mais aprofundada desse problema — extrusão, vazamento de mipmap e dimensões em potências de dois — o equivalente para engines de jogos é abordado em Empacotamento de atlas de sprites: espaçamento, potências de dois e vazamento de cor.
Falhas comuns
- Ícones vizinhos aparecendo nas bordas. A caixa do elemento é maior que o ícone, então a janela expõe conteúdo da folha além dele. A largura/altura está errada ou o padding do elemento está aumentando a caixa — verifique se box-sizing: border-box está em uso, porque muda o que a largura declarada inclui.
- Esquecer background-repeat: no-repeat. O padrão é repeat, então a folha se repete e fragmentos de outros ícones preenchem a caixa. É fácil não perceber esse problema quando o ícone está perto da origem da folha e parece quase correto.
- Um span sem dimensões. Elementos inline ignoram width e height inteiramente, então um vazio <span> com uma classe de sprite fica com tamanho zero, e nada é renderizado. Defina display: inline-block (ou block, ou transforme-o em um item flex) na classe base.
- Folha desatualizada após uma recompilação. Você empacota o sprite novamente, as coordenadas mudam, mas um visitante que retorna ainda tem a folha antiga em cache junto do novo CSS — então todos os ícones ficam um pouco errados para ele e perfeitos para você. Invalide o cache mudando o nome do arquivo em cada recompilação (um hash de conteúdo, sprite.a1b2c3.png) em vez de depender de uma string de consulta ou de os usuários forçarem a atualização da página.
- Deslocamentos de meio pixel. Tamanhos ímpares de ícones, separações ímpares ou uma margem inicial ímpar em uma folha 2x produzem deslocamentos fracionários após a divisão pela metade, e deslocamentos fracionários são justamente o que causa bordas contaminadas. Mantenha todas as dimensões da folha 2x pares.
Perguntas frequentes
Os sprites CSS estão obsoletos em 2026?
A justificativa original está obsoleta; a técnica, não. Com HTTP/2 e HTTP/3, reduzir a quantidade de requisições já não é um motivo forte por si só. O que permanece é o caso de conjuntos de ícones com muitos gráficos raster pequenos, em que uma única entrada de cache, versionamento atômico, ausência de aparecimento gradual de cada ícone durante o carregamento e estados instantâneos ao passar o mouse ainda fazem diferença. Para ícones monocromáticos de interface, porém, SVG realmente substituiu o sprite raster — usar um nesse contexto em 2026 é escolher a ferramenta pior.
Sprite CSS ou sprite SVG — qual devo usar?
Decida com base na arte, não na forma de entrega. Se seus ícones forem planos, geométricos e monocromáticos ou de dois tons, use um sprite SVG: ele redimensiona sem uma folha 2x e muda de cor por meio de currentColor, e fica no DOM, onde as tecnologias assistivas podem acessá-lo. Se suas imagens forem fotografias, tiverem muitos gradientes ou forem arte rasterizada realmente multicolorida — bandeiras, miniaturas de produtos, logotipos de plataformas, pixel art —, o SVG não oferece vantagens, e um sprite CSS rasterizado é a escolha certa.
Quanto uma folha pode crescer antes de prejudicar o desempenho?
Há dois limites separados. Na prática: a folha bloqueia a renderização de todos os ícones nela, então uma folha grande o bastante para atrasar a primeira pintura deixou de ajudar — mantenha-a em poucas centenas de kilobytes e separe ícones pouco usados (telas de administração, painéis de configurações) em uma segunda folha carregada apenas onde necessário. Tecnicamente: navegadores e dispositivos móveis limitam as dimensões da imagem decodificada e a memória total de canvas, e folhas muito grandes podem ser reduzidas silenciosamente ou falhar na decodificação em celulares com pouca memória. Uma folha com mais de aproximadamente 2000 pixels por lado já merece ser dividida só por esse motivo, e lembre-se de que uma folha 2x já tem o dobro das dimensões projetadas.
Posso mudar a cor de um ícone de sprite com CSS?
Não exatamente, e essa é a resposta honesta, não a conveniente. As cores estão incorporadas ao arquivo rasterizado. Você pode aproximar o resultado com filter — encadeamento invert, sepia, saturate, e hue-rotate para aproximar um ícone preto de um tom desejado — mas é uma gambiarra no sentido literal: os valores são encontrados por tentativa ou por um solucionador, não atingem uma cor exata da marca, falham em arte multicolorida e são ilegíveis para a próxima pessoa que trabalhar no código. Uma segunda folha na cor alternativa é mais honesta que uma cadeia de filtros. Se recolorir é um requisito real, esse requisito está dizendo para você usar SVG.
Agrupe seus ícones em uma folha de sprites
O Sprite Gen pega as imagens que você solta, agrupa-as em uma única folha com o espaçamento definido e gera as coordenadas para que você nunca precise ler deslocamentos manualmente em um canvas. Escolha CSS e você recebe uma regra base compartilhada com background-image, background-repeat: no-repeat e display: inline-block, além de uma regra por sprite com seu valor negativo de background-position e largura e altura em pixels — exatamente a estrutura descrita acima. Escolha SCSS e a mesma saída chega como um %sprite-base placeholder que cada regra de ícone utiliza por meio de @extend. Escolha JSON para obter as coordenadas brutas dos quadros e usá-las nas suas próprias ferramentas. Tudo funciona no seu navegador — nada é enviado para lugar nenhum.
Abrir Gerador de folhas de sprites