Design

Por que gradientes CSS ficam cinza no meio, e duas formas de corrigir isso

De azul para amarelo o gradiente passa por um cinza morto por aritmética, não porque seu navegador está quebrado. Aqui está a aritmética, e as duas correções.

Um gradiente CSS fica cinza no meio porque o navegador mistura suas duas cores um canal por vez, em sRGB, e o ponto médio entre dois matizes opostos é neutro. Pegue o azul #0000FF indo para o amarelo #FFFF00: o canal vermelho vai de 0 a 255, o canal verde de 0 a 255, o canal azul de 255 a 0. Na metade do caminho, os três ficam em 127,5, que o navegador pinta como #808080. Mudar o ângulo não vai tirar isso do lugar, porque não é o ângulo que decide.

Esse é todo o mecanismo, e as correções saem direto dele. Ou você dá à mistura uma parada por onde passar, ou diz ao navegador para interpolar em algum espaço que não seja o sRGB.

Quais pares de cores ficam cinza

Os que ficam mais ou menos opostos na roda de cores, porque são os pares cujos canais se cancelam. O matiz é medido em graus ao redor da roda, então a distância entre as suas duas pontas prevê o estrago antes de você renderizar qualquer coisa.

GradienteDistância de matizPonto médio em sRGBO que você vê
#0000FF#FFFF00180°#808080Cinza médio morto
#00FFFF#FF0000180°#808080Cinza médio morto
#FF00FF#00FF00180°#808080Cinza médio morto
#FF0000#00FF00120°#808000Verde-oliva militar escuro
#6D5EFC#22D3EE58°#4899F5Um azul limpo

A última linha é a que ensina. Dois matizes a uma curta distância nunca passam perto do eixo neutro, então nada escorre do meio. É por isso que tantos gradientes que ficam bons em sites reais acabam sendo duas cores que são quase a mesma cor. Defina as duas pontas no gerador de gradientes e troque uma delas por um matiz vizinho: o meio volta a encher conforme a distância diminui, e dá para ver o ponto exato em que ele deixa de ser lama.

O cinza é só a falha mais barulhenta, não a geral. O par de 120° não fica cinza, fica escuro: vermelho e verde estão os dois a 50% de luminosidade, e o oliva #808000 está a 25%. Do azul para o amarelo se perde o contrário — a luminosidade de 50% se mantém de ponta a ponta e toda a saturação vai embora. O que a aritmética garante é que o ponto médio é o que a média dos canais produzir, e quanto mais afastadas as pontas, menos isso se parece com qualquer uma delas.

Correção 1: dê à mistura algum lugar para ir

Adicione uma terceira parada num matiz que você realmente queira, e o navegador interpola duas vezes por uma distância mais curta em cada uma, em vez de uma vez só pelo meio do nada.

O azul fica a 240° na roda e o amarelo a 60°, o que faz com que os dois caminhos tenham exatamente o mesmo comprimento. Pelo verde, o matiz do ponto médio é 150° — #00FF80. Pelo magenta, é 330° — #FF0080. Escolha o que pertence ao seu design:

