Ao criar componentes de interface reutilizáveis (como selos ou botões) ou oferecer suporte a temas dinâmicos, garantir que o texto seja legível em uma cor de plano de fundo imprevisível pode ser um desafio. A função CSS contrast-color() é resolvida como black ou white, maximizando a proporção de contraste em relação à cor de fundo especificada.
Determinar a cor ideal do texto
Quando um elemento tem uma cor de plano de fundo variável ou injetada dinamicamente, você pode confiar no navegador para calcular a cor de texto mais segura.
OBRIGATÓRIO: defina a propriedade color usando a função contrast-color() e transmita a cor de fundo (ou a propriedade personalizada dela) como argumento.
.badge {
/* Define the background color as a custom property */
--badge-bg: #e02424;
background-color: var(--badge-bg);
/* MANDATORY: Dynamic contrasting color */
color: contrast-color(var(--badge-bg));
}
NÃO use contrast-color() para background-color. Ela foi projetada especificamente para que as cores de primeiro plano (como texto ou bordas) contrastem com uma determinada cor de plano de fundo.
Integrar com temas
Como contrast-color() aceita qualquer valor <color> válido, incluindo propriedades personalizadas de CSS, ele é especialmente útil em sistemas de design. Ao centralizar os temas de plano de fundo como variáveis, você garante que o conteúdo em primeiro plano atualize automaticamente o contraste sempre que o tema mudar, mesmo para cores especificadas pelo usuário.
RECOMENDAÇÃO: faça referência a variáveis de segundo plano compartilhadas dentro de contrast-color() para garantir a sincronização em todo o aplicativo.
.theme-card {
background-color: var(--theme-surface-color);
color: contrast-color(var(--theme-surface-color));
}
.theme-container[data-theme="light"] {
--theme-surface-color: #f4f4f4;
}
.theme-container[data-theme="dark"] {
--theme-surface-color: #1a1a1a;
}
.theme-container[data-theme="custom"] {
--theme-surface-color: var(--user-custom-theme);
}
NÃO FAÇA: codificar uma cor de texto separada para cada estado de interação quando o plano de fundo for dinâmico. Isso reintroduz a incompatibilidade que o contrast-color() existe para evitar.
Estratégias de fallback
Para oferecer suporte a navegadores sem contrast-color(), é necessário fornecer um substituto. Se a cor do plano de fundo for conhecida e fixa, use uma cor contrastante codificada. Para planos de fundo dinâmicos em que a cor é desconhecida, escolha uma estratégia na tabela abaixo com base nos requisitos da interface e nas metas de suporte do navegador.
| Estratégia | Ideal para… | Considerações |
|---|---|---|
| Sintaxe de cor relativa | Cálculo de contraste automatizado e de alta qualidade somente com CSS. | Substituto somente CSS de alta qualidade. |
| Sombra do texto | Melhora rápida da legibilidade em qualquer plano de fundo. | Pode parecer "sujo" ou "brilhante" e não se adequar a todos os designs. |
| Traço do texto | Preservação da espessura da fonte e garantia de contraste de borda. | Use paint-order para evitar o afinamento de formas de letras, se disponível. |
| Sobreposição translúcida | Garantir uma área de contraste mínima atrás do texto. | Muda a aparência da cor do plano de fundo sob o texto, requer um elemento do texto separado. |
| Filtros SVG | Contraste reativo que se atualiza conforme o plano de fundo muda. | Requer um elemento do texto separado; implementação de hack. |
Recomendado: sintaxe de cor relativa (RCS)
Para navegadores compatíveis, o RCS oferece o substituto automatizado mais robusto para cores dinâmicas.
@supports (color: oklch(from red l c h)) {
.badge {
/* Highest threshold that passes WCAG. Higher values may be more legible. */
--threshold: 0.623;
--l: max(0, sign(var(--threshold) - l));
color: oklch(from var(--badge-bg) var(--l) 0 h);
}
}
@supports (color: contrast-color(red)) {
.badge {
color: contrast-color(var(--badge-bg));
}
}
Alternativas de CSS
FAÇA: selecione o substituto que melhor se adapta às suas restrições de design se o RCS não for suficiente ou não for compatível.
Opção 1: traçado do texto
.badge--stroke {
-webkit-text-stroke: 4px black;
}
Opção 2: traço de texto com paint-order para preservar as formas das letras
.badge--stroke {
-webkit-text-stroke: 4px black;
paint-order: stroke fill;
}
Opção 3: sobreposição de plano de fundo translúcido
.badge--overlay {
position: relative;
color: #fff;
overflow: hidden;
}
.badge--overlay::before {
content: "";
position: absolute;
inset: 0;
background-color: rgb(0 0 0 / 0.4);
z-index: 0;
}
.badge--overlay span {
position: relative;
z-index: 1;
}
Opção 4: sombra de texto
.badge--shadow {
text-shadow: 0 1px 3px rgb(0 0 0 / 0.8);
}
Opção 5: filtros SVG
Carregue esta definição de filtro SVG. Ele precisa ser adicionado diretamente ao documento HTML se você precisar oferecer suporte a versões do Safari afetadas por este bug do WebKit, que impede referências de URL de dados e externas para filtros. Caso contrário, ele precisa ser carregado de um arquivo externo, seja um arquivo SVG ou inline em CSS.
<!-- Technique from https://miunau.com/posts/dynamic-text-contrast-in-css/ --> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" height="0" style="display: none;"> <defs> <filter id="contrast-filter" color-interpolation-filters="sRGB"> <feColorMatrix type="matrix" values="0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0.2126 0.7152 0.0722 0 0 0 0 0 1 0"/> <feMorphology operator="dilate" radius="2"/> <feComponentTransfer> <feFuncR type="linear" slope="-255" intercept="128"/> <feFuncG type="linear" slope="-255" intercept="128"/> <feFuncB type="linear" slope="-255" intercept="128"/> </feComponentTransfer> <feComposite operator="in" in2="SourceGraphic"/> </filter> </defs> </svg>Envolva o texto em um
<span>.<div class="badge--svg"> <span>Badge content</span> </div>Aplique o filtro ao
<span>.
.badge--svg span{
color: var(--badge-bg);
filter: url(#contrast-filter);
}