Estado do CSS 2022

Recursos de estilização da Web de hoje e de amanhã, como visto no Google I/O 2022, além de alguns extras.

2022 será um dos melhores anos do CSS, tanto em recursos quanto em lançamentos cooperativos de recursos do navegador, com o objetivo de implementar 14 recursos.

Visão geral

Esta postagem é a versão em artigo da palestra apresentada no Google IO 2022. Ele não é um guia detalhado sobre cada recurso, mas sim uma introdução e uma breve visão geral para despertar seu interesse, oferecendo amplitude em vez de profundidade. Se você tiver interesse, confira o final de uma seção para ver links de recursos com mais informações.

Índice

Use a lista abaixo para acessar os tópicos de interesse:

Compatibilidade com navegadores

Um dos principais motivos para o lançamento cooperativo de tantos recursos do CSS é o trabalho da Interop 2022. Antes de estudar as iniciativas de interoperabilidade, é importante analisar as iniciativas do Compat 2021.

Compat 2021

As metas para 2021, impulsionadas pelo feedback dos desenvolvedores em pesquisas, eram estabilizar os recursos atuais, melhorar o conjunto de testes e aumentar as pontuações de aprovação dos navegadores em cinco recursos:

  1. Posicionamento de sticky
  2. Dimensionamento de aspect-ratio
  3. Layout do flex
  4. Layout do grid
  5. Posicionamento e animação do transform

As pontuações dos testes aumentaram em geral, demonstrando maior estabilidade e confiabilidade. Parabéns às equipes!

Interop 2022

Este ano, os navegadores se reuniram para discutir os recursos e as prioridades em que pretendiam trabalhar, unindo esforços. Eles planejaram oferecer os seguintes recursos da Web para desenvolvedores:

  1. @layer
  2. Espaços e funções de cor
  3. Contenção
  4. <dialog>
  5. Compatibilidade de formulário
  6. Rolagem
  7. Subgrid
  8. Tipografia
  9. Unidades da janela de visualização
  10. Compatibilidade com a Web

Essa é uma lista ambiciosa e empolgante que mal posso esperar para ver se concretizar.

Novidades para 2022

Não é surpresa que o estado do CSS 2022 seja muito afetado pelo trabalho do Interop 2022.

Camadas em cascata

Browser Support

  • Chrome: 99.
  • Edge: 99.
  • Firefox: 97.
  • Safari: 15.4.

Source

Antes de @layer, a ordem descoberta de folhas de estilo carregadas era muito importante, já que os estilos carregados por último podem substituir os carregados anteriormente. Isso levou a folhas de estilo de entrada gerenciadas meticulosamente, em que os desenvolvedores precisavam carregar primeiro os estilos menos importantes e depois os mais importantes. Existem metodologias completas para ajudar os desenvolvedores a gerenciar essa importância, como o ITCSS.

Com o @layer, o arquivo de entrada pode pré-definir camadas e a ordem delas com antecedência. À medida que os estilos são carregados ou definidos, eles podem ser colocados em uma camada, preservando a importância da substituição de estilo, mas sem a orquestração de carregamento meticulosamente gerenciada.

O vídeo mostra como as camadas em cascata definidas permitem um processo de criação e carregamento mais livre e estilo livre, mantendo a cascata conforme necessário.

O Chrome DevTools é útil para visualizar quais estilos vêm de quais camadas:

Captura de tela da barra lateral &quot;Estilos&quot; do Chrome DevTools, destacando como os estilos aparecem em novos grupos de camadas.

Recursos

Subgrid

Browser Support

  • Chrome: 117.
  • Edge: 117.
  • Firefox: 71.
  • Safari: 16.

Source

Antes do subgrid, uma grade dentro de outra não podia se alinhar às células ou linhas da grade principal. Cada layout de grade era único. Muitos designers colocam uma única grade em todo o design e alinham constantemente os itens nela, o que não pode ser feito em CSS.

Depois de subgrid, um filho de uma grade pode adotar as colunas ou linhas dos pais como as próprias e se alinhar ou alinhar os filhos a elas.

Na demonstração a seguir, o elemento body cria uma grade clássica de três colunas: a coluna do meio é chamada de main, e as colunas esquerda e direita nomeiam as linhas fullbleed. Em seguida, cada elemento no corpo, <nav> e <main>, adota as linhas nomeadas do corpo definindo grid-template-columns: subgrid.

​​body {
  display: grid;
  grid-template-columns:
    [fullbleed-start]
    auto [main-start] min(90%, 60ch) [main-end] auto
    [fullbleed-end]
  ;
}

body > * {
  display: grid;
  grid-template-columns: subgrid;
}

Por fim, os filhos de <nav> ou <main> podem se alinhar ou dimensionar usando as colunas e linhas fullbleed e main.

.main-content {
  grid-column: main;
}

.fullbleed {
  grid-column: fullbleed;
}

As DevTools podem ajudar você a ver as linhas e as subgrades (somente no Firefox no momento). Na imagem a seguir, a grade principal e as subgrades foram sobrepostas. Agora ele se parece mais com a forma como os designers pensavam no layout.

Captura de tela de uma demonstração de subgrade usando as ferramentas de sobreposição de grade do Chrome DevTools para mostrar as linhas definidas pelo CSS.

No painel de elementos das ferramentas de desenvolvimento, é possível ver quais elementos são grades e subgrades, o que é muito útil para depurar ou validar o layout.

Captura de tela do painel &quot;Elementos&quot; do Chrome DevTools mostrando quais elementos têm layouts de grade ou subgrade.
Captura de tela das ferramentas de desenvolvimento do Firefox

Recursos

Consultas em contêiner

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 110.
  • Safari: 16.

Source

Antes do @container, os elementos de uma página da Web só podiam responder ao tamanho de toda a janela de visualização. Isso é ótimo para layouts macro, mas para layouts micro, em que o contêiner externo não é toda a janela de visualização, é impossível que o layout se ajuste de acordo.

Depois do @container, os elementos podem responder ao tamanho ou estilo de um contêiner principal. A única ressalva é que os contêineres precisam se declarar como possíveis destinos de consulta, o que é um requisito pequeno para um grande benefício.

/* establish a container */
.day {
  container-type: inline-size;
  container-name: calendar-day;
}

