Melhoria no estilo padrão do modo escuro com a propriedade CSS do esquema de cores e a metatag correspondente

A propriedade CSS color-scheme e a metatag correspondente permitem que os desenvolvedores optem por aplicar as páginas aos padrões específicos do tema da folha de estilo do user agent.

Contexto

O recurso de mídia de preferência do usuário prefers-color-scheme

O recurso de mídia de preferência do usuário prefers-color-scheme dá aos desenvolvedores controle total sobre a aparência das páginas. Se você não conhece o modo escuro, leia meu artigo prefers-color-scheme: Hello darkness, my old friend, em que documentei tudo o que sei sobre a criação de experiências incríveis no modo escuro.

Uma peça do quebra-cabeça que foi mencionada apenas brevemente no artigo é a propriedade CSS color-scheme e a metatag correspondente com o mesmo nome. Elas facilitam sua vida como desenvolvedor permitindo que você ative a página para padrões específicos de tema da folha de estilo do agente do usuário, como controles de formulário, barras de rolagem e cores do sistema CSS. Ao mesmo tempo, esse recurso impede que os navegadores apliquem transformações por conta própria.

Suporte ao navegador

prefers-color-scheme

Compatibilidade com navegadores

  • Chrome: 76.
  • Edge: 79.
  • Firefox: 67.
  • Safari: 12.1.

Origem

color-scheme

Compatibilidade com navegadores

  • Chrome: 81.
  • Edge: 81.
  • Firefox: 96.
  • Safari: 13.

Origem

A folha de estilo do user agent

Antes de continuar, vamos descrever brevemente o que é uma folha de estilo de user agent. Na maioria das vezes, a palavra user agent (UA) é uma maneira elegante de dizer navegador. A folha de estilo do UA determina a aparência padrão de uma página. Como o nome sugere, uma folha de estilo do UA depende do UA em questão. Você pode conferir a folha de estilo UA do Chrome (e do Chromium) e compará-la com a do Firefox ou do Safari (e do WebKit). Normalmente, as folhas de estilo do UA concordam com a maioria das coisas. Por exemplo, todos eles tornam os links azuis, o texto geral preto e a cor de plano de fundo branco, mas também há diferenças importantes (e às vezes irritantes), como o estilo dos controles de formulário.

Confira a folha de estilo do UA do WebKit e o que ela faz em relação ao modo escuro. Faça uma pesquisa de texto completo para "dark" na folha de estilo. O padrão fornecido pela folha de estilo muda de acordo com a ativação ou desativação do modo escuro. Para ilustrar isso, aqui está uma dessas regras de CSS usando a pseudoclasse :matches e variáveis internas do WebKit, como -apple-system-control-background, bem como a diretiva do processador interno do WebKit #if defined:

input,
input:matches([type="password"], [type="search"]) {
  -webkit-appearance: textfield;
  #if defined(HAVE_OS_DARK_MODE_SUPPORT) &&
      HAVE_OS_DARK_MODE_SUPPORT
    color: text;
    background-color: -apple-system-control-background;
  #else
    background-color: white;
  #endif
  /* snip */
}

Você vai notar alguns valores não padrão para as propriedades color e background-color acima. Nem text nem -apple-system-control-background são cores CSS válidas. Elas são cores semânticas internas do WebKit.

O CSS padronizou as cores semânticas do sistema. Elas são especificadas no nível 4 do módulo de cor do CSS. Por exemplo, Canvas (não confundir com a tag <canvas>) é para o plano de fundo do conteúdo ou dos documentos do aplicativo, enquanto CanvasText é para o texto no conteúdo ou nos documentos do aplicativo. Os dois vão juntos e não devem ser usados separadamente.

As folhas de estilo do UA podem usar as cores proprietárias ou as cores do sistema semântico padrão para determinar como os elementos HTML serão renderizados por padrão. Se o sistema operacional estiver definido como modo escuro ou usar um tema escuro, CanvasText (ou text) será condicionalmente definido como branco, e Canvas (ou -apple-system-control-background) será definido como preto. A folha de estilo do UA atribui o CSS a seguir apenas uma vez e abrange os modos claro e escuro.

