Crédito: Esse documento é uma tradução. O original encontra-se em: https://cssguidelin.es/
Este documento denominado Diretrizes CSS foi escrito por mim, Harry Roberts. Eu sou um consultor de arquiteturas frontend residente na Inglaterra e meu trabalho é prestar consultoria para companhias de qualquer lugar do mundo que pretendam criar e mater interfaces de usuários de alta qualidade para divulgar seus produtos e pessoal. Eu estou disponível para novos contratos de trabalho.
Diretrizes CSS foram criadas segundo um modelo do tipo pague por elas o que você achar conveniente. Se as Diretrizes CSS forem úteis para você ou seu time, por favor considere apoiá-la financeiramente.
Receba notificações sobre atualizações, alterações, acréscimos e próximas seções seguindo @cssguidelines no Twitter.
CSS não é uma linguagem amigável. É uma linguagem fácil de se aprender e começar a usar, mas torna-se problemática quando se trata de projetar em larga escala. Não há muita coisa que se possa fazer para mudar a forma como CSS funciona, contudo podemos alterar a forma como escrevemos e estruturamos as folhas de estilos.
Ao trabalhar em projetos grandes e de longa duração, com dezenas de desenvolvedores de diferentes especialidades e habilidades, é importante que todos trabalhem de forma unificada para, entre outras coisas:
Há uma variedade de técnicas que podemos empregar com a finalidade de atingir esses objetivos, e as Diretrizes CSS fornecem recomendações e abordagens destinadas a atingir tais objetivos.
Um guia de estilos para escrever CSS (nota: não se trata de um guia de aplicação de estilo visual) é uma ferramenta valiosa para equipes que buscam:
Embora guias de estilos sejam normalmente mais adequadas para equipes de produtos dependentes de grandes bases de código em projetos de vida longa e em evolução, com vários desenvolvedores contribuindo, todos os desenvolvedores devem se esforçar para obter padronização em seus códigos.
Um bom guia de estilos, quando bem seguido possibilita e facilita:
Os guias de estilo devem ser aprendidos, compreendidos e implementados em todos os momentos de um projeto e qualquer desvio do que prescreve o guia deve ser plenamente justificado.
Diretrizes CSS é um guia de estilos; não é o guia de estilos. Ele contém metodologias, técnicas e dicas que eu recomendaria firmemente aos meus clientes e equipes, mas seus próprios gostos e circunstâncias podem ser diferentes. Suas necessidades podem variar.
Essas diretrizes embora tenham um carater opinativo foram repetidamente testadas, enfatizadas, refinadas, quebradas, retrabalhadas e revistas ao longo de vários anos em projetos de todos os tamanhos.
Uma das formas mais simples de um guia de estilo se resume a um conjunto de regras relacionadas a sintaxe e a formatação do código. Criar uma maneira padrão de escrever CSS ( escrita em sentido literal ) significa que o código sempre será familiar para todos os membros da equipe.
Além disso, o código que parece limpo é limpo. É um ambiente de trabalho muito melhor e motiva os outros membros da equipe a manter o padrão de limpeza que encontraram. Código feio cria um mau precedente.
Em alto nível o que queremos é:
Mas, como em geral acontece com qualquer metodologia, os detalhes são pouco relevantes — a consistência é que é fundamental.
Ultimamente, com a ascensão meteórica dos pré-processadores o mais comum é dividir-se as CSS em vários arquivos.
Ainda que não se esteja usando um pré-processador, é uma boa ideia dividir partes discretas de código em seus próprios arquivos, que serão concatenados na etapa de compilação.
Se, por qualquer motivo você não pretenda trabalhar com vários arquivos, as próximas seções deste guia devem ser lidas com espírito analítico sisanso a adequá-las às suas necessidades.
Criar uma tabela de conteúdos é, sem dúvida, uma sobrecarga de manutenção bastante substancial, mas os benefícios que ela traz superam em muito os custos. É preciso ser um desenvolvedor diligente para manter uma tabela de conteúdos atualizada, mas vale a pena mantê-la. Uma tabela de conteúdos atualizada fornece para a equipe um valioso catálogo canônico com tudo o que consta de um projeto CSS, o que faz e em que ordem.
Uma tabela de conteúdos simples permitir consultar o nome das seções que compõem o projeto na sequência em que as seções aparecem e fornece um breve resumo do que é e do que faz cada seção. Observe o exemplo a seguir:
/**
* CONTEÚDOS
*
* SETTINGS
* Global...............Variáveis globais e configurações.
*
* FERRAMENTAS
* Mixins...............Mixins utilitários.
*
* GERAIS
* Normalize.css........Equalizando CSS.
* Box-sizing...........Box model default 'box-sizing'.
*
* BASE
* Headings.............Estilos para H1–H6.
*
* OBJETOS
* Wrappers.............Wrapping e elementos containers.
*
* COMPONENTES
* Page-head............Header da página.
* Page-foot............Footer da página.
* Buttons..............Botões.
*
* COMPLEMENTOS
* Text.................Helpers para textos.
*/
Cada item se refere a uma seção e/ou um arquivo include.
Na maioria dos projetos cada seção será substancialmente maior do que a mostrada no exemplo, mas qualquer que seja o tamanho da tabela sua finalidade é fornecer aos desenvolvedores uma visão ampla do que está sendo usado onde e porquê.
Sempre que for possível, limite a largura das linhas dos arquivos CSS a 80 caracteres. As razões para isso incluem:
/**
* Eu sou uma longa linha de comentário. Eu descrevo com detalhes as regras
* CSS a seguir. Por ser uma linha de comentários longa e ter mais de 80
* caracteres eu devo ser qebrada em várias linhas.
*/
Existirão inevitáveis exceções a essa regra — por exemplo: existência de longas URLs ou de sintaxe CSS para gradientes — com as quais você não deve se preocupar.
Comece cada nova seção principal de um projeto CSS com um título:
/*------------------------------------*\
#TÍTULO-DA-SEÇÃO
\*------------------------------------*/
.selector { }
O título da seção deve ser prefixado com um o símbolo de hash ( # ) para permitir a realização de pesquisas e buscas mais direcionadas ( por exemplo grep, etc. ): em vez de realizar uma busca por TÍTULO-DA-SEÇÃO — o que pode gerar muitos resultados — uma busca mais precisa por #TÍTULO-DA-SEÇÃO deverá retornar apenas a seção em questão.
Deixe uma linha em branco ( carriage return ) entre o título da seção e a próxima linha de código ( seja um comentário, algum Sass ou algum CSS ).
Se você estiver trabalhando em um projeto em que cada seção tem seu próprio arquivo, o título da seção deve aparecer no topo de cada arquivo correspondente. Se você estiver trabalhando em um projeto com várias seções por arquivo, cada título deverá ser precedido por cinco (5) linhas em branco ( carriage return ). Esses espaços em branco extras, associados a um título, facilita a localização de novas seções ao rolar arquivos grandes:
/*------------------------------------*\
#SEÇÃO-A
\*------------------------------------*/
.seletor { }
/*------------------------------------*\
#OUTRA-SEÇÃO
\*------------------------------------*/
/**
* Comentário
*/
.outro-selector { }
Antes de discutirmos como escrever conjuntos de regras CSS, primeiro nos familiarizemos com a terminologia pertinente:
[seletor] {
[propriedade]: [valor];
[<--declaração--->]
}
Por exemplo:
.foo, .foo--bar,
.baz {
display: block;
background-color: green;
color: red;
}
O bserve no exemplo mostrado:
{);:);{) na mesma linha do último seletor;{);}) em sua própria linha;;) na última declaração.Esse formato é um padrão universal amplamente adotado ( exceto variações no número de espaços, com muitos desenvolvedores preferindo dois (2) espaços ).
Como tal, o seguinte seria incorreto:
.foo, .foo--bar, .baz
{
display:block;
background-color:green;
color:red }
Os problemas com o código mostrado incluem:
{) em sua própria linha;}) não está em sua linha própria;;) à direita da última declaração foi omitido (ok, é opcional);:).CSS deve ser escrito em várias linhas, exceto em circunstâncias muito específicas. Há uma série de benefícios para isso:
diffs, porque uma linha define uma só mudança.Exceções a essa regra são bem específicas, tal como em conjuntos de regras semelhantes que só contêm uma declaração, conforme mostrado a seguir:
.icon {
display: inline-block;
width: 16px;
height: 16px;
background-image: url(/img/sprite.svg);
}
.icon--home { background-position: 0 0 ; }
.icon--person { background-position: -16px 0 ; }
.icon--files { background-position: 0 -16px; }
.icon--settings { background-position: -16px -16px; }
Tais conjuntos de regras se beneficiam de serem alinhados individualmente pelas seguintes razões:
Além de indentar declarações individuais, indente também conjuntos de regras relacionadas para destacar o relacionamento entre elas. Observe o exemplo a seguir:
.foo { }
.foo__bar { }
.foo__baz { }
Ao fazer isso, o desenvolvedor pode ver de relance que .foo__baz {} está aninhado dentro de
.foo__bar {} que por sua vez está aninhado dentro de .foo {}.
Essa quase replicação do DOM fornece informações valiosas aos desenvolvedores sobre onde as classes devem ser usadas sem precisar se referir ao fragmento da marcação HTML.
O Sass fornece funcionalidade de aninhamento. Isto é, escrevendo isto:
.foo {
color: red;
.bar {
color: blue;
}
}
…obteremos a seguinte CSS compilada:
.foo { color: red; }
.foo .bar { color: blue; }
Para indentar Sass, usamos os mesmos dois (2) espaços, e também deixamos uma linha em branco antes e depois do conjunto de regras aninhadas.
N.B. Aninhamento no Sass deve ser evitado sempre que possível. Veja a Seção Especificidade para mais detalhes.Alinhe cadeias idênticas comuns e relacionadas em declarações. Observe o exemplo a seguir:
.foo {
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.bar {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
margin-right: -10px;
margin-left: -10px;
padding-right: 10px;
padding-left: 10px;
}
Isso torna a vida um pouco mais fácil para os desenvolvedores cujos editores de texto suportam a edição de colunas, permitindo que eles alterem várias linhas idênticas e alinhadas de uma só vez..
Parece que você está gostando dessas diretrizes…
Além do recuo em si, podemos fornecer muitas informações por meio do uso criterioso de espaços em branco entre os conjuntos de regras. Nós usamos e recomendamos:
Por exemplo:
/*------------------------------------*\
#FOO
\*------------------------------------*/
.foo { }
.foo__bar { }
.foo--baz { }
/*------------------------------------*\
#BAR
\*------------------------------------*/
.bar { }
.bar__baz { }
.bar__foo { }
Nunca deve haver um cenário em que dois conjuntos de regras não tenham uma linha em branco entre eles. O exemplo a seguir seria incorreto:
.foo { }
.foo__bar { }
.foo--baz { }
Dada a natureza intrinsecamente interconectada da HTML com as CSS, seria omisso não incluir algumas diretrizes de sintaxe e formatação para marcação.
Sempre circunde valores de atributos da HTML com aspas, mesmo que eles sejam válidos sem elas. Isso reduz a chance de acidentes e é um formato mais familiar para a maioria dos desenvolvedores. O código a seguir funciona ( e é válido ):
<div class=box>
…contudo prefira o seguinte formato para o código:
<div class="box">
As aspas não são obrigatórias, mas vamos incluí-las.
Ao escrever vários valores para um atributo de classe, separe-os com dois espaços, conforme mostrado a seguir::
<div class="foo bar">
Quando várias classes estiverem relacionadas entre si, considere agrupá-las entre colchetes ([ e ]), da seguinte maneira:
<div class="[ box box--highlight ] [ bio bio--long ]">
Esta não é uma recomendação firme, mas é algo que ainda estou testando e que traz uma série de benefícios. Leia mais em Agrupando classes relacionadas em sua marcação.
Assim como em conjuntos de regras CSS, é possível usar espaços em branco significativos na marcação HTML. Você pode denotar quebras temáticas no conteúdo com cinco (5) linhas em branco, conforme mostrado a seguir:
<header class="page-head">
...
</header>
<main class="page-content">
...
</main>
<footer class="page-foot">
...
</footer>
Separe trechos de marcação independentes, mas vagamente relacionados, com uma única linha em branco, conforme mostrado a seguir:
<ul class="primary-nav">
<li class="primary-nav__item">
<a href="/" class="primary-nav__link">Home</a>
</li>
<li class="primary-nav__item primary-nav__trigger">
<a href="/about" class="primary-nav__link">About</a>
<ul class="primary-nav__sub-nav">
<li><a href="/about/products">Products</a></li>
<li><a href="/about/company">Company</a></li>
</ul>
</li>
<li class="primary-nav__item">
<a href="/contact" class="primary-nav__link">Contact</a>
</li>
</ul>
Isso permite que se identifique partes separadas do DOM rapidamente e também permite que determinados editores de texto - como o Vim, por exemplo — manipulem blocos de marcação delimitados por linhas em branco.
Trabalhar com CSS exige muito conhecimento. Com tanto para se conhecer, e tantas nuances específicas do projeto a serem lembradas, a pior situação para a maioria dos desenvolvedores é quando não foi ele que escreveu o código. Lembrar de nomes de classes, regras, objetos e helpers quando foi você que escreveu o código é gerenciável até certo ponto, mas se foi outro as dificuldades aumentam muito.
CSS precisa de mais comentários.
Como a CSS é uma espécie de linguagem declarativa não deixa muitas evidências no código e muitas vezes é difícil de se tirar conclusões — olhando apenas para o CSS — relacionadas a:
Isso sem citar algumas das muitas peculiaridades do CSS - como vários estados de overflow alterando o contexto de formatação ou certas propriedades de transformação que acionam a aceleração de hardware — o que resulta em ainda mais dificuldades para os desenvolvedores que herdam projetos.
Como o código CSS não conta muito bem sua própria história, é uma linguagem que realmente precisa ser muito comentada.
Como regra geral, você deve comentar qualquer coisa que não seja imediatamente óbvia apenas lendo-se o código. Ou seja, não há necessidade de dizer a alguém que color: red; fará algo vermelho, mas se você estiver usando overflow: hidden; para clarear floats — em vez de recortar o excesso de um elemento — isso provavelmente é algo que vale a pena documentar.
Para comentários extensos que documentam seções ou componentes inteiros, usa-se distribuí-lo por várias linhas criando colunas de largura para acomodar 80 caracteres.
Observe a seguir um exemplo da vida real do CSS que estiliza o cabeçalho das páginas do site CSS Wizardry:
/**
* O cabeçalho principal das páginas do site (page-head) pode ser estilizado
* de duas maneiras distintas:
*
* 1) page-head simples sem background e estilizações complementares,
* apenas com o logo e a navegação.
* 2) page-head master com altura fluida (fixa a partir de deteminado ponto)
* com uma grande imagem de fundo e algum texto complementar.
* O page-head simples é extremamente simples, contudo o page-head master
* guarda certa dependência com o container dentro dele.
*/
O nível de detalhe mostrado neste exemplo deve ser a norma para todos os códigos não triviais, tais como descrições de estados, permutações, condições e tratamentos.
Quando trabalhar com vários partials (parciais), ou de maneira OOCSS, você geralmente descobrirá que conjuntos de regras que podem trabalhar em conjunto com outras regras nem sempre estão no mesmo arquivo ou local. Por exemplo: um objeto botão genérico destinado a definir estilos puramente estruturais e criado para ser estendido deve residir em um arquivo e as estilizações para adicionar cosméticos estendendo o botão residir em um arquivo 'partial'. Nós documentamos esse relacionamento entre arquivos com simples ponteiros de extensão de objeto. Assim, no arquivo de objetos teríamos:
/**
* Extende '.btn {}' em _components.buttons.scss.
*/
.btn { }
E no arquivo de temas para botões teríamos:
/**
* As regras a seguir extendem '.btn {}' em _objects.buttons.scss.
*/
.btn--positive { }
.btn--negative { }
Os comentários mostrados no exemplo são simples e não exigem grande esforço do desenvolvedor, mas podem fazer muita diferença para os desenvolvedores que não conhecem os relacionamentos entre os projetos, ou que estão querendo saber como, por que e de onde outros estilos podem ser herdados.
Muitas vezes queremos comentar declarações específicas (ou seja, linhas) em um conjunto de regras. Para fazer isso, usamos uma espécie de nota de rodapé reversa (reversa porque a nota vem antes da referência a ela). Observe a seguir um comentário mais complexo detalhando os cabeçalhos principais de um site e usando nota de rodapé reversa:
/**
* Cabeçalhos de sites grandes desempenham função de mastheads. Eles têm
* uma altura fluida controlada por um elemento container dentro deles.
*
* 1. Mastheads, em geral, têm fundo escuro, assim temos que nos certificar
* que o contraste está OK. Atenção para as alterações nas imagens de fundo.
* 2. A maioria das regras de estilização do masterhead deve ser definida
* para o container que se encontra dentro dele. É nesse container que
* a maioria dos elementos são posicionados.
* 3. O container deve criar um contexto de posicionamento para facilitar
* a criação do layout da navegaçãodo e do texto ali inseridos.
* 4. Técnica para faux-fluid-height: defina padding em porcentagem para criar
* a ilusão de altura fluida e posicione os boxes.
* 5. Para viewport de 758px de largura e razão de aspecto igual a 16:9 o
* masterhead será renderizado com 480px de altura. Considere...
* 6. …tornar a altura fixa para viewports menores que 758px…
* 7. …define altura fixa em 480px. A partir daí a altura deixa de ser fluida
* e o masthead muda de fluido para fixo. Esse valor leva em conta o
* padding e a borda superior do header.
*/
.page-head--masthead {
margin-bottom: 0;
background: url(/img/css/masthead.jpg) center center #2e2620;
@include vendor(background-size, cover);
color: $color-masthead; /* [1] */
border-top-color: $color-masthead;
border-bottom-width: 0;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1) inset;
@include media-query(lap-and-up) {
background-image: url(/img/css/masthead-medium.jpg);
}
@include media-query(desk) {
background-image: url(/img/css/masthead-large.jpg);
}
> .wrapper { /* [2] */
position: relative; /* [3] */
padding-top: 56.25%; /* [4] */
@media screen and (min-width: 758px) { /* [5] */
padding-top: 0; /* [6] */
height: $header-max-height - double($spacing-unit) - $header-border-width; /* [7] */
}
}
}
Comentários, tais como os mostrados, nos permite manter toda a documentação em um único lugar, ao mesmo tempo em que nos referimos às partes do conjunto de regras ao qual elas pertencem.
Como na maioria dos pré-processadores, senão todos, temos uma opção de escrever comentários com sintaxe apropiada a não permitir os comentários nos arquivos CSS resultantes. Use tal sintaxe para documentar os códigos escritos para serem processados. Se você estiver documentando código que será compilado, use a sintaxe para comentários apropriadas a serem compiladas. Por exemplo:
// Dimensões do sprite para imagens @2x:
$sprite-width: 920px;
$sprite-height: 212px;
/**
* 1. A dimensão padrão dos ícones é 16px x 16px.
* 2. Reduz a retina sprite para sua dimensão adequada.
*/
.sprite {
width: 16px; /* [1] */
height: 16px; /* [1] */
background-image: url(/img/sprites/main.png);
background-size: ($sprite-width / 2 ) ($sprite-height / 2); /* [2] */
}
No exemplo mostrado as variáveis estão comentadas com uso de código para comentários Sass (//) e não serão compilados no arquivo CSS, enquanto as CSS estão documentadas com uso de código para comentários CSS (/* ... */) que serão compilados no arquivo CSS. Isso significa que teremos apenas as informações desejáveis e relevantes disponíveis ao depurar as folhas de estilo compiladas.
Não é preciso dizer que nenhum comentário deve entrar em ambiente de produção — todo o CSS deve ser minificado o que resulta em perda de comentários antes da implantação das folhas de estilos.
As convenções de nomenclatura em CSS são extremamente úteis para tornar seu código mais estríto, mais transparente e mais informativo.
Uma boa convenção de nomenclatura dirá a você e sua equipe o seguinte:
A convenção de nomenclatura que eu sigo é muito simples: strings delimitadas por hífen
(-), com nomes semelhantes aos da metodologia BEM, para trechos de código mais complexos.
Vale a pena notar que uma convenção de nomenclatura não é normalmente útil para o desenvolvimento das CSS, ela é realmente poderosa e muito útil quando usadas na marcação HTML.
Todas as strings que compõem um valor de classe devem ser separadas com hífen (-), conforme mostrado a seguir:
.page-head { }
.sub-content { }
Camel case e sublinhados não devem ser usados em valores de classes. O exemplo a seguir está incorreto:
.pageHead { }
.sub_content { }
Para partes de UI maiores, mais inter-relacionadas e que requerem um grande número de classes, usamos uma convenção de nomenclatura semelhante ao da metodologia BEM.
BEM, que é a sigla para Block Element Modifier é uma metodologia de desenvolvimento frontend criada por desenvolvedores que trabalham no Yandex. Enquanto o BEM é uma metodologia completa, aqui estaremos preocupados apenas com a convenção de nomenclatura. Além disso, a convenção de nomenclatura aqui é apenas semelhante à da metodologia BEM. Os princípios são exatamente os mesmos, mas a sintaxe real difere ligeiramente.
BEM divide as classes dos componentes em três grupos:
Observe uma analogia ( não é um exemplo ):
.person { }
.person__head { }
.person--tall { }
Elementos são separados com dois (2) sublinhados (__) e Modificadores são separados com dois (2) hífens (--).
Podemos ver claramente que .person {} é o Bloco, ou a raiz única de uma entidade discreta. .person__head {} é um Elemento, ou uma parte menor do Bloco .person {}. E .person--tall {} é um Modificador, ou uma variante específica do Bloco .person {}.
O contexto de um Bloco começa no local mais lógico, independente e discreto. Para continuar com a nossa analogia que tomou como base o valor de classe person, não temos um valor de classe do tipo .room__person {}, pois room pertence a um contexto mais elevado. Nós provavelmente teríamos Blocos conforme mostrados a seguir:
.room { }
.room__door { }
.room--kitchen { }
.person { }
.person__head { }
Se quiséssemos denotar um .person {} dentro de um .room {}, seria mais correto usar um seletor tal como .room .person {} que liga dois Blocos do que aumentar o escopo de Blocos e Elementos já existentes.
Um exemplo mais realista de Blocos com escopo adequado pode ser algo como mostrado a seguir, em que cada parte do código representa seu próprio Bloco:
.page { }
.content { }
.sub-content { }
.footer { }
.footer__copyright { }
Observe a seguir uma amostra de notação incorreta para o mesmo exemplo anterior:
.page { }
.page__content { }
.page__sub-content { }
.page__footer { }
.page__copyright { }
É importante saber quando o escopo do BEM é iniciado e interrompido. Como regra geral, o BEM se aplica a partes discretas e autônomas da interface do usuário.
Precisa de ajuda com algo?
Se fôssemos adicionar outro elemento chamado, digamos .person__eye {} a esse componente de .person {} não precisaríamos percorrer todas as camadas do DOM. Isto é, a notação correta seria .person__eye {} e não .person__head__eye {}. Suas classes não refletem a trilha completa do DOM.
Você pode criar variantes de Elementos, as variações podem ser indicadas de diversas maneiras, dependendo de como e do porquê das modificações. Continuando com nosso exemplo de person, a modificação para um olho azul poderia ser indicada como mostrado a seguir:
.person__eye--blue { }
Notar que estamos modificando diretamente o Elemento olho.
As coisas podem ficar mais complexas. Por favor, desculpe a analogia grosseira, mas vamos imaginar que temos um Elemento Face que é bonito, maspessoa como um todo não é tão bonita. Então modificamos diretamente o Elemento Face, um rosto bonito em uma pessoa normal:
.person__face--handsome { }
Mas e se essa pessoa for bonita e quisermos estilizar sua face por causa disso? Um rosto normal em uma pessoa bonita:
.person--handsome .person__face { }
Esta é uma das poucas ocasiões em que usamos um seletor descendente para modificar um elemento com base em um modificador de Bloco.
Se usarmos o Sass, provavelmente escreveríamos como mostrado a seguir:
.person { }
.person__face {
.person--handsome & { }
}
.person--handsome { }
Observe que não aninhamos uma nova instância de .person__face {} dentro de .person--handsome {}, em vez disso, usamos os seletores-pai do Sass para prefixar .person--handsome dentro de .person__face {}, seletor já existente . Isso significa que todas as regras relacionadas a .person__face {} coexistem em um único local e não estão espalhadas por todo o arquivo. Essa é uma boa prática geral ao lidar com código aninhado: mantenha todo o seu contexto (por exemplo, todo o código para .person__face {}) encapsulado em um local.
Como eu já havia dito anteriormente, estabelecer convenções de nomes não é necessariamente útil para as CSS. O poder de se estabelecer uma convenção de nomes faz-se presente na marcação HTML. Considere a marcação HTML mostrada a seguir, na qual não se considerou qualquer convenção para nomear as classes:
<div class="box profile pro-user">
<img class="avatar image" />
<p class="bio">...</p>
</div>
Qual é o relacionamento entre as classes box e profile? Qual é o relacionamento entre as classes profile e avatar? Elas estão relacionadas entre si?
Você deveria ter usado as classes pro-user e bio juntas? As classes image e
profile serão definidas em uma mesma seção das CSS? Podemos usar a classe avatar em outros lugares a marcação?
Simplesmente observando a marcação HTML é muito difícil responder às questões levantadas. Observe a seguir como tudo fica mais claro quando se adota um convenção de nomes:
<div class="box profile profile--is-pro-user">
<img class="avatar profile__image" />
<p class="profile__bio">...</p>
</div>
Agora podemos dizer com facilidade e sem dúvidas como se relacionam as classes entre entre si, e mais, quais são as classes passíveis de serem usadas fora do escopo do componente, bem como quais classes podemos usar livre e seguramente em qualquer lugar da marcação.
Como regra geral, não é prudente vincular CSS e JS à mesma classe da marcação HTML. Isto é porque, assim fazendo você não poderá ter (ou remover) um sem (remover) o outro. É muito mais limpo, muito mais transparente e muito mais sustentável usar classes específicas para vincular à JS.
Eu mesmo já vivenciei ocasiões em que, ao tentar refatorar algum CSS, a funcionalidade do JS foi removida involuntariamente, porque os dois estavam ligados um ao outro — era impossível ter um sem o outro.
Normalmente usamos o prefixo js-, para indicar classes vinculadas à JS, conforme mostrado a seguir:
<input type="submit" class="btn js-btn" value="Siga-me" />
Isso nos dá a flexibilidade de poder ter, em qualquer lugar da interface, um botão estilizado segundo a classe .btn {} e que não se comporte conforme projetado para a classe .js-btn.
data-*É prática comum usar-se o atributo data-* como vínculo ( hook ) para JS, mas isso não é correto, pois os atributos do tipo data-* são usados, segundo as especificações, para armazenar dados personalizados e privados na página da aplicação
(ênfase do autor). Assim, atributos do tipo data-*
se destinam a armazenar dados e não servir de vínculo.
Nos exemplos,demostramos, em sua forma mais simples, convenção de nomenclatura para três grupos distintos de classes.
Eu aconselho fortemente que você consulte outras fontes sobre convenção de nomenclatura para conhecer mais funcionalidades disponíveis por essa metodologia — eu reconheço que devo pesquisar mais fontes de consulta sobre este assunto.
Talvez, por incrível que pareça, um dos aspectos fundamentais e críticos de se escrever CSS sustentável e escalonável sejam os seletores. Sua especificidade, portabilidade e capacidade de reutilização têm um impacto direto na consistência de nosso CSS e nas dores de cabeça que isso poderá nos trazer.
Ao escrever CSS é importante que se projete seletores corretamente e que selecionem as coisas certas pelas razões certas. Intenção de seleção é o termo para designar o processo de decidir e definir o que você deseja estilizar e como você irá selecionar. Por exemplo: se você está querendo estilizar o menu de navegação principal do seu site, um seletor conforme mostrado a seguir seria incrivelmente insensato:
header ul { }
A intenção deste seletor é estilizar qualquer elemento ul dentro de qualquer elemento header, enquanto nossa intenção era estilizar a navegação principal do site. Essa é uma intenção de seleção muito pobre, pois você pode ter qualquer quantidade de elementos header em uma página e por sua vez eles podem conter qualquer quantidade de elementos ul, então um seletor como este corre o risco de aplicar um estilo muito específico a um número muito grande de elementos. Isso resultará na necessidade de escrever mais CSS para sobrescrever a natureza gananciosa desse seletor.
Um seletor muito mais apropriado para este caso seria conforme mostrado a seguir:
.site-nav { }
Um seletor explícito e não ambíguo com uma boa intenção de seleção. Estamos explicitamente selecionando a coisa certa pelo motivo certo.
Intenção de seleção pobre é uma das maiores razões para dores de cabeça em projetos CSS. Escrever regras CSS que são muito gananciosas, regras que aplicam estilos muito específicos por meio de seletores muito abrangentes, causam efeitos colaterais inesperados e resultam em folhas de estilo muito complicadas, com seletores extrapolando suas intenções, impactando e interferindo com conjuntos de regras que não se relacionam.
CSS não pode ser encapsulado, e isso é da natureza da linguagem, mas nós podemos mitigar alguns efeitos da falta de encapsulamento, evitando escrever seletores que produzam efeitos globais;seus seletores devem ser tão explícitos e tão bem fundamentados quanto seus motivos para querer selecionar algo.
Em uma abordagem baseada em componentes para a construção de interfaces de usuário, a idéia de reutilização é primordial. Buscamos ter a opção de mover, reciclar, duplicar e distribuir componentes em nossos projetos.
Para atingir tal objetivo, fazemos uso pesado de classes. As IDs, além de serem excessivamente específicas, não podem ser usadas mais de uma vez na mesma página, enquanto as classes podem ser reutilizadas uma quantidade infinita de vezes. Tudo o que você escolhe, do tipo de seletor ao nome do seu valor, deve ser pensado com o objetivo de que seja reutilizável.
Devido a natureza de constante mudança da maioria dos projetos de interface do usuário e a mudança para arquiteturas baseadas em componentes, é de nosso interesse não estilizar as coisas com base em onde elas estão, mas no que elas são. Ou seja, o estilo de nossos componentes não deve depender de onde os colocamos, eles devem permanecer totalmente independentes da localização.
Considere o exemplo de estilização de um botão interativo para o qual escolhemos o seletor conforme mostrado a seguir:
.promo a { }
Tal seletor não só tem uma pobre intenção de seleção, pois ele estiliza avidamente como botão todo e qualquer link contido em um elemento com a classe .promo, como também é um grande desperdício pelo fato de ser tão localizado; não podemos reutilizar esse botão com seu estilo correto fora de .promo porque ele está explicitamente atrelado a esse local. Um seletor muito melhor seria conforme mostrado a seguir:
.btn { }
Essa classe única pode ser reutilizada em qualquer lugar fora do .promo e sempre carregará seu estilo correto. Como resultado de um melhor seletor, esta parte da interface do usuário é mais portátil, mais reciclável, não tem dependências e o seletor escolhido tem muito melhor intenção de seleção. Um componente não deve estar contido em um determinado lugar para ter uma aparência específica.
Reduzir, ou idealmente, remover a dependência de localização significa que podemos mover componentes em na marcação HTML mais livremente, mas que tal melhorar nossa capacidade de mover classes em torno de componentes? Em um nível mais baixo, há mudanças que podemos fazer em nossos seletores que tornam os próprios seletores, em oposição aos componentes que eles criam, mais portáteis. Considere o seguinte exemplo:
input.btn { }
Este é um seletor qualificado: um input está vinculado a um conjunto de regras que se aplicam somente a elementos input. Ao omitir a qualificação, podemos reutilizar a classe .btn em qualquer elemento que escolhermos, tais como button.
Seletores qualificados não se prestam a serem reutilizados, e cada seletor que escrevemos deve ser criado com a reutilização em mente.
É claro que há situações em que você legitimamente pode criar e usar seletores qualificados; talvez seja necessário aplicar um estilo muito específico a um determinado elemento quando ele contem uma determinada classe conforme o exemplo mostrado a seguir:
/**
* Define peso e cor da fonte para elementos com a classe '.error'.
*/
.error {
color: red;
font-weight: bold;
}
/**
* Se o elemento for um 'div', estilize, também, em forma de box.
*/
div.error {
padding: 10px;
border: 1px solid;
}
Esse exemplo mostra o caso em que justifica-se a criação de um seletor qualificado, porém eu recomendaria uma abordagem conforme mostrada a seguir:
/**
* Erros textuais.
*/
.error-text {
color: red;
font-weight: bold;
}
/**
* Elementos que contém erros.
*/
.error-box {
padding: 10px;
border: 1px solid;
}
Isso nos dá flexibilidade para aplicar os estilos do seletor .error-box a qualquer elemento, e não apenas aos elementos div, neste caso o seletor é mais reutilizável do que um seletor qualificado.
Uma boa prática para nomear seletor qualificado consiste em sinalizar onde a classe pode ser esperada ou destinada a ser usada, conforme mostrado no exemplo a seguir:
ul.nav { }
Neste exemplo nota-se claramente que a classe .nav destina-se a ser usada em elementos ul e não em elementos nav. Criar seletores quase qualificados nos dá a possibilidade de estilizar mesmo sem qualificar o seletor, conforme mostrado a seguir:
/*ul*/.nav { }
Comentar o elemento principal não invalida o seletor que continua a aplicar estilos ainda que não seja qualificado e consequentemente sem sua especificidade aumentada.
Phil Karlton certa vez disse o seguinte: existem apenas duas coisas difíceis na ciência da computação: invalidação de cache e nomeação de coisas.
Não vou comentar sobre essa afirmação, mas nomear coisas tem me atormentado por anos. Meu conselho no que diz respeito a nomear coisas em CSS é escolher um nome que seja sensato e um pouco ambíguo com vistas a buscar uma alta reutilização. Por exemplo, em vez de uma classe como .site-nav, escolha algo como .primary-nav em vez de uma classe como.footer-links prefira uma classe como .sub-links.
A diferença nesses nomes é que o primeiro de cada um dos dois exemplos está vinculado a um caso de uso muito específico, pois eles podem ser usados apenas para a navegação do site ou os links do rodapé, respectivamente. Usando nomes um pouco mais ambíguos, podemos aumentar nossa capacidade de reutilizar esses componentes em diferentes cenários.
Citando Nicolas Gallagher:
Atrelar a semântica do nome de classe à natureza do conteúdo reduz a escalabilidade da arquitetura e a facilidade de ser usada por outros desenvolvedores.
Ou seja, devemos usar nomes sensíveis. Classes como .border ou .red nunca devem ser usadas, mas devemos também, evitar o uso de classes que descrevam a natureza do conteúdo e/ou suas finalidades de uso. Usar um nome de classe para descrever o conteúdo é redundante porque o conteúdo se descreve por si só.
O debate em torno da semântica tem durado anos, mas é importante que adotemos uma abordagem mais pragmática e sensata para nomear as coisas com a finalidade de criar um ambiente de trabalho mais eficiente e eficaz. Em vez de focar na 'semântica', observe mais de perto a sensibilidade e a longevidade, escolha nomes baseados na facilidade de manutenção e não com o propósito de transmitir um significado.
Nomeie as coisas para as pessoas, pois são elas as únicas que realmente leem suas classes (tudo mais simplesmente casa com elas). Mais uma vez insisto que é melhor se esforçar para criar classes reutilizáveis e recicláveis do que criá-las para uso específico. Observe o exemplo a seguir:
/**
* Auto risco de se tornar desatualizada e de manutenção complicada.
*/
.blue {
color: blue;
}
/**
* Dependente de local para renderização apropriada.
*/
.header span {
color: blue;
}
/**
* Muito específica e de reutilização limitada.
*/
.header-color {
color: blue;
}
/**
* Maravilhosamente abstrata, muito portátil e sem riscos de se tornar desatualizada.
*/
.highlight-color {
color: blue;
}
É importante encontrar um equilíbrio entre nomes que não descrevam literalmente o estilo bem como não descrevam explicitamente uso específico. Em vez de .home-page-panel prefira .masthead, em vez de .site-nav prefira .primary-nav, em vez de .btn-login prefira optar por .btn-primary.
Nomear os componentes de forma agnóstica e focada na sua capacidade de reutilização realmente ajuda os desenvolvedores a construir e modificar as interfaces de usuário com muito mais rapidez e com muito menos desperdício. No entanto, as vezes pode ser benéfico criar uma nomenclatura mais específica ou significativa com escolha de nomes de classes mais ambíguos, especialmente quando várias classes agnósticas se juntam para formar um componente mais complexo e específico que pode se beneficiar de ter um nome mais significativo. Neste cenário, criamos um atributo do tipo data-ui-component que define um nome mais específico, por exemplo:
<ul class="tabbed-nav" data-ui-component="Main Nav">
Aqui temos os benefícios de um nome de classe altamente reutilizável que não descreve, e portanto vincula-se a, um uso específico e adiciona significado por meio do atributo data-ui-component. O valor dos atributos data-ui-component pode ter qualquer formato desejado, seja um título:
<ul class="tabbed-nav" data-ui-component="Main Nav">
ou o valor típico de classe:
<ul class="tabbed-nav" data-ui-component="main-nav">
ou ainda um valor tipo namespace:
<ul class="tabbed-nav" data-ui-component="nav-main">
A implementação é basicamente uma preferência pessoal, mas o conceito ainda permanece: adicione qualquer significado útil ou específico por meio de um mecanismo que não iniba a capacidade de você e sua equipe reciclar e reutilizar o CSS.
Parece que você está gostando dessas diretizes…
Um tópico que, considerando-se o suporte dos navegadores atuais, é mais interessante do que importante é o desempenho do seletor. Isto é, a rapidez com que um navegador pode combinar os seletores que você escreve em CSS com os nós encontrados no DOM.
De um modo geral, quanto mais verboso um seletor é (ou seja, quanto mais partes componentes), mais lento ele é, por exemplo:
body.home div.header ul { }
…é muito menos eficiente que:
.primary-nav { }
Isso ocorre porque os navegadores leem os seletores CSS da direita para a esquerda. Um navegador lerá o primeiro seletor conforme descrito a seguir:
ul do DOM;.header;div que contém a classe .header;.home;.home está em um elemento body.O segundo seletor, em contraste, é de leitura imediata pelo navegador
.primary-nav.Para aumentar ainda mais o problema no exemplo mostrado usamos seletores descendentes (por exemplo: .foo
.bar {}). O resultado disso é que um navegador inicia a busca pela parte mais à direita do seletor (.bar) e continua procurando indefinidamente no DOM até encontrar a próxima parte (.foo). Isso pode significar necessidade de percorrer o DOM repetidamente até que uma correspondência seja encontrada.
Esta é apenas uma das razões pelas quais aninhar seletores com uso de pré-processadores é, em geral, uma falsa economia, pois além de tornar os seletores desnecessariamente mais específicos e criar dependência de local, também cria mais trabalho para o navegador.
Usar o seletor filho (por exemplo .foo > .bar {}) ), pode tornar o processo muito mais eficiente, porque isso exige que o navegador olhe apenas um nível acima no DOM e pare, independentemente de encontrar ou não uma correspondência.
Como os navegadores leem os seletores da direita para a esquerda, o seletor mais à direita costuma ser crítico para o desempenho de um seletor. Ele chamado de seletor chave.
O seletor mostrado a seguir pode parecer de alta performance à primeira vista. Ele usa um ID que é bom e rápido, e só pode haver um em uma página, então, certamente, será uma pesquisa rápida, apenas encontre um ID e depois estilize tudo dentro dele:
#foo * { }
O problema com esse seletor é que o seletor universal (*) é muito, muito abrangente. O que esse seletor realmente faz é encontrar todos os nós do DOM (inclusive os elementos <title>, <link>, <head> e todos os demais) e depois verificar se ele está contido em qualquer lugar e em qualquer nível dentro de #foo. Esse é um seletor muito ineficiente e deve ser evitado ou reescrito.
Felizmente, ao escrever seletores com boa intenção de seleção, estaremos evitando seletores ineficientes por padrão. É muito improvável que tenhamos seletores ávidos se estivermos escolhendo as coisas certas pelo motivo certo.
No entanto, a preocupação com o desempenho do seletor CSS não deve ser uma prioridade alta na lista de coisas para otimizar, pois os navegadores estão cada vez mais rápidos e somente em casos muito específicos seletores ineficientes podem ser um problema.
Assim como os problemas específicos do seletor em si, o aninhamento, qualificação e baixa intenção de seleção também contribuem para seletores menos eficientes.
Os seletores são fundamentais para escrever CSS de alta qualidade. Resumindo as seções anteriores:
Focar nesses pontos manterá seus seletores muito mais sadios e fáceis de trabalhar principalmente em projetos sujeitos a mudanças e de longa duração.
É sabido que CSS não é uma linguagem amigável. Ela aplica estilos globalmente, é muito permeável, dependente do local onde inserida, difícil de encapsular, baseada em herança… Mas, nada disso chega perto dos horrores da especificidade.
Não importa o quanto você se esforçou para criar uma nomenclatura consistente, independentemente de quão perfeito seja o gerenciamento da ordem de escrita das regras CSS, dos efeitos cascata, e de quão bem você delimitou seus conjuntos de regras, apenas um seletor excessivamente específico pode desfazer tudo. Especificidade vai contra a própria natureza do efeito cascata, da herança e da ordem de escrita das regras CSS.
O problema da especificidade é que ela estabelece precedentes e cria comportamentos que não podem ser simplesmente desfeitos. Observe a seguir um exemplo real pelo qual eu fui responsável há alguns anos:
#content table { }
Esse seletor mostra pouca intenção de seleção, e é extremamente específico, pois minha intenção não era selecionar todos os elementos table contidos em #content, mas sim um tipo específico de elemento table ali contido. Isso ficou claro algumas semanas depois, quando eu precisei de um segundo tipo de table:
#content table { }
/**
* Uh oh! Meus novos estilos foram sobrescritos pelos do seletor
* '#content table {}'.
*/
.my-new-table { }
O primeiro seletor, por ser mais específico do que o segundo estava sobrescrevendo-o apesar deste ter sido declarado após aquele, contrariando a prioridade de aplicação de estilos baseada na ordem de declaração dos seletores. Para corrigir isso eu dispunha de duas opções:
Infelizmente, a refatoração demandaria muito tempo, pois tratava-se de um projeto maduro e os efeitos colaterais da remoção daquele ID poderiam ser mais danosos do que adotar a segunda opção que consistia em escrever um seletor mais específico.
#content table { }
#content .my-new-table { }
Agora eu tinha um seletor que era mais específico ainda! E se eu precisar sobrescrevê-lo, precisarei de outro seletor com pelo menos a mesma especificidade e definido depois dele. Eu iniciei um processo de seleção como se fosse uma espécie de espiral descendente.
A especificidade em CSS pode, entre outras coisas:
Todos esses problemas são multiplicados quando se trabalha em um projeto maior com vários desenvolvedores trabalhando no código.
O problema com a especificidade não é, necessariamente, o fato de ela ser alta ou baixa, mas o fato de ela ser diversificada e de não poder ser excluída. A única maneira de lidar com isso é torná-la progressivamente mais específica. Vide os casos de especificidade típicos que mostramos anteriormente.
Uma dica simples para facilitar a escrita de CSS, particularmente em qualquer nível de projeto, é manter sempre a especificidade a mais baixa possível em qualquer situação. Assegure que não haja muita variação entre os seletores na sua base de código e que todos os seletores foram escritos para serem o menos específico possível.
Fazer isso resulta em facilitar o gerenciamento do seu projeto, o que significa que nenhum seletor excessivamente específico irá impactar ou afetar qualquer coisa de menor especificidade em outro lugar. Isso também significa que é menos provável que você precise resolver conflitos de especificidade, e provavelmente estará escrevendo folhas de estilo muito menores.
Mudanças simples na maneira como trabalhamos incluem, mas não se limitam a:
A especificidade pode ser discutida e entendida, mas é mais seguro simplesmente evitá-la.
Se quisermos manter a especificidade baixa, e isso é o que desejamos, siga uma regrinha simples e fácil de implementar: evite usar IDs no CSS.
Não apenas os IDs são de natureza não reutilizáveis, mas também são muito mais específicos do que qualquer outro seletor e, portanto, podem criar anomalias de especificidade. Enquanto o restante de seus seletores tem especificidade relativamente baixa, seus seletores baseados em ID são, comparativamente, de especificidade muito, muito mais alta.
Para bem entender a gravidade desse aspecto da especificidade, criei um exemplo real mostrando que mil classes encadeadas não sobrepõem a especificidade de um único ID, veja o exemplo em: jsfiddle.net/0yb7rque. (Por favor, note que no Firefox você pode ver a renderização do texto em azul: este é um bug conhecido , e um ID será substituído por 256 classes encadeadas.)
N.B. Convém notar que não existe qualquer restrição ao uso de ID's na marcação HTML e em JS. Somente em CSS seu uso deve ser evitado.
É comum considerar que os desenvolvedores que optam por não usar IDs no CSS simplesmente não entendem como a especificidade funciona
. Essa é uma suposição equivocada e ofensiva, pois não importa o quão experiente você seja como desenvolvedor, esse comportamento não pode ser contornado e nenhum nível de conhecimento, por mais alto que seja, conseguirá projetar um ID menos específico.
Trabalhar com ID's no CSS cria a possibilidade de ocorrerem problemas mais adiante, e particularmente quando se trabalha em projetos escaláveis, todos os esforços devem ser feitos para evitar a possibilidade de surgirem problemas. Resumindo:
Não vale a pena correr o risco.
Já vimos como o aninhamento pode levar a um código dependente de localização e potencialmente ineficiente, mas agora é hora de dar uma olhada em outra de suas armadilhas; ele torna os seletores mais específicos.
Quando falamos de aninhamento, não necessariamente queremos dizer aninhamento de pré-processador conforme mostrado a seguir:
.foo {
.bar { }
}
Na verdade, estamos falando de seletores descendentes ou filhos, seletores que dependem de uma coisa dentro de uma coisa, tal como mostrado nos exemplos a seguir:
/**
* Um elemento com a classe '.bar' descendente de um elemento com a classe '.foo'
*/
.foo .bar { }
/**
* Um elemento com a classe '.module-title' que seja elemento-filho de um
*elemento com a classe '.module'.
*/
.module > .module-title { }
/**
* Elemento 'li' descendente de elemento 'ul' que é descendente de elemento 'nav'
*/
nav ul li { }
Quer seu CSS seja gerado por um pré-processador ou não, isso não é importante, convem enfatizar que aninhamento é um recurso nativo dos pré-processadores e deve ser evitado sempre que possível.
De um modo geral, cada parte componente de um seletor composto adiciona especificidade. Logo, quanto menos partes forem necessárias para criar um seletor composto, menor será sua especificidade e sempre queremos manter a especificidade baixa. Citando Jonathan Snook:
…para estilizar um elemento use o menor número de seletores possível.
Observe o exemplo a seguir:
.widget {
padding: 10px;
}
.widget > .widget__title {
color: red;
}
Para estilizar um elemento com a classe .widget__title temos um seletor duas vezes mais específico do que o necessário. Isso significa que, se quisermos fazer modificações de estilo em .widget__title precisaremos de outro seletor pelo menos igualmente específico:
.widget { ... }
.widget > .widget__title { ... }
.widget > .widget__title--sub {
color: blue;
}
Nós mesmos criamos o problema escrevendo um seletor que tem literalmente o dobro da especificidade que precisaria ter e isso é totalmente evitável. Usamos 200% da especificidade realmente necessária. E não apenas isso, pois também criamos verbosidade desnecessária em nosso código, ou seja, mais código desnecessário.
Como regra geral, se um seletor funciona sem necessidade de aninhamento não o aninhe..
Uma possível vantagem do aninhamento, que infelizmente não compensa e nem sobrepõe as desvantagens do aumento da especificidade, é que ele nos fornece um espécie de namespace. Um seletor tal como .widget .title cria um escopo de estilo para elementos com a classe .title que estejam contidos dentro de elementos com a classe .widget.
Isso de certa forma fornece ao nosso CSS um escopo e um encapsulamento, mas nossos seletores serão duas vezes mais específicos do que eles precisariam ser. Uma maneira melhor de criar esse escopo seria através de namespaces tal como mostrado na seção BEM-like Naming, maneira que não levaria a um aumento desnecessário da especificidade.
Assim é possível estabelecer um escopo melhor para o CSS e ao mesmo tempo com especificidade mínima, ou seja, o melhor dos dois mundos.
!importantA diretiva CSS !important causa arrepios na espinha de quase todos os desenvolvedores frontend. !important é uma espécie de reconhecimento explícito de que estamos enfrentando problemas com a especificidade, na verdade trata-se de uma forma de resolver problemas de especificidade, mas geralmente pagando-se um preço alto. É frequentemente visto como um último recurso, uma tentativa desesperada de corrigir as causas de um problema muito maior com o código.
A regra geral é que !important é sempre uma coisa ruim e aqui vale citar Jamie Mason:
Regras são filhas de princípios.
Ou seja, uma única regra é a maneira mais simples de se aderir a um princípio muito maior. Quando você está começando, a regra nunca use
é uma boa regra a seguir.!important
No entanto, quando você começa a crescer e amadurecer como desenvolvedor, começa a entender que o princípio por trás dessa regra é simplesmente manter baixa a especificidade. Você também aprenderá quando e onde as regras podem ser quebradas ...
A diretiva !important pode sim ser usada em projetos CSS, mas com parcimônia e proatividade.
Usar !important proativamente significa usar antes de você encontrar algum problema de especificidade, usar como garantia e não como uma correção. Por exemplo:
.one-half {
width: 50% !important;
}
.hidden {
display: none !important;
}
Essas duas classes auxiliares ou utilitárias são muito específicas em suas intenções. Você as usaria apenas se quisesse que algo fosse renderizado com largura de 50% ou não fosse renderizado. Se você não quisesse esses comportamentos, você não usaria essas classes, portanto, sempre que usá-las, você definitivamente quer que elas sejam aplicadas em quaisquer circunstâncias.
Neste caso aplicamos proativamente !important para garantir que esses estilos sempre sejam aplicados. Esse é o uso correto de !important garantindo a aplicação da regra e que ela não seja acidentalmente substituída por outra mais específica.
O uso incorreto e reativo da diretiva !important é quando ela é empregada para corrigir problemas de especificidade depois que eles ocorrem, ou ainda, declarar !important em consequência de uma arquitetura CSS mal planejada. Considere a marcaçã HTML mostrada a seguir:
<div class="content">
<h2 class="heading-sub">...</h2>
</div>
…à qual se aplica a seguinte CSS:
.content h2 {
font-size: 2em;
}
.heading-sub {
font-size: 1.5em !important;
}
Aqui usou-se !important para forçar reativamente a estilização dos elementos com a classe .heading-sub {} sobrescrevendo os estilos definidos pelo seletor .content h2 {}. Isso poderia ter sido contornado de diferentes maneiras, incluindo o uso de uma melhor intenção de seleção, ou evitando o aninhamento.
Nestas situações é preferível que você analise e refatore regras CSS mal definidas com a finalidade de diminuir sua especificidade, em vez de introduzir esses pesos pesados de especificidade.
Use !important de forma proativa, não de forma reativa.
Considerando tudo o que foi dito sobre a especificidade e mantendo-a baixa, é inevitável que encontremos problemas. Não importa o quanto tentemos, e quão conscientes nós somos, sempre haverá momentos em que precisaremos hackear a especificidade.
Quando tais situações surgirem, é importante lidarmos com os hacks da maneira mais segura e elegante possível.
Você dispõe de várias opções caso precise aumentar a especificidade de um seletor classe. Poderá aninhar a classe dentro de outro seletor para elevar sua especificidade. Por exemplo, poderíamos usar o seletor .header .site-nav {} para aumentar a especificidade do seletor simples .site-nav {}.
O problema com essa solução, como já discutimos, é que ela introduz a dependência de localização, pois os estilos só serão aplicados quando o componente .site-nav estiver dentro do componente .header.
Em vez disso, podemos usar um hack muito mais seguro que não impactará a portabilidade desse componente encadeando essa classe consigo mesma:
.site-nav.site-nav { }
Esse encadeamento duplica a especificidade do seletor, mas não introduz nenhuma dependência de localização.
Caso, por qualquer razão, tenhamos um ID em nossa marcação que não possa ser substituido por uma classe, selecione-o por meio de um seletor de atributos em vez de um seletor de ID. Por exemplo, vamos imaginar que incorporamos um widget de terceiros em nossa página. Podemos estilizar o widget por meio da marcação que ele gera, mas não podemos editá-lo:
<div id="third-party-widget">
...
</div>
Mesmo sabendo que não usamos IDs em CSS, que outra opção nós temos? Queremos estilizar a marcação HTML, mas não temos acesso a ela, e tudo o que ela contém é um ID.
Podemos usar o seletor de atributo conforme mostrado a seguir:
[id="third-party-widget"] { }
Estamos selecionando com base em um atributo em vez de um ID, e os seletores de atributo têm a mesma especificidade de uma classe. Isso nos permite estilizar com base em um ID, mas sem introduzir sua especificidade.
Tenha em mente que o que acabamos de ver são hacks e não devem ser usados a não ser que não haja uma alternativa melhor.
Seria razoável você pensar que uma arquitetura CSS é um conceito um tanto grandioso e desnecessário. Por que algo tão simples, tão direto, precisa de algo tão complexo como ser tratado como uma arquitetura ?!
Bem, como vimos, a simplicidade do CSS, sua solidez e sua natureza indisciplinada significam que a melhor maneira de gerenciar (ler, domesticar) em qualquer escala razoável é por meio de uma arquitetura rígida e específica. Uma arquitetura sólida pode nos ajudar a controlar a especificidade, impor convenções de nomenclatura, gerenciar a ordem de origem das regras CSS, criar um ambiente de desenvolvimento sensato e, geralmente, tornar o gerenciamento dos projetos CSS muito mais consistente e confortável.
Não há ferramenta, pré-processador ou mágica, que torne seu CSS melhor por conta própria. A melhor ferramenta de um desenvolvedor ao trabalhar com uma sintaxe tão solta é a autodisciplina, consciência, diligência e uma arquitetura bem definida com a finalidade de facilitar a aplicação dessas características.
Arquiteturas são coleções grandes, abrangentes e guiadas por princípios de convenções menores, que reunidas criam um ambiente gerenciável no qual o código é escrito e mantido. As arquiteturas normalmente são de alto nível e delegam os detalhes de implementação, tais como como convenções de nomenclatura ou sintaxe e formatação, por exemplo, para a equipe que está implementando-a.
A maioria das arquiteturas geralmente se baseia em padrões e paradigmas de design existentes e, na maioria das vezes, esses paradigmas nasceram de cientistas da computação e engenheiros de software. Para eles CSS não é 'código', e não tem muitas das características das linguagens de programação. Nós achamos que podemos aplicar alguns destes mesmos princípios ao nosso próprio trabalho.
Na próxima seção, veremos alguns desses padrões e paradigmas de design e como podemos usá-los para reduzir o código e aumentar a reutilização de código em nossos projetos CSS.
Em alto nível uma arquitetura deverá ajudá-lo a:
Normalmente, isso resultará em uma arquitetura baseada em classes, componentizada e dividida em módulos gerenciáveis, provavelmente usando um pré-processador. Claro, há muito mais em uma arquitetura do que isso, então vamos ver alguns princípios ...
Orientação a objetos é um paradigma de programação que divide programas maiores em objetos menores e in(ter)dependentes que têm seus próprios papéis e responsabilidades. Da Wikipedia:
A programação orientada a objeto (OOP) é um paradigma de programação que representa o conceito de 'objetos' […], que são geralmente instâncias de classes, [e] que são usados para interagir uns com os outros para projetar aplicativos e programas de computador.
Quando aplicado ao CSS, nós o chamamos de CSS orientado a objeto, ou OOCSS. OOCSS foi cunhado e popularizado por Nicole Sullivan, cujo Media Object se tornou o exemplo da metodologia.
OOCSS lida com a separação das camadas dos integrantes da interface de usuário em estrutura e skin (aspecto ou apresentação), dividindo os componentes da interface em suas formas estruturais e suas formas cosméticas, ou de apresentação, separadamente. Isso significa que podemos reciclar padrões de projeto comuns e recorrentes de maneira muito fácil, sem precisar necessariamente reciclar seus detalhes de implementação específicos ao mesmo tempo. O OOCSS promove a reutilização de código, o que resulta em desenvolvimento mais rápido, além de manter baixo o tamanho da nossa base de código.
Aspectos estruturais podem ser pensados como esqueletos, quadros comuns e recorrentes que fornecem construções livres de design conhecidas como objetos e abstrações. Objetos e abstrações são padrões de design simples, desprovidos de cosméticos. Abstraímos os traços estruturais compartilhados de uma série de componentes em um objeto genérico.
Skin é uma camada que nós (opcionalmente) adicionamos à estrutura com a finalidade de definir para os objetos e abstrações uma aparência específica. Vejamos um exemplo:
/**
* Um objeto botão sem estilização. Extenda esse objeto com a classe '.btn--*'
* para criar skins.
*/
.btn {
display: inline-block;
padding: 1em 2em;
vertical-align: middle;
}
/**
* Botões com skin 'positivo'. Extende '.btn'.
*/
.btn--positive {
background-color: green;
color: white;
}
/**
* Botões com skin 'negativo'. Extende '.btn'.
*/
.btn--negative {
background-color: red;
color: white;
}
No exemplo mostrado a classe .btn {} simplesmente fornece um estilo estrutural a um elemento e não se preocupa com nenhum cosmético ou apresentação. Complementamos o objeto .btn {} com uma segunda classe denominada .btn--negative {} com a finalidade de definir para o objeto um estilo específico. Observe o exemplo a seguir
<button class="btn btn--negative">Delete</button>
Essa abordagem de criar várias classes para usar em sua marcação, em vez de agrupar as classes em uma só usando um pré-processador tem as seguintas vantagens:
Sempre que você estiver construindo um componente de UI, tente dividi-lo em duas partes: uma para estilos estruturais (preenchimentos, layout, etc.) e outra para skin (cores, tipos, etc.).
O princípio da responsabilidade única é um paradigma que, em caráter geral, afirma que todos os trechos de código (no nosso caso, classes) devem se concentrar em fazer uma coisa e apenas uma coisa. Mais formalmente pode-se dizer:
…o princípio da responsabilidade única afirma que todo contexto (classe, função, variável, etc.) deve ter uma única responsabilidade, e que a responsabilidade deve ser totalmente encapsulada no contexto.
O significado deste princípio é que nosso CSS deve ser composto de uma série de classes muito menores que se concentram em fornecer funcionalidades muito específicas e limitadas. Isso significa que precisamos decompor UIs em suas partes menores, cada uma delas com uma única responsabilidade. Todas essas partes fazem apenas um trabalho que podem ser facilmente combinados e compostos para criar construções muito mais versáteis e complexas. Vejamos a seguir um exemplo de CSS que não segue o princípio da responsabilidade única:
.error-message {
display: block;
padding: 10px;
border-top: 1px solid #f00;
border-bottom: 1px solid #f00;
background-color: #fee;
color: #f00;
font-weight: bold;
}
.success-message {
display: block;
padding: 10px;
border-top: 1px solid #0f0;
border-bottom: 1px solid #0f0;
background-color: #efe;
color: #0f0;
font-weight: bold;
}
Nesse exemplo observe que apesar de terem sido nomeadas para uso muito específico, as duas classes estão definindo estilos para vários aspectos: layout, estrutura e cosméticos. Além disso há também muita repetição de código. Precisamos refatorar isso para abstrair alguns objetos compartilhados (OOCSS) e adequarmos ao princípio da responsabilidade única. Podemos dividir essas duas classes em quatro , cada uma com responsabilidade muito menor:
.box {
display: block;
padding: 10px;
}
.message {
border-style: solid;
border-width: 1px 0;
font-weight: bold;
}
.message--error {
background-color: #fee;
color: #f00;
}
.message--success {
background-color: #efe;
color: #0f0;
}
Agora nós temos uma abstração geral para boxes que pode ser usada, completamente separada do nosso componente para mensagens, e um componente para mensagem que pode ser estendido por várias classes, cada uma com sua responsabilidade específica e menor. A quantidade de repetições de código foi bastante reduzida e nossa capacidade de ampliar e compor nosso CSS aumentou consideravelmente. Este é um ótimo exemplo do OOCSS e do princípio de responsabilidade única trabalhando em conjunto.
Concentrando-nos em responsabilidades únicas, podemos dar muito mais flexibilidade ao nosso código, e a extensão das funções dos componentes torna-se muito simples quando nos atemos ao princípio aberto/fechado, que iremos analisar a seguir..
Na minha opinião a designação princípio aberto/fechado, foi uma má escolha de nome. Digo que foi uma má escolha de nome porque 50% do que prescreve esse princípio não se intui da simples leitura desse nome. Esse princípio afirma que:
entidades de [s]oftware (classes, módulos, funções, etc.) devem ser abertas para extensão e fechadas para modificação.
Viu? As palavras mais importantes — extensão e modificação — estão completamente ausentes no nome do princípio, o que afinal, não é muito útil.
Uma vez que você se lembre a que as palavras abrir e fechar realmente se referem, constatará que o princípio aberto/fechado é notavelmente simples. Qualquer acréscimo, nova funcionalidade ou recursos que adicionarmos às nossas classes devem ser adicionados por extensão, pois não se deve modificar classes diretamente. Isso nos acostumará a escrever responsabilidades únicas à prova de balas. Como não devemos modificar objetos e abstrações diretamente, precisamos nos assegurar de que elas sejam projetadas, desde seu início, de maneira mais simples possível. Isso significa que nunca devemos realmente mudar uma abstração, simplesmente paramos de usá-la, mas qualquer pequena variação dela pode ser criada facilmente estendendo-a.
Vejamos um exemplo:
.box {
display: block;
padding: 10px;
}
.box--large {
padding: 20px;
}
Nesse exemplo note que o objeto .box {} é incrivelmente simples. Ele foi criado com uma responsabilidade muito pequena e muito focada. Para modificar esse objeto, nós o estendemos com outra classe .box--large {}. Aqui a classe .box {} está fechada para modificação, mas aberta para ser estendida.
Uma maneira errada de se projetar o código mostrado anteriormente é a seguinte:
.box {
display: block;
padding: 10px;
}
.content .box {
padding: 20px;
}
Nesse exemplo não só criou-se um seletor excessivamente específico, dependente de localização, com fraca intenção seletora, como estamos modificando o objeto .box {} diretamente. É muito raro, ou nunca deveríamos projetar um objeto ou classe de abstração com uso de um seletor composto.
Seletor, tal como .content .box {} é potencialmente problemático porque:
.box quando inserido dentro de componentes .content será estilizado de forma rígida no sentido em que não haja opção de escolha pelo desenvolvedor e o desejável é que ao desenvolvedor seja dada a opção de optar por modificações explicitamente;.box é imprevisivel, pois o princípio da responsabilidade simples foi quebrado uma vez que seletores aninhados produzem estilização forçada.Todas as modificações, acréscimos e alterações devem ser sempre 'opt-in' e não obrigatórias. Se você constatar que algo precisando de um pequeno ajuste irá tirá-lo da norma, crie outra classe que adicione o pequeno ajuste.
Ao trabalhar em equipe, certifique-se de escrever CSS em conformidade com a API, sempre garanta que as classes existentes permaneçam compatíveis com versões anteriores (ou seja, sem alterações na raiz) e crie novos seletores para introduzir novos recursos. Alterar o objeto raiz, a abstração ou o componente pode causar enormes efeitos secundários para os desenvolvedores que usam esse código em outro lugar, portanto, nunca modifique diretamente o código existente.
Exceções podem ser o caso quando se verifica que um objeto raiz precisa ser reescrito ou refatorado, mas é somente nesses casos específicos que você deve modificar o código. Lembre-se: aberto para extensão e fechado para modificação.
DRY é a sigla para Don’t Repeat Yourself (em tradução livre: não se repita) é um microprincípio usado em desenvolvimento de software que visa manter a repetição de informações chave em um nível mínimo. Sua definição formal é que:
em um sistema, cada porção de conhecimento deve ter uma representação única, não ambígua e autoral.
Embora se trate de um princípio muito simples o DRY muitas vezes é, mal interpretado, como sendo a necessidade de nunca repetir exatamente a mesma coisa em um projeto. Isso é impraticável e geralmente contraproducente podendo levar a abstrações forçadas, a códigos excessivamente pensados e com engenharia e dependências incomuns.
A chave não é evitar toda repetição, mas sim normatizar e abstrair a repetição relevante. Se duas coisas compartilham as mesmas declarações de forma coincidente, não precisamos tirar nada, pois trata-se de repetição puramente circunstancial que não pode ser compartilhada ou abstraída. Por exemplo:
.btn {
display: inline-block;
padding: 1em 2em;
font-weight: bold;
}
[...]
.page-title {
font-size: 3rem;
line-height: 1.4;
font-weight: bold;
}
[...]
.user-profile__title {
font-size: 1.2rem;
line-height: 1.5;
font-weight: bold;
}
Observando o código mostrado, podemos deduzir que a declaração font-weight: bold; aparece três vezes puramente por coincidência. Criar uma abstração, mixagem ou extensão para eliminar essa repetição seria um exagero e uniria os três conjuntos de regras com base puramente nas circunstâncias.
Suponha que estamos usando uma web-font que requer a declaração font-weight: bold; toda vez que ela for declarada em font-family. Observe a seguir:
.btn {
display: inline-block;
padding: 1em 2em;
font-family: "My Web Font", sans-serif;
font-weight: bold;
}
[...]
.page-title {
font-size: 3rem;
line-height: 1.4;
font-family: "My Web Font", sans-serif;
font-weight: bold;
}
[...]
.user-profile__title {
font-size: 1.2rem;
line-height: 1.5;
font-family: "My Web Font", sans-serif;
font-weight: bold;
}
Estamos repetindo um trecho significativo das CSS, as duas declarações devem sempre ser declaradas juntas. Neste exemplo, provavelmente nós aplicaríamos o princípio DRY na nossa CSS.
Eu recomendaria usar um mixin @extend porque, embora as duas declarações sejam agrupadas tematicamente, os conjuntos de regras em si são entidades separadas e não relacionadas: usar @extend implicaria em agrupar fisicamente esses conjuntos de regras não relacionadas em nosso CSS, transformando o não relacionado em relacionado.
Nosso mixin:
@mixin my-web-font() {
font-family: "My Web Font", sans-serif;
font-weight: bold;
}
.btn {
display: inline-block;
padding: 1em 2em;
@include my-web-font();
}
[...]
.page-title {
font-size: 3rem;
line-height: 1.4;
@include my-web-font();
}
[...]
.user-profile__title {
font-size: 1.2rem;
line-height: 1.5;
@include my-web-font();
}
Agora as duas declarações só aparecem uma vez, o que significa que não estamos repetindo. Se alterarmos a nossa web-font, ou mudarmos para uma versão com font-weight: normal;, precisaremos fazer a alteração em um único local.
Resumindo, aplique o princípio DRY apenas em código relacionado tematicamente. Não tente reduzir a repetição que seja puramente coincidente, a duplicação é melhor que a abstração errada.
Agora que aprendemos a identificar abstrações e criar responsabilidades únicas, estamos em uma ótima posição para começar a construir composições mais complexas a partir de componentes muito menores. Nicole Sullivan compara esse processo a usar o Lego, peças minúsculas e de responsabilidade única que podem ser combinadas em várias quantidades e permutações diferentes para criar uma infinidade de resultados com aparências muito diferentes.
Essa idéia de construir compondo não é nova, e é freqüentemente chamada de composição sobre herança. Esse princípio sugere que sistemas maiores devem ser compostos de partes individuais muito menores em vez de herdarem o comportamento de um objeto monolítico muito maior. Isso deverá manter seu código desacoplado, pois nada inerentemente depende de qualquer outra coisa.
A composição é um princípio muito valioso para se usar em uma arquitetura, em particular considerando-se a mudança para UIs baseadas em componentes. Isso significa que você pode reciclar e reutilizar funcionalidades com mais facilidade, bem como construir rapidamente partes maiores da interface do usuário a partir de um conjunto conhecido de objetos compostos. Lembre-se do exemplo de mensagem de erro que mostramos na seção Princípio da responsabilidade única. Criamos um componente completo da interface do usuário compondo vários objetos muito menores e não relacionados.
O princípio da separação de responsabilidades em um primeiro momento pode soar parecido com o princípio da responsabilidade única. Esse princípio afirma que o código deve ser quebrado
em seções distintas, de modo que cada seção se encarregue de uma responsabilidade separadamente. Uma responsabilidade é um conjunto de informações que afetam o código de um programa de computador. […] Um programa que incorpora SoC é chamado de programa modular.
Modular é uma palavra com a qual, provavelmente, estamos acostumados. A ideia geral é quebrar UIs e CSS em pedaços muito menores e compostos. A separação de responsabilidade é apenas uma definição formal que abrange os conceitos de modularidade e encapsulamento do código. Em CSS, isso significa construir componentes individuais e escrever códigos que só se concentram em uma tarefa por vez.
O termo foi cunhado por Edsger W. Dijkstra, que disse:
Deixe-me tentar explicar para você, o que na minha avaliação é característico de todo pensamento inteligente. Alguém que se dispõe a estudar com profundidade isoladamente um dos aspectos de um assunto tem consciência o tempo todo que está se ocupando apenas com um dos aspectos. Sabemos que um programa deve estar correto e podemos estudá-lo somente sob este ponto de vista, sabemos também que o programa deve ser eficiente e podemos estudar sua eficiência em outro dia. Em outro contexto, podemos nos perguntar se, e em caso afirmativo: por que, o programa é desejável. Mas nada é ganho — pelo contrário! — abordando esses vários aspectos simultaneamente. É o que eu às vezes chamo de "separação das responsabilidades", a qual, mesmo que não seja perfeitamente possível, é a única técnica disponível para a ordenação efetiva dos pensamentos, que eu conheço. É isso que quero dizer com "concentrar a atenção em algum aspecto": isso não significa ignorar os outros aspectos, é apenas fazer justiça ao fato de que, do ponto de vista do aspecto estudado, o outro é irrelevante. É algo como ocupar uma, e múltiplas, pistas simultaneamente.
A ideia aqui é focar completamente em uma coisa de cada vez. Construa uma coisa para fazer o seu trabalho muito bem, prestando a menor atenção possível a outras facetas do seu código. Depois de abordar e criar todas as diferentes responsabilidades isoladamente — o que significa que elas serão muito modulares, dissociadas e encapsuladas — você pode começar a reuni-las em um projeto maior.
Um ótimo exemplo é o layout. Se você estiver usando um sistema de grid, todo o código pertencente ao layout deve existir por si mesmo, sem incluir mais nada. Você escreveu um código que lida com o layout e pronto:
<div class="layout">
<div class="layout__item two-thirds">
</div>
<div class="layout__item one-third">
</div>
</div>
A seguir você precisará escrever um código novo e separado para lidar com o que está dentro do layout:
<div class="layout">
<div class="layout__item two-thirds">
<section class="content">
...
</section>
</div>
<div class="layout__item one-third">
<section class="sub-content">
...
</section>
</div>
</div>
A separação de responsabilidades permite que você mantenha o código autosuficiente, ignorante e, em última instância, muito mais sustentável. O código que adere à separação de responsabilidades pode ser modificado, editado, ampliado e mantido com muito mais segurança, porque sabemos até que ponto suas responsabilidades chegam. Sabemos que modificar o layout, por exemplo, só modificará o layout e nada mais..
A separação de responsabilidades aumenta a capacidade de reutilização e a confiança, reduzindo a dependência.
Há, eu sinto, uma série de equívocos infelizes em torno da separação de responsabilidades quando aplicados a HTML e as CSS. Todos eles parecem girar em torno de que:
O uso de classes na marcação, para servir às CSS, quebra o princípio de separação das responsabilidades.
Infelizmente, essa concepção simplesmente não é verdadeira. A separação de responsabilidades existe no contexto da HTML e CSS (e JS), mas não da forma como muitas pessoas acreditam.
A separação de responsabilidades quando aplicada ao código frontend não é sobre a quebra do princípio da separação de responsabilidades quando se usa classes em HTML puramente para estilizar, mas é sobre o fato de que estamos usando linguagens diferentes para marcação e estilo.
Antes que as CSS fossem amplamente adotadas, usávamos tables para posicionar conteúdo e elementos font com atributo color para estilização cosmética. O problema aqui é que a HTML estava sendo usada para criar conteúdo e também para estilizá-lo, pois um não era possível sem o outro. Isso sim era uma completa falta de separação de responsabilidades e que foi um problema. O trabalho das CSS era fornecer uma sintaxe completamente nova para aplicar estilos, permitindo que separássemos as responsabilidades de conteúdo e estilização entre duas tecnologias.
Outro argumento comum é que colocar classes na HTML coloca informações de estilo na marcação
.
Então, em uma tentativa de contornar isso, as pessoas adotam seletores que se parecem como os mostrados a seguir:
body > header:first-of-type > nav > ul > li > a {
}
Esse CSS — presumivelmente para estilizar a navegação principal do site — tem os problemas usuais de dependência de localização, baixa seletividade e alta especificidade, mas também consegue fazer exatamente o que os desenvolvedores estão tentando evitar, pois coloca informações do DOM nas CSS. Tentativas agressivas de evitar colocar dicas de estilo e hooks na marcação apenas sobrecarregam as folhas de estilo com informações do DOM.
Resumindo: usar classes na marcação não viola a separação de responsabilidades. As classes agem como uma API para vincular duas responsabilidades distintas. A maneira mais simples de separar as responsabilidades é escrever HTML e CSS bem formados e vincular os dois com uso sensato e criterioso de classes.
Precisa de ajuda com alguma coisa?