No primeiro artigo sobre CSS Blocks (o primeiro brasuca, aliás!) apresentamos o otimizador de CSS da LinkedIn, explicando um pouco sobre o porquê de sua existência e seus princípios-base. Agora, vamos a mais algumas explicações de código para entender melhor o que CSS Blocks é capaz de fazer.
Como uma breve recapitulação, CSS Blocks é um compilador inspirado em CSS Modules, BEM e Atomic CSS que, usando o OptiCSS, promete entregar estilos bem mais enxutos e performáticos. No momento da publicação deste artigo, é possível usá-lo nas templating languages JSX/React e Glimmer (com Webpack, Broccoli ou Ember-CLI) — por se tratar de um projeto open source, a tendência é que mais e mais opções surjam bem rapidamente.
O que é um “Bloco”?
O nome da coisa é CSS Blocks, “Blocos CSS”; mas, segundo a metáfora de desenvolvimento escolhida, o que seria um “Bloco”?
Um Bloco é uma folha de estilo isolada, escrita em seu próprio arquivo, que contém todos os conjuntos de regras para quaisquer elementos e seus vários modos e estados de interação para uma unidade discreta de estilo — como um componente, por exemplo.
Normalmente, um único bloco conterá estilos para um componente ou conceito específico, mas é totalmente natural — e incentivado — um template usar vários Blocos e os compor juntos na marcação.
Um Bloco pode conter:
- Seletor
:scope
- Seletores de classe
- Seletores de estado
- Seletores de sub-estado
Seletor :scope
O conjunto de regras de escopo contém estilos aplicados à raiz da sub-árvore de estilo com escopo. Trocando em miúdos, todos os outros elementos atribuídos a estilos de um Bloco devem estar contidos na sub-árvore do documento de um elemento atribuído ao escopo desse Bloco. Usa-se a pseudo-classe :scope para representar esses estilos.
O seletor :scope
pode conter a propriedade especial block-name
que torna possível fornecer um nome específico para o Bloco para um debugging mais fácil e respectiva geração de classe BEM. Se nenhum nome de bloco for fornecido, infere-se o nome do bloco a partir do nome do arquivo.
Por exemplo:
1 2 3 4 5 |
:scope { block-name: custom-block-name; /* ? opcional! */ /* ... mais estilos ... */ } |
Seletores de classe
Blocos podem conter outras classes que podem ser aplicadas a elementos dentro da sub-árvore de estilo escopada. São apenas seletores de classe comuns, mas são locais para esse Bloco e isolados de todas as outras classes com nomes iguais em outros Blocos.
1 2 3 |
.sub-element { /* ... */ } .other-sub-element { /* ... */ } |
Juntos, o seletor :scope
e todos os seletores de classe (.class
) declarados definem a interface completa dos elementos estilizados disponíveis para quem vai usar um Bloco.
Seletores de Estado
Estados representam um modo ou estado de interação no qual :scope
ou uma classe — denominada Elemento Originário do Estado — pode estar. Os Estados são gravados como seletores de atributos com o namespace especial state
.
1 2 3 4 5 |
:scope { /* ... */ } :scope[state|enabled] { /* ... */ } .sub-element { /* ... */ } .sub-element[state|is-active] { /* ... */ } |
Seletores de sub-estado
Estados no seletor :scope
ou um seletor de classe podem conter sub-estados para um estilo mais granular. Os sub-estados de um Estado são mutuamente exclusivos, ou seja, um elemento só pode estar em um sub-estado de um Estado por vez.
1 2 3 4 5 6 7 8 9 10 11 12 |
:scope { /* ... */ } :scope[state|theme="inverse"] { /* ... */ } .sub-element { /* ... */ } /* Aplicado a *qualquer* valor de `color` (incluindo valor nenhum) */ .sub-element[state|color] { /* ... */ } /* Aplicado a valores *específicos* de `color` */ .sub-element[state|color="red"] { /* ... */ } .sub-element[state|color="blue"] { /* ... */ } .sub-element[state|color="yellow"] { /* ... */ } |
Restrições na API de CSS Blocks
CSS Blocks implementa um subconjunto estrito de CSS. Isso significa que alguns dos recursos que normalmente poderiam ser usados em um arquivo em um arquivo de Bloco foram intencionalmente restringidos para garantir que a compilação final possa gerar estilos da maneira mais otimizada possível.
Para ficar claro:
- Permitido:
::before
,::after
, e todos os outros pseudo-elementos:hover
,:active
, e todas as outras pseudo-classes@media
,@breakpoint
, e todas as outras @at-rules- Especificidade de cascata e de seletor
- Proibido (por enquanto):
!important
(você já deveria tê-lo banido, de qualquer maneira)tag
,[attribute]
que não seja de Estado,#id
e seletores*
- Os Combinadores Lógicos
:matches()
,:not()
,:something()
e:has()
- Importante: seletores devem se manter rasos (shallow)
No contexto de CSS Blocks, “seletores rasos” significa:
Apenas 1 combinador por seletor
1 2 3 4 5 |
/* ✅ Permitido */ :scope:hover > .my-class { /* ... */ } /* ❌ Proibido */ :scope:hover > .my-class + .my-class { /* ... */ } |
Seletores de Combinadores Hierárquicos (” “ e “>“) devem ser um estado de :scope
, sub-estados ou pseudo-classes
1 2 3 4 5 6 7 8 9 |
/* ✅ Permitido */ :scope:hover .my-class { /* ... */ } :scope[state|active] > .my-class { /* ... */ } :scope[state|color=red] .my-class { /* ... */ } /* ❌ Proibido */ .container:hover > .my-class { /* ... */ } .container[state|active] .my-class { /* ... */ } .container[state|color=red] .my-class { /* ... */ } |
Seletores de irmão (“+” e “~“) devem ter como target a mesma classe ou :scope
usado no seletor-chave
1 2 3 4 5 6 7 8 9 |
/* ✅ Permitido */ .my-class + .my-class { /* ... */ } .my-class:hover ~ .my-class { /* ... */ } .my-class[state|active] + .my-class { /* ... */ } /* ❌ Proibido */ :scope + .my-class { /* ... */ } .another-class:hover ~ .my-class { /* ... */ } .another-class[state|active] + .my-class { /* ... */ } |
Como todo o código é analisado e compilado estaticamente (statically) antes de atingir o navegador, obviamente um erro bastante útil será gerado caso alguma dessas restrições de sintaxe seja violada.
Conclusão
É difícil tentar decorar todas essas peculiaridades; com o tempo de uso de CSS Blocks, naturalmente isso vai se internalizar. De qualquer maneira, é bom saber que existe uma tabela bem útil mostrando o que é permitido e proibido na API — que, devido à incipiência de CSS Blocks, deve ser alterada (para melhor) bem rapidinho e com bastante frequência.
Para ficar claro, eis o que acontece na fase de Compilação de CSS Blocks:
O processo de otimização vai além; existem mais passos até que o output final aconteça — ou você achou que CSS Blocks servia só para escrever BEM de maneira mais complicadinha? Mas isso já é assunto para outro artigo…
Fique ligado nas novidades do desenvolvimento para web para não perder nada!