Aguarde...

5 de dezembro de 2022

A evolução do CSS escalável

A evolução do CSS escalável

Um mergulho profundo nos problemas de escalar CSS em grandes projetos. Entenda a evolução das práticas recomendadas de CSS.

A forma como escrevemos e pensamos sobre CSS mudou significativamente desde o início da web.

Percorremos um longo caminho de layouts baseados em tabelas para design responsivo da Web e agora para uma nova era de layouts adaptativos alimentados por recursos modernos de CSS.

Gerenciar e organizar CSS sempre foi um desafio, onde é difícil encontrar um consenso.

Nesta postagem, desenvolveremos uma compreensão mais profunda do CSS, analisando os problemas subjacentes que dificultam o dimensionamento.

Vamos entender a evolução das várias práticas recomendadas de CSS que surgiram e mudaram ao longo do tempo.

No final, teremos uma boa compreensão das abordagens anteriores para escalar CSS em grandes projetos e como ferramentas populares como o Tailwind e uma série de outras abordam essas questões de maneiras contra-intuitivas.

O tempo antes do CSS

No começo, a web só tinha HTML. Escrevemos tudo em maiúsculas e estilizamos as páginas usando propriedades diretamente na marcação:

  <BODY>
    <P SIZE="8" COLOR="RED">LOUD NOISES</P>
  </BODY>

Eram tempos sombrios para quem desejava páginas elegantes.

Além do número limitado de estilos disponíveis, uma clara limitação era toda a duplicação necessária.

Um exemplo por excelência da web nessa época era o bom e velho site do Space Jam .

Agora, alguns podem estar pensando “isso se parece com os adereços que passo para minha biblioteca de componentes”. Como veremos mais adiante, muitas vezes as coisas fecham o círculo com uma reviravolta no ciclo da inovação.

Folhas de estilo e separação de interesses

O CSS entrou no jogo e, como muitas inovações nascidas de pontos problemáticos anteriores, agora poderíamos remover toda essa duplicação.

As folhas de estilo nos permitiram estilizar páginas de forma declarativa, afetando vastas faixas de elementos com muito pouco código:

p {
  color: red;
}

Agora poderíamos pensar sobre a estrutura do nosso conteúdo e sua aparência visual e layout separadamente. Mover preocupações de layout de HTML usando tabelas para CSS.

Inicialmente, o CSS era mínimo, fortemente inspirado na editoração eletrônica física, com termos como “float” que davam suporte à formatação de colunas de texto.

Com o passar do tempo, começamos a coletar exemplos em um site chamado CSS Zen Garden .

O CSS Zen Garden tornou-se um centro para mostrar como as pessoas podem usar o CSS de forma criativa. As pessoas podiam enviar arquivos CSS que reestilizavam o mesmo HTML de maneiras interessantes e únicas.

Isso foi muito influente na divulgação da ideia de separar o conteúdo de seu estilo , HTML reestilizável e pensar em aplicar “skins” temáticos a um esqueleto central.

Descobrindo as melhores práticas

Começamos a construir sites e aplicativos mais complexos, colocando novas demandas no CSS para evoluir.

Qualquer nova tecnologia geralmente leva alguns ciclos de diferentes abordagens antes que surjam as melhores práticas.

Vimos ferramentas como Less e Sass surgirem que ampliavam os recursos nativos de CSS. Dando-nos coisas como variáveis ​​e funções de cálculo, melhorando muito a experiência do desenvolvedor.

À medida que passamos mais tempo nessas folhas de estilo, procuramos maneiras de organizar todas essas regras e seletores.

Muitos padrões diferentes para escalar CSS surgiram. Eles tentaram encontrar um equilíbrio entre manutenção, desempenho e legibilidade e são chamados de “arquiteturas CSS”.

Antes de mergulharmos nessas arquiteturas, vamos primeiro entender por que o gerenciamento de CSS em projetos de grande escala se torna complicado rapidamente.

Por que CSS é difícil de gerenciar em escala

“Em escala” refere-se à interseção de várias coisas, incluindo pessoas, ferramentas, processos e desempenho.

