CSS

19 abr, 2012

Sobre semântica de HTML e arquitetura front-end

Publicidade

Texto original disponível em http://nicolasgallagher.com/about-html-semantics-front-end-architecture/

?

Uma coleção de pensamentos, experiências, ideias que eu gosto, e ideias que eu tenho experimentado desde o ano passado. Abrange semântica de HTML, componentes e abordagens para arquitetura front-end, padrões de nomenclatura de classes e de compressão HTTP.

Nós não cessaremos de explorar

E o final de toda exploração

Será chegar aonde começamos

E conhecer o lugar pela primeira vez.

T.S. Eliot – “Little Gidding”

Sobre semântica

Semântica é o estudo das relações entre os signos e símbolos e o que eles representam. Na linguística, ela é, sobretudo, o estudo do significado dos signos (tais como palavras, frases ou sons) na linguagem. No contexto do desenvolvimento web front-end, a semântica tem muito a ver com o conceito usual de  HTML, atributos e valores de atributos (incluindo extensões como Microdata). Tais semânticas, que geralmente são formalizadas em especificações, podem ser usadas para ajudar os programas (e posteriormente seres humanos) a compreender melhor os aspectos da informação em um site. No entanto, mesmo após a formalização, as semânticas dos elementos, atributos e valores de atributos estão sujeitas à adaptação e à cooptação pelos desenvolvedores. Isso pode levar a modificações posteriores das semânticas formalmente acordadas (e é um princípio de design do HTML).

A distinção entre diferentes tipos de semântica do HTML

O princípio de escrever “semântica em HTML” é um dos fundamentos do desenvolvimento front-end moderno e profissional. A maioria das semânticas está relacionada a aspectos da natureza do conteúdo existente ou esperado (por exemplo, elemento h1, atributo lang, valor e-mail do atributo type, Microdata).

No entanto, nem todas as semânticas precisam ter conteúdo derivado. Nomes de classes não podem ser “não-semânticos”. Quaisquer nomes estão sendo usados: eles têm um significado, eles têm um propósito. O nome da classe semântica pode ser diferente para aquelas de elementos HTML. Podemos potencializar as semânticas “globais” acordadas dos elementos HTML, certos atributos HTML, Microdata etc., sem confundir seu propósito com aquelas semânticas do site “local” / application-specific que estão normalmente contidas nos valores de atributos, como o atributo class.

Apesar de a seção de especificação HTML5 em classes repetir o “princípio de melhores práticas” de que…

…autores são incentivados a usar [atributo class] os valores que descrevem a natureza do conteúdo, em vez de valores que descrevem a apresentação desejada do conteúdo.

…não há nenhuma razão intrínseca para se fazer isso. Na verdade, muitas vezes é um impedimento quando se trabalha com grandes sites ou aplicativos.

  • Conteúdo de camadas semânticas já é servido por elementos HTML e outros atributos.
  • Nomes de classes transmitem pouca ou nenhuma informação semântica útil para as máquinas ou para visitantes humanos, a menos que sejam parte de um pequeno conjunto acordado (e legível por máquina) de nomes – Microformats.
  • O objetivo principal de um nome de classe é ser um gancho para CSS e JavaScript. Se você não precisa adicionar apresentação e comportamento para os seus documentos web, então você provavelmente não precisa de classes em seu código HTML.
  • Os nomes de classe devem comunicar informações úteis para os desenvolvedores. É útil para entender o que um nome de classe específico vai fazer quando você lê um trecho de DOM, especialmente para equipes de vários desenvolvedores, nas quais desenvolvedores front-end não serão as únicas pessoas que trabalham com componentes HTML.

Veja este exemplo muito simples:

<div class="news">
<h2>News</h2>
[news content]
</div>

O nome de classe news não lhe diz nada que já não esteja evidente a partir do conteúdo. Ele não lhe fornece nenhuma informação sobre a estrutura arquitetônica do componente, e não pode ser usado com o conteúdo que não é “news”. Amarrando sua semântica de nomes de classe firmemente à natureza do conteúdo já reduziu a capacidade de sua arquitetura para dimensionar ou ser facilmente colocado em uso por outros desenvolvedores.

Nomes de classe de conteúdo independente

Uma alternativa é derivar o nome da classe de semânticas a partir das repetições de padrões estruturais e funcionais em um projeto. Os componentes mais reutilizáveis são aqueles cujos nomes de classe são independentes do conteúdo.

Não devemos ter medo de fazer as conexões claras e explícitas entre as camadas, em vez de ter nomes de classes rigidamente refletindo o conteúdo específico. Fazer isso não torna as classes “não-semânticas”, mas significa apenas que suas semânticas não são derivadas do conteúdo. Não devemos ter medo de incluir outros elementos HTML se eles ajudam a criar componentes mais robustos, flexíveis e reutilizáveis. Fazer isso não torna o HTML “não-semântico”, significa apenas que você usa elementos além do mínimo necessário para a marcação o conteúdo.

Arquitetura front-end