Esses estilos permitem que as colunas "Seg", "Ter", "Qua", "Qui" e "Sex" no vídeo a seguir sejam consultadas pelos elementos de evento.

Demonstração de Una Kravets

Confira o CSS para consultar o tamanho do contêiner calendar-day e ajustar um layout e tamanhos de fonte:

@container calendar-day (max-width: 200px) {
  .date {
    display: block;
  }

  .date-num {
    font-size: 2.5rem;
    display: block;
  }
}

Outro exemplo: um componente de livro se adapta ao espaço disponível na coluna em que é arrastado:

Demonstração de Max Böck

A Una está certa ao avaliar a situação como o novo responsivo. Há muitas decisões de design interessantes e significativas a serem tomadas ao usar o @container.

Recursos

accent-color

Browser Support

  • Chrome: 93.
  • Edge: 93.
  • Firefox: 92.
  • Safari: 15.4.

Source

Antes do accent-color, quando você queria um formulário com cores correspondentes à marca, podia acabar com bibliotecas complexas ou soluções de CSS difíceis de gerenciar com o tempo. Embora eles tenham oferecido todas as opções e, esperamos, incluído acessibilidade, a escolha de usar os componentes integrados ou adotar os seus se torna cansativa.

Depois de accent-color, uma linha de CSS traz uma cor da marca para os componentes integrados. Além de uma tonalidade, o navegador escolhe de forma inteligente cores de contraste adequadas para partes auxiliares do componente e se adapta aos esquemas de cores do sistema (claro ou escuro).

/* tint everything */
:root {
  accent-color: hotpink;
}

/* tint one element */
progress {
  accent-color: indigo;
}

Elementos HTML claros e escuros acentuados lado a lado para comparação.

Para saber mais sobre accent-color, confira minha postagem no web.dev, em que exploro muitos outros aspectos dessa propriedade útil do CSS.

Recursos

Níveis de cor 4 e 5

A Web é dominada pelo sRGB há décadas, mas em um mundo digital em expansão de telas de alta definição e dispositivos móveis pré-equipados com telas OLED ou QLED, o sRGB não é suficiente. Além disso, espera-se que as páginas dinâmicas se adaptem às preferências do usuário, e o gerenciamento de cores tem sido uma preocupação crescente para designers, sistemas de design e mantenedores de código.

Mas não em 2022. O CSS tem várias novas funções e espaços de cores: - Cores que alcançam os recursos de cores HD das telas. - Espaços de cores que correspondem a uma intent, como uniformidade perceptual. - Espaços de cores para gradientes que mudam drasticamente os resultados da interpolação. - Funções de cor para ajudar você a misturar e contrastar, além de escolher em qual espaço fazer o trabalho.

Antes de todos esses recursos de cores, os sistemas de design precisavam pré-calcular as cores de contraste adequadas e garantir paletas vibrantes, enquanto pré-processadores ou JavaScript faziam o trabalho pesado.

Depois de todos esses recursos de cores, o navegador e o CSS podem fazer todo o trabalho, de forma dinâmica e just-in-time. Em vez de enviar muitos KBs de CSS e JavaScript para os usuários ativar temas e cores de visualização de dados, o CSS pode fazer a orquestração e os cálculos. O CSS também está mais bem equipado para verificar o suporte antes do uso ou processar substituições de maneira adequada.

@media (dynamic-range: high) {
  .neon-pink {
    --neon-glow: color(display-p3 1 0 1);
  }
}

@supports (color: lab(0% 0 0)) {
  .neon-pink {
    --neon-glow: lab(150% 160 0);
  }
}

hwb()

Browser Support

  • Chrome: 101.
  • Edge: 101.
  • Firefox: 96.
  • Safari: 15.

Source

HWB significa matiz, brancura e negrura. Ele se apresenta como uma maneira fácil de articular a cor, já que é apenas um matiz e uma quantidade de branco ou preto para clarear ou escurecer. Artistas que misturam cores com branco ou preto podem gostar dessa adição à sintaxe de cores.

O uso dessa função de cor resulta em cores do espaço de cores sRGB, o mesmo que HSL e RGB. Em termos de novidades para 2022, isso não oferece novas cores, mas pode facilitar algumas tarefas para os fãs da sintaxe e do modelo mental.

Recursos

Espaços de cor

A representação das cores é feita com um espaço de cores. Cada espaço de cor oferece vários recursos e compensações para trabalhar com cores. Alguns podem juntar todas as cores claras, enquanto outros podem alinhar primeiro com base no brilho.

O CSS de 2022 vai oferecer 10 novos espaços de cores, cada um com recursos exclusivos para ajudar designers e desenvolvedores a mostrar, escolher e misturar cores. Antes, o sRGB era a única opção para trabalhar com cores, mas agora o CSS desbloqueia um novo potencial e um novo espaço de cores padrão, o LCH.

color-mix()

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 113.
  • Safari: 16.2.

Source

Antes do color-mix(), os desenvolvedores e designers precisavam de pré-processadores como o Sass para misturar as cores antes que o navegador as visse. A maioria das funções de mistura de cores também não oferecia a opção de especificar em qual espaço de cores fazer a mistura, o que às vezes resultava em resultados confusos.

Depois de color-mix(), os desenvolvedores e designers podem misturar cores no navegador, junto com todos os outros estilos, sem executar processos de build ou incluir JavaScript. Além disso, é possível especificar em qual espaço de cores fazer a combinação ou usar o espaço de cores de combinação padrão LCH.

Muitas vezes, uma cor da marca é usada como base, e variantes são criadas a partir dela, como cores mais claras ou mais escuras para estilos de passar o cursor. Veja como isso fica com color-mix():

.color-mix-example {
  --brand: #0af;

  --darker: color-mix(var(--brand) 25%, black);
  --lighter: color-mix(var(--brand) 25%, white);
}

e se você quiser misturar essas cores em um espaço de cor diferente, como srgb, mude assim:

.color-mix-example {
  --brand: #0af;

  --darker: color-mix(in srgb, var(--brand) 25%, black);
  --lighter: color-mix(in srgb, var(--brand) 25%, white);
}

Confira a seguir uma demonstração de temas usando color-mix(). Tente mudar a cor da marca e veja o tema ser atualizado:

Aproveite para misturar cores em vários espaços de cores nas suas folhas de estilo em 2022!

Recursos