O dimensionamento eficaz exige que gerenciemos cuidadosamente o crescimento da complexidade. Assim, à medida que o sistema cresce, ele ainda é compreensível, mutável e eficiente. Onde o custo de adicionar novo código permanece o mais baixo possível e as pessoas estão confiantes em alterar e excluir o código antigo.

A cascata (o C em CSS) tem origem nos primórdios da web. Os navegadores podem aplicar estilos padrão a esses novos documentos eletrônicos. Os autores do documento podem fornecer seus próprios estilos, que podem ser substituídos pelas preferências individuais do usuário.

Essa imagem de regras em cascata é fundamental para entender o CSS. As mesmas propriedades que tornam o CSS poderoso também dificultam a obtenção dessas propriedades de dimensionamento em grandes projetos. Em particular, seu namespace global , regras em cascata e especificidade do seletor .

O espaço de nomes global

O namespace CSS global pode ser poderoso se for usado com cuidado. Mas em grandes projetos, muitas vezes é uma maldição.

Quando tudo é global, qualquer coisa pode afetar inesperadamente outra coisa. Agora ou em algum momento no futuro, quando as coisas mudarem.

Isso se torna problemático rapidamente. Existem razões pelas quais não colocamos tudo em um namespace global em outros idiomas. À medida que mais código é adicionado, as coisas se tornam menos previsíveis e difíceis de manter.

Vale a pena notar que as camadas em cascata do CSS são um recurso promissor que pode ajudar a resolver esse problema nativamente.

Nomear as coisas é difícil

Criar uma série de nomes de classe semântica geralmente parece uma tarefa árdua sem muitos benefícios ao iterar rapidamente com CSS.

Criar nomes úteis é difícil porque estamos tentando compactar um monte de informações em um rótulo preciso. Fazer isso direito se torna ainda mais importante quando tudo é global.

Nomear as coisas muito cedo é uma forma de abstração prematura. Porque muitas vezes as coisas que estamos nomeando ainda precisam ser totalmente formadas e ainda não são reutilizáveis.

Mudanças de design são comuns no front-end, e esses rótulos regularmente ficam desatualizados e exigem refatoração de estilos e marcações.

Refatorar CSS é difícil

Tanto o design quanto o desenvolvimento de software moderno são altamente iterativos. Muitas vezes, só começamos a desenvolver uma imagem clara depois que algumas iterações foram acumuladas.

Isso exige que reavaliemos regularmente nossa compreensão do problema que estamos resolvendo. No código, isso significa refatorar à medida que nosso entendimento muda e se solidifica.

A refatoração pode ser desafiadora, mas é um método testado e aprovado para chegar a boas abstrações ao longo do tempo com base em requisitos reais em vez de teoria.

Em CSS é bem difícil. Sem testes de regressão visual sólidos, muitas falhas de CSS são “silenciosas”, onde é fácil criar bugs imprevistos e efeitos colaterais. Isso leva a alguns cenários comuns.

  1. Anexar apenas folhas de estiloOs projetos começam com folhas de estilo que parecem gerenciáveis. Mas é comum que o novo código fique preso no final dos arquivos após algumas iterações e correções de bugs posteriormente.Saber quando podemos alterar ou excluir uma regra com segurança é difícil. Assim, sobrescrevemos o que veio antes na cascata no final do arquivo.Esta é a causa das batalhas de especificidade . Todos nós provavelmente já tivemos a experiência de precisar substituir alguns outros estilos. É um caminho fácil para as artes das trevas, onde !importantcomeçam a surgir, aprofundando a carga de manutenção.
  2. código mortoNa prática, muitas vezes usamos as mesmas propriedades CSS repetidamente. Geralmente é muito mais seguro duplicar regras continuamente, em vez de assumir o risco de refatorar grandes quantidades de CSS em um namespace global.Isso geralmente leva a muito CSS não utilizado, difícil de saber se algo está dependendo dele. O que acaba inchando o CSS espalhado por vários arquivos.

Depurar CSS é difícil

Uma grande parte da depuração é simular o que o computador faz em nossas cabeças.