background-image: linear-gradient(90deg, #0000FF, #00FF80, #FFFF00);

Isso funciona em qualquer coisa que entenda linear-gradient, ou seja, em todo navegador que você tem chance de encontrar. O problema é que agora você tem duas rampas em vez de uma, e se a luminosidade da parada do meio ficar acima ou abaixo da das duas pontas você ganha uma faixa clara ou escura visível exatamente onde estava tentando consertar as coisas. Escolha a parada do meio em HSL para conseguir segurar a saturação e a luminosidade e mexer só no matiz, e depois converta de volta para hex para a folha de estilos. Esse é o único trabalho em que o HSL é de fato melhor que o hex, e vale a pena conhecer as vantagens e desvantagens entre hex, RGB e HSL antes de ajustar uma rampa de cor na mão.

Um aviso se você for adicionar a parada no gerador daqui em vez de na mão: uma parada nova chega com a cor da média dos canais das duas vizinhas. Para azul e amarelo essa média é #808080 — a ferramenta te entrega exatamente o cinza do qual você veio se livrar. Mude a cor dela pela amostra. O controle deslizante de posição só move onde a parada fica, não o que ela é.

A porcentagem que não ajuda

Uma porcentagem solta entre duas cores é uma dica de interpolação de cor. Ela move o ponto em que as duas cores estão misturadas 50/50:

background-image: linear-gradient(90deg, #0000FF, 30%, #FFFF00);

As pessoas recorrem a isso quando o meio parece errado, e isso não conserta nada. A mistura continua percorrendo o mesmo caminho pelo espaço de cor, então o cinza continua lá — só que agora ele chega aos 30% em vez dos 50%. Uma dica remodela o ritmo da mistura. Não consegue mudar por onde a mistura passa.

Correção 2: interpole em outro espaço de cor

O CSS moderno deixa você nomear o espaço em que o navegador mistura, logo ao lado do ângulo:

background-image: linear-gradient(90deg in oklch, #0000FF, #FFFF00);

Duas paradas, sem lama. OKLCH é um espaço polar: luminosidade, croma, matiz. Interpolar nele gira o matiz ao redor da roda enquanto o croma anda em linha reta entre as duas pontas, então o meio continua sendo uma cor saturada em vez de desabar na direção do neutro. Ele também distribui a luminosidade de forma uniforme de um jeito que combina com o funcionamento do olho, e não com o modo como os números do sRGB acabam caindo, e é por isso que o resultado costuma parecer mais suave mesmo quando o cinza nunca foi o problema.

A ressalva honesta é o gamut. Do azul para o amarelo em OKLCH se pede um ponto médio com mais ou menos o dobro do croma que uma tela sRGB consegue produzir naquela luminosidade e naquele matiz, então o navegador mapeia isso de volta para dentro do gamut. Você ganha um meio saturado. Você não ganha o que os números descrevem.

Por padrão o navegador vai pelo caminho mais curto da roda. Adicione longer hue para mandá-lo pelo outro lado e varrer tudo que existe no meio, que é como se tira um arco-íris de duas paradas:

background-image: linear-gradient(90deg in oklch longer hue, #0000FF, #FFFF00);

OKLAB ou OKLCH?

OKLAB é o mesmo modelo de cor em coordenadas retangulares: luminosidade mais dois eixos. Misturar nele corrige a queda de luminosidade que faz os gradientes em sRGB parecerem sujos, e para a maioria dos pares de cores isso basta. Mas uma linha reta traçada entre dois matizes opostos passa perto do eixo neutro em qualquer espaço retangular, então para pontas genuinamente complementares o ponto médio do OKLAB ainda tem pouco croma — mais claro e mais bem comportado que #808080, mas não vivo. Se manter a cor viva no meio é o objetivo inteiro, use OKLCH e deixe ele pegar a roda de matizes.

O fallback que você mesmo tem que escrever

Um navegador antigo não ignora a parte in oklch e segue em frente. Ele falha ao fazer o parse do valor, o que torna a declaração inteira inválida, e aí você fica sem gradiente nenhum. A correção é o truque padrão da cascata do CSS — coloque a versão simples primeiro e a versão melhorada depois:

background-image: linear-gradient(90deg, #0000FF, #00FF80, #FFFF00);
background-image: linear-gradient(90deg in oklch, #0000FF, #FFFF00);

Qualquer coisa que entenda a segunda linha usa ela. Qualquer coisa que não entenda fica com a primeira. O suporte a espaços de interpolação já é amplo, então isso é um seguro e não uma restrição de verdade — mas você não consegue ver o que os seus visitantes estão rodando, e o modo de falha é um fundo em branco em vez de um gradiente simples. Custa uma linha.

Por que sumir para transparente também pode parecer cinza

Esse aqui tem outra causa e pega gente que nem está misturando dois matizes. No CSS, transparent não é “nada” — ele é definido como preto transparente, rgba(0, 0, 0, 0). Uma mistura ingênua de branco para transparent portanto leva a cor na direção do preto enquanto o alpha caminha para zero, e o meio sai como uma névoa cinza sobre o que quer que esteja atrás.

A especificação exige que os navegadores interpolem com alpha pré-multiplicado, o que evita o problema, e os motores atuais fazem isso. Se você ainda está vendo o fantasma — num motor mais antigo, ou em algo que renderiza CSS mas não é um navegador — escreva a versão explícita com alpha zero da sua própria cor em vez da palavra-chave:

background-image: linear-gradient(180deg, #FFFFFF, #FFFFFF00);

Não custa nada, é inequívoco, e se lê melhor que transparent de qualquer jeito, porque quem lê enxerga qual cor está sumindo.

Névoa cinza ou listras cinza? Não é o mesmo bug

Se o meio não está lavado e sim listrado — faixas visíveis avançando em degraus por uma mistura suave — isso é banding, e nenhuma das correções acima mexe nisso. Oito bits por canal dão 256 valores. Estique um gradiente sutil por 1200 pixels e há muito mais pixels do que cores distintas para preenchê-los, então a transição chega em degraus que dá para contar. Aumente a distância de cor entre as paradas, diminua a área, ou ponha uma textura de ruído bem leve por cima. Os navegadores aplicam um pouco de dither e de forma inconsistente, então o mesmo gradiente pode ter banding numa máquina e ficar limpo na próxima.

O diagnóstico rápido: um meio sujo parece igual em qualquer tamanho e em qualquer tela, porque é aritmética. O banding muda quando você redimensiona o elemento, porque é quantização.

O que fazer da próxima vez, antes de ficar cinza

Decida a rota do matiz antes de escolher as duas pontas. Se o briefing te entrega duas cores complementares, você está escolhendo entre três desfechos: uma terceira parada que se compromete com um lado da roda, uma mistura em OKLCH que se compromete por você, ou um meio cinza. Não existe uma quarta opção em que o sRGB faça a coisa certa em silêncio. Gradientes feitos com matizes a menos de uns 60° de distância nunca levantam a questão, o que é boa parte do motivo pelo qual as cores análogas aparecem sempre como a recomendação segura nos guias para montar uma paleta de cores que se sustenta.

O jeito mais rápido de ver tudo isso é definir as duas pontas no gerador de gradientes CSS, adicionar uma terceira parada e escolher a cor dela até a lama sumir — a prévia é o seu próprio navegador renderizando a mesma linha que você está prestes a colar, então nada está sendo aproximado. Ele escreve sRGB simples em uma camada só, então se você quiser a versão OKLCH copia o CSS e acrescenta in oklch depois do ângulo por conta própria.

Escolher essa parada do meio é um problema de cor mais do que de CSS, e vai muito mais rápido em HSL do que em hex. Hex, RGB e HSL explicados cobre qual formato deixa você segurar uma cor firme enquanto mexe em uma propriedade dela, e onde a ideia de luminosidade do HSL vai te enganar.

Perguntas frequentes

Por que meu gradiente CSS parece cinza no meio?

Os navegadores misturam as paradas de um gradiente em sRGB, um canal por vez. Quando as duas cores ficam em lados opostos da roda de cores, cada canal acaba com uma média perto do meio da sua faixa, então o ponto médio fica perto do cinza neutro. De azul para amarelo cai em #808080.

Como impedir que um gradiente CSS fique cinza?

Adicione uma terceira parada num matiz por onde você queira que a mistura passe, ou interpole em outro espaço de cor com linear-gradient(90deg in oklch, ...). A terceira parada funciona em todos os navegadores; a versão OKLCH mantém duas paradas e precisa de uma declaração simples de fallback acima dela.

O que “in oklch” faz num gradiente CSS?

Diz ao navegador para misturar em OKLCH em vez de sRGB. OKLCH é polar, então o matiz gira ao redor da roda de cores em vez de a mistura cortar pelo meio dela, o que impede o ponto médio de desabar na direção do cinza. Acrescentar “longer hue” manda a mistura pelo outro lado da roda e dá um arco-íris.

OKLAB ou OKLCH é melhor para gradientes?

OKLAB corrige a luminosidade desigual e serve para a maioria dos pares de cores. Para dois matizes quase complementares, OKLAB ainda dessatura no meio, porque uma linha reta entre matizes opostos passa perto do eixo neutro em qualquer espaço de cor retangular. OKLCH gira o matiz em vez disso e mantém a cor viva.

Por que um gradiente que some para transparente parece cinza?

A palavra-chave transparent do CSS significa preto transparente, rgba(0, 0, 0, 0), então uma mistura ingênua escurece na direção do preto enquanto some. Os navegadores atuais interpolam com alpha pré-multiplicado e evitam isso. Se você ainda vê uma névoa cinza, escreva a versão com alpha zero da sua própria cor, como #FFFFFF00, em vez da palavra-chave.

Qual é a diferença entre um gradiente sujo e um com banding?

Um meio sujo é uma cor dessaturada causada pela matemática da interpolação em sRGB, e parece idêntico em qualquer tamanho. Banding são listras visíveis causadas por existirem só 256 valores por canal numa área grande, e muda quando você redimensiona o elemento.

Última atualização 19 de setembro de 2026