color-contrast()

Antes do color-contrast(), os autores de folhas de estilo precisavam conhecer as cores acessíveis com antecedência. Muitas vezes, uma paleta mostrava texto preto ou branco em uma amostra de cor para indicar ao usuário do sistema de cores qual cor de texto seria necessária para contrastar adequadamente com essa amostra.

Captura de tela de três paletas do Material, mostrando 14 cores e as cores de contraste branco ou preto adequadas para texto.
Exemplo das paletas de cores do Material Design de 2014

Depois do color-contrast(), os autores de folhas de estilo podem transferir a tarefa completamente para o navegador. Além de usar o navegador para escolher automaticamente uma cor preta ou branca, você pode dar a ele uma lista de cores adequadas do sistema de design e fazer com que ele escolha a primeira que atenda à taxa de contraste desejada.

Confira uma captura de tela de uma demonstração de conjunto de paleta de cores HWB em que as cores do texto são escolhidas automaticamente pelo navegador com base na cor da amostra:

Captura de tela da demonstração do HWB em que cada paleta tem uma combinação diferente de texto claro ou escuro, conforme determinado pelo navegador.
Teste a demonstração

Os conceitos básicos da sintaxe são assim, em que o cinza é transmitido para a função e o navegador determina se o preto ou o branco têm mais contraste:

color: color-contrast(gray);

A função também pode ser personalizada com uma lista de cores, da qual ela vai escolher a cor de maior contraste da seleção:

color: color-contrast(gray vs indigo, rebeccapurple, hotpink);

Por fim, caso seja preferível não escolher a cor de maior contraste da lista, uma taxa de contraste de destino pode ser fornecida, e a primeira cor a passar por ela será escolhida:

color: color-contrast(
  var(--bg-blue-1)
  vs
  var(--text-lightest), var(--text-light), var(--text-subdued)
  to AA /* 4.5 could also be passed */
);

Essa função pode ser usada para mais do que apenas a cor do texto, embora eu estime que esse será o principal caso de uso dela. Pense em como será mais fácil oferecer interfaces acessíveis e legíveis quando a escolha de cores contrastantes adequadas for incorporada à própria linguagem CSS.

Recursos

Sintaxe de cor relativa

Browser Support

  • Chrome: 111.
  • Edge: 111.
  • Firefox: 113.
  • Safari: 15.

Source

Antes da sintaxe de cor relativa, para calcular a cor e fazer ajustes, os canais de cor precisavam ser colocados individualmente em propriedades personalizadas. Essa limitação também fez do HSL a principal função de cor para manipular cores, porque o matiz, a saturação ou a luminosidade podiam ser ajustados de maneira simples com calc().

Depois da sintaxe de cores relativas, qualquer cor em qualquer espaço pode ser desconstruída, modificada e retornada como uma cor, tudo em uma linha de CSS. Não há mais limitações para o HSL. As manipulações podem ser feitas em qualquer espaço de cor desejado, e muitas propriedades personalizadas precisam ser criadas para facilitar isso.

No exemplo de sintaxe a seguir, um hexadecimal de base é fornecido e duas novas cores são criadas em relação a ele. A primeira cor --absolute-change cria uma nova cor em LCH com base na cor primária e substitui a luminosidade da cor primária por 75%, mantendo o croma (c) e a matiz (h). A segunda cor --relative-change cria uma nova cor em LCH com base na cor primária, mas reduz o croma (c) em 20%.

.relative-color-syntax {
  --color: #0af;
  --absolute-change: lch(from var(--color) 75% c h);
  --relative-change: lch(from var(--color) l calc(c-20%) h);
}

É como misturar cores, mas mais parecido com alterações do que com mistura. Você pode transmitir uma cor de outra, tendo acesso aos três valores de canal nomeados pela função de cor usada, com a oportunidade de ajustar esses canais. No geral, essa é uma sintaxe muito legal e poderosa para cores.

Na demonstração a seguir, usei a sintaxe de cores relativas para criar variantes mais claras e escuras de uma cor base e usei color-contrast() para garantir que os rótulos tenham o contraste adequado:

Captura de tela com três colunas. Cada uma delas é mais escura ou mais clara do que a coluna central.
Teste a demonstração

Essa função também pode ser usada para gerar paletas de cores. Confira uma demonstração em que paletas inteiras são geradas com base em uma cor principal fornecida. Esse conjunto de CSS alimenta todas as paletas, e cada uma delas oferece uma base diferente. Como bônus, já que usei LCH, veja como as paletas são perceptualmente uniformes. Não há pontos quentes ou mortos, graças a esse espaço de cores.

:root {
  --_color-base: #339af0;

  --color-0:  lch(from var(--_color-base) 98% 10 h);
  --color-1:  lch(from var(--_color-base) 93% 20 h);
  --color-2:  lch(from var(--_color-base) 85% 40 h);
  --color-3:  lch(from var(--_color-base) 75% 46 h);
  --color-4:  lch(from var(--_color-base) 66% 51 h);
  --color-5:  lch(from var(--_color-base) 61% 52 h);
  --color-6:  lch(from var(--_color-base) 55% 57 h);
  --color-7:  lch(from var(--_color-base) 49% 58 h);
  --color-8:  lch(from var(--_color-base) 43% 55 h);
  --color-9:  lch(from var(--_color-base) 39% 52 h);
  --color-10: lch(from var(--_color-base) 32% 48 h);
  --color-11: lch(from var(--_color-base) 25% 45 h);
  --color-12: lch(from var(--_color-base) 17% 40 h);
  --color-13: lch(from var(--_color-base) 10% 30 h);
  --color-14: lch(from var(--_color-base) 5% 20 h);
  --color-15: lch(from var(--_color-base) 1% 5 h);
}
Captura de tela de 15 paletas geradas dinamicamente por CSS.
Teste a demonstração

Esperamos que agora você entenda como os espaços de cores e as diferentes funções de cores podem ser usados para fins distintos, com base nos pontos fortes e fracos deles.

Recursos

Espaços de cores gradientes

Antes dos espaços de cores gradientes, o sRGB era o espaço de cores padrão usado. O sRGB é geralmente confiável, mas tem algumas deficiências, como a zona morta cinza.

Quatro gradientes em uma grade, todos de ciano a rosa intenso. LCH e LAB têm uma vibração mais consistente, enquanto o sRGB fica um pouco dessaturado no meio.