A depuração é difícil com CSS complexo porque estamos calculando mentalmente a cascata e calculando as regras finais enquanto consideramos a ordem do código-fonte.

Particularmente com as muitas nuances do CSS em relação ao posicionamento, alinhamento, contextos de empilhamento, margens e altura. Sem uma abordagem sistemática, um fluxo de trabalho comum de depuração de CSS geralmente envolve o ajuste de alguns valores para ver o que acontece. Atualizando a página, e absolutamente nada mudou. Ou algo quebrou completamente.

Isso é particularmente desafiador ao trabalhar com código que você não controla ou bugs específicos do navegador.

Domando a complexidade com arquiteturas CSS

CSS tem um modelo simples, mas é fácil que as coisas fiquem confusas rapidamente. Eventualmente, começamos a procurar aplicar princípios de engenharia de software para nos ajudar a gerenciar.

Essas arquiteturas são mais como esquemas de alto nível para organizar arquivos CSS e suas regras e seletores.

Vamos obter uma rápida visão geral de algumas das arquiteturas CSS mais influentes e populares e suas principais ideias.

OOCSS: CSS Orientado a Objetos

O OOCSS distinguiu os diferentes tipos de CSS que escrevemos na prática. CSS que faz layout e CSS que temas ou “skins” HTML, como cores, fontes, etc.

Um “objeto” em OOCSS é um padrão visual repetitivo que pode ser abstraído e reutilizado. A ideia é identificar padrões visuais comuns e extrair blocos de código duplicados em classes reutilizáveis.

Um dos frameworks CSS mais usados ​​que alavancam essas ideias é o Bootstrap .

SMACSS: CSS escalável e modular

Na prática, grandes arquivos CSS de arquivo único rapidamente se tornam incontroláveis ​​e difíceis de depurar.

SMCASS foi um guia para categorizar os diferentes tipos de CSS e foi compatível com abordagens como OOCSS.

A ideia principal era pegar todos esses nomes de classe e organizá-los em compartimentos separados e fornecer uma estrutura muito necessária para nossos arquivos CSS . Além de algumas convenções sobre a nomenclatura de classes.

BEM: Bloco, Elemento, Modificador

O BEM é um modelo de como pensar em dividir as coisas em componentes, seus subelementos e seus vários estados discretos.

Originalmente criado no Yandex , ele fornece uma convenção de nomenclatura sistemática que evita batalhas de especificidade, mantendo todos os seletores planos (sem seletores descendentes), onde cada elemento que é estilizado recebe seu próprio nome de classe.

O BEM funcionou bem com pré-processadores populares de CSS, como Sass, com regras aninhadas que compilariam em seletores CSS simples:

.nav {
  // block styles
	&__link {
    // element styles that depend on the parent block
		&--active {
          // modifer styles
		}
	}
}

ITCSS: Triângulo Invertido

Uma das principais ideias por trás do ITCSS é pensar em nossas folhas de estilo através das lentes das camadas para ajudar a domar a cascata.

ITCSS é como um “meta-framework” para CSS, compatível com outros sistemas.

A ideia é domar o caos de tudo que se sobrepõe de forma imprevisível, fornecendo camadas explícitas de especificidade crescente.

O “triângulo invertido” vem de cada camada progressiva formando uma forma de pirâmide invertida.

É uma metodologia influente para gerenciar arquivos CSS em projetos de grande escala. Para mergulhar mais fundo, você pode conferir uma palestra proferida por seu criador .

CSS do cubo

Cube CSS funciona com o namespace global e cascata em vez de tentar contorná-lo.

Cube CSS fornece um conjunto de baldes bem definidos que categorizam CSS. Estes formam o acrônimo do cubo: Composition, Utility, Block, Exception.

Os documentos fazem um ótimo trabalho explicando os princípios . É uma metodologia solta que é como um modelo mental para organizar o CSS.

Semelhante ao ITCSS, é um influente “framework meta CSS”, compatível com várias abordagens.

Repensando a separação de interesses

Com o surgimento de SPAs e desenvolvimento orientado a componentes , começamos a ver novas abordagens para CSS.