O objetivo de uma arquitetura de componente/template/orientada a objeto é ser capaz de desenvolver um número limitado de componentes reutilizáveis que podem conter uma variedade de diferentes tipos de conteúdo. O importante para o nome da classe semântica em aplicações não triviais é ser conduzida pelo pragmatismo e servir melhor o seu propósito primordial – fornecer ganchos significativos, flexíveis e reutilizáveis de apresentação/de comportamento para que os desenvolvedores usem.


Componentes reutilizáveis e combináveis

HTML/CSS escalonáveis deve, em grande parte, contar com classes dentro do HTML para permitir a criação de componentes reutilizáveis. Um componente flexível e reutilizável é aquele que não se baseia em outros existentes numa certa parte da árvore DOM, nem requer a utilização de tipos de elementos específicos. Deve ser capaz de se adaptar a diferentes recipientes e ser facilmente themed. Se necessário, elementos HTML extras (além dos necessários apenas para a marcação do conteúdo) podem ser usados para tornar o componente mais robusto. Um bom exemplo é o que Nicole Sullivan chama de objeto de mídia.

Os componentes que podem ser facilmente combinados beneficiam ao evitarem os tipos de seletores a favor das classes. O exemplo a seguir impede a fácil combinação do componente btn com o componente uilist. Os problemas são que a especificidade do .btn é menor do que a do .uilist a (que irão substituir quaisquer propriedades comuns), e o componente uilist requer âncoras como nós filhos.

.btn { /* styles */ }
.uilist { /* styles */ }
.uilist a { /* styles */ }
<nav class="uilist">
<a href="#">Home</a>
<a href="#">About</a>
<a class="btn" href="#">Login</a>
</nav>

Uma abordagem que melhora a facilidade com que é possível combinar outros componentes uilist é usar classes ao estilo dos sub-objetos. Embora isso ajude a reduzir a especificidade da regra, o principal benefício é que ele oferece a opção para aplicar os estilos estruturais a qualquer tipo de nó filho.

.btn { /* styles */ }
.uilist { /* styles */ }
.uilist-item { /* styles */ }
<nav class="uilist">
<a class="uilist-item" href="#">Home</a>
<a class="uilist-item" href="#">About</a>
<span class="uilist-item">
<a class="btn" href="#">Login</a>
</span>
</nav>

Classes específicas de JavaScript

Usar alguma forma de classes específicas JavaScript pode ajudar a reduzir o risco de que mudanças temáticas ou estruturais para componentes quebrem qualquer JavaScript que também seja aplicado. Uma abordagem que achei útil é a utilização de certas classes somente para ganchos JavaScript – js-* – e não para travar qualquer apresentação além deles.

<a href="/login" class="btn btn-primary js-login"></a>

Dessa forma, você pode reduzir a chance de que mudando a estrutura ou o tema de objetos irá, de forma inadvertida, afetar qualquer comportamento do JavaScript exigido e qualquer funcionalidade complexa.


Modificadores de componentes

Frequentemente, componentes têm variantes com apresentações ligeiramente diferentes a partir do componente base, por exemplo, um fundo de cor diferente ou uma borda. Existem dois padrões principais usados para criar essas variantes de componentes. Vou chamá-los de padrões “single-class” e “multi-class”.

O padrão “single-class”

.btn, .btn-primary { /* button template styles */ }
.btn-primary { /* styles specific to save button */ }

<button class="btn">Default</button>
<button class="btn-primary">Login</button>

O padrão “multi-class”

.btn { /* button template styles */ }
.btn-primary { /* styles specific to primary button */ }

<button class="btn">Default</button>
<button class="btn btn-primary">Login</button>

Se você usar um pré-processador, você pode usar a funcionalidade @extend do Sass para reduzir alguns dos trabalhos de manutenção envolvidos na utilização do padrão “single-class”. No entanto, mesmo com a ajuda de um pré-processador, minha preferência é usar o padrão “multi-class” e adicionar classes modificadoras ao HTML.

Eu pensei que isso seria um padrão mais escalonável. Por exemplo, pegue o objeto base btn e adicione 5 tipos de botões e 3 tamanhos. Usando um padrão “multi-class”, você acaba com 9 classes que podem ser misturadas e pareadas. Usando um padrão “single-class”, você termina com 24 classes.

Além disso, é mais fácil fazer melhorias de componente de nível de um objeto, caso seja absolutamente necessário. Você pode desejar fazer pequenos ajustes para qualquer btn que aparecer dentro de outro componente.

/* "multi-class" adjustment */
.thing .btn { /* adjustments */ }

/* "single-class" adjustment */
.thing .btn,
.thing .btn-primary,
.thing .btn-danger,
.thing .btn-etc { /* adjustments */ }

Um padrão “multi-class” significa que você só precisa de um único seletor intra-componente para atingir qualquer tipo de btn baseado em objeto dentro do componente. Já um padrão “single-class” significa que você pode ter que representar qualquer tipo de botão possível e ajustar o seletor sempre que uma variante nova de botão for criada.

Nomes de classe estruturada

Ao criar componentes e “temas” que criam sobre eles, algumas classes são usadas como modificadores, algumas como limites de componentes, outras como componente sub-objetos etc.