Depois dos espaços de cores de gradiente, informe ao navegador qual espaço de cores usar para a interpolação de cores. Isso permite que desenvolvedores e designers escolham o gradiente que preferem. O espaço de cor padrão também muda para LCH em vez de sRGB.

A adição de sintaxe vem depois da direção do gradiente, usa a nova sintaxe in e é opcional:

background-image: linear-gradient(
  to right in hsl,
  black, white
);

background-image: linear-gradient(
  to right in lch,
  black, white
);

Este é um gradiente básico e essencial do preto ao branco. Analise o intervalo de resultados em cada espaço de cor. Alguns atingem o preto escuro mais cedo do que outros, alguns desaparecem para o branco tarde demais.

11 espaços de cores mostrados comparando preto e branco.

No próximo exemplo, o preto é transformado em azul porque é um espaço de problema conhecido para gradientes. A maioria dos espaços de cor se aproxima do roxo durante a interpolação de cores ou, como gosto de pensar, à medida que as cores viajam dentro do espaço de cor do ponto A ao ponto B. Como o gradiente vai de um ponto A a um ponto B em linha reta, a forma do espaço de cores muda drasticamente os pontos que o caminho faz ao longo do trajeto.

11 espaços de cores mostrados comparando azul e preto.

Para mais análises, exemplos e comentários, leia esta thread do Twitter.

Recursos

inert

Browser Support

  • Chrome: 102.
  • Edge: 102.
  • Firefox: 112.
  • Safari: 15.5.

Source

Antes do inert, era recomendável direcionar o foco do usuário para áreas da página ou do app que precisavam de atenção imediata. Essa estratégia de foco guiado ficou conhecida como captura de foco porque os desenvolvedores colocavam o foco em um espaço interativo, detectavam eventos de mudança de foco e, se o foco saísse do espaço interativo, ele era forçado a voltar. Os usuários de teclados ou leitores de tela são guiados de volta ao espaço interativo para garantir que a tarefa seja concluída antes de continuar.

Depois do inert, não é necessário fazer trapping porque é possível congelar ou proteger seções inteiras da página ou do app. Clicks e tentativas de mudança de foco simplesmente não estão disponíveis enquanto essas partes de um documento estão inertes. Também é possível pensar nisso como guardas em vez de uma armadilha. Nesse caso, inert não quer que você fique em um lugar, mas sim que outros lugares fiquem indisponíveis.

Um bom exemplo disso é a função alert() do JavaScript:

O site é mostrado como interativo, depois um alert() é chamado, e a página não fica mais ativa.

No vídeo anterior, observe como a página era acessível com mouse e teclado até que um alert() fosse chamado. Depois que a caixa de diálogo de alerta foi mostrada, o restante da página ficou congelado ou inert. O foco dos usuários é colocado dentro da caixa de diálogo de alerta e não tem para onde ir. Depois que o usuário interage e conclui a solicitação da função de alerta, a página volta a ser interativa. inert permite que desenvolvedores alcancem essa mesma experiência de foco guiado com facilidade.

Confira um pequeno exemplo de código para mostrar como funciona:

<body>
  <div class="modal">
    <h2>Modal Title</h2>
    <p>...<p>
    <button>Save</button>
    <button>Discard</button>
  </div>
  <main inert>
    <!-- cannot be keyboard focused or clicked -->
  </main>
</body>

Uma caixa de diálogo é um ótimo exemplo, mas o inert também é útil para coisas como a experiência do usuário do menu lateral deslizante. Quando um usuário desliza o menu lateral para fora, não é adequado permitir que o mouse ou o teclado interajam com a página por trás dele. Isso é um pouco complicado para os usuários. Em vez disso, quando o menu lateral estiver aberto, torne a página inerte. Assim, os usuários precisam fechar ou navegar dentro desse menu lateral, e nunca se perdem em outro lugar na página com um menu aberto.

Recursos

Fontes COLRv1

Antes das fontes COLRv1, a Web tinha fontes OT-SVG, também um formato aberto para fontes com gradientes e cores e efeitos integrados. No entanto, eles podiam ficar muito grandes e, embora permitissem editar o texto, não havia muito espaço para personalização.

Depois das fontes COLRv1, a Web tem fontes menores, escalonáveis por vetor, reposicionáveis, com gradiente e com modo de mesclagem que aceitam parâmetros para personalizar a fonte por caso de uso ou para corresponder a uma marca.

Visualização de comparação e gráfico de barras mostrando como as fontes COLRv1 são mais nítidas e menores.
Imagem extraída de https://developer.chrome.com/blog/colrv1-fonts/

Confira um exemplo da postagem do blog para desenvolvedores do Chrome sobre emojis. Talvez você tenha percebido que, se aumentar o tamanho da fonte de um emoji, ele não vai ficar nítido. É uma imagem, não arte vetorial. Muitas vezes, em aplicativos, quando um emoji é usado, ele é substituído por um recurso de qualidade superior. Com as fontes COLRv1, os emojis são vetoriais e bonitos:

As fontes de ícones podem fazer coisas incríveis com esse formato, oferecendo paletas de cores personalizadas de dois tons e muito mais. Carregar uma fonte COLRv1 é como qualquer outro arquivo de fonte:

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

A personalização da fonte COLRv1 é feita com @font-palette-values, uma regra-at especial do CSS para agrupar e nomear um conjunto de opções de personalização em um pacote para referência posterior. Observe como você especifica um nome personalizado, assim como uma propriedade personalizada, começando com --:

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

@font-palette-values --colorized {
  font-family: "Bungee Spice";
  base-palette: 0;
  override-colors: 0 hotpink, 1 cyan, 2 white;
}

Com --colorized como um alias para as personalizações, a última etapa é aplicar a paleta a um elemento que está usando a família de fontes de cores:

@import url(https://fonts.googleapis.com/css2?family=Bungee+Spice);

@font-palette-values --colorized {
  font-family: "Bungee Spice";
  base-palette: 0;
  override-colors: 0 hotpink, 1 cyan, 2 white;
}

.spicy {
  font-family: "Bungee Spice";
  font-palette: --colorized;
}
Captura de tela da fonte Bungee Spice com a palavra DUNE.
Fonte Bungee Spice mostrada com cores personalizadas, fonte de https://developer.chrome.com/blog/colrv1-fonts/

Com o aumento da disponibilidade de fontes variáveis e de cores, a tipografia da Web está em um caminho magnífico rumo à personalização avançada e à expressão criativa.

Recursos

Unidades da janela de visualização

Gráfico mostrando como a tela do dispositivo, a janela do navegador e um iframe têm diferentes viewports.

Antes das novas variantes de janela de visualização, a Web oferecia unidades físicas para ajudar a ajustar as janelas. Havia um para altura, largura, menor tamanho (vmin) e maior lado (vmax). Eles funcionavam bem para muitas coisas, mas os navegadores para dispositivos móveis introduziram uma complexidade.

Em dispositivos móveis, ao carregar uma página, a barra de status com o URL é mostrada, e essa barra consome parte do espaço da janela de visualização. Depois de alguns segundos e alguma interatividade, a barra de status pode deslizar para permitir uma experiência de viewport maior para o usuário. Mas quando essa barra desliza para fora, a altura da janela de visualização muda, e todas as unidades vh são movidas e redimensionadas conforme o tamanho desejado muda. Nos anos seguintes, a unidade vh precisou decidir qual dos dois tamanhos de viewport seria usado, porque estava causando problemas de layout visual em dispositivos móveis. Foi determinado que o vh sempre representaria a maior janela de visualização.

.original-viewport-units {
  height: 100vh;
  width: 100vw;
  --size: 100vmin;
  --size: 100vmax;
}

Depois das novas variantes de janela de visualização, as unidades pequenas, grandes e dinâmicas são disponibilizadas, com a adição de equivalentes lógicos às físicas. A ideia é dar aos desenvolvedores e designers a capacidade de escolher qual unidade eles querem usar para o cenário específico. Talvez seja aceitável ter uma pequena mudança de layout quando a barra de status desaparece. Assim, dvh (altura dinâmica da janela de visualização) pode ser usada sem problemas.

Um gráfico com três smartphones para ajudar a ilustrar DVH, LVH e SVH. O smartphone de exemplo do DVH tem duas linhas verticais: uma entre a parte de baixo da barra de pesquisa e a parte de baixo da janela de visualização, e outra entre a parte de cima da barra de pesquisa (abaixo da barra de status do sistema) e a parte de baixo da janela de visualização. Isso mostra como o DVH pode ter qualquer um desses dois comprimentos. O LVH é mostrado no meio com uma linha entre a parte de baixo da barra de status do dispositivo e o botão da janela de visualização do smartphone. O último é o exemplo da unidade SVH, que mostra uma linha da parte de baixo da barra de pesquisa do navegador até a parte de baixo da janela de visualização.

Confira uma lista completa de todas as novas opções de unidade de viewport disponibilizadas com as novas variantes de viewport:

Unidades de altura da janela de visualização
​​.new-height-viewport-units {
  height: 100vh;
  height: 100dvh;
  height: 100svh;
  height: 100lvh;
  block-size: 100vb;
  block-size: 100dvb;
  block-size: 100svb;
  block-size: 100lvb;
}
Unidades de largura da janela de visualização
.new-width-viewport-units {
  width: 100vw;
  width: 100dvw;
  width: 100svw;
  width: 100lvw;
  inline-size: 100vi;
  inline-size: 100dvi;
  inline-size: 100svi;
  inline-size: 100lvi;
}
Menores unidades laterais da janela de visualização
.new-min-viewport-units {
  --size: 100vmin;
  --size: 100dvmin;
  --size: 100svmin;
  --size: 100lvmin;
}
Maiores unidades laterais da janela de visualização
.new-max-viewport-units {
  --size: 100vmax;
  --size: 100dvmax;
  --size: 100svmax;
  --size: 100lvmax;
}

Esperamos que isso dê aos desenvolvedores e designers a flexibilidade necessária para criar designs responsivos de viewport.

Recursos

:has()

Browser Support

  • Chrome: 105.
  • Edge: 105.
  • Firefox: 121.
  • Safari: 15.4.

Source

Antes do :has(), o sujeito de um seletor sempre ficava no final. Por exemplo, o assunto deste seletor é um item de lista: ul > li. Os pseudoseletores podem alterar o seletor, mas não mudam o assunto: ul > li:hover ou ul > li:not(.selected).

Depois de :has(), um assunto mais acima na árvore de elementos pode permanecer o assunto ao mesmo tempo em que fornece uma consulta sobre filhos: ul:has(> li). É fácil entender como :has() recebeu o nome comum de "seletor pai", já que o assunto do seletor agora é o pai neste caso.

Confira um exemplo de sintaxe básica em que a classe .parent permanece o assunto, mas só é selecionada se um elemento filho tiver a classe .child:

.parent:has(.child) {...}

Veja um exemplo em que um elemento <section> é o assunto, mas o seletor só corresponde se um dos filhos tiver :focus-visible:

section:has(*:focus-visible) {...}

O seletor :has() se torna uma utilidade fantástica quando casos de uso mais práticos se tornam aparentes. Por exemplo, no momento não é possível selecionar tags <a> quando elas envolvem imagens, o que dificulta ensinar a tag de âncora a mudar os estilos nesse caso de uso. É possível com :has(), mas:

a:has(> img) {...}

Todos esses foram exemplos em que :has() parece apenas um seletor de elemento pai. Considere o caso de uso de imagens dentro de elementos <figure> e ajuste os estilos nas imagens se a figura tiver um <figcaption>. No exemplo a seguir, figuras com legendas são selecionadas e, em seguida, imagens nesse contexto. :has() é usado e não muda o assunto, já que o assunto que estamos segmentando são imagens, não figuras:

figure:has(figcaption) img {...}

As combinações são infinitas. Combine :has() com consultas de quantidade e ajuste os layouts de grade CSS com base no número de filhos. Combine :has() com estados de pseudoclasse interativa e crie aplicativos que respondem de novas maneiras criativas.

Verificar o suporte é simples com @supports e a função selector() dela, que testa se o navegador entende a sintaxe antes de usá-la:

@supports (selector(:has(works))) {
  /* safe to use :has() */
}

Recursos

2022 e depois

Ainda há algumas coisas que serão difíceis de fazer depois que todos esses recursos incríveis forem lançados em 2022. A próxima seção analisa alguns dos problemas restantes e as soluções que estão sendo desenvolvidas para resolvê-los. Essas soluções são experimentais, mesmo que possam ser especificadas ou disponibilizadas por flags nos navegadores.

As próximas seções mostram que os problemas listados têm muitas pessoas de várias empresas buscando soluções, não que essas soluções serão lançadas em 2023.

Propriedades personalizadas com tipagem flexível

Browser Support

  • Chrome: 85.
  • Edge: 85.
  • Firefox: 128.
  • Safari: 16.4.

Source

As propriedades personalizadas do CSS são incríveis. Elas permitem que todos os tipos de coisas sejam armazenados em uma variável nomeada, que pode ser estendida, calculada, compartilhada e muito mais. Na verdade, eles são tão flexíveis que seria bom ter alguns que fossem menos flexíveis.

Considere um cenário em que um box-shadow usa propriedades personalizadas para os valores:

box-shadow: var(--x) var(--y) var(--blur) var(--spread) var(--color);

Tudo funciona bem até que uma das propriedades seja alterada para um valor que o CSS não aceita, como --x: red. Toda a sombra será interrompida se alguma das variáveis aninhadas estiver faltando ou definida como um tipo de valor inválido.

É aqui que entra o @property: --x pode se tornar uma propriedade personalizada tipada, não mais solta e flexível, mas segura com alguns limites definidos:

@property --x {
  syntax: '<length>';
  initial-value: 0px;
  inherits: false;
}

Agora, quando o box-shadow usa var(--x) e depois --x: red é tentado, red é ignorado porque não é um <length>. Isso significa que a sombra continua funcionando, mesmo que um valor inválido tenha sido atribuído a uma das propriedades personalizadas dela. Em vez de falhar, ele volta ao initial-value de 0px.

Animação

Além da segurança de tipos, ele também abre muitas portas para animação. A flexibilidade da sintaxe CSS impossibilita a animação de algumas coisas, como gradientes. @property ajuda aqui porque a propriedade CSS tipada pode informar o navegador sobre a intenção de um desenvolvedor em uma interpolação excessivamente complexa. Isso limita o escopo de possibilidades de modo que um navegador pode animar aspectos de um estilo que não podia antes.

Considere este exemplo de demonstração, em que um gradiente radial é usado para criar uma parte de uma sobreposição, criando um efeito de foco de destaque. O JavaScript define o x e y do mouse quando a tecla alt/opt é pressionada e muda o tamanho do foco para um valor menor, como 25%, criando o círculo de foco do destaque na posição do mouse:

Teste a demonstração
.focus-effect {
  --focal-size: 100%;
  --mouse-x: center;
  --mouse-y: center;

  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    transparent 0%,
    transparent var(--focal-size),
    black 0%
  );
}