Neste mundo, gerenciar CSS tornou-se ainda mais difícil, porque os componentes agora carregam de forma assíncrona, sem garantias de ordem de origem.

Um problema comum é quando algum elemento na página parece diferente ao fazer uma transição SPA da página A para B, mas parece bom se você carregar diretamente para B. Levando a algumas sessões de depuração interessantes.

Começamos a procurar soluções mais concretas para gerenciar CSS que combinassem com essa nova abordagem centrada em componentes para estruturar nossos frontends.

Essas ferramentas muitas vezes quebravam muitas das melhores práticas estabelecidas que estávamos construindo e pensando até agora. Vamos entendê-los.

Estilos embutidos

A mudança para estruturas baseadas em componentes muitas vezes viu estilos aplicados em linha dentro dos componentes. Em frameworks como React, passamos um objeto Javascript para o styleprop, convertendo-o em estilos inline.

Isso causa uma reação visceral para muitos porque é como se estivéssemos voltando ao início, quando não tínhamos folhas de estilo externas, jogando fora as melhores práticas existentes.

No contexto dos componentes, os estilos inline não enfrentam o problema original de duplicação massiva porque estão encapsulados dentro do componente. O fato de os estilos afetarem apenas o elemento em que estão é uma boa maneira de adicionar e modificar CSS com segurança em componentes.

O principal problema com estilos inline é a falta de acesso a recursos CSS mais poderosos, como pseudoseletores e consultas de mídia. Além da dificuldade de alavancar tokens de design compartilhado, cache, análise estática e pré-processamento.

CSS em JS

Nos primeiros dias do React, Veujx deu uma palestra sobre a abordagem do Facebook ao CSS. Superficialmente, isso parecia muito com estilos embutidos, mas com acesso ao poder das folhas de estilo.

Essa palestra levou a uma proliferação de bibliotecas de código aberto que adotaram uma abordagem baseada em Javascript para CSS.

A primeira onda de CSS em bibliotecas JS tornou-se popular com bibliotecas como Styled Components , Emotion , e uma série de outras no ecossistema React.

Isso resolveu a maioria dos problemas que o CSS vanilla tinha em grandes projetos usando componentes, tornando o trabalho com valores dinâmicos do JS incrivelmente fácil.

O problema era o imposto de desempenho que os usuários finais estavam pagando. Havia ineficiências de renderização do lado do servidor, problemas relacionados ao armazenamento em cache e custos de tempo de execução do cliente.

Isso exacerbou os tempos lentos de inicialização do aplicativo, exigindo várias novas renderizações depois que o Javascript foi hidratado. Muito trabalho foi adicionado rapidamente para aplicativos grandes.

Uma segunda onda mais recente de CSS em bibliotecas JS visa oferecer o melhor da experiência do desenvolvedor sem o custo de tempo de execução.

Ferramentas como Vanilla extract , Linaria e Compiled extraem folhas de estilo de componentes em uma etapa de compilação. Isso move muito do que acontece no tempo de execução nos navegadores dos usuários para o tempo de compilação.

O CSS geralmente é compilado em Atomic CSS (uma arquitetura CSS que abordaremos daqui a pouco) para evitar arquivos CSS inchados e é muito mais armazenável em cache em comparação com folhas de estilo dinâmicas de tempo de execução.

Módulos CSS

Os módulos CSS encontram um equilíbrio entre escrever CSS regular (ou Sass) e atingir muitas das propriedades de dimensionamento que estamos procurando.

Os módulos CSS permitem que você use todo o poder e controle do CSS sem se preocupar com estilos que se espalham pelos componentes, mantendo as coisas localizadas em um diretório de componentes.

Particularmente com a primeira onda de CSS em bibliotecas JS, vincular CSS a uma biblioteca de exibição específica foi um passo longe demais para alguns, onde os módulos CSS eram uma ótima alternativa. No entanto, alguns podem considerar isso uma forma de CSS em JS, porque depende de um empacotador como o Webpack para gerar e garantir que os seletores tenham escopo.