/**
  Not actual UA stylesheet code.
  For illustrative purposes only.
*/
body {
  color: CanvasText;
  background-color: Canvas
}

A propriedade CSS color-scheme

A especificação do Módulo de ajuste de cor do CSS nível 1 apresenta um modelo e controles sobre o ajuste automático de cores pelo agente do usuário com o objetivo de processar as preferências do usuário, como o modo escuro, o ajuste de contraste ou esquemas de cores específicos.

A propriedade color-scheme definida permite que um elemento indique com quais esquemas de cores ele pode ser renderizado. Esses valores são negociados com as preferências do usuário, resultando em um esquema de cores escolhido que afeta a interface do usuário (IU), como as cores padrão dos controles de formulário e barras de rolagem, bem como os valores usados das cores do sistema CSS. No momento, os seguintes valores são permitidos:

  • normal Indica que o elemento não tem conhecimento de esquemas de cores, portanto, ele precisa ser renderizado com o esquema de cores padrão do navegador.

  • [ light | dark ]+ Indica que o elemento conhece e pode processar os esquemas de cores listados e expressa uma preferência ordenada entre eles.

Nesta lista, light representa um esquema de cores claras, com cores de fundo claras e cores de primeiro plano escuras, enquanto dark representa o oposto, com cores de fundo escuras e cores de primeiro plano claras.

Para todos os elementos, a renderização com um esquema de cores precisa fazer com que as cores usadas em toda a interface fornecida pelo navegador para o elemento correspondam à intenção do esquema de cores. Exemplos são barras de rolagem, sublinhados de verificação ortográfica, controles de formulário etc.

No elemento :root, a renderização com um esquema de cores também precisa afetar a cor da superfície da tela (ou seja, a cor de plano de fundo global), o valor inicial da propriedade color e os valores usados das cores do sistema, e também precisa afetar as barras de rolagem da viewport.

/*
  The page supports both dark and light color schemes,
  and the page author prefers dark.
*/
:root {
  color-scheme: dark light;
}

A metatag color-scheme

Para respeitar a propriedade CSS color-scheme, é necessário primeiro fazer o download do CSS (se ele for referenciado por <link rel="stylesheet">) e depois fazer a análise. Para ajudar os agentes do usuário a renderizar o plano de fundo da página com o esquema de cores desejado imediatamente, um valor color-scheme também pode ser fornecido em um elemento <meta name="color-scheme">.

<!--
  The page supports both dark and light color schemes,
  and the page author prefers dark.
-->
<meta name="color-scheme" content="dark light">

Como combinar color-scheme e prefers-color-scheme

Como a metatag e a propriedade CSS (se aplicadas ao elemento :root) resultam no mesmo comportamento, sempre recomendo especificar o esquema de cores pela metatag para que o navegador possa adotar o esquema preferido mais rapidamente.

Embora para páginas de referência absolutas não sejam necessárias regras CSS adicionais, no caso geral, sempre combine color-scheme com prefers-color-scheme. Por exemplo, a cor -webkit-link do CSS do WebKit, usada pelo WebKit e pelo Chrome para o link azul clássico rgb(0,0,238), tem uma proporção de contraste insuficiente de 2,23:1 em um plano de fundo preto e não atende aos requisitos do WCAG AA e do WCAG AAA.

Eu abri bugs para o Chrome, WebKit e Firefox, bem como um problema de meta no padrão HTML para corrigir isso.

Interação com prefers-color-scheme

A interação da propriedade CSS color-scheme e a metatag correspondente com o recurso de mídia de preferência do usuário prefers-color-scheme pode parecer confusa no início. Na verdade, eles funcionam muito bem juntos. O mais importante é entender que color-scheme determina exclusivamente a aparência padrão, enquanto prefers-color-scheme determina a aparência estilizável. Para deixar isso mais claro, considere a seguinte página:

<head>
  <meta name="color-scheme" content="dark light">
  <style>
    fieldset {
      background-color: gainsboro;
    }
    @media (prefers-color-scheme: dark) {
      fieldset {
        background-color: darkslategray;
      }
    }
  </style>
</head>
<body>
  <p>
    Lorem ipsum dolor sit amet, legere ancillae ne vis.
  </p>
  <form>
    <fieldset>
      <legend>Lorem ipsum</legend>
      <button type="button">Lorem ipsum</button>
    </fieldset>
  </form>
</body>

O código CSS inline na página define o background-color do elemento <fieldset> como gainsboro no caso geral, e como darkslategray se o usuário preferir um esquema de cores dark de acordo com o recurso de mídia de preferência do usuário prefers-color-scheme.

Pelo elemento <meta name="color-scheme" content="dark light">, a página informa ao navegador que oferece suporte a temas claros e escuros, com preferência por um tema escuro.

Dependendo se o sistema operacional está definido como modo claro ou escuro, toda a página aparece clara em escuro ou vice-versa, com base na folha de estilos do agente do usuário. Não há CSS fornecido pelo desenvolvedor para mudar o texto do parágrafo ou a cor de fundo da página.

Observe como o background-color do elemento <fieldset> muda de acordo com a ativação do modo escuro, seguindo as regras da folha de estilos inline fornecida pelo desenvolvedor na página. É gainsboro ou darkslategray.

Uma página no modo claro.
Modo claro:estilos especificados pelo desenvolvedor e pelo agente do usuário. O texto é preto e o plano de fundo é branco, de acordo com a folha de estilo do agente do usuário. O background-color do elemento <fieldset> é gainsboro de acordo com a folha de estilo do desenvolvedor inline.
Uma página no modo escuro.
Modo escuro:estilos especificados pelo desenvolvedor e pelo user agent. O texto é branco e o plano de fundo é preto, de acordo com a folha de estilo do agente do usuário. O background-color do elemento <fieldset> é darkslategray de acordo com a folha de estilo do desenvolvedor inline.

A aparência do elemento <button> é controlada pela folha de estilos do user agent. O color é definido como a cor do sistema ButtonText, e o background-color e os quatro border-colors são definidos como a cor do sistema ButtonFace.

Uma página no modo claro que usa a propriedade ButtonFace.
Modo claro:o background-color e os vários border-colors são definidos como a cor do sistema ButtonFace.

Agora observe como o border-color do elemento <button> muda. O valor computado para border-top-color e border-bottom-color muda de rgba(0, 0, 0, 0.847) (preto) para rgba(255, 255, 255, 0.847) (branco), já que o agente do usuário atualiza ButtonFace dinamicamente com base no esquema de cores. O mesmo se aplica ao color do elemento <button> definido como a cor do sistema ButtonText correspondente.

Mostrando que os valores de cores calculados correspondem a ButtonFace.
Modo claro:os valores computados de border-top-color e border-bottom-color, ambos definidos como ButtonFace na folha de estilos do user agent, agora são rgba(0, 0, 0, 0.847).
Mostrando que os valores de cor calculados ainda correspondem a ButtonFace no modo escuro.
Modo escuro:os valores computados de border-top-color e border-bottom-color, definidos como ButtonFace na folha de estilos do user agent, agora são rgba(255, 255, 255, 0.847).

Demonstração

Confira os efeitos de color-scheme aplicados a um grande número de elementos HTML em uma demonstração no Glitch. A demonstração de forma deliberada mostra a violação do WCAG AA e do WCAG AAA com as cores de link mencionadas no aviso acima.

A demonstração no modo claro.
A demonstração foi alternada para color-scheme: light.
A demonstração no modo escuro.
A demonstração foi ativada para color-scheme: dark. Observe a violação do WCAG AA e do WCAG AAA do link com as cores dos links.

Agradecimentos

A propriedade CSS color-scheme e a metatag correspondente foram implementadas por Rune Lillesveen. Rune também é coeditor da especificação do Módulo de ajuste de cor do CSS nível 1. Imagem principal de Philippe Leone no Unsplash.