No entanto, não é possível animar gradientes. Eles são muito flexíveis e complexos para o navegador "apenas derivar" como você quer que eles sejam animados. Com @property, no entanto, uma propriedade pode ser digitada e animada isoladamente, e o navegador pode entender facilmente a intenção.

Os videogames que usam esse efeito de foco sempre animam o círculo, de um círculo grande para um círculo de furo de agulha. Veja como usar @property com nossa demonstração para que o navegador anime a máscara de gradiente:

@property --focal-size {
  syntax: '<length-percentage>';
  initial-value: 100%;
  inherits: false;
}

.focus-effect {
  --focal-size: 100%;
  --mouse-x: center;
  --mouse-y: center;

  mask-image: radial-gradient(
    circle at var(--mouse-x) var(--mouse-y),
    transparent 0%,
    transparent var(--focal-size),
    black 0%
  );

  transition: --focal-size .3s ease;
}
Teste a demonstração

Agora o navegador consegue animar o tamanho do gradiente porque reduzimos a área de superfície da modificação para apenas uma propriedade e digitamos o valor para que o navegador possa interpolar os comprimentos de maneira inteligente.

O @property pode fazer muito mais, mas essas pequenas ativações podem ajudar bastante.

Recursos

Esteve em min-width ou max-width

Antes dos intervalos de consultas de mídia, uma consulta de mídia CSS usava min-width e max-width para articular condições acima e abaixo. Ela pode ter esta aparência:

@media (min-width: 320px) {
  
}

Depois dos intervalos de media query, o mesmo media query pode ficar assim:

@media (width >= 320px) {
  
}

Uma consulta de mídia CSS que usa min-width e max-width pode ter esta aparência:

@media (min-width: 320px) and (max-width: 1280px) {
  
}

Depois dos intervalos de media query, o mesmo media query pode ficar assim:

@media (320px <= width <= 1280px) {
  
}

Dependendo da sua experiência em programação, um deles vai parecer muito mais legível do que o outro. Com as adições de especificações, os desenvolvedores poderão escolher qual preferem ou até mesmo usar os dois de forma intercambiável.

Recursos

Nenhuma variável de consulta de mídia

Antes de @custom-media, as consultas de mídia precisavam se repetir várias vezes ou depender de pré-processadores para gerar a saída adequada com base em variáveis estáticas durante o tempo de build.

Depois do @custom-media, o CSS permite o uso de alias em consultas de mídia e a referência a elas, assim como uma propriedade personalizada.

Dar nomes é muito importante: isso pode alinhar o propósito com a sintaxe, facilitando o compartilhamento e o uso em equipes. Confira algumas consultas de mídia personalizadas que me seguem entre projetos:

@custom-media --OSdark  (prefers-color-scheme: dark);
@custom-media --OSlight (prefers-color-scheme: light);

@custom-media --pointer (hover) and (pointer: coarse);
@custom-media --mouse   (hover) and (pointer: fine);

@custom-media --xxs-and-above (width >= 240px);
@custom-media --xxs-and-below (width <= 240px);

Agora que eles estão definidos, posso usar um deles assim:

@media (--OSdark) {
  :root {
    
  }
}

Encontre uma lista completa de consultas de mídia personalizadas que uso na minha biblioteca de propriedades personalizadas do CSS Open Props.

Recursos

Aninhar seletores é muito bom

Antes do @nest, havia muita repetição nas folhas de estilo. Isso se tornou especialmente complicado quando os seletores eram longos e cada um tinha como alvo pequenas diferenças. A conveniência do aninhamento é um dos motivos mais comuns para adotar um pré-processador.

