HTML

O fim do CSS global

Qualquer pessoa que tenha trabalhado com CSS por tempo suficiente teve que entrar em acordo com sua natureza agressivamente global - um modelo claramente projetado na era dos documentos, agora lutando para oferecer um ambiente de trabalho sadio para os aplicativos modernos da web de hoje.

Cada seletor tem o potencial de ter efeitos colaterais indesejados, segmentando elementos indesejados ou entrando em conflito com outros seletores. Mais surpreendentemente, nossos selecionadores podem até perder na guerra de especificidade global, tendo, em última análise, pouco ou nenhum efeito na página.

Sempre que fizermos uma alteração em um arquivo CSS, precisamos considerar com cuidado o ambiente global em que nossos estilos se encaixarão. Nenhuma outra tecnologia de ponta exige muita disciplina apenas para manter o código em um nível mínimo de manutenção.

Mas não precisa ser assim.

É hora de deixar a era das folhas de estilo globais para trás.

É hora de CSS local.

 

Na comunidade JavaScript, graças a ferramentas como Browserify , WebpackJSPM , espera-se agora que nosso código consistirá de pequenos módulos, cada um encapsulando suas dependências explícitas, exportando uma API mínima.

No entanto, de alguma forma, o CSS ainda parece estar recebendo um passe livre.

Muitos de nós - inclusive eu, até recentemente - têm trabalhado com CSS há tanto tempo que não vemos a falta de escopo local como um problema que podemos resolver sem a ajuda significativa de fornecedores de navegadores. Mesmo assim, ainda precisaríamos esperar que a maioria dos usuários usasse um navegador com o suporte adequado ao Shadow DOM .

Nós trabalhamos em torno das questões de escopo global com uma série de convenções de nomenclatura como OOCSS , SMACSS , BEM e SUIT , cada uma delas fornecendo uma maneira de evitar a nomenclatura de colisões e emular regras de escopo sãs.

Embora, sem dúvida, seja um grande avanço para domar CSS, nenhuma dessas metodologias abordou o problema real de nossas folhas de estilo. Não importa qual convenção escolhermos, ainda estamos presos a seletores globais.

Mas tudo isso mudou em 22 de abril de 2015.


Como abordamos em uma postagem anterior -  "Bloquear, Elemento, Modificar seus Componentes JavaScript"  - podemos aproveitar o Webpackpara importar nosso CSS a partir de um módulo JavaScript. Se isso soa estranho para você, provavelmente é uma boa idéia ler esse artigo agora , para não perder a importância do que deve seguir.

Usando o CSS-loader do Webpack , importar o CSS de um componente se parece com isto:

require ('./MyComponent.css');

À primeira vista - mesmo ignorando o fato de que estamos importando CSS em vez de JavaScript - isso é muito estranho.

Normalmente, uma chamada require deve fornecer algo para o escopo local. Se isso não acontecer, é um sinal claro de que um efeito colateral global foi introduzido - muitas vezes um sintoma de design pobre.

Mas isso é CSS - os efeitos colaterais globais são um mal necessário.

Ou então pensamos.


Em 22 de abril de 2015 , Tobias Koppers  - o autor sempre incansável da Webpack - comprometeu a primeira iteração de um novo recurso para o css-loader, na época chamado de placeholders , agora conhecido como escopo local .

Esse recurso nos permite exportar nomes de classes do nosso CSS para o código JavaScript consumidor.

Em suma, em vez de escrever isso:

require ('./MyComponent.css');

Nós escrevemos isto:

Estilos de importação de './MyComponent.css';

Então, neste exemplo, o que os estilos avaliam?

Para ver o que é exportado do nosso CSS, vamos dar uma olhada em um exemplo da aparência da nossa folha de estilo.

: local (. foo) { 
cor : vermelho;
}
: local (. bar) { 
cor : azul;
}

Nesse caso, usamos a sintaxe personalizada : local (.identifier) ​​do css-loader  para exportar dois identificadores -  foo e bar .

Esses identificadores mapeiam para cadeias de classes que podemos usar em nosso arquivo JavaScript. Por exemplo, ao usar o React :

Estilos de importação de './MyComponent.css';
import React, {Component} from 'react';
classe padrão de exportação MyComponent estende o componente {
  render () { 
return (
<div>
<div className = { styles.foo }> Foo </ div>
<div className = { styles.bar }> Barra </ div>
</ div>
);
}
}

É importante observar que esses identificadores são mapeados para cadeias de classes com garantia de serem exclusivas em um contexto global.

Não precisamos mais adicionar prefixos longos a todos os nossos seletores para simular o escopo. Mais componentes poderiam definir seus próprios identificadores foo e bar que - ao contrário do tradicional modelo global de seletores - não produziriam colisões de nomes.


É fundamental reconhecer a enorme mudança que está ocorrendo aqui.

Agora podemos fazer alterações em nosso CSS com a certeza de que não estamos afetando acidentalmente elementos em outros lugares da página. Introduzimos um modelo de escopo sano em nosso CSS.

Os benefícios da reutilização global do estilo CSS entre componentes através de classes de utilitários, etc. - ainda são possíveis com este modelo. A principal diferença é que, assim como quando trabalhamos em outras tecnologias, precisamos importar explicitamente as classes das quais dependemos. Nosso código não pode fazer muitas suposições sobre o ambiente global.

Escrever CSS sustentável agora é incentivado, não pela adesão cuidadosa a uma convenção de nomenclatura, mas pelo encapsulamento de estilo durante o desenvolvimento.

Como resultado desse modelo de escopo, entregamos o controle dos nomes reais das classes ao Webpack. Felizmente, isso é algo que podemos configurar.

Por padrão, o css-loader transforma nossos identificadores em hashes.

Por exemplo, isso:

: local (.foo) {…}

É compilado para isso:

._1rJwx92-gmbvaLiDdzgXiJ {…}

Em desenvolvimento, isso não é muito útil para fins de depuração. Para tornar as classes mais úteis, podemos configurar o formato da classe em nossa configuração do Webpack como parâmetro para o css-loader:

loaders: [ 
...
{
test: /\.css$/,
loader: 'css? localIdentName = [nome] __ [local] ___ [hash: base64: 5] '
}
]

Nesse caso, nosso identificador de classe foo anterior compilaria isso:

. MyComponent__foo___1rJwx {…}

Agora podemos ver claramente o nome do identificador, bem como o componente do qual ele veio.

Usando a variável de ambiente node_env , podemos configurar padrões de classes diferentes para desenvolvimento e produção.

carregador: 'css? localIdentName = '+ ( 
process.env.NODE_ENV ===' desenvolvimento '?
' [nome] __ [local] ___ [hash: base64: 5]
':
' [hash: base64: 5] '
)
Agora que o Webpack tem controle de nossos nomes de classes, podemos 
adicionar suporte para classes minificadas na produção.

Assim que descobrimos esse recurso, não hesitamos em localizar os estilos em nosso projeto mais recente. Nós já estávamos escopo nosso CSS para cada componente com BEM - se apenas por convenção - por isso era um ajuste natural.

Curiosamente, um padrão rapidamente surgiu. A maioria dos nossos arquivos CSS continha apenas identificadores locais:

: local (. pano de fundo) {…}
: local (. root_isCollapsed .backdrop) {…}
: local ( campo . ) {…}
: local ( campo . ): foco {…}
etc…

Seletores globais eram necessários apenas em alguns lugares do aplicativo. Isso instintivamente levou a uma questão muito importante.

E se - em vez de exigir uma sintaxe especial - nossos seletores fossem locais por padrão e os seletores globais fossem a exceção de opt-in?

E se pudéssemos escrever isso em vez disso?

.pano de fundo { … }
.root_isCollapsed .backdrop {…}
.field {…}
.field: focus {…}

Embora esses seletores normalmente sejam muito vagos, transformá-los no formato de escopo local do css-loader eliminaria esse problema e garantiria que permanecessem no escopo do módulo no qual eles foram usados.

Nos poucos casos em que não pudemos evitar estilos globais, poderíamos marcá-los explicitamente com uma sintaxe especial  : global .

Por exemplo, ao estilizar as classes sem escopo geradas pelo ReactCSSTransitionGroup :

.panel : global .transition-active-enter {…}

Nesse caso, não estamos apenas definindo o identificador de painel local para nosso módulo - também estamos denominando uma classe global que está fora de nosso controle.


Depois que começamos a investigar como eu poderia implementar essa sintaxe de classe local por padrão, percebemos que não seria muito difícil.

Para conseguir isso, aproveitamos o PostCSS  - uma ferramenta fantástica que permite escrever transformadores CSS personalizados como plugins. Uma das ferramentas de criação de CSS mais populares atualmente -  Autoprefixer  - é na verdade um plugin PostCSS que funciona como uma ferramenta autônoma.

Para formalizar o uso de CSS local, eu abri um 
plugin altamente experimental para PostCSS chamado 
postcss-local-scope . Ainda está em desenvolvimento pesado, portanto, use-o em produção por sua conta e risco.

Se você estiver usando o Webpack, é um processo relativamente simples para conectar o postcss-loader e o postcss-local-scope ao seu processo de criação de CSS. Em vez de documentá-lo aqui, criei um repositório de exemplo -  postcss-local-scope-example - que mostra um pequeno exemplo de trabalho.

 

Emocionante, introduzir o escopo local é realmente apenas o começo.

Deixar a ferramenta de construção lidar com a geração de nomes de classes tem algumas implicações potencialmente enormes. A longo prazo, poderíamos decidir deixar de ser compiladores humanos e deixar o computador otimizar a saída.

No futuro, poderíamos começar a gerar classes compartilhadas entre componentes automaticamente, tratando a reutilização de estilo como uma otimização em tempo de compilação.

Depois de tentar trabalhar com o CSS local, não há como voltar atrás. Experimentar o verdadeiro escopo local em nossas folhas de estilo - de uma maneira que funcione em todos os navegadores - não é algo a ser facilmente ignorado.

A introdução do escopo local teve um efeito significativo sobre como abordamos nosso CSS. Convenções de nomenclatura, padrões de reutilização e a potencial extração de estilos em pacotes separados são todos diretamente afetados por essa mudança, e estamos apenas no início dessa nova era do CSS local.

Entender as ramificações dessa mudança é algo que ainda estamos trabalhando. Com sua valiosa contribuição e experimentação, espero que esta seja uma conversa que possamos ter juntos como uma comunidade maior.

Para se envolver, certifique-se de vê-lo com seus próprios olhos, 
verificando postcss-local-scope-example .

Uma vez que você o tenha visto em ação, eu acho que você concordará que não é apenas hipérbole - os dias do CSS global estão chegando ao fim. O futuro do CSS é local .


Nota: otimizar automaticamente a reutilização de estilo entre os componentes seria um avanço incrível, mas definitivamente requer ajuda de pessoas muito mais inteligentes do que eu. Com sorte, é aí que você entra ☺


Termo aditivo

24 de maio de 2015: As ideias originais apresentadas no postcss-local-scopeforam aceitas no Webpack por Tobias Koppers, o que significa que o projeto está obsoleto. Suporte para Módulos CSS - como eles são provisoriamente conhecidos - estão agora disponíveis no css-loader via um sinalizador de módulo opt-in . Eu criei um exemplo funcional de CSS Modules no css-loaderpara demonstrar seu uso, incluindo herança de classes para compartilhar de forma inteligente os estilos comuns entre os componentes.

Compartilhe

Ebrahim P. Leite

Ebrahim P. Leite

Escreva aqui uma descriçao breve sobre você...

Veja também

Comentários (0)