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:
- Posicionamento de
sticky
- Dimensionamento de
aspect-ratio
- Layout do
flex
- Layout do
grid
- 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:
@layer
- Espaços e funções de cor
- Contenção
<dialog>
- Compatibilidade de formulário
- Rolagem
- Subgrid
- Tipografia
- Unidades da janela de visualização
- 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
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:
Recursos
- Especificação do CSS Cascade 5 (em inglês)
- Explicação sobre camadas em cascata
- Camadas em cascata no MDN
- Una Kravets: Camadas em cascata
- Ahmad Shadeed: Olá, camadas em cascata do CSS
Subgrid
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.
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.

Recursos
Consultas em contêiner
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.
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:
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
- Especificação de consultas de contêiner
- Explicação sobre consultas de contêiner
- Consultas de contêineres na MDN
- O novo responsivo no web.dev
- Demonstração da Agenda por Una
- Coleção incrível de consultas de contêiner
- Como criamos o Designcember no web.dev
- Ahmad Shadeed: Say Hello To CSS Container Queries
accent-color
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;
}
Para saber mais sobre accent-color
, confira minha postagem no
web.dev, em que exploro muitos outros aspectos dessa
propriedade útil do CSS.
Recursos
- Especificação de accent-color
- accent-color no MDN
- accent-color no web.dev
- Bramus: Controles de interface do usuário de matiz com CSS accent-color
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()
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()
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
- Especificação color-mix()
- color-mix() no MDN
- Demonstração de temas
- Outra demonstração de temas
- Fabio Giolito: Crie um tema de cores com estes recursos futuros de CSS
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.

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:

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
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:

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);
}

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
- Especificação da sintaxe de cores relativas
- Como criar paletas de cores com sintaxe de cores relativas
- Como criar variantes de cores com sintaxe de cores relativas
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.
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.
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.
Para mais análises, exemplos e comentários, leia esta thread do Twitter.
Recursos
- Especificação de interpolação de gradiente
- Demonstração comparando gradientes em espaços
- Notebook do Observable comparando gradientes
inert
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:
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.

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;
}

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
- Especificação Colrv1 no GitHub
- Desenvolvedores do Chrome: fontes Colrv1
- Vídeo explicativo para desenvolvedores do BlinkOn
Unidades da janela de visualização
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.
Confira uma lista completa de todas as novas opções de unidade de viewport disponibilizadas com as novas variantes de viewport:
.new-height-viewport-units { height: 100vh; height: 100dvh; height: 100svh; height: 100lvh; block-size: 100vb; block-size: 100dvb; block-size: 100svb; block-size: 100lvb; }
.new-width-viewport-units { width: 100vw; width: 100dvw; width: 100svw; width: 100lvw; inline-size: 100vi; inline-size: 100dvi; inline-size: 100svi; inline-size: 100lvi; }
.new-min-viewport-units { --size: 100vmin; --size: 100dvmin; --size: 100svmin; --size: 100lvmin; }
.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
- Especificação de unidades relativas à janela de visualização
- Bramus: The Large, Small, and Dynamic Viewports
:has()
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
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:
.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;
}
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
- @property specification
- @property na MDN
- @property no web.dev
- Demonstração do foco do zoom
- CSS Tricks: Exploring @property and its animating powers (em inglês)
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
- Especificação da sintaxe de intervalo de consultas de mídia
- Sintaxe de intervalo de consultas de mídia na MDN
- Plug-in PostCSS de sintaxe de intervalo de consulta de mídia
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
- Especificação de consultas de mídia personalizadas
- Plugin PostCSS de consultas de mídia personalizadas
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
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.

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
- Especificação do layout em alvenaria
- Layout de alvenaria na MDN
- Smashing Magazine: Native CSS Masonry Layout with CSS Grid (em inglês)
O CSS não ajuda os usuários a reduzir o consumo de dados
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.
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.
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
- especificação prefers-reduced-data
- prefers-reduced-data no MDN
- Desafio "prefers-reduced-data" em uma GUI
- Smashing Magazine: Improving Core Web Vitals, A Smashing Magazine Case Study (em inglês)
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
- Especificação de rascunho do Scroll Snap 2
- Explicações sobre o ajuste de rolagem 2
- Demonstrações de ajuste
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;
}
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.