Depois de @nest, a repetição desaparece. Quase todos os recursos de aninhamento ativado por pré-processador serão disponibilizados no CSS.

article {
  color: darkgray;
}

article > a {
  color: var(--link-color);
}

/* with @nest becomes */

article {
  color: darkgray;

  & > a {
    color: var(--link-color);
  }
}

O mais importante sobre o aninhamento para mim, além de não repetir article no seletor aninhado, é que o contexto de estilização permanece em um bloco de estilo. Em vez de ir de um seletor e seus estilos para outro seletor com estilos (exemplo 1), o leitor pode permanecer no contexto de um artigo e ver os links que ele contém. A relação e o objetivo de estilo são agrupados, então article parece ter estilos próprios.

A propriedade também pode ser considerada como centralização. Em vez de procurar em uma folha de estilo os estilos relevantes, todos eles podem ser encontrados aninhados em um contexto. Isso funciona com relações de pai para filho e de filho para pai.

Considere um elemento filho que quer se ajustar quando está em um contexto pai diferente, em vez de o pai possuir o estilo e mudar um filho:

/* parent owns this, adjusting children */
section:focus-within > article {
  border: 1px solid hotpink;
}

/* with @nest becomes */

/* article owns this, adjusting itself when inside a section:focus-within */
article {
  @nest section:focus-within > & {
     border: 1px solid hotpink;
  }
}

O @nest ajuda na organização, centralização e propriedade de estilos mais saudáveis. Os componentes podem agrupar e ter seus próprios estilos, em vez de serem espalhados entre outros blocos de estilo. Pode parecer pequeno nesses exemplos, mas pode ter impactos muito grandes, tanto para conveniência quanto para legibilidade.

Recursos

É muito difícil definir o escopo dos estilos

Browser Support

  • Chrome: 118.
  • Edge: 118.
  • Firefox: behind a flag.
  • Safari: 17.4.

Source

Antes do @scope, existiam muitas estratégias porque os estilos em CSS são em cascata, herdam e têm escopo global por padrão. Esses recursos do CSS são muito convenientes para muitas coisas, mas para sites e aplicativos complexos, com muitos estilos diferentes de componentes, o espaço global e a natureza da cascata podem fazer com que os estilos pareçam estar vazando.

Depois de @scope, os estilos não só podem ser definidos apenas em um contexto, como uma classe, mas também podem articular onde os estilos terminam e não continuam a cascatear ou herdar.

No exemplo a seguir, o escopo da convenção de nomenclatura BEM pode ser revertido para a intenção real. O seletor BEM está tentando definir o escopo da cor de um elemento header para um contêiner .card com convenções de nomenclatura. Isso exige que o cabeçalho tenha esse nome de classe, concluindo a meta. Com @scope, não é necessário usar convenções de nomenclatura para concluir a mesma meta sem marcar o elemento de cabeçalho:

.card__header {
  color: var(--text);
}

/* with @scope becomes */

@scope (.card) {
  header {
    color: var(--text);
  }
}

Confira outro exemplo, menos específico do componente e mais sobre a natureza do escopo global do CSS. Os temas claro e escuro precisam coexistir em uma folha de estilo, em que a ordem é importante para determinar um estilo vencedor. Normalmente, isso significa que os estilos do tema escuro vêm depois do tema claro. Isso estabelece o claro como padrão e o escuro como estilo opcional. Evite a ordenação e a disputa de escopo com @scope:

​​@scope (.light-theme) {
  a { color: purple; }
}

@scope (.dark-theme) {
  a { color: plum; }
}

Para concluir a história aqui, @scope também permite estabelecer onde o escopo de estilo termina. Isso não pode ser feito com nenhuma convenção de nomenclatura ou pré-processador. É especial e só pode ser feito com algo integrado ao CSS no navegador. No exemplo a seguir, os estilos img e .content são aplicados exclusivamente quando um filho de um .media-block é um irmão ou pai de .content:

@scope (.media-block) to (.content) {
  img {
    border-radius: 50%;
  }

  .content {
    padding: 1em;
  }
}

Recursos

Não há maneira de CSS para um layout em alvenaria

Antes do CSS masonry com grade, o JavaScript era a melhor maneira de criar um layout em alvenaria, já que qualquer um dos métodos de CSS com colunas ou flexbox representaria de forma imprecisa a ordem do conteúdo.

Depois do CSS masonry com grade, nenhuma biblioteca JavaScript será necessária e a ordem do conteúdo estará correta.

Captura de tela do layout em alvenaria que mostra números viajando pela parte de cima e depois descendo.
Imagem e demonstração da Smashing Magazine
https://www.smashingmagazine.com/native-css-masonry-layout-css-grid/

A demonstração anterior é feita com o seguinte CSS:

.container {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: masonry;
}

É bom saber que essa estratégia de layout ausente está no radar. Além disso, você pode testar hoje mesmo no Firefox.

Recursos

O CSS não ajuda os usuários a reduzir o consumo de dados

Browser Support

  • Chrome: behind a flag.
  • Edge: behind a flag.
  • Firefox: not supported.
  • Safari: not supported.

Source

Antes da consulta de mídia prefers-reduced-data, o JavaScript e um servidor podiam mudar o comportamento com base na opção "economia de dados" do sistema operacional ou navegador de um usuário, mas o CSS não podia.

Depois da consulta de mídia prefers-reduced-data, o CSS pode participar da melhoria da experiência do usuário e ajudar a economizar dados.

@media (prefers-reduced-data: reduce) {
  picture, video {
    display: none;
  }
}

O CSS anterior é usado neste componente de rolagem de mídia, e a economia pode ser enorme. Quanto maior a janela de visualização, maior a economia no carregamento da página. A economia continua enquanto os usuários interagem com os scrollers de mídia. Todas as imagens têm atributos loading="lazy", e isso, combinado com o CSS que oculta o elemento por completo, significa que uma solicitação de rede para a imagem nunca é feita.

Captura de tela de uma interface de carrossel de programas de TV com muitas miniaturas e títulos.

Para meus testes, em uma janela de visualização de tamanho médio, 40 solicitações e 700 kb de recursos foram carregados inicialmente. À medida que o usuário rola a seleção de mídia, mais solicitações e recursos são carregados. Com CSS e a consulta de mídia de dados reduzidos, 10 solicitações e 172 kb de recursos são carregados. Isso representa uma economia de meio megabyte, e o usuário nem rolou nenhuma mídia. Nesse ponto, não há mais solicitações feitas.