É difícil deduzir a relação entre btn (componente), btn-primary (modificador), btn-group (componente), e btn-group-item (sub-objeto do componente) porque os nomes não mostram claramente a finalidade da classe. Não existe um padrão consistente.

Durante o ano passado, eu experimentei padrões de nomeação que me ajudam a entender mais rapidamente a relação de apresentação entre nodes em um trecho de DOM, em vez de tentar juntar arquitetura do site alternando pra lá e pra cá entre arquivos HTML, CSS, e JS. O padrão é influenciado principalmente pela abordagem do sistema BEM para nomeação, mas adaptado para uma forma que achei mais fácil para fazer a varredura.

t-template-name
t-template-name--modifier-name
t-template-name__sub-object
t-template-name__sub-object--modifier-name

component-name
component-name--modifier-name
component-name__sub-object
component-name__sub-object--modifier-name

is-state-type

js-action-name

Eu trato algumas estruturas como “templates” abstratos e outros estão mais para componentes distintos (que normalmente são construídos sobre “templates”). Mas esse tipo de distinção não é sempre necessária.

Este é apenas um padrão de nomenclatura que estou julgando útil no momento. Poderia assumir qualquer forma. Mas o benefício encontra-se em retirar a ambiguidade de nomes de classes que dependem apenas de (único) hífens, ou underscores, ou camel case.

Uma nota sobre o tamanho do arquivo bruto e compressão HTTP

Relacionada a qualquer discussão sobre CSS modular/escalável/orientado a objeto está a preocupação com o tamanho do arquivo e “bloat”. As palestras de Nicole Sullivan muitas vezes mencionam as economias de tamanho de arquivo (bem como melhorias de manutenção) que empresas como Facebook experimentaram quando adotaram esse tipo de abordagem. Na sequência, eu achei que iria partilhar as minhas anedotas sobre os efeitos da compressão HTTP sobre pré-processador de saída e do uso extensivo de classes HTML.

Quando surgiu o Bootstrap do Twitter, eu reescrevi o CSS compilado para melhor refletir sobre como faria para escrever isso à mão e comparar os tamanhos dos arquivos. Depois de reduzir o tamanho de dois arquivos, o CSS artesanal foi cerca de 10% menor do que a saída pré-processada. Mas, quando ambos os arquivos foram também compactados, a saída pré-processada foi cerca de 5% menor do que o CSS artesanal.

Isso destaca o quanto é importante comparar o tamanho dos arquivos após a compressão do HTTP, porque o tamanho dos arquivos reduzidos não conta toda a história. Ele sugere que os desenvolvedores CSS experientes usando pré-processadores não precisam ser excessivamente preocupados com um certo grau de repetição no CSS compilado, pois pode prestar-se bem em tamanhos menores de arquivos após a compressão HTTP. Os benefícios da manutenção mais fácil do código “CSS” via pré-processadores devem superar preocupações sobre a estética ou o tamanho da saída bruta e reduzida do CSS.

Em outro experimento, tirei todos os atributos de classe de um arquivo HTML de 60KB retirado de um site ativo (já composto de muitos componentes reutilizáveis). Fazendo isso, o tamanho do arquivo foi reduzido para 25KB. Quando os arquivos originais e stripped foram compactados, seus tamanhos eram 7.6KB e 6KB respectivamente – uma diferença de 1.6KB. As consequências reais do tamanho do arquivo de uso liberal de classe irão, raramente, valer a pena enfatizar mais.

Como aprendi a parar de me preocupar…

A experiência de muitos desenvolvedores qualificados, durante muitos anos, levou a uma mudança na forma como sites de grande escala e aplicativos são desenvolvidos. Apesar disso, para os indivíduos desacostumados com uma ideologia na qual “HTML semântico” significa o uso de nomes de conteúdo derivados de classe (e, mesmo assim, apenas como último recurso), isso normalmente exige que você trabalhe em um grande aplicativo antes de você se tornar consciente da natureza impraticável dessa abordagem. Você tem que estar preparado para desprezar velhas ideias, buscar alternativas, e até mesmo rever formas que você pode ter previamente rejeitado.

Uma vez que você começar a escrever sites e aplicativos não-triviais que você e outras pessoas devem não só manter, mas fazer a iteração ativamente, você rapidamente percebe (como Nicole Sullivan vem dizendo há anos) que, apesar de seus esforços, o código começa a ficar cada vez mais difícil de manter. Vale a pena tomar um tempo para explorar o trabalho de algumas pessoas que já procuraram enfrentar esses problemas: o blog da Nicole e o projeto Object Oriented CSS, Scalable Modular Architeture CSS de Jonathan Snook, e o método Block Element Modifier que Yandex desenvolveu.

Quando você opta por autor HTML CSS de uma forma que visa a reduzir a quantidade de tempo que você gasta escrevendo e editando CSS, isso envolve aceitar que você deve passar mais tempo, em vez disso, alterarando classes de HTML em elementos se você quiser mudar seus estilos. Isso acaba sendo bastante prático, tanto para os desenvolvedores front-end quanto back-end – qualquer um pode reorganizar “blocos de Lego” pré-construídos; mas ninguém pode realizar a alquimia do CSS.