Independentemente disso, os módulos CSS são um ótimo meio-termo entre o mundo CSS regular e abordagens totalmente centradas em componentes, como CSS em JS. A criação de nomes ainda é necessária e compatível com convenções como BEM.

Práticas recomendadas de CSS desafiadoras

Enquanto isso, fora do mundo dos SPAs baseados em componentes, as melhores práticas originais influenciadas pelo CSS Zen Garden foram desafiadas em outra frente .

O Atomic CSS nasceu na escuridão do gerenciamento de CSS em grandes projetos. Moldado por ela.

Sua motivação inicial era totalmente pragmática – habilitar o estilo sem precisar editar ou anexar regras a uma folha de estilo existente. Evitando todos os problemas que vêm com isso.

Estava no outro extremo do espectro em comparação com outras arquiteturas CSS como OOCSS, BEM e SMACSS, e completamente contra-intuitivo.

Atomic CSS foi um nível mais baixo do que “blocks” e “objects”, focando em átomos de propósito único. Indo diretamente contra as melhores práticas estabelecidas, mesmo descritas na especificação HTML sobre como não nomear classes CSS.

Tornou-se uma abordagem popular de aumento de produtividade para equipes em projetos em que parece muito arriscado modificar o CSS existente. Algumas bibliotecas populares de CSS incluem ACSS , Tachyons , WindiCSS e muitas outras.

De acordo com o estado do CSS , uma das implementações mais populares dessa arquitetura CSS que o torna acessível hoje é o framework Tailwind CSS.

A ascensão de Tailwind

O Tailwind vem ganhando popularidade rapidamente desde seu lançamento em 2017. Um testemunho típico do Tailwind é que ele aumenta a produtividade ao tornar o CSS mais acessível para não especialistas, ao mesmo tempo em que leva a um CSS mais fácil de manter. Freqüentemente, o conselho é “apenas experimente e você não vai voltar”.

Princípios que impulsionam o Tailwind

Para entender sua popularidade, vamos examinar os princípios subjacentes ao método do Tailwind.

Apesar de aparentemente jogar fora as melhores práticas estabelecidas, veremos por baixo que há uma coleção de princípios pragmáticos que funcionam na prática.

Adie nomear coisas

Não ter que nomear as coisas continuamente é uma das razões pelas quais o Tailwind se sente tão produtivo. Esse fluxo de trabalho é apoiado pela ideia de compor átomos de propósito único de baixo para cima.

De uma perspectiva de manutenção, é uma ótima maneira de evitar abstrações apressadas .

Nunca nomear as coisas prejudica a legibilidade do código. Muitas vezes levando a uma sopa de classes atômicas (ou componentes) sem limites claros.

Essa é uma crítica válida. Mas, na prática, é surpreendente o quão longe isso pode ir antes de ser um problema legítimo. Muitas vezes, é a compensação certa em bases de código sujeitas a alterações regulares ou com muitas pessoas.

Este é um poderoso fluxo de trabalho em um modelo de componente. Você já tem um nome semântico para o componente em que está trabalhando e acaba construindo nesse sentido, com estilos contidos no componente.

Este é o mesmo princípio de nível superior descrito em Construindo front-ends voltados para o futuro . Não se trata de nunca criar abstrações, mas de não criá-las prematuramente.

Resumo quando for a hora certa

Ao operar de forma iterativa, a capacidade de excluir e alterar facilmente o código ofusca muito o custo da duplicação.

Um ponto problemático comum ao escrever CSS é que tornar as coisas excessivamente DRY ou otimizadas no início geralmente parece um desperdício quando as coisas são difíceis de mudar posteriormente.

É muito mais fácil secar código repetido por trás de uma abstração comum do que trabalhar em torno de código excessivamente abstrato e fazê-lo funcionar para seu novo caso de uso.

O Tailwind oferece duas técnicas para abstrair no momento certo, seja criando uma classe CSS compartilhada representando um bloco (semelhante ao OOCSS).

Ou mais encorajado ao usar estruturas baseadas em componentes, é extrair as classes duplicadas em um componente reutilizável (React, Vue, Solid, Svelte, etc.) e compartilhá-lo.

Ter refatoração de confiança

Como as classes são localizadas para a marcação em que estão, podemos refatorá-las com confiança sem nos preocupar em afetar outros elementos ou componentes em outro lugar.

Isso funciona tanto para modelos mentais da web como um documento quanto para modelos centrados em componentes. Isso leva à sensação de que o Tailwind pode aumentar ou diminuir dependendo do tipo de site ou aplicativo que você está construindo.

Evite código morto

Tailwind e CSS em bibliotecas JS que pré-compilam para Atomic CSS resolvem os problemas de arquivos CSS inchados cheios de regras duplicadas.

Com o Atomic CSS, o crescimento do CSS está vinculado ao número de estilos exclusivos usados, não à quantidade de recursos que os desenvolvedores estão enviando.

Por exemplo, é comum reutilizar certas propriedades como flexem qualquer lugar. Ao invés de tê-los duplicados em folhas de estilo com nomes de classes diferentes, pagamos esse custo apenas uma vez. Isso é verdade para cada combinação de propriedade/valor.

Preenchendo a lacuna do design

Vamos fazer uma pausa em todos esses princípios e arquiteturas e lembrar que CSS é basicamente implementar designs visuais.

Uma grande razão pela qual o CSS parece difícil para muitos desenvolvedores é o fato de que o design é difícil.

Acertar os fundamentos é um longo caminho. No caso do design visual, alguns elementos-chave são alinhamento, espaçamento, consistência, dimensionamento, tipografia e cor.

Em CSS, para qualquer propriedade como font-sizecolorou padding, existem várias maneiras de implementar valores.

Muitas vezes, fazemos isso de maneira ad-hoc, levando a uma proliferação de tamanhos de fonte ligeiramente diferentes, espaçamento e inconsistências de cores que se somam a uma aparência não polida.

Uma parte fundamental do dimensionamento do CSS é preencher essa lacuna de design, tendo uma base sólida de primitivos compartilháveis ​​que definem valores para espaçamento, tamanho da fonte, cores, pontos de interrupção, etc.

Eles geralmente são chamados de tokens de design e formam a base de um sistema de design. Sem essa base, as coisas podem parecer muito arbitrárias e caóticas.

Um aspecto que é fundamental para a popularidade do Tailwind é fornecer um conjunto de primitivos de design fundamentais pré-pensados ​​que você pode usar na prateleira. Isso elimina uma grande quantidade de tomada de decisão que geralmente é feita ad-hoc com inconsistência.

Outra ótima opção de código aberto para uma base sólida é Open Props , compatível com qualquer abordagem CSS que você adotar e fornece uma tonelada de excelentes variáveis ​​e tokens pré-configurados.

Considerações finais

Absorva o que é útil, descarte o que é inútil e acrescente o que é especificamente seu

Nenhuma ferramenta é perfeita e cada projeto e equipe é diferente. Qualquer que seja a abordagem adotada, o estabelecimento de bases que preenchem a lacuna do design é um elemento-chave para escalar o CSS.

Concentrar-se nos primitivos que são compostos e construídos em cima também ajuda bastante. Isso também se aplica a grandes aplicativos baseados em componentes usando bibliotecas de componentes. Fornecer primitivos de layout de componentes que podem ser compostos, como BoxStackInlineetc, é uma ótima maneira de gerenciar CSS sem que os desenvolvedores precisem escrever nenhum CSS.

Recentemente, tem havido um número impressionante de recursos enviados para navegadores evergreen que abordam muitos dos pontos problemáticos que tornam o CSS difícil de escalar. Recursos mais recentes, como camadas em cascata, consultas de contêiner, subgrade has, e muitos outros provavelmente mudarão a forma como pensamos e aproveitamos o CSS no futuro.

O sucesso no dimensionamento do CSS é menos sobre adesão dogmática a princípios específicos ou melhores práticas e mais sobre definir o que você precisa com base em restrições do mundo real e fazer o que funciona de forma sustentável e com bom desempenho para realizar o trabalho.

Postado em Blog
Escreva um comentário