Captura de tela de uma interface de carrossel de programas de TV sem miniaturas e com muitos títulos mostrados.

Essa experiência de dados reduzidos tem mais vantagens do que apenas economia de dados. Mais títulos podem ser vistos, e não há imagens de capa que distraiam e roubem a atenção. Muitos usuários navegam em um modo de economia de dados porque pagam por megabyte de dados. É muito bom ver o CSS ajudando nesse caso.

Recursos

Os recursos de ajuste de rolagem são muito limitados

Antes dessas propostas de ajuste de rolagem, escrever seu próprio JavaScript para gerenciar um carrossel, controle deslizante ou galeria poderia se tornar rapidamente complexo, com todos os observadores e gerenciamento de estado. Além disso, se não tiver cuidado, as velocidades de rolagem natural poderão ser normalizadas por script, fazendo com que a interação do usuário pareça um pouco artificial e potencialmente desajeitada.

Novas APIs

snapChanging()

Esse evento é disparado assim que o navegador libera um filho de ajuste. Isso permite que a interface reflita a falta de um filho de ajuste e o estado de ajuste indeterminado do scroller, já que ele está sendo usado e vai aparecer em algum lugar novo.

document.querySelector('.snap-carousel').addEventListener('snapchanging', event => {
  console.log('Snap is changing', event.snappedTargetsList);
});
snapChanged()

Esse evento é disparado assim que o navegador se ajusta a um novo filho e o scroller fica parado. Isso permite que qualquer interface que dependa do elemento filho ajustado seja atualizada e reflita a conexão.

document.querySelector('.snap-carousel').addEventListener('snapchanged', event => {
  console.log('Snap changed', event.snappedTargetsList);
});
scroll-start

A rolagem nem sempre começa no início. Considere componentes que podem ser deslizados para a esquerda ou direita para acionar eventos diferentes ou uma barra de pesquisa que, ao carregar a página, fica inicialmente oculta até você rolar para a parte de cima. Essa propriedade CSS permite que os desenvolvedores especifiquem que um rolador deve começar em um ponto específico.

:root { --nav-height: 100px }

.snap-scroll-y {
  scroll-start-y: var(--nav-height);
}
:snap-target

Esse seletor de CSS vai corresponder a elementos em um contêiner de ajuste de rolagem que estão ajustados no momento pelo navegador.

.card {
  --shadow-distance: 5px;
  box-shadow: 0 var(--shadow-distance) 5px hsl(0 0% 0% / 25%);
  transition: box-shadow 350ms ease;
}

.card:snapped {
  --shadow-distance: 30px;
}

Depois dessas propostas de ajuste de rolagem, criar um controle deslizante, um carrossel ou uma galeria fica muito mais fácil, já que o navegador agora oferece conveniências para a tarefa, eliminando observadores e código de orquestração de rolagem em favor do uso de APIs integradas.

Ainda estamos no início desses recursos de CSS e JS, mas fique de olho nos polyfills que podem ajudar na adoção e no teste deles em breve.

Recursos

Ciclo entre estados conhecidos

Antes do toggle(), apenas os estados integrados ao navegador podiam ser usados para estilização e interação. A entrada de caixa de seleção, por exemplo, tem :checked, um estado de navegador gerenciado internamente para a entrada que o CSS pode usar para mudar o elemento visualmente.

Depois de toggle(), estados personalizados podem ser criados em qualquer elemento para que o CSS mude e use para estilização. Ele permite grupos, ciclos, alternância direcionada e muito mais.

No exemplo a seguir, o mesmo efeito de um item de lista tachado em completo é alcançado, mas sem elementos de caixa de seleção:

<ul class='ingredients'>
   <li>1 banana
   <li>1 cup blueberries
  ...
</ul>

E os estilos CSS toggle() relevantes:

li {
  toggle-root: check self;
}

li:toggle(check) {
  text-decoration: line-through;
}

Se você conhece máquinas de estado, vai perceber o quanto elas se cruzam com toggle(). Com esse recurso, os desenvolvedores podem criar mais estados em CSS, o que resulta em maneiras mais claras e semânticas de orquestrar a interação e o estado.

Recursos

Como personalizar elementos de seleção

Antes de <selectmenu>, o CSS não tinha a capacidade de personalizar elementos <option> com HTML avançado ou mudar muito a exibição de uma lista de opções. Isso levou os desenvolvedores a carregar bibliotecas externas que recriavam grande parte da funcionalidade de um <select>, o que acabou sendo muito trabalhoso.

Depois do <selectmenu>, os desenvolvedores podem fornecer HTML avançado para elementos de opções e estilizá-los o quanto for necessário, sem deixar de atender aos requisitos de acessibilidade e fornecer HTML semântico.

No exemplo a seguir, extraído da página de explicação do <selectmenu>, um novo menu de seleção é criado com algumas opções básicas:

<selectmenu>
  <option>Option 1</option>
  <option>Option 2</option>
  <option>Option 3</option>
</selectmenu>

O CSS pode segmentar e estilizar as partes do elemento:

.my-select-menu::part(button) {
  color: white;
  background-color: red;
  padding: 5px;
  border-radius: 5px;
}

.my-select-menu::part(listbox) {
  padding: 10px;
  margin-top: 5px;
  border: 1px solid red;
  border-radius: 5px;
}

Um menu elegante com cores de destaque em vermelho.

Você pode testar o elemento <selectmenu> no Chromium Canary com a flag web experiments ativada. Em 2023 e nos próximos anos, fique de olho nos elementos de menu de seleção personalizáveis.

Recursos

Como fixar um elemento em outro

Antes do anchor(), as posições absoluta e relativa eram estratégias de posicionamento fornecidas para que os desenvolvedores movessem elementos filhos dentro de um elemento pai.

Depois de anchor(), os desenvolvedores podem posicionar elementos em relação a outros, sejam eles filhos ou não. Ele também permite que os desenvolvedores especifiquem qual borda usar como referência e outras funcionalidades para criar relações de posição entre elementos.

A explicação tem alguns exemplos e amostras de código excelentes, se você quiser saber mais.

Recursos