Como mudar a cor do placeholder em CSS (com o bug do Firefox resolvido)
Como mudar a cor do placeholder com ::placeholder e prefixos legados. Inclui o bug de opacidade do Firefox e o contraste mínimo WCAG de 4.5:1.
Neste artigo
Para mudar a cor do placeholder, use o pseudo-elemento ::placeholder com a propriedade color. O código mínimo funcional:
input::placeholder {
color: #767676;
opacity: 1;
}Dois detalhes que quebram na prática e quase nenhum tutorial menciona: o Firefox aplica opacity: 0.54 por padrão no placeholder (por isso a cor sai mais clara que você definiu, e a correção é opacity: 1 explícito), e a cor cinza padrão dos navegadores falha no contraste mínimo WCAG de 4.5:1. Use #767676 ou mais escuro sobre fundo branco para passar em acessibilidade.
Referência rápida dos seletores por navegador:
| Seletor | Navegadores | Status |
|---|---|---|
::placeholder | Todos modernos (desde 2017) | Padrão atual |
::-webkit-input-placeholder | Chrome, Safari, Edge antigo | Legado |
::-moz-placeholder | Firefox 19+ | Legado (tem opacity 0.54) |
:-ms-input-placeholder | Internet Explorer 10-11 | Legado |
:-moz-placeholder | Firefox 4-18 | Obsoleto |
Se seu público usa navegadores atuais (99% dos casos em 2026), só ::placeholder resolve. Os prefixos entram apenas se você precisa suportar IE11 ou versões muito antigas de Firefox.
Código completo com todos os prefixos (compatibilidade máxima)
Quando o projeto exige suporte legado, use o bloco completo. Cada seletor precisa de regra separada porque navegadores ignoram o bloco inteiro se encontram um seletor desconhecido:
/* Padrão moderno */
input::placeholder,
textarea::placeholder {
color: #767676;
opacity: 1;
}
/* Chrome, Safari, Edge antigo */
input::-webkit-input-placeholder,
textarea::-webkit-input-placeholder {
color: #767676;
}
/* Firefox 19+ */
input::-moz-placeholder,
textarea::-moz-placeholder {
color: #767676;
opacity: 1;
}
/* Internet Explorer 10-11 */
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #767676;
}Detalhe crítico: não agrupe os seletores com vírgula em um único bloco. Se você escrever input::placeholder, input::-moz-placeholder { color: red; }, o Chrome não entende ::-moz-placeholder e descarta a regra inteira. Cada família de prefixo precisa de bloco próprio.
O bug de opacidade do Firefox (por que sua cor sai errada)
O Firefox aplica opacity: 0.54 por padrão ao placeholder. Isso significa que se você define color: #333333, o navegador renderiza algo próximo de #8B8B8B porque a opacidade suaviza a cor.
O resultado prático: seu placeholder fica visivelmente mais claro no Firefox do que no Chrome, mesmo com o mesmo CSS.
A correção é declarar opacity: 1 explicitamente:
input::placeholder {
color: #333333;
opacity: 1; /* corrige o Firefox */
}Esse comportamento existe desde o Firefox 19 e continua ativo em 2026. Sempre inclua opacity: 1 quando definir cor de placeholder, mesmo que pareça redundante nos outros navegadores.
Contraste mínimo WCAG (a parte que quase todo mundo ignora)
Placeholders são texto e, pelas diretrizes WCAG 2.2, precisam do mesmo contraste mínimo que qualquer outro texto da página:
| Tipo de texto | Contraste mínimo exigido |
|---|---|
| Texto normal (até 18px) | 4,5:1 |
| Texto grande (18px+ ou 14px negrito) | 3:1 |
O problema: navegadores usam cinza claro combinado com opacidade de 40-50% no placeholder padrão, o que falha no critério 1.4.3 da WCAG em praticamente todos os casos.
Cores seguras para fundo branco (#FFFFFF):
#767676→ contraste 4,54:1 (passa por pouco)#6B6B6B→ contraste 5,32:1 (confortável)#595959→ contraste 7,00:1 (excelente)
Cores que falham em fundo branco:
#999999→ contraste 2,85:1 (reprovado)#AAAAAA→ contraste 2,32:1 (reprovado)#CCCCCC→ contraste 1,61:1 (muito reprovado)
Para testar qualquer combinação, use o WebAIM Contrast Checker, que calcula a razão e indica se passa nos níveis AA e AAA.
Nunca use opacidade para clarear placeholder. Defina a cor hexadecimal exata que passa no contraste e mantenha opacity: 1. Opacidade cria resultados imprevisíveis em telas com calibração diferente.
Placeholder não substitui label (o erro de acessibilidade mais comum)
Antes de qualquer ajuste de cor, um alerta que vale mais que o CSS: placeholder não é rótulo. Usar placeholder no lugar do <label> cria três problemas sérios:
- O texto some quando o usuário digita, então ele perde a referência do que aquele campo pede
- Leitores de tela tratam placeholder de forma inconsistente, alguns ignoram completamente
- Usuários com dificuldade cognitiva perdem o contexto ao preencher formulários longos
O padrão correto mantém os dois:
<label for="email">Seu email</label>
<input type="email" id="email" placeholder="exemplo@dominio.com">O label diz o que o campo é. O placeholder mostra um exemplo do formato esperado. Funções diferentes, ambos necessários.
Cor diferente por tipo de campo
Você pode aplicar cores distintas por tipo de input usando seletor de atributo:
/* Campo de email */
input[type="email"]::placeholder {
color: #4A6FA5;
opacity: 1;
}
/* Campo de senha */
input[type="password"]::placeholder {
color: #A54A4A;
opacity: 1;
}
/* Campo de busca */
input[type="search"]::placeholder {
color: #767676;
font-style: italic;
opacity: 1;
}
/* Textarea */
textarea::placeholder {
color: #6B6B6B;
opacity: 1;
}Use com moderação. Cores diferentes por campo podem confundir mais do que ajudar. O caso onde faz sentido é diferenciar campos obrigatórios de opcionais.
Placeholder em estados diferentes (:focus e :disabled)
Combine pseudo-classes de estado com ::placeholder para mudar a cor conforme a interação:
/* Estado normal */
input::placeholder {
color: #767676;
opacity: 1;
transition: color 0.2s ease;
}
/* Ao focar no campo, placeholder mais claro */
input:focus::placeholder {
color: #AAAAAA;
opacity: 1;
}
/* Campo desabilitado */
input:disabled::placeholder {
color: #CCCCCC;
opacity: 1;
}A transição suave ao focar é um detalhe visual que melhora a percepção de qualidade do formulário. O placeholder clareia levemente indicando que o campo está ativo, sem sumir de vez.
Cuidado com :focus: clarear demais prejudica quem precisa do texto de exemplo como referência ao digitar.
Placeholder com dark mode
Formulários que respondem ao tema do sistema precisam de cor de placeholder adaptada. Cinza escuro sobre fundo escuro fica ilegível:
/* Tema claro (padrão) */
input::placeholder {
color: #767676;
opacity: 1;
}
/* Tema escuro */
@media (prefers-color-scheme: dark) {
input::placeholder {
color: #9E9E9E;
opacity: 1;
}
}Sobre fundo escuro (#1A1A1A), a cor #9E9E9E entrega contraste de 5,74:1, dentro do exigido pela WCAG. Se seu tema escuro usa fundo diferente, teste a combinação antes.
Alternativa moderna com variáveis CSS:
:root {
--placeholder-color: #767676;
}
@media (prefers-color-scheme: dark) {
:root {
--placeholder-color: #9E9E9E;
}
}
input::placeholder {
color: var(--placeholder-color);
opacity: 1;
}A variável centraliza a manutenção. Se você precisar ajustar a cor depois, muda em um lugar só.
Placeholder no select (que não existe nativamente)
O elemento <select> não suporta o atributo placeholder. A técnica padrão simula o comportamento com uma option desabilitada:
<select required>
<option value="" disabled selected hidden>Escolha uma opção</option>
<option value="1">Opção 1</option>
<option value="2">Opção 2</option>
</select>select:invalid {
color: #767676;
}
select option {
color: #333333;
}O select:invalid pega o estado onde nenhuma opção válida foi escolhida ainda, permitindo estilizar como se fosse placeholder. O detalhamento completo dessa técnica está em como colocar placeholder no select.
5 Erros comuns ao estilizar cor de placeholder
Erro 1: agrupar prefixos no mesmo bloco CSS Escrever input::placeholder, input::-moz-placeholder { } faz navegadores descartarem a regra inteira ao encontrar seletor desconhecido. Separe em blocos independentes.
Erro 2: esquecer opacity: 1 no Firefox Sua cor sai mais clara que o esperado. Sempre declare opacidade explícita.
Erro 3: usar cinza claro demais #CCCCCC tem contraste de 1,61:1 sobre branco, muito abaixo do mínimo 4,5:1 da WCAG. Use #767676 ou mais escuro.
Erro 4: usar placeholder como substituto de label O texto some ao digitar e leitores de tela tratam de forma inconsistente. Mantenha <label> sempre.
Erro 5: aplicar opacidade para clarear em vez de escolher a cor certa Opacidade renderiza diferente conforme calibração de tela. Defina a cor hexadecimal exata.
Se você quer ir além da cor e ajustar fonte, tamanho e outros estilos do placeholder, o guia complementar está em como editar placeholder CSS. E para entender o elemento desde o conceito, veja placeholder: o que é e sua importância.
Perguntas frequentes
Como mudar a cor do placeholder em CSS?
Use o pseudo-elemento ::placeholder com a propriedade color. Código mínimo: input::placeholder { color: #767676; opacity: 1; }. O opacity: 1 é necessário porque o Firefox aplica opacidade 0.54 por padrão, deixando a cor mais clara que o definido.
Por que a cor do placeholder fica diferente no Firefox?
O Firefox aplica opacity: 0.54 por padrão ao placeholder desde a versão 19. Isso suaviza a cor definida, deixando o texto visivelmente mais claro que no Chrome. A correção é declarar opacity: 1 explicitamente na regra CSS.
Preciso usar prefixos de navegador em 2026?
Só se o projeto exige suporte a Internet Explorer 10-11 ou Firefox anterior à versão 19. Para navegadores atuais (99% dos casos), apenas ::placeholder resolve. O pseudo-elemento é padrão desde abril de 2017.
Qual cor de placeholder passa no contraste WCAG?
Sobre fundo branco, #767676 entrega 4,54:1 (mínimo aceito), #6B6B6B entrega 5,32:1 e #595959 entrega 7,00:1. Cores como #999999 (2,85:1) e #CCCCCC (1,61:1) reprovam no critério 1.4.3 da WCAG.
Posso usar placeholder no lugar do label?
Não. O texto some quando o usuário digita, leitores de tela tratam de forma inconsistente e usuários com dificuldade cognitiva perdem o contexto. Use <label> para identificar o campo e placeholder apenas para mostrar exemplo de formato.
Como mudar a cor do placeholder no dark mode?
Use media query prefers-color-scheme: dark redefinindo a cor. Sobre fundo escuro (#1A1A1A), a cor #9E9E9E entrega contraste de 5,74:1. A abordagem mais limpa é definir uma variável CSS e trocar apenas o valor dela dentro da media query.
Por que meu CSS de placeholder não funciona?
As três causas mais comuns: agrupar prefixos diferentes no mesmo bloco (navegador descarta a regra inteira), especificidade insuficiente contra o CSS do framework, ou esquecer que ::placeholder é pseudo-elemento e precisa de dois-pontos duplos, não simples.
Select aceita placeholder?
Não nativamente. O elemento <select> não suporta o atributo placeholder. A técnica padrão usa uma <option> com os atributos disabled selected hidden e valor vazio, estilizada via select:invalid no CSS.