Sass Guidelines

Um guia de estilo opinado para escrever Sass com pés e cabeça, escalável e de fácil manutenção.

You are viewing the Portuguese translation by Ricardo Magalhães, Gonçalo Morais, Sara Vieira, Eduardo Bouças of the original Sass Guidelines from Hugo Giraudel.

This version is exclusively maintained by contributors without the review of the main author, therefore might not be completely up-to-date, especially since it is currently in version 1.1 while the English version is in version 1.3.

Abra o painel de opções

Sobre o autor

O meu nome é Hugo Giraudel e sou um Programador Front End francês atualmente em Berlim, Alemanha. Escrevo Sass já há mais de dois anos e sou também autor de vários projetos relacionados com Sass, tais como o SassDoc e Sass-Compatibility. Escrevi também um livro sobre CSS (em francês) intitulado CSS3 Pratique du Design Web.

Escrevi também algumas bibliotecas de Sass, algumas delas simplesmente por diversão: SassyJSON, SassyLists, SassySort, SassyCast, SassyMatrix, SassyBitwise, SassyIteratorsGenerators, SassyLogger, SassyStrings e SassyGradients.

Contribuir

Sass Guidelines é um projecto sem fins lucrativos que mantenho no meu tempo livre. Escusado será dizer que é necessário uma generosa quantidade de tempo para o manter sempre actualizado, bem documentado e relevante. Obviamente, saber que gostaram deste guia já é muito bem vindo!

Se quiserem contribuir, lembrem-se que tweetar, espalhar a palavra ou corrigir pequenos erros abrindo um issue ou criando pull requests no repositório de Github, seria excelente!

Por último, antes de começarmos: se gostaram deste documento ou se o consideraram com valor pessoal ou para a vossa equipa, por favor considerem apoiá-lo!

Sobre Sass

Esta é forma como Sass se auto-descreve na sua própria documentação:

Sass é uma extensão de CSS que adiciona novas possibilidades e elegância à linguagem base.

O objectivo final de Sass é corrigir as falhas de CSS. CSS, como todos sabemos, não é a melhor linguagem no mundo [carece de fontes]. Enquanto é muito simples de aprender, pode rapidamente tornar-se uma confusão, especialmente em projectos grandes.

É aqui que Sass entra em cena, como meta-linguagem, para melhorar a sintaxe original de CSS de maneira a oferecer novas funcionalidades e ferramentas úteis. No entanto, Sass pretende manter-se algo conservadora no que diz respeito à linguagem de CSS.

A ideia não é tornar CSS numa linguagem de programação de alto nível; Sass pretende apenas oferecer ajuda onde CSS demonstra falhas. Graças a esta abordagem, começar a aprender Sass não é mais difícil que aprender CSS: simplesmente adiciona funcionalidades em cima da linguagem.

Dito isto, existem imensas formas de utilizar estas funcionalidades. Algumas são boas, outras más, outras pouco usuais. Este guia pretende providenciar uma forma documentada e consistente de escrever código em Sass.

Leitura adicional

Ruby Sass ou LibSass

O primeiro commit de Sass foi feito em 2006, já há mais de 8 anos. Escusado será dizer que evoluiu imenso desde então. Inicialmente desenvolvido utilizando Ruby, diversas adaptações foram surgindo. A mais bem sucedida, LibSass (written in C/C++) está hoje muito perto de ser totalmente compatível com a versão original.

Em 2014, ambas as equipas de Ruby Sass e LibSass decidiram esperar até que as versões se sincronizassem antes de continuar o desenvolvimento. Desde então, LibSass tem ativamente vindo a lançar versões com paridade de funcionalidades com a sua irmã mais velha. As últimas inconsistências estão a ser reunidas e listadas por mim no projeto Sass-Compatibility. Se estiverem a par de alguma incompatibilidade entre as duas versões que não esteja listada, por favor abram um issue no repositório.

Voltando à questão de escolher o compilador. Na verdade, tudo depende do vosso projeto. Se se tratar de um projecto em Ruby on Rails, será preferível utilizar Ruby Sass, já que é perfeitamente adequado para o caso. Convém manter em mente que Ruby Sass vai sempre ser a implementação de referência e por isso estará sempre um passo à frente no que diz respeito a funcionalidades.

Em projetos que não sejam em Ruby que precisem de integração no método de trabalho, LibSass será provavelmente uma melhor opção, uma vez que esta é praticamente dedicada a adaptações externas. Por exemplo, se quiserem utilizar NodeJS, node-sass, a escolha já está feita.

Leitura adicional

Sass ou SCSS

Existe alguma confusão no que diz respeito à semântica do nome Sass, e justificadamente: Sass significa tanto o pré-processador e a sua própria sintaxe. Não é lá muito conveniente, pois não?

Inicialmente, Sass descrevia a sintaxe cuja característica que a definia era a sua sensibilidade à indentação. Rapidamente, os escritores de Sass decidiram encurtar a distância entre Sass e CSS providenciando uma sintaxe mais próxima de CSS original chamada SCSS para Sassy CSS. O lema é: se é CSS válido, é valido SCSS também.

Desde então, Sass (enquanto pré-processador) oferece duas sintaxes diferentes: Sass (que não é escrito em maiúsculas, por favor), também conhecido como a sintaxe indentada, e SCSS. Qual delas utilizar é uma questão de preferência pessoal, uma vez que ambas são equivalentes a nível de funcionalidades. Até aqui, é tudo uma questão de estética.

A sensibilidade aos espaços em Sass depende na indentação para se ver livre de chavetas, pontos e vírgula e outros símbolos de pontuação, o que leva a uma sintaxe mais curta e mais limpa. Por outro lado, SCSS é mais fácil de aprender já que consiste maioritariamente em pedaços extra que sao escritos adicionalmente ao CSS habitual.

Pessoalmente, prefiro SCSS a Sass apenas porque é mais aproximada de CSS e mais amigável para os programadores. Por isso, vou utilizar SCSS em vez de Sass ao longo deste guia de estilo. Podem sempre alterar para a sintaxe indentada de Sass no painel de .

Leitura adicional

Outros pré-processadores

Entre outras coisas, Sass é um pré-processador. O seu principal rival será LESS, um pré-processador baseado em NodeJS que se tornou bastante popular graças à framework de CSS Bootstrap, que faz uso deste. Existe ainda o Stylus, que acaba por ser a versão mais nerdy e livre de LESS - onde é possível fazer tudo o que nós quisermos, já que praticamente converte CSS numa verdadeira linguagem de programação.

Porquê escolher Sass em vez de LESS ou outro processador qualquer? continua a ser uma questão válida. Não há muito tempo, era habitual recomendar Sass para projectos baseados em Ruby apenas porque foi o primeiro construído nesta mesma linguagem e andava de mãos dadas com Ruby on Rails. Agora que LibSass já está a par (maioritariamente) com o Sass original, esta recomendação não é mais relevante.

O que eu mais aprecio em Sass é a sua abordagem conservadora para com CSS. O design em Sass é fundamentado em princípios fortes: a grande parte da abordagem de design vem das crenças básicas das equipas que a) adicionar funcionalidades extra possui um custo de complexidade que precisa de ser justificado pela sua utilidade, e b) deve ser simples e intuitivo compreender o que um bloco de estilo é responsável por, olhando apenas para esse mesmo bloco individualmente. Sass possui também uma maior atenção ao detalhe relativamente a outros pré-processadores. Tanto quanto posso afirmar, os seus designers responsáveis preocupam-se bastante com todos os casos de compatibilidade com CSS e com a consistência de comportamento da linguagem.

Por outras palavras, Sass não é um pré-processador com a finalidade de agradar a programadores extremamente nerdy como eu, adicionando funcionalidades em cima de uma linguagem que não foi construída para as suportar de origem. É sim, um software com o intuito de resolver problemas reais; oferecendo funcionalidades úteis a CSS onde este geralmente está em falha.

Pré-processadores à parte, devemos também mencionar pós-processadores, que têm recebido exposição significativa nos últimos meses, maioritariamente graças ao PostCSS e cssnext. Pós-processadores são mais ou menos equivalentes aos pré-processadores, com a exceção que não oferecem nada mais do que sintaxe CSS que está ainda para chegar.

Podemos pensar em pré-processadors como um polyfill para funcionalidades de CSS não suportadas. Por exemplo, poderíamos escrever variáveis como estão descritas atualmente nas especificações de CSS, compilar as folhas de estilo com um pós-processador e reparar que todas as ocorrências dessas variáveis foram substituídas pelos seus devidos valores, como Sass faria.

A ideia por detrás dos pós-processadores é oferecer este tipo de suporte silencioso para funcionalidades novas ainda não suportadas por alguns browsers (como variáveis em CSS); assim que estes o façam, o pós-processador deixa as folhas de estilo em paz.

Enquanto que oferecer a sintaxe futura é uma ideia bastante nobre, pessoalmente continuo a preferir Sass para a maioria das tarefas. No entanto, existem certas ocasiões onde acredito que os pós-processadores são mais aptos que Sass - adicionar prefixos a CSS, por exemplo - mas lá chegaremos em breve.

Leitura adicional

Introdução

Porquê um guia de estilo?

Um guia de estilo não é só um documento agradável à leitura que pretende descrever um estado perfeito para o nosso código. Um guia de estilo é um documento importante no ciclo de vida de um projeto, descrevendo como e porquê o código deve ser escrito. Para pequenos projetos pode parecer excessivo, mas ajuda imenso à manutenção e escalabilidade do código-fonte.

Invariavelmente, quantos mais programadores estiverem envolvidos num projeto, mais regras serão necessárias. Da mesma forma, quanto maior o projeto, mais necessário se torna um guia de estilo.

Harry Roberts refere muito bem nas CSS Guidelines:

Um guia de estilo de código (e não um visual) é uma ferramenta de grande valor para equipas que:

  • criem e mantenham produtos durante uma quantidade de tempo considerável;
  • possuam programadores com habilidades e especialidades variadas;
  • possuam vários programadores a trabalhar num produto num dado momento;
  • recebam novos membros regularmente;
  • possuam várias bases de código que os programadores alternem;

Termos de Responsabilidades

Em primeiro lugar: isto não é um guia de estilo de CSS. Este documento não pretende discutir convenções de nomes para classes em CSS, padrões de módulos ou a questão de utilizar IDs no mundo de CSS. Estas guias dizem apenas respeito a conteúdo específico de Sass.

Para além disso, este guia de estilo é da minha autoria e consequentemente muito parcial. Podem pensar nele como uma coleção de metodologias e conselhos que eu tenho vindo a polir ao longo dos anos. Isto dá-me também a oportunidade de referenciar um punhado de recursos valiosos, por isso certifiquem-se que espreitam a secção de leitura adicional.

Obviamente, esta não será a única maneira de fazer as coisas e poderá ou não adequar-se ao vosso projeto. Sintam-se livres de o adaptar às vossas necessidades.

Princípios-chave

No final do dia, se há algum conceito que eu gostava que toda a gente retirasse, seria que Sass deve ser mantido o mais simples possível.

Graças às minhas experiências, tais como operadores binários, iteradores e geradores e um interpretador de JSON em Sass, temos todos uma boa ideia das capacidades deste pré-processador.

Apesar de tudo, CSS é uma linguagem simples. Sass, com o propósito de escrever CSS, não deverá tornar-se muito mais complexa que CSS normal. O princípio KISS (Keep It Simple Stupid) é chave, aqui, e deverá manter a precedência sobre o princípio DRY (Don’t Repeat Yourself) na maior parte das situações.

Por vezes é preferível repetirmo-nos um pouco para manter o código em boas condições de manutenção, em vez de construir um sistema desnecessariamente complicado, convulso e impossível de manter devido à sua complexidade.

Permitam-me ainda citar novamente o Harry Roberts, pragmatismo vence sobre a perfeição. A determinada altura, vão provavelmente dar por vocês a ir contra as regras aqui descritas. Se fizer sentido, se parecer a atitude correta, simplesmente façam-no. Código é o meio, e nunca o fim.

Leitura adicional

Formatação e sintaxe

Se me perguntarem a mim, diria que a primeira coisa que um guia de estilo deve ser capaz de nos dizer é descrição do aspecto visual que queremos para o nosso código.

Quando vários programadores estão responsáveis por escrever CSS simultaneamente nos mesmos projectos, é apenas uma questão de tempo até que um deles comece a escrever as coisas à sua maneira. Guias de estilo que promovam consistência não só previnem isto, mas ajudam também à leitura e manutenção do código.

Sucintamente, queremos que (desavergonhadamente inspirado nas CSS Guidelines):

  • dois (2) espaços de indentação, em vez de tabs;
  • idealmente, linhas de no máximo 80 caráteres
  • regras de CSS multi-linha devidamente escritas
  • uso significativo de espaço em branco
// Yep
.foo {
  display: block;
  overflow: hidden;
  padding: 0 1em;
}

// Nope
.foo {
    display: block; overflow: hidden;

    padding: 0 1em;
}
// Uma vez que Sass força estes padrões de indentação,
// Não há como errar aqui.
.foo
  display: block
  overflow: hidden
  padding: 0 1em

Não vamos entrar na discussão de organização de ficheiros nesta secção, mas discutiremos esse tópico na secção de arquitetura.

Strings

Acreditem ou não, strings desempenham um papel muito importante tanto em ecosistemas CSS e Sass. A maior parte dos valores de CSS são medidas ou strings (geralmente sem aspas), por isso é bastante crucial seguir algumas regras.

Codificação

Para evitar algum eventual problema com codificação de carateres, é recomendado forçar o modo UTF-8 na principal folhas de estilo, utilizando a directiva @charset. Certifiquem-se que é o primeiro elemento da folhas de estilo e que não existe mais nenhum caráter antes deste.

@charset 'utf-8';
@charset 'utf-8'

Aspas

CSS não necessita que strings apareçam entre aspas, nem mesmo as que contêm espaços. Peguemos no exemplo de font-family: não importa se utilizamos aspas no início e no fim.

Graças a isto, Sass também não necessita que as strings sejam entre aspas. Melhor ainda (e felizmente), uma string entre aspas é estritamente equivalente à sua irmã gémea sem aspas (por ex., 'abc' é estritamente igual a abc).

Dito isto, linguagens que não necessitam de aspas à volta de strings são uma minoria e, desta forma, strings devem sempre ser escritas com aspas curvas simples (também chamadas plicas) em Sass (as aspas simples ' são mais fáceis de escrever do que as duplas " em teclados qwerty). Para além de consistência com outras linguagens, incluindo JavaScript, existem outros motivos para esta escolha:

  • nomes de cores são tratados como cores quando não possuem aspas, o que pode levar a conflitos;
  • a maior parte dos highlighters de sintaxe dão problemas com strings sem aspas;
  • ajuda em geral à leitura;
  • não existe uma razão válida para não as usar;
// Yep
$direction: 'left';

// Nope
$direction: left;
// Yep
$direction: 'left'

// Nope
$direction: left

Strings como valores CSS

Valores específicos de CSS, tais como initial ou sans-serif não necessitam de aspas. É verdade que a declaração font-family: sans-serif vai falhar silenciosamente porque o CSS está à espera de encontrar um indentificador, não uma string envolta em aspas. Deste modo, não utilizamos aspas nestes valores.

// Yep
$font-type: sans-serif;

// Nope
$font-type: 'sans-serif';

// Okay I guess
$font-type: unquote('sans-serif');
// Yep
$font-type: sans-serif

// Nope
$font-type: 'sans-serif'

// Okay I guess
$font-type: unquote('sans-serif')

Desta forma, podemos fazer uma distinção entre strings que pretendemos utilizar como valores de CSS (identificadores) como no exemplo anterior, e strings quando nos referimos aos tipos de dados em Sass, como por exemplo índices de mapas.

Não utilizamos aspas no primeiro, mas no segundo exemplo utilizamos aspas simples.

Strings com aspas

Se uma string contém uma ou várias aspas, vale a pena considerar envolver a string com aspas duplas ("), de modo a evitar fazer o escaping de demasiados carateres dentro da string.

// Okay
@warn 'You can\'t do that.';

// Okay
@warn "You can't do that.";
// Okay
@warn 'You can\'t do that.'

// Okay
@warn "You can't do that."

URLs

URLs devem ser envolvidos em aspas:

// Yep
.foo {
  background-image: url('/images/kittens.jpg');
}

// Nope
.foo {
  background-image: url(/images/kittens.jpg);
}
// Yep
.foo
  background-image: url('/images/kittens.jpg')

// Nope
.foo
  background-image: url(/images/kittens.jpg)
Leitura adicional

Números

Em Sass, um número representa um tipo de dados que inclui tudo desde números sem unidades a medidas, a frequências, ângulos, e outros. Isto permite que cálculos sejam efetuados nestas medidas.

Zeros

Números devem mostrar zeros à esquerda da vírgula em valores abaixo de um (1). Nunca se deve mostrar zeros no final.

// Yep
.foo {
  padding: 2em;
  opacity: 0.5;
}

// Nope
.foo {
  padding: 2.0em;
  opacity: .5;
}
// Yep
.foo
  padding: 2em
  opacity: 0.5

// Nope
.foo
  padding: 2.0em
  opacity: .5

Unidades

Quando estamos a lidar com medidas, um valor 0 nunca deve ter unidade.

// Yep
$length: 0;

// Nope
$length: 0em;
// Yep
$length: 0

// Nope
$length: 0em

O erro mais comum que me consigo lembrar no que diz respeito a números em Sass é pensar que as unidades representam strings que podem ser adicionadas livremente a um número. Enquanto isto pode parecer correto, não é como as unidades funcionam. Pensem em unidades como símbolos algébricos. Por exemplo, no mundo real, multiplicar 5 centímetros por 5 centímetros resulta em 25 centímetros quadrados. A mesma lógica aplica-se em Sass.

Para adicionar uma unidade a um número, devemos multiplicar este número por 1 unidade.

$value: 42;

// Yep
$length: $value * 1px;

// Nope
$length: $value + px;
$value: 42

// Yep
$length: $value * 1px

// Nope
$length: $value + px

Reparem que adicionar 0 dessa mesma unidade também funciona, mas recomendo o primeiro método, uma vez que adicionar 0 unidades é algo confuso. Na verdade, quando tentamos converter um número para outra unidade comparável, adicionar 0 não irá funcionar.

$value: 42 + 0px;
// -> 42px

$value: 1in + 0px;
// -> 1in

$value: 0px + 1in;
// -> 96px
$value: 42 + 0px
// -> 42px

$value: 1in + 0px
// -> 1in

$value: 0px + 1in
// -> 96px

No final de contas, depende tudo do que estivermos a tentar obter. Lembrem-se apenas que adicionar o número-unidade como string não é uma boa prática.

Para remover a unidade de um valor, temos que dividi-lo por uma unidade do seu tipo.

$length: 42px;

// Yep
$value: $length / 1px;

// Nope
$value: str-slice($length + unquote(''), 1, 2);
$length: 42px

// Yep
$value: $length / 1px

// Nope
$value: str-slice($length + unquote(''), 1, 2)

Adicionar uma unidade como string a um número resulta numa string, prevenindo qualquer operação adicional no seu valor. Cortar a parte numérica de um número com uma unidade também resulta numa string, o que não é o desejado.

Cálculos

Cálculos numéricos de maior prioridade devem sempre estar presentes entre parênteses. Isto não só melhora significativamente a sua leitura, como evita que aconteçam alguns casos extremos como forçar Sass a avaliar e computar o conteúdo entre parênteses.

// Yep
.foo {
  width: (100% / 3);
}

// Nope
.foo {
  width: 100% / 3;
}
// Yep
.foo
  width: (100% / 3)

// Nope
.foo
  width: 100% / 3

Números mágicos

“Números mágicos” (magic numbers) dizem respeito a um termo antigo computacional para constante numérica não definida. Basicamente, é um número aleatório que simplesmente parece funcionar por magia num caso específico e que não tem qualquer lógica por detrás dele.

Escusado será dizer que números mágicos são uma praga e devem ser evitados a todo o custo. Quando não conseguirem encontrar uma explicação para um determinado número funcionar, escrevam pelo menos um comentário que explique como chegaram a ele e porque é que acham que ele funciona. Admitir que não sabemos porque algo funciona é sempre mais útil do que deixar o programador seguinte tentar adivinhar o que se passa, sem qualquer pista.

/**
 * 1. Número mágico. Este é o valor mais baixo que encontrei para
 * alinhar o topo de `.foo` com o elemento pai.
 * Idealmente devíamos corrigir isto.
 */
.foo {
  top: 0.327em; /* 1 */
}
/**
 * 1. Número mágico. Este é o valor mais baixo que encontrei para
 * alinhar o topo de `.foo` com o elemento pai.
 * Idealmente devíamos corrigir isto.
 */
.foo
  top: 0.327em /* 1 */
Leitura adicional

Cores

Cores ocupam um lugar muito importante em CSS. Naturalmente, Sass acaba por se tornar um aliado poderoso no que toca à manipulação de cores, especialmente porque providencia um punhado de funções úteis para tal.

Formatos de cores

De maneira a tornar cores em Sass o mais simples possível, o meu conselho é que respeitem a seguinte ordem de preferência para formatação de cores:

  1. Chaves de cores CSS;
  2. Anotação HSL;
  3. Anotação RGB;
  4. Anotação hexadecimal, preferencialmente em minúsculas.

Para começar, os nomes das chaves de cor normalmente falam por si só. A representação HSL é não só a mais fácil para o cérebro humano compreender carece de fontes, como também facilita aos autores das folhas de estilo a manipulação das cores, ajustando apenas os valores individuais de matiz, saturação e luminosidade. RGB ainda tem como vantagem o facto de mostrar imediatamente se a cor tem um tom mais azulado, esverdejado ou avermelhado, mas não facilita nada a construção de uma nova com com as três partes. Por último, hexadecimal é quase indecifrável para o nosso cérebro.

// Yep
.foo {
  color: hsl(0, 100%, 50%);
}

// Also yep
.foo {
  color: rgb(255, 0, 0);
}

// Meh
.foo {
  color: #f00;
}

// Nope
.foo {
  color: #FF0000;
}

// Nope
.foo {
  color: red;
}
.foo
  color: hsl(0, 100%, 50%)

// Also yep
.foo
  color: rgb(255, 0, 0)

// Nope
.foo
  color: #f00

// Nope
.foo
  color: #FF0000

// Nope
.foo
  color: red

Quando usarem a anotação HSL ou RGB, adicionem sempre um espaço simples depois da vírgula (,) e removam os espaços entre os parênteses ((, )) e o conteúdo.

// Yep
.foo {
  color: rgba(0, 0, 0, 0.1);
  background: hsl(300, 100%, 100%);
}

// Nope
.foo {
  color: rgba(0,0,0,0.1);
  background: hsl( 300, 100%, 100% );
}
// Yep
.foo
  color: rgba(0, 0, 0, 0.1)
  background: hsl(300, 100%, 100%)

// Nope
.foo
  color: rgba(0,0,0,0.1)
  background: hsl( 300, 100%, 100% )

Cores e variáveis

No caso de utilizarmos uma cor mais que uma vez, será provavelmente útil guardá-la numa variável cujo nome diga algo sobre essa cor.

$sass-pink: hsl(330, 50%, 60%);
$sass-pink: hsl(330, 50%, 60%)

Assim podemos usar esta variável onde for necessário. No entanto, se o seu uso estiver demasiado preso a um tema, eu não recomendaria utilizar a variável desta forma. Em vez disso, devemos guardá-la numa variável cujo nome explique como deve ser utilizada.

$main-theme-color: $sass-pink;
$main-theme-color: $sass-pink

Assim previne-se que a mudança de um tema leve a algo como $sass-pink: blue.

Clarear e Escurecer cores

Tanto o clarear lighten como o escurecer darken representam funções que permitem manipular a luz de uma cor no espaço HSL adicionando ou subtraindo ao valor do espaço HSL. Basicamente, representam atalhos para o parâmetro $lightness do método de ajuste de cor adjust-color.

É importante referir que estas funções muitas vezes não geram os resultados que se esperaria. Por outro lado, a função de mistura mix é uma boa forma de escurecer ou clarear uma cor misturando-a com branco ou preto.

A vantagem de usar a mix em vez das primeiras funções mencionadas é que esta fará com que a cor tenda progressivamente para preto (ou branco) à medida que subtraímos a proporção da cor, enquanto que darken e lighten irão rapidamente esgotar a cor para os extremos de branco ou preto.

Ilustração da diferença entre lighten/darken e mix por KatieK
Ilustração da diferença entre lighten/darken e mix por KatieK

Se não quiserem escrever a função mix todas as vezes, podem criar duas funções fáceis-de-usar tint e shade (que já fazem parte de Compass) que fazem essencialmente o mesmo:

/// Clarear ligeiramente uma cor
/// @access public
/// @param {Color} $color - cor
/// @param {Number} $percentage - percentagem da `$color` na cor devolvida
/// @return {Color}
@function tint($color, $percentage) {
  @return mix($color, white, $percentage);
}

/// Escurecer ligeiramente uma cor
/// @access public
/// @param {Color} $color - cor
/// @param {Number} $percentage - percentagem da `$color` na cor devolvida
/// @return {Color}
@function shade($color, $percentage) {
  @return mix($color, black, $percentage);
}
/// Clarear ligeiramente uma cor
/// @access public
/// @param {Color} $color - cor
/// @param {Number} $percentage - percentagem da `$color` na cor devolvida
/// @return {Color}
@function tint($color, $percentage)
  @return mix($color, white, $percentage)

/// Escurecer ligeiramente uma cor
/// @access public
/// @param {Color} $color - cor
/// @param {Number} $percentage - percentagem da `$color` na cor devolvida
/// @return {Color}
@function shade($color, $percentage)
  @return mix($color, black, $percentage)

A função scale-color permite efetuar um escalamento das propriedades mais fluído tendo em conta o quão alto ou baixo o seu valor já é. Deverá oferecer resultados que são tão agradáveis como o mix mas com uma convenção mais clara. O fator de escala não é exactamente o mesmo, no entanto.

Leitura adicional

Listas

Listas são o equivalente de arrays. Uma lista é uma estrutura de dados “flat” (ao contrário de mapas) usada para guardar valores de qualquer tipo (incluindo listas, dando origem a listas aninhadas).

As listas devem respeitar as seguintes orientações:

  • sejam uma linha ou várias;
  • necessariamente em várias linhas se forem demasiado longas para caber numa linha de 80 carateres;
  • a não ser para fins de CSS, sempre separadas por vírgula;
  • sempre envolvivas em parêntesis;
  • finalizadas com um ponto final em multi-linha.
// Yep
$font-stack: ('Helvetica', 'Arial', sans-serif);

// Yep
$font-stack: (
  'Helvetica',
  'Arial',
  sans-serif,
);

// Nope
$font-stack: 'Helvetica' 'Arial' sans-serif;

// Nope
$font-stack: 'Helvetica', 'Arial', sans-serif;

// Nope
$font-stack: ('Helvetica', 'Arial', sans-serif,);
// Yep
$font-stack: ('Helvetica', 'Arial', sans-serif)

// Nope (not supported)
$font-stack: (
  'Helvetica',
  'Arial',
  sans-serif,
)

// Nope
$font-stack: 'Helvetica' 'Arial' sans-serif

// Nope
$font-stack: 'Helvetica', 'Arial', sans-serif

// Nope
$font-stack: ('Helvetica', 'Arial', sans-serif,)

Ao adicionar novos itens a uma lista, usar sempre a API fornecida. Nunca tentar adicionar novos itens manualmente.

$shadows: (0 42px 13.37px hotpink);

// Yep
$shadows: append($shadows, $shadow, comma);

// Nope
$shadows: $shadows, $shadow;
$shadows: (0 42px 13.37px hotpink)

// Yep
$shadows: append($shadows, $shadow, comma)

// Nope
$shadows: $shadows, $shadow
Leitura adicional

Mapas

Desde o Sass 3.3, autores de folhas de estilo podem definir mapas — o termo do Sass para arrays associativos, mapas de hash ou mesmo objectos em JavaScript. Um mapa é uma estrutura de dados que mapeia chaves (que podem ser de qualquer tipo de dados, incluindo mapas, embora eu não o recomende) em valores de qualquer tipo.

Mapas devem ser escritos da seguinte forma:

  • espaços depois dos dois pontos (:);
  • parêntesis de abertura (() na mesma linha dos dois pontos (:);
  • chaves com aspas se forem strings (o que representa 99% dos casos);
  • cada par chave/valor na sua própria linha;
  • vírgula (,) após cada par chave/valor;
  • vírgula final (,) no último item, para que seja mais fácil adicionar, remover ou reordenar items;
  • parêntesis de fecho ()) na sua própria linha;
  • não colocar um espaço um um caractér de nova linha entre o parêntesis de fecho ()) e o ponto e vírgula (;).

Ilustração:

// Yep
$breakpoints: (
  'small': 767px,
  'medium': 992px,
  'large': 1200px,
);

// Nope
$breakpoints: ( small: 767px, medium: 992px, large: 1200px );
// Yep
$breakpoints: ('small': 767px, 'medium': 992px, 'large': 1200px,)

// Nope
$breakpoints: ( 'small': 767px, 'medium': 992px, 'large': 1200px )

// Nope
$breakpoints: (small: 767px, medium: 992px, large: 1200px,)

// Nope (since it is not supported)
$breakpoints: (
  'small': 767px,
  'medium': 992px,
  'large': 1200px,
)

Debugging num mapa Sass

Se alguma vez estiveres perdido e a pensar que diabo de magia negra está a acontecer num mapa Sass, não te preocupes porque existe caminho para a salvação.

@mixin debug-map($map) {
  @at-root {
    @debug-map {
      __toString__: inspect($map);
      __length__: length($map);
      __depth__: if(function-exists('map-depth'), map-depth($map), null);
      __keys__: map-keys($map);
      __properties__ {
        @each $key, $value in $map {
          #{'(' + type-of($value) + ') ' + $key}: inspect($value);
        }
      }
    }
  }
}
=debug-map($map)
  @at-root
    @debug-map
      __toString__: inspect($map)
      __length__: length($map)
      __depth__: if(function-exists('map-depth'), map-depth($map), null)
      __keys__: map-keys($map)
      __properties__
        @each $key, $value in $map
          #{'(' + type-of($value) + ') ' + $key}: inspect($value)

Se quiseres saber a profundidade do mapa, adiciona a seguinte função. O mixin vai mostrá-la automaticamente.

/// Calcula a profundidade máxima de um mapa
/// @param {Map} $map
/// @return {Number} profundidade máxima de `$map`
@function map-depth($map) {
  $level: 1;

  @each $key, $value in $map {
    @if type-of($value) == 'map' {
      $level: max(map-depth($value) + 1, $level);
    }
  }

  @return $level;
}
/// Calcula a profundidade máxima de um mapa
/// @param {Map} $map
/// @return {Number} profundidade máxima de `$map`
@function map-depth($map)
  $level: 1

  @each $key, $value in $map
    @if type-of($value) == 'map'
      $level: max(map-depth($value) + 1, $level)

  @return $level;
Leitura adicional

Conjunto de regras para CSS

A esta altura, isto é essencialmente uma revisão do que toda a gente sabe, mas é assim que um conjunto de regras para CSS deve ser escrito (pelo menos, de acordo com a maioria dos guias de orientação, incluindo CSS Guidelines):

  • seletores relacionados na mesma linha; seletores sem relação numa nova linha;
  • chaveta de abertura ({) separada do último seletor por um espaço único;
  • cada declaração na sua própria linha;
  • um espaço a seguir aos dois pontos (:);
  • um ponto e vírgula terminal (;) no final de cada declaração;
  • chaveta de fecho (}) na sua própria linha;
  • uma linha em branco após a chaveta de fecho (}).

Ilustração:

// Yep
.foo, .foo-bar,
.baz {
  display: block;
  overflow: hidden;
  margin: 0 auto;
}

// Nope
.foo,
.foo-bar, .baz {
    display: block;
    overflow: hidden;
    margin: 0 auto }
// Yep
.foo, .foo-bar,
.baz
  display: block
  overflow: hidden
  margin: 0 auto

// Nope
.foo,
.foo-bar, .baz
    display: block
    overflow: hidden
    margin: 0 auto

Acrescentando às regras relacionadas com CSS, queremos prestar atenção a:

  • variáveis locais declaradas antes de qualquer outra declaração, separadas de declarações seguintes por uma linha em branco;
  • chamadas de mixins sem @content feitas antes de qualquer declaração;
  • seletores aninhados sempre precedidos por uma linha em branco;
  • chamadas de mixins com @content feitas depois de qualquer seletor aninhado;
  • não adicionar uma linha em branco depois da chaveta de fecho (}).

Ilustração:

.foo, .foo-bar,
.baz {
  $length: 42em;

  @include ellipsis;
  @include size($length);
  display: block;
  overflow: hidden;
  margin: 0 auto;

  &:hover {
    color: red;
  }

  @include respond-to('small') {
    overflow: visible;
  }
}
.foo, .foo-bar,
.baz
  $length: 42em

  +ellipsis
  +size($length)
  display: block
  overflow: hidden
  margin: 0 auto

  &:hover
    color: red

  +respond-to('small')
    overflow: visible
Leitura adicional

Ordenar as declarações

Não me ocorrem muitos tópicos em que as opiniões sejam tão divididas como são em relação a ordenar declarações em CSS. Na verdade, existem duas facções:

  • manter a ordem alfabética;
  • ordenar declarações por tipo (position, display, cores, tipo de letra, outros).

Existem prós e contras em ambas as abordagens. Por um lado, a ordem alfabética é universal (pelo menos em todos os idiomas que usam o alfabeto latino), portanto não existe dúvida quanto a colocar uma propriedade antes de outra. No entanto, parece-me bastante estranho ver propriedades como bottom e top separadas uma da outra. Por que motivo apareceriam animações antes do tipo display? Há imensas peculiaridades na ordenação alfabética.

.foo {
  background: black;
  bottom: 0;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
  height: 100px;
  overflow: hidden;
  position: absolute;
  right: 0;
  width: 100px;
}
.foo
  background: black
  bottom: 0
  color: white
  font-weight: bold
  font-size: 1.5em
  height: 100px
  overflow: hidden
  position: absolute
  right: 0
  width: 100px

Por outro lado, ordenar propriedades por tipo faz todo o sentido. Todas as declarações relacionadas com tipos de letra estão próximas, top e bottom estão juntas e ler um conjunto de regras quase se assemelha a ler uma pequena história. Mas a não ser que te mantenhas fiel a algumas convenções, como Idiomatic CSS, há imenso espaço para interpretação própria nesta forma de fazer as coisas. Onde ficaria white-space: font ou display? Onde é que overflow pertence exactamente? Qual é a ordem das declarações dentro de um grupo (poderia ser ordem alfabética; ah, a ironia)?

.foo {
  height: 100px;
  width: 100px;
  overflow: hidden;
  position: absolute;
  bottom: 0;
  right: 0;
  background: black;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
}
.foo
  height: 100px
  width: 100px
  overflow: hidden
  position: absolute
  bottom: 0
  right: 0
  background: black
  color: white
  font-weight: bold
  font-size: 1.5em

Há ainda outro tipo de ordenação interessante chamado Concentric CSS, que parece ser também bastante popular. Basicamente, o Concentric CSS baseia-se no box-model para definir uma ordem: começa no exterior, move-se para o interior.

.foo {
  width: 100px;
  height: 100px;
  position: absolute;
  right: 0;
  bottom: 0;
  background: black;
  overflow: hidden;
  color: white;
  font-weight: bold;
  font-size: 1.5em;
}
.foo
  width: 100px
  height: 100px
  position: absolute
  right: 0
  bottom: 0
  background: black
  overflow: hidden
  color: white
  font-weight: bold
  font-size: 1.5em

Devo dizer que pessoalmente não me consigo decidir. Uma recente sondagem no CSS-Tricks apurou que mais de 45% dos programadores ordenam as suas declarações por tipo, contra 14% que o fazem alfabeticamente. Há ainda 39% que o fazem de uma forma totalmente aleatória, incluindo eu próprio.

Gráfico demonstrativo de como os programadores ordenam as suas declarações de CSS
Gráfico demonstrativo de como os programadores ordenam as suas declarações de CSS

Por esse motivo, não vou impor uma escolha neste guia de orientações. Escolham aquele que preferirem, desde que sejam consistentes ao longo das vossas folhas de estilo (isto é, não escolham a opção aleatória).

Um estudo recente mostra que usar CSS Comb (que por sua vez usa ordenação por tipo) para ordenar declarações em CSS acaba por diminuir o tamanho média dos ficheiros comprimidos com Gzip em cerca de 2.7%, comparando com 1.3% quando ordenados alfabeticamente.

Leitura adicional

Seletores aninhados

Uma das características do Sass que está a ser extremamente mal usada por muitos programadores são os seletores aninhados. Alinhar seletores oferece aos autores de folhas de estilo uma forma de computar seletores longos, aninhando seletores mais pequenos dentro de outros.

Regra geral

Por exemplo, o seguinte código aninhado em Sass:

.foo {
  .bar {
    &:hover {
      color: red;
    }
  }
}
.foo
  .bar
    &:hover
      color: red

… vai gerar este CSS:

.foo .bar:hover {
  color: red;
}

Do mesmo modo, desde o Sass 3.3 que é possível usar uma referência ao seletor atual (&) para gerar seletores avançados. Por exemplo:

.foo {
  &-bar {
    color: red;
  }
}
.foo
  &-bar
    color: red

… vai gerar este CSS:

.foo-bar {
  color: red;
}
.foo-bar
  color: red

Este método é geralmente usado em conjunto com a nomenclatura BEM para gerar os seletores .block__element e .block--modifier baseados no seletor original (neste caso, .block).

Embora pareça anedótico, gerar novos seletores a partir da referência ao seletor atual (&) torna os seletores impossíveis de procurar no código, já que não existem per se.

O problema com aninhar seletores é que, em última instância, torna o código mais difícil de ler. O leitor tem de mentalmente computador o seletor resultante a partir dos níveis de indentação; nem sempre é óbvio o que é que o CSS resultante acabará por ser.

Este argumento torna-se ainda mais verdadeiro à medida que os seletores se tornam mais longos e as referências ao seletor atual (&) mais frequentes. A dada altura, o risco de perder o fio à meada e deixar de perceber o que se passa e onde é tão alto que deixa de valer a pena.

Para evitar essa situação, evitamos aninhar seletores tanto quanto possível. No entanto, há exceções óbvias a esta regra.

Exceções

Para começar, é permitido e até aconselhado aninhar pseudo-classes e pseudo-elementos no seletor inicial.

.foo {
  color: red;

  &:hover {
    color: green;
  }

  &::before {
    content: 'pseudo-element';
  }
}
.foo
  color: red

  &:hover
    color: green

  &::before
    content: 'pseudo-element'

Aninhar seletores em pseudo-classes e pseudo-elementos não só faz sentido (porque lida com seletores intimamente ligados), mas também ajuda a manter tudo relacionado com um determinado componente num único sítio.

Além disso, quando se usam classes de estado independentes de um componente, como .is-active, é perfeitamente aceitável aninhá-las sob o selctor do componente, de modo a manter tudo limpo.

.foo {
  // …

  &.is-active {
    font-weight: bold;
  }
}
.foo
  // …

  &.is-active
    font-weight: bold

Por fim, quando se estiliza um elemento só porque ele está dentro de outro elemento específico, é também aceitável aninhá-lo, de modo a manter tudo sobre um determinado componente no mesmo sítio.

.foo {
  // …

  .no-opacity & {
    display: none;
  }
}
.foo
  // …

  .no-opacity &
    display: none

Quando se trabalha com programadores inexperientes, um seletor como .no-opacity & pode parecer um pouco estranho. Para evitar confusões, pode-se construir um pequeno mixin que transforma esta sintaxe estranha numa API explícita.

/// Mixin auxiliar que fornece uma API simples para aninhar selectores
/// @param {String} $selector - Selector
@mixin when-inside($selector) {
  #{$selector} & {
    @content;
  }
}
/// Mixin auxiliar que fornece uma API simples para aninhar selectores
/// @param {String} $selector - Selector
=when-inside($selector) {
  #{$selector} &
    @content
}

Reescrevendo o nosso exemplo anterior, ficaria então assim:

.foo {
  // …

  @include when-inside('.no-opacity') {
    display: none;
  }
}
.foo
  // …

  +when-inside('.no-opacity')
    display: none

Como tudo, as especificidades são de certa forma irrelevantes, o importante é a consistência. Se te sentes perfeitamente confiante com seletores aninhados, então usa seletores aninhados. Certifica-te apenas que toda a tua equipa está confortável com isso.

Leitura adicional

Convenções de nomenclatura

Nesta secção, não iremos lidar com as melhores convenções de nomenclatura para manutenção e escalabilidade; não só isso apenas vos diz respeito, como também está fora do âmbito de um guia de estilo de Sass. Eu sugiro os recomendados por CSS Guidelines.

Existem algumas coisas às quais podem dar nome em Sass, e é importante nomeá-las corretamente para que toda a base de código pareça consistente e fácil de ler:

  • variáveis;
  • funções;
  • mixins.

Placeholders de Sass são deliberadamente omitidos desta lista visto que podem ser considerados selectores normais de CSS, seguindo assim o mesmo padrão de nomenclatura como classes.

Em relação às variáveis, funções e mixins, mantemos algo bastante semelhante a CSS: minúsculas delimitadas por hífens, e acima de tudo significativos.

$vertical-rhythm-baseline: 1.5rem;

@mixin size($width, $height: $width) {
  // …
}

@function opposite-direction($direction) {
  // …
}
$vertical-rhythm-baseline: 1.5rem

=size($width, $height: $width)
  // …

@function opposite-direction($direction)
  // 
Leitura adicional

Constantes

Se por acaso são programadores de frameworks ou bibliotecas, poderão encontrar-se a lidar com variáveis que não é suposto serem alteradas em qualquer circunstância: constantes. Infelizmente (ou felizmente?), Sass não fornece nenhuma forma de definir tais entidades, por isso temos que ficar pelas rigorosas convenções de nomenclatura de forma a nos darmos a entender.

Tal como para várias linguagens, eu sugiro variáveis em maiúsculas e delimitadas por subtraços (_) quando são constantes. Não só é uma convenção muito antiga, mas também contrasta bem com as habituais variáveis em minúsculas e separadas por hífens.

// Yep
$CSS_POSITIONS: (top, right, bottom, left, center);

// Nope
$css-positions: (top, right, bottom, left, center);
// Yep
$CSS_POSITIONS: (top, right, bottom, left, center)

// Nope
$css-positions: (top, right, bottom, left, center)
Leitura adicional

Namespace

Se tencionam distribuir o vosso código Sass, no caso de uma biblioteca, uma framework, um sistema de grelhas ou o que seja, talvez queiram considerar colocar todas as vossas variáveis, funções, mixins e placeholders no seu próprio namespace, para que não entrem em conflito com o código de alguém.

Por exemplo, se trabalharem num projecto chamado Sassy Unicorn que se destina a ser usado por programadores de todo o mundo (quem não o faria, não é?), poderiam considerar usar su- como namespace. É específico o suficiente para prevenir colisões de nomes e curto o suficiente para não ser maçador a escrever.

$su-configuration: (  );

@function su-rainbow($unicorn) {
  // …
}
$su-configuration: (  )

@function su-rainbow($unicorn)
  // 

De notar que namespacing automático é definitivamente um objectivo de design para a próxima reformulação do @import de Sass 4.0. À medida que tal se aproxima da sua concretização, será cada vez menos útil fazer namespacing manual; eventualmente, bibliotecas manualmente namespaced poderão realmente ser mais difíceis de usar.

Leitura adicional

Comentários

CSS é uma linguagem traiçoeira, repleta de hacks e esquisitices. Por causa disto, deverá ser altamente comentada, especialmente se vocês ou outras pessoas pretenderem ler e actualizar o código daqui a 6 meses ou 1 ano. Não deixem vocês ou qualquer outra pessoa estar na posição de eu-não-escrevi-isto-ó-meu-deus-porquê.

Por mais simples que CSS seja, há sempre lugar para comentários. Estes poderão ser a explicar:

  • a estrutura e/ou função de um ficheiro;
  • o objetivo de um conjunto de regras;
  • a ideia por trás de um “número mágico”;
  • a razão para uma determinada declaração de CSS;
  • a ordem das declarações de CSS;
  • o processo mental por trás de uma determinada maneira de fazer as coisas.

E eu provavelmente esqueci-me de um monte de outros motivos também. Comentar leva muito pouco tempo quando é feito juntamente com o código, por isso façam-no na hora certa. Voltar a um pedaço de código para comentá-lo não é só completamente irrealista, mas também extremamente irritante.

Escrever comentários

Idealmente, qualquer conjunto de regras de CSS deveria ser precedido por um comentário ao estilo de C, explicando o propósito do bloco de CSS. Este comentário também incluiria explicações numeradas referentes a partes específicas conjunto de regras. Por exemplo:

/**
 * Classe auxiliar para truncar e adicionar reticências a uma
 * string comprida demais para caber em apenas uma linha.
 * 1. Previne o conteúdo de transbordar para outras linhas,
 *    forçando-o a uma linha apenas.
 * 2. Adiciona reticências no final da linha.
 */
.ellipsis {
  white-space: nowrap; /* 1 */
  text-overflow: ellipsis; /* 2 */
  overflow: hidden;
}
/**
 * Classe auxiliar para truncar e adicionar reticências a uma
 * string comprida demais para caber em apenas uma linha.
 * 1. Previne o conteúdo de transbordar para outras linhas,
 *    forçando-o a uma linha apenas.
 * 2. Adiciona reticências no final da linha.
 */
.ellipsis
  white-space: nowrap /* 1 */
  text-overflow: ellipsis /* 2 */
  overflow: hidden

Basicamente, tudo o que não é óbvio à primeira vista deverá ser comentado. Documentação excessiva é algo que não existe. Lembrem-se de que não conseguem comentar em demasia, por isso vão em frente e escrevam comentários para tudo que valer a pena.

Ao comentar uma secção específica de Sass, usem comentários in-line em vez de blocos ao estilo de C. Isto fará o comentário invisível no output, mesmo no modo expandido durante desenvolvimento.

// Adiciona o módulo actual à lista de módulos importados.
// A opção `!global` é necessário para realmente actualizar a variável global.
$imported-modules: append($imported-modules, $module) !global;
// Adiciona o módulo actual à lista de módulos importados.
// A opção `!global` é necessário para realmente actualizar a variável global.
$imported-modules: append($imported-modules, $module) !global
Leitura adicional

Documentação

Cada variável, função, mixin e placeholder que se destina a ser reutilizado em todo o código deverá ser documentado como parte da API global usando SassDoc.

/// Linha base de ritmo vertical usado em todo o código.
/// @type Length
$vertical-rhythm-baseline: 1.5rem;
/// Linha base de ritmo vertical usado em todo o código.
/// @type Length
$vertical-rhythm-baseline: 1.5rem

Três barras (/) necessárias.

SassDoc tem duas funções principais:

  • forçar comentários padronizados usando um sistema baseado em anotações para tudo o que faz parte de uma API pública ou privada;
  • ser capaz de gerar uma versão HTML da documentação da API usando qualquer um dos endpoints de SassDoc (linha de comandos, Grunt, Gulp, Broccoli, Node…).
Documentação gerada por SassDoc
Documentação gerada por SassDoc

Aqui está um exemplo de um mixin documentado extensivamente com SassDoc:

/// Mixin auxiliar definindo `largura` e `altura` simultaneamente.
///
/// @author Hugo Giraudel
///
/// @access public
///
/// @param {Length} $width - `Largura` do elemento
/// @param {Length} $height [$width] - `Altura` do elemento
///
/// @example scss - Uso
///   .foo {
///     @include size(10em);
///   }
///
///   .bar {
///     @include size(100%, 10em);
///   }
///
/// @example css - CSS resultante
///   .foo {
///     width: 10em;
///     height: 10em;
///   }
///
///   .bar {
///     width: 100%;
///     height: 10em;
///   }
@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}
/// Mixin auxiliar definindo `largura` e `altura` simultaneamente.
///
/// @author Hugo Giraudel
///
/// @access public
///
/// @param {Length} $width - `Largura` do elemento
/// @param {Length} $height ($width) - `Altura` do elemento
///
/// @example scss - Uso
///   .foo
///     +size(10em)
///
///   .bar
///     +size(100%, 10em)
///
/// @example css - CSS resultante
///   .foo {
///     width: 10em;
///     height: 10em;
///   }
///
///   .bar {
///     width: 100%;
///     height: 10em;
///   }
=size($width, $height: $width)
  width: $width
  height: $height
Leitura adicional

Arquitetura

Criar a arquitetura de um projeto de CSS é uma das coisas mais difíceis que se pode fazer durante a vida de um projeto. Manter essa arquitetura constante e significativa é ainda mais difícil.

Felizmente, um dos benefícios principais de usar um pré-processador de CSS é ter a habilidade de dividir a nossa base de código em vários ficheiros sem ter impacto na performance (como a diretiva de CSS @import fazia). Graças à sobrecarga da diretiva @import no Sass, é perfeitamente seguro (e até recomendado) usar os ficheiros necessários na fase de desenvolvimento, todos estes compilados numa única folha de estilo quando o projeto for para produção.

Em cima disso, não posso salientar o suficiente a necessidade de pastas, mesmo em pequenos projetos. Em casa, não deixas todas as folhas de papel na mesma caixa. Tens uma para os papéis da casa, uma para documentos do banco, uma para contas e assim em diante. Não há razão para fazeres de maneira diferente quando estás a estruturar um projeto de CSS. Separa o teu código em pastas com nomes compreensíveis para depois ser fácil encontrar qualquer código quando mais tarde tiveres que voltar ao projeto.

Há muitas arquiteturas populares para projectos de CSS: OOCSS, Atomic Design, arquitetura usada no Bootstrap, arquitetura usada no Foundation… Todas têm os seus méritos, prós e contras.

Eu uso uma abordagem que acaba por ser muito parecida com SMACSS de Jonathan Snook, e que se foca em manter as coisas simples e óbvias.

Eu aprendi que a arquitetura é muito especifica para o projeto. Estejam à vontade para ignorarem ou adaptarem a solução proposta para lidarem com um sistema que se adapta melhor às vossas necessidades.

Leitura Adicional

Componentes

Há uma grande diferença entre fazer algo funcionar e fazer algo bom. Mais uma vez, CSS é uma linguagem muito desorganizada [carece de fontes]. Quanto menos CSS tiverem, melhor. Nós não queremos lidar com megabytes de CSS. Para manter as nossas folhas de estilo pequenas e efecientes—e isto não será nenhuma surpresa para ti— é normalmente uma boa ideia pensar numa interface como uma coleção de componentes.

Componentes podem ser qualquer coisa desde que:

  • façam uma e apenas uma coisa;
  • sejam reutilizáveis e usados por todo o projeto;
  • sejam independentes.

Por exemplo um formulário de pesquisa deve ser tratado como um componente. Deverá ser reutilizável, em diferentes posições, páginas e em situações diversas. Não deve depender da sua posição na DOM (rodapé, barra lateral, conteúdo principal…).

A maior parte de qualquer interface pode ser pensada como pequenos componentes e eu recomendo que fique com este paradigma. Isto vai não só diminuir a quantidade de CSS necessário para um projeto completo, mas também acaba por ser muito mais fácil do que manter uma desorganização onde está tudo junto.

O padrão 7-1

Vamos então voltar à arquitetura, ok? Eu normalmente uso o que chamo O padrão 7-1: 7 pastas, 1 ficheiro. Basicamente, tudo o que tens são ficheiros parciais colocados em 7 pastas diferentes, e um único ficheiro na raiz do projeto (normalmente chamado main.scss) que importa todos os ficheiros parciais para serem compilados numa única folha de estilo de CSS.

  • base/
  • components/
  • layout/
  • pages/
  • themes/
  • utils/
  • vendors/

E claro:

  • main.scss
Fundo de parede por Julien He
Fundo de parede por Julien He

Idealmente, teríamos algo como:

sass/
|
|- base/
|   |- _reset.scss       # Reset
|   |- _typography.scss  # Regras de Tipografia
|   …                    # Etc.
|
|- components/
|   |- _buttons.scss     # Botões
|   |- _carousel.scss    # Carossel
|   |- _cover.scss       # Cover
|   |- _dropdown.scss    # Dropdown
|   …                    # Etc.
|
|- layout/
|   |- _navigation.scss  # Navegação
|   |- _grid.scss        # Grelha
|   |- _header.scss      # Cabeçalho
|   |- _footer.scss      # Rodapé
|   |- _sidebar.scss     # Barra Lateral
|   |- _forms.scss       # Formulários
|   …                    # Etc.
|
|- pages/
|   |- _home.scss        # Estilos especificos à página Inicial
|   |- _contact.scss     # Estilos especificos à página de Contacto
|   …                    # Etc.
|
|- themes/
|   |- _theme.scss       # Tema Padrão
|   |- _admin.scss       # Tema de Administração
|   …                    # Etc.
|
|- utils/
|   |- _variables.scss   # Variáveis
|   |- _functions.scss   # Funções
|   |- _mixins.scss      # Mixins
|   |- _helpers.scss     # Auxiliares de classes e placeholders
|
|- vendors/
|   |- _bootstrap.scss   # Bootstrap
|   |- _jquery-ui.scss   # jQuery UI
|   …                    # Etc.
|
|
`- main.scss             # Prinicipal ficheiro de Sass

Os ficheiros seguem as mesma convenções de nome descritas acima: são delimitados com um hífen.

Pasta Base

A pasta base/ contém o que nós podemos chamar de código padrão para o projeto. Aqui podemos encontrar um ficheiro de reset, algumas regras tipográficas e provavelmente uma folha de estilo (que estou a chamar _base.scss), que define alguns estilos padrão para elementos de HTML mais usados.

  • _base.scss
  • _reset.scss
  • _typography.scss

Pasta Layout

A pasta layout/ contêm tudo que é necessário para criar o layout do site ou aplicação. Esta pasta contêm as folhas de estilo para as partes principais do site (cabeçalho, rodapé, navegação, barra lateral…), a grelha ou mesmo o CSS de todos os formulários.

  • _grid.scss
  • _header.scss
  • _footer.scss
  • _sidebar.scss
  • _forms.scss
  • _navigation.scss

A pasta layout/ também pode ser chamada partials/, sendo isto uma questão de preferência.

Pasta Components

Para componentes mais pequenos, há a pasta components/. Enquanto a pasta layout/é macro (definindo a estrutura global), a pasta components/ é mais focada em módulos. Contêm todo o tipo de módulos específicos como um slider, um carregador e tudo que seja desse tipo. Há normalmente imensos ficheiros na pasta components/ tendo em conta que todo o site/aplicação deverá ser constituído por pequenos módulos.

  • _media.scss
  • _carousel.scss
  • _thumbnails.scss

A pasta components/ também se poderá chamar modules/, sendo isto uma questão de preferência.

Pasta Pages

Se tiveres estilos específicos a páginas, o melhor será colocá-las na pasta pages/, num ficheiro com o nome da página. Por exemplo, não deixa de ser comum ter estilos muito específicos para a página inicial que criam a necessidade de ter um _home.scss na pasta pages/.

  • _home.scss
  • _contact.scss

Dependendo do processo de desenvolvimento estes ficheiros podem ser chamados individualmente para evitar que eles se juntem com outros quando todas as folhas de estilo se juntam. Na realidade é uma questão de preferência.

Pasta Themes

Em grandes sites ou aplicações, é comum existirem vários temas. Há certamente diversas maneira de lidar com temas mas eu pessoalmente gosto de colocar tudo numa pasta com o nome themes/.

  • _theme.scss
  • _admin.scss

Isto é algo muito específico a cada projeto e em muitos deles podem nem existir a necessidade.

Pasta Utils

A pasta de /utils guarda todas as ferramentas e auxiliares de SASS usados por todo o projeto. Todas as funções globais, mixins e placeholders devem ser colocados nesta pasta.

A regra desta pasta é que não deve produzir uma única linha de CSS se for compilada sozinha. Tudo o que está aqui deverá ser nada mais que auxiliares.

  • _variables.scss
  • _mixins.scss
  • _functions.scss
  • _placeholders.scss (normalmente chamado _helpers.scss)

A pasta utils/ também pode ser chamada de helpers/, sass-helpers/ ou sass-utils/, sendo uma questão de preferência.

Pasta Vendors

E por fim, a maioria dos projetos irão ter uma pasta vendors/ que vai conter todo o CSS usado por biblioteca e frameworks externas - Normalize, Bootstrap, jQueryUI, FancyCarouselSliderjQueryPowered, e por ai. Por todos estes componentes na mesma pasta é uma boa maneira de dizer “Hey, este código não é meu, não o escrevi, não é a minha responsabilidade”.

  • _normalize.scss
  • _bootstrap.scss
  • _jquery-ui.scss
  • _select2.scss

Se tiveres que substituir uma secção de alguma destas extensões, eu recomendo criar um oitava pasta chamada vendors-extensions/ na qual estarão ficheiros com os mesmos nomes dos quais estás a substituir.

Por exemplo, vendors-extensions/_boostrap.scss é um ficheiro que contém todas as regras de CSS que tiveste que declarar de novo por cima do CSS padrão do Bootstrap. Isto é para evitar editar os ficheiros originais das extensões, o que normalmente não é uma boa ideia.

Ficheiro Principal

O ficheiro principal (normalmente chamado main.scss) deverá ser o único ficheiro de SASS que não começa com um underscore. Este ficheiro não deve conter nada mais que @import e comentários.

Os ficheiros devem ser importados de acordo com a pasta onde estão, uma depois da outra na seguinte ordem:

  1. vendors/
  2. utils/
  3. base/
  4. layout/
  5. components/
  6. pages/
  7. themes/

De maneira a preservar a legibilidade, o ficheiro principal deverá seguir as seguintes diretrizes:

  • um ficheiro por @import;
  • um @importpor linha;
  • não colocar linha de espaço entre importações da mesma pasta;
  • uma linha de espaço depois da última importação de uma pasta;
  • extensões e underscores antes do nome devem ser omitidos.
@import 'abstracts/variables';
@import 'abstracts/functions';
@import 'abstracts/mixins';
@import 'abstracts/placeholders';

@import 'vendors/bootstrap';
@import 'vendors/jquery-ui';

@import 'base/reset';
@import 'base/typography';

@import 'layout/navigation';
@import 'layout/grid';
@import 'layout/header';
@import 'layout/footer';
@import 'layout/sidebar';
@import 'layout/forms';

@import 'components/buttons';
@import 'components/carousel';
@import 'components/cover';
@import 'components/dropdown';

@import 'pages/home';
@import 'pages/contact';

@import 'themes/theme';
@import 'themes/admin';
@import vendors/bootstrap
@import vendors/jquery-ui

@import utils/variables
@import utils/functions
@import utils/mixins
@import utils/placeholders

@import base/reset
@import base/typography

@import layout/navigation
@import layout/grid
@import layout/header
@import layout/footer
@import layout/sidebar
@import layout/forms

@import components/buttons
@import components/carousel
@import components/cover
@import components/dropdown

@import pages/home
@import pages/contact

@import themes/theme
@import themes/admin

Existe outra maneira de importar parciais que também considero válida. O lado positivo dela é que torna o ficheiro mais legível. Por outro lado, faz qualquer mudança a esse ficheiro mais difícil. De qualquer maneira eu vou deixar isto ao vosso critério pois a diferença não é muita. Ao importar desta maneira o ficheiro principal deve seguir as seguintes diretrizes:

  • um @import por pasta;
  • linha de espaço entre cada @import;
  • cada ficheiro fica na sua linha;
  • uma linha de espaço depois da ultima importação numa pasta;
  • extensões e underscores antes do nome devem ser omitidos.
@import
  'abstracts/variables',
  'abstracts/functions',
  'abstracts/mixins',
  'abstracts/placeholders';

@import
  'vendors/bootstrap',
  'vendors/jquery-ui';

@import
  'base/reset',
  'base/typography';

@import
  'layout/navigation',
  'layout/grid',
  'layout/header',
  'layout/footer',
  'layout/sidebar',
  'layout/forms';

@import
  'components/buttons',
  'components/carousel',
  'components/cover',
  'components/dropdown';

@import
  'pages/home',
  'pages/contact';

@import
  'themes/theme',
  'themes/admin';
@import
  vendors/bootstrap,
  vendors/jquery-ui

@import
  utils/variables,
  utils/functions,
  utils/mixins,
  utils/placeholders

@import
  base/reset,
  base/typography

@import
  layout/navigation,
  layout/grid,
  layout/header,
  layout/footer,
  layout/sidebar,
  layout/forms

@import
  components/buttons,
  components/carousel,
  components/cover,
  components/dropdown

@import
  pages/home,
  pages/contact

@import
  themes/theme,
  themes/admin

De maneira a não ter que importar cada ficheiro manualmente, existe uma extensão para o Sass chamada sass-globbing, que torna possivel usar padrões globais no @import como @import "components/*".

Tendo isto dito, eu não recomendo o uso desta extensão porque ela importa os ficheiros por ordem alfabética e normalmente não é isto que queremos, principalmente quando lidamos com uma linguagem que se baseia na ordem.

Ficheiro Vergonhoso

Existe um conceito interessante que foi tornado popular por Harry Roberts, Dave Rupert e Chris Coyier que consiste em colocar todas as declarações, hacks e coisas das quais não estamos propriamente orgulhosos num ficheiro vergonhoso. Este ficheiro dramaticamente chamado _shame.css, seria importado depois de todos os outros mesmo no fim da folha de estilo.

/**
 * Arranjar a navegação.
 *
 * Alguém usou um ID no código do cabeçalho (`#header a {}`) que
 * passa por cima dos seletores da navegação (`.site-nav a {}`).
 * Usar !important para sobrepor estes até ter tempo de refazer
 * o código do cabeçalho.
 */
.site-nav a {
    color: #BADA55 !important;
}
/**
 * Arranjar a navegação.
 *
 * Alguém usou um ID no código do cabeçalho (`#header a {}`) que
 * passa por cima dos seletores da navegação (`.site-nav a {}`).
 * Usar !important para sobrepor estes até ter tempo de refazer
 * o código do cabeçalho.
 */
.site-nav a
    color: #BADA55 !important
Leitura Adicional

Web Design Responsivo (RWD) e breakpoints

Não creio que tenhamos ainda de apresentar o que é Web Design Responsivo, agora que está em todo o lado. No entanto podem-se perguntar porque há uma secção de RWD num guia de estilo? Na realidade existem algumas coisas que podem ser feitas para facilitar o trabalho com breakpoints, como tal achei que podia não ser má ideia listá-las aqui.

Definir breakpoints

Acho que é seguro dizer que as media queries não devem estar agarradas a dispositivos específicos. Por exemplo, seria definitivamente uma má ideia ter especificamente iPhones ou Blackberries como público-alvo. As media queries devem afetar um conjunto de tamanhos de ecrã, pelo menos até o design partir e a próxima media query entrar em acção.

Pelos mesmos motivos, breakpoints não devem ter nomes de dispositivos, mas sim algo mais geral. Especialmente agora, porque alguns telemóveis são maiores que tablets e algumas tablets são maiores que pequenos ecrãs de computadores, e por aí além…

// Yep
$breakpoints: (
  'medium': (min-width: 800px),
  'large': (min-width: 1000px),
  'huge': (min-width: 1200px),
);

// Nope
$breakpoints: (
  'tablet': (min-width: 800px),
  'computer': (min-width: 1000px),
  'tv': (min-width: 1200px),
);
// Yep
$breakpoints: ('medium': (min-width: 800px), 'large': (min-width: 1000px), 'huge': (min-width: 1200px))

// Nope
$breakpoints: ('tablet': (min-width: 800px), 'computer': (min-width: 1000px), 'tv': (min-width: 1200px))

Por esta altura, qualquer convenção serve, desde que ajude a clarificar que o design não está intimamente agarrado a um tipo de dispositivo especifíco e que dê uma ideia da escala de magnitudes.

$breakpoints: (
  'semente': (min-width: 800px),
  'rebento': (min-width: 1000px),
  'planta': (min-width: 1200px),
);
$breakpoints: ("semente": (min-width: 800px), "rebento": (min-width: 1000px), "planta": (min-width: 1200px))

Os exemplos anteriores utilizam mapas aninhados para definir breakpoints; no entanto isto depende bastante no tipo de gestor de breakpoints que seja utilizado. Podem optar por strings em vez de usar mapas, para maior flexibilidade (e.g. '(min-width: 800px)').

Leitura Adicional

Gestor de Breakpoints

Assim que definam os breakpoints da maneira que quiserem, é necessário arranjar uma maneira de os usar realmente em media queries. Há imensas maneiras de o fazer, mas confesso que sou grande fã do mapa de breakpoints acedido por uma função de leitura. Este sistema é simples e eficiente.

/// Gestor Responsivo
/// @access public
/// @param {String} $breakpoint - Breakpoint
/// @requires $breakpoints
@mixin respond-to($breakpoint) {
  @if map-has-key($breakpoints, $breakpoint) {
    @media #{inspect(map-get($breakpoints, $breakpoint))} {
      @content;
    }
  }

  @else {
    @error 'No value found for `#{$breakpoint}`. '
         + 'Please make sure it is defined in `$breakpoints` map.';
  }
}
/// Gestor Responsivo
/// @access public
/// @param {String} $breakpoint - Breakpoint
/// @requires $breakpoints
=respond-to($breakpoint)
  @if map-has-key($breakpoints, $breakpoint)
    @media #{inspect(map-get($breakpoints, $breakpoint))}
      @content

  @else
    @error 'Nenhum valor encontrado para `#{$breakpoint}`. '
         + 'Por favor assegure que está definido no mapa `$breakpoints`.'

Obviamente, este é um gestor de breakpoints bastante simplista. Se precisarem de algo mais permissivo,recomendo que não reinventem a roda e utilizem algo que já provou ser eficaz, tal como Sass-MQ, Sass-MQ, Breakpoint ou include-media..

Leitura Adicional

Media Queries

Não há muito tempo atrás, houve um debate aceso sobre onde é que as media queries deveriam ser escritas: deveriam ser escritas dentro dos selectores (tal como Sass o permite), ou deveriam ser separadas? Tenho a dizer que sou um defensor renhido do sistema media-queries-dentro-de-selectores, um vez que acho que encaixa bem com a ideia de componentes.

.foo {
  color: red;

  @include respond-to('medium') {
    color: blue;
  }
}
.foo
  color: red

  +respond-to('medium')
    color: blue

Que gera o seguinte resultado em CSS:

.foo {
  color: red;
}

@media (min-width: 800px) {
  .foo {
    color: blue;
  }
}

Podem ter ouvido que esta convenção resulta na duplicação das media queries no resultado de CSS. Isto é definitivamente verdade. No entanto, foram feitos testes e o resultado final é que não interessa assim que Gzip (ou algo equivalente) fizer o seu trabalho:

… we hashed out whether there were performance implications of combining vs scattering Media Queries and came to the conclusion that the difference, while ugly, is minimal at worst, essentially non-existent at best.
Sam Richards, regarding Breakpoint

Agora, se realmente tiverem preocupados com a duplicação das media queries, podem usar uma ferramenta para as juntar, tal como esta gema no entanto, sinto que vos devo avisar na possibilidade de efeitos secundários de alteram a ordem do código, uma vez que já sabem que a ordem do código-fonte é importante.

Leitura Adicional

Variáveis

As variáveis são a essência de qualquer linguagem de programação. Elas permitem-nos reutilizar valores sem ter que os copiar constantemente. Mais importante ainda, elas permitem-nos actualizar um valor facilmente. Acabou-se a procura e substituição de valores manualmente.

No entanto o CSS não é mais do que um cesto com todos os vossos ovos. Ao contrário de muitas outras linguagens, não há verdadeiros contextos no CSS. Como tal, temos de prestar atenção aos riscos de adicionar variáveis pois podemos entrar em conflicto com variáveis existentes.

O meu conselho passa por apenas criar variáveis quando estas fazem sentido. Não inicializer variáveis só porque sim, isso não ajudará. Uma nova variável deve ser criada quando acontece os seguintes critérios:

  • o seu valor é repetido pelo menos duas vezes;
  • o seu valor provavelemnte irá ser atualizado, pelo menos uma vez;
  • todas as ocorrências do seu valor, estã diretamente ligadas à variável (por exemplo, não ser uma coicidência).

Basicamente, não vale a pena declarar uma variável que nunca irá ser atualizada ou que apenas irá ser usada uma vez.

Contextos

O contexto das variáveis no Sass mudou ao longo dos anos. Até muito recentemente, as declarações de variáveis dentro de rulesets e outros contextos eram locais por omissão. No entanto quando já havia uma variável global com o mesmo nome, a declaração local iria alterar o valor da variável global. Desde a versão 3.4, o Sass lida corretamente com o conceito de contextos, e cria uma variável local em vez de substituir a global.

A documentação fala na ocultação de variáveis globais. Quando se declara uma variável, que já existe no contexto global, dentro de um contexto local (selector, função, mixin…), diz-se que a variável local está a ocultar a variável global. Basicamente substitui o seu valor dentro do contexto local.

O enxerto de código seguinte explica o conceito de ocultação de variável

// Inicia uma variável global ao nível de raiz.
// Neste caso, a flag `!global` é opcional.
$variable: 'initial value' !global;

// Cria um mixin que substitui a variável global anterior.
@mixin global-variable-overriding {
  $variable: 'mixin value' !global;
}

.local-scope::before {
  // Cria uma variável local que se sobrepõe à global anterior.
  $variable: 'local value';

  // Inclui o mixin: substitui a variável global.
  @include global-variable-overriding;

  // Imprime o valor da variável.
  // É o valor **local**, visto que se sobrepõe à global.
  content: $variable;
}

// Imprime a variável noutro seletor que nada sobrepõe.
// É a variável **global**, como esperado.
.other-local-scope::before {
  content: $variable;
}
// Inicia uma variável global ao nível de raiz.
// Neste caso, a flag `!global` é opcional.
$variable: 'initial value' !global

// Cria um mixin que substitui a variável global anterior.
@mixin global-variable-overriding
  $variable: 'mixin value' !global

.local-scope::before
  // Cria uma variável local que se sobrepõe à global anterior.
  $variable: 'local value'

  // Inclui o mixin: substitui a variável global.
  +global-variable-overriding

  // Imprime o valor da variável.
  // É o valor **local**, visto que se sobrepõe à global.
  content: $variable

// Imprime a variável noutro seletor que nada sobrepõe.
// É a variável **global**, como esperado.
.other-local-scope::before
  content: $variable

Flag !default

Quando se está a construir uma biblioteca, uma framework, um sistema de grelhas ou qualquer pedaço de código Sass com a intenção de ser distribuido e usado por programadores externos, todas as variáveis de configuração devem ser definidas com o marcador !default de forma a que possam ser substituidas.

$baseline: 1em !default;
$baseline: 1em !default

Graças a isto, um programador pode definir a sua própria variável $baseline antes de importar a vossa biblioteca, sem que esta substitua o valor da vossa variável.

// Variável definida pelo programador
$baseline: 2em;

// A declaração de `$baseline` da vossa biblioteca
@import 'your-library';

// $baseline == 2em;
// Variável definida pelo programador
$baseline: 2em

// A declaração de `$baseline` da vossa biblioteca
@import your-library

// $baseline == 2em

Flag !global

O marcador !global deve ser usado apenas quando se vai substituir uma variável global a partir de um contexto local. Quando se define uma variável num contexto global, o uso deste marcador deve ser omitido.

// Yep
$baseline: 2em;

// Nope
$baseline: 2em !global;
// Yep
$baseline: 2em

// Nope
$baseline: 2em !global

Variáveis multiplas ou mapas

Existem vantagens em usar mapas em vez de múltiplas variáveis. A principal é poder iterar um mapa, algo que não é possivel com variáveis distintas.

Outra vantagem de usar um mapa, é a habilidade de criar uma função de leitura para providenciar uma API mais amigável. Por exemplo, considerem o seguinte código Sass:

/// Mapa dos z-indexes, reunindo todas as camadas Z da aplicação
/// @access private
/// @type Map
/// @prop {String} key - Nome da camada
/// @prop {Number} value - Valor de Z mapeado à chave (key)
$z-indexes: (
  'modal': 5000,
  'dropdown': 4000,
  'default': 1,
  'below': -1,
);

/// Obtém um valor de z-index através do nome de uma camada
/// @access public
/// @param {String} $layer - Nome da camada
/// @return {Number}
/// @require $z-indexes
@function z($layer) {
  @return map-get($z-indexes, $layer);
}
/// Mapa dos z-indexes, reunindo todas as camadas Z da aplicação
/// @access private
/// @type Map
/// @prop {String} key - Nome da camada
/// @prop {Number} value - Valor de Z mapeado à chave (key)
$z-indexes: ('modal': 5000, 'dropdown': 4000, 'default': 1, 'below': -1,)

/// Obtém um valor de z-index através do nome de uma camada
/// @access public
/// @param {String} $layer - Nome da camada
/// @return {Number}
/// @require $z-indexes
@function z($layer)
  @return map-get($z-indexes, $layer)

Extend

A diretiva @extend tem de ser uma das características que tornou Sass tão popular há uns anos atrás. Como lembrete, ela permite dizer ao Sass para estilizar um elemento A, tal como se ele fosse abrangido pelo seletor B. Escusado será dizer que isto pode ser um valioso aliado quando se escreve CSS modular.

No entanto sinto-me na necessidade de vos avisar que esta característica, por muito inteligente que seja, é um conceito traiçoeiro, especialmente quando usado indevidamente. O problema é que quando se extende um seletor, há pouca ou nenhuma forma de responder as seguintes perguntas sem terem um conhecimento profundo de toda a base de código.

  • onde é que o seletor vai ser colocado?
  • é provavel que eu cause efeitos secundários?
  • quando grande é o CSS gerado por simples extend?

Por tudo o que sei, o resultado pode variar entre fazer nada e causar efeitos secundários desastrosos. Por causa disso, o meu primeiro conselho é de evitar a diretiva @extend por completo. Pode soar bruto, mas no fim do dia pode salvar-vos de alguns problemas e dores de cabeça.

Posto desta forma, conhecem o ditado:

Nunca digas nunca.
— Aparentemente, não foi a Beyonce.

Há cenários onde extender seletores pode valer a pena e até ajudar. No entanto, estejam sempre conscientes destas regras para evitar problemas:

  • Usem o extend apenas dentro do módulo, não através de módulos diferentes.
  • Usem o extend em placeholders exclusivamente e não em seletores reais.
  • Tenham a certeza que o placeholder que estão a extender se encontra presente o mínimo possivel pela folhas de estilo.

Se vão usar o extend, deixem-me também lembrar que ele não funciona bem com blocos @media. Tal como devem saber, o Sass é incapaz de extender um seletor exterior a partir de dentro de uma media query. Quando se experimenta, o compilador simplesmente rebenta, avisando-vos de que não consegue fazer tal coisa. Não é bom. Especialmente porque media queries são maioritariamente o que fazemos agora.

%button {
  display: inline-block;
  // … button styles

  // Relationship: a %button that is a child of a %modal
  %modal > & {
    display: block;
  }
}

.button {
  @extend %button;
}

// Yep
.modal {
  @extend %modal;
}

// Nope
.modal {
  @extend %modal;

  > .button {
    @extend %button;
  }
}
%button
  display: inline-block
  // … button styles

  // Relationship: a %button that is a child of a %modal
  %modal > &
    display: block

.button
  @extend %button

// Yep
.modal
  @extend %modal

// Nope
.modal
  @extend %modal

  > .button
    @extend %button

Não devem usar o @extend num seletor exterior a partir de dentro de um bloco @media.
Só devem usar o @extend em seletors a partir de dentro da mesma diretiva.

É ocasionalmente dito que @extend ajuda a reduzir o tamanho do ficheiro, uma vez que combina seletores em vez de duplicar propiedades. Isto é verdade, no entanto a diferença é negligenciável assim que o Gzip faça a sua compressão.

Dito isso, se não puderem usar Gzip (ou algo equivalente) então mudar a abordagem para usar o @extend pode não ser assim tão mau, desde que saibam o que estão a fazer.

Para resumir, eu desaconselho o uso da diretiva @extend, com exceção de algumas circunstâncias específicas, mas não iria tão longe como proibir o seu uso.

Leitura Adicional

Mixins

Os Mixins são uma das funcionalidades mais usadas em todo o Sass. São a chave para reutilização de código e componentes DRY (DRY significa, em Inglês, Don’t Repeat Yourself. Em português podemos traduzir para “Não te repitas”). Os Mixins têm esta caraterística por uma boa razão: eles permitem que o programador defina estilos que podem ser reutilizados em todas os ficheiros de estilos, sem precisarem de utilizar classes não semânticas como .float-left.

Os Mixins podem conter regras de CSS e praticamente tudo o que é permitido num documento Sass. Podem por exemplo, ter argumentos da mesma forma que uma função. As funcionalidades que um Mixin permite são imensas.

No entanto é necessário termos cuidado para não abusarmos das potencialidades de um Mixin. A palavra-chave neste aspecto é simplicidade. Pode ser tentator escrever mixins extremamente capazes com imensa quantidade de lógica. A isto chama-se excesso de engenharia e é algo de que a maioria dos programadores sofre. Não pensem demasiado sobre o vosso código e acima de tudo mantenham-no simples. Se um mixin tiver mais do que 20 linhas, então devem dividi-lo ou revê-lo.

Básicos

Tendo em conta o que foi dito acima, os Mixins são extremamente úteis e devem usar algums. Regra geral, se têm um grupo de propriedades de CSS que aparecem juntas por alguma razão e não por coincidência, devem pô-las num mixin. Por exemplo, o código abaixo, o micro-clearfix hack de Nicolas Gallagher merece ser colocado num mixin:

/// Ajudante para limpar floats
/// @author Nicolas Gallagher
/// @link http://nicolasgallagher.com/micro-clearfix-hack/ Micro Clearfix
@mixin clearfix {
  &::after {
    content: '';
    display: table;
    clear: both;
  }
}
/// Ajudante para limpar floats
/// @author Nicolas Gallagher
/// @link http://nicolasgallagher.com/micro-clearfix-hack/ Micro Clearfix
@mixin clearfix
  &::after
    content: ''
    display: table
    clear: both

Outro exemplo de código que deveria estar num Mixin é definir o tamanho de um elemento, definindo tanto a largura como a altura ao mesmo tempo. Isto não só vai fazer como o vosso código seja mais fácil de escrever como mais fácil de ler.

/// Ajudante para dar tamanho a um elemento
/// @author Hugo Giraudel
/// @param {Length} $width
/// @param {Length} $height
@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}
/// Ajudante para dar tamanho a um elemento
/// @author Hugo Giraudel
/// @param {Length} $width
/// @param {Length} $height
=size($width, $height: $width)
  width: $width
  height: $height
Leitura Adicional

Lista de argumentos

Quando estão a lidar com um mixin que tem um número desconhecido de argumentos, o Sass suporta “argumentos variáveis”. “Argumentos variáveis” são os argumentos que estão no fim de um mixin ou de uma declaração de função e são transformados numa lista, à qual vamos chamar arglist. Isto é implicitamente usado quando passamos um número de argumentos para um mixin ou uma função cuja assinatura de uma função contém ....

@mixin shadows($shadows...) {
  // type-of($shadows) == 'arglist'
  // …
}
=shadows($shadows...)
  // type-of($shadows) == 'arglist'
  // 

Quando estiverem a construir um mixin que aceita três ou mais argumentos, deves pensar duas vezes antes de os passar como uma lista ou uma mapa só porque é mais fácil do que os passar uma a um. O Sass é bastante inteligente relativamente aos mixins e a declaração de funções. Tanto que podemos passar uma lista ou um mapa de argumentos como uma arglist para um mixin ou uma função e estes vão ser lidos como uma série de argumentos.

@mixin dummy($a, $b, $c) {
  // …
}

// Yep
@include dummy(true, 42, 'kittens');

// Yep but nope
$params: (true, 42, 'kittens');
$value: dummy(nth($params, 1), nth($params, 2), nth($params, 3));

// Yep
$params: (true, 42, 'kittens');
@include dummy($params...);

// Yep
$params: (
  'c': 'kittens',
  'a': true,
  'b': 42,
);
@include dummy($params...);
=dummy($a, $b, $c)
  // …

// Yep
+dummy(true, 42, 'kittens')

// Yep but nope
$params: (true, 42, 'kittens')
$value: dummy(nth($params, 1), nth($params, 2), nth($params, 3))

// Yep
$params: (true, 42, 'kittens')
+dummy($params...)

// Yep
$params: ('c': 'kittens', 'a': true, 'b': 42,)
+dummy($params...)
Leitura adicional

Mixins e prefixos de fabricantes

Pode ser tentador definir mixins personalizados para prefixos de fabricantes que são propriedades de CSS não suportadas ou fracamente suportadas. Mas nós não queremos fazer isso. Primeiro, se puderem usar o Autoprefixer, usem-no. O Autoprefixer vai remover código Sass do projeto, vai estar sempre atualizado e vai muito provavelmente fazer um melhor trabalho do que nós a colocar prefixos.

Infelizmente, usar o Autoprefixer nem sempre é uma opção. Se usam Bourbon ou Compass, provavelmente já sabem que tanto um como outro oferecem uma coleção de mixins que tratam dos prefixos de fabricantes por nós.

Se não podem usar Autoprefixer nem Bourbon ou Compass, então, apenas nesta situação, podem criar o vosso próprio mixin para colocar prefixos em propriedades de CSS. Mas, por favor, não criem um mixin por propriedade.

// Nope
@mixin transform($value) {
  -webkit-transform: $value;
  -moz-transform: $value;
  transform: $value;
}
// Nope
=transform($value)
  -webkit-transform: $value
  -moz-transform: $value
  transform: $value

Façam isso de uma forma inteligente.

/// Ajudante para expor prefixos de fabricantes
/// @access public
/// @author HugoGiraudel
/// @param {String} $propriedade - Propriedade de CSS sem prefixo
/// @param {*} $valor - valor de CSS crú
/// @param {List} $prefixos - Lista de prefixos a serem expostos
@mixin prefixo($propriedade, $valor, $prefixos: ()) {
  @each $prefixo in $prefixos {
    -#{$prefixo}-#{$propriedade}: $valor;
  }

  #{$propriedade}: $valor;
}
/// Ajudante para expor prefixos de fabricantes
/// @access public
/// @author HugoGiraudel
/// @param {String} $propriedade - Propriedade de CSS sem prefixo
/// @param {*} $valor - valor de CSS crú
/// @param {List} $prefixos - Lista de prefixos a serem expostos
=prefixo($propriedade, $valor, $prefixos: ()) {
  @each $prefixo in $prefixos {
    -#{$prefixo}-#{$propriedade}: $valor;
  }

  #{$propriedade}: $valor;

Usar este mixin deve ser bastante simples:

.foo {
  @include prefix(transform, rotate(90deg), ('webkit', 'ms'));
}
.foo
  +prefix(transform, rotate(90deg), ('webkit', 'ms'))

Por favor não se esqueçam que isto não é uma solução feliz. Por exemplo, esta solução não consegue lidar com polyfills complexos como os que são necessários para o Flexbox. Neste sentido, usar o Autoprefixer é uma solução muito melhor.

Leitura Adicional

Instruções condicionais

Provavelmente já sabem que Sass fornece instruções condicionais através das diretivas @if e @else. A menos que possuam alguma lógica média ou altamente complexa no vosso código, não há necessidade para instruções condicionais nas vossas folhas de estilo do dia a dia. Na verdade, estas instruções existem maioritariamente para bibliotecas e frameworks.

De qualquer forma, se alguma vez necessitarem delas, por favor respeitem as seguintes diretrizes:

  • Não usar parêntesis exceto quando necessário;
  • Colocar sempre uma linha em branco antes de um @if;
  • Colocar sempre uma linha em branco após abrir chavetas ({);
  • Colocar a instrução @else na mesma linha que o fecho das chavetas anteriores (});
  • Colocar sempre uma linha em branco após fechar as últimas chavetas (}), a menos que a linha seguinte seja uma chaveta de fecho (}).
// Yep
@if $support-legacy {
  // …
} @else {
  // …
}

// Nope
@if ($support-legacy == true) {
  // …
}
@else {
  // …
}
// Yep
@if $support-legacy
  // …
@else
  // …

// Nope
@if ($support-legacy == true)
  // …
@else
  // 

Quando se testa um valor falso, usem sempre a palavra-chave not ao invés de testar contra false ou null.

// Yep
@if not index($list, $item) {
  // …
}

// Nope
@if index($list, $item) == null {
  // …
}
// Yep
@if not index($list, $item)
  // …

// Nope
@if index($list, $item) == null
  // 

Quando se usa instruções condicionais dentro de uma função para devolver um resultado diferente baseado nalguma condição, certifiquem-se que a função ainda possui uma instrução @return fora de qualquer bloco condicional.

// Yep
@if $value == 42 {
  // …
}

// Nope
@if 42 == $value {
  // …
}
// Yep
@if $value == 42
  // …

// Nope
@if 42 == $value
  // 

Quando se utilizam expresões condicionais dentro de uma função para retornar um resultado diferente baseado numa condição, certifiquem-se que a função devolve sempre uma expressão @return fora de qualquer bloco condicional.

// Yep
@function dummy($condition) {
  @if $condition {
    @return true;
  }

  @return false;
}

// Nope
@function dummy($condition) {
  @if $condition {
    @return true;
  } @else {
    @return false;
  }
}
// Yep
@function dummy($condition)
  @if $condition
    @return true

  @return false;

// Nope
@function dummy($condition)
  @if $condition
    @return true
  @else
    @return false

Ciclos

Visto que Sass providencia estruturas de dados complexas, tal como listas e mapas, não é surpresa fornecer também formas de iterar sobre essas entidades.

No entanto, a presença de ciclos normalmente implica lógica moderadamente complexa que provavelmente não pertence a Sass. Antes de usar um ciclo, certifiquem-se que tal faz sentido e que realmente resolve um problema.

Each

O ciclo @each é definitivamente o mais usado dos três ciclos que Sass oferece. Apresenta uma API simples para iterar uma lista ou mapa.

@each $theme in $themes {
  .section-#{$theme} {
    background-color: map-get($colors, $theme);
  }
}
@each $theme in $themes
  .section-#{$theme}
    background-color: map-get($colors, $theme)

Ao iterar um mapa, usem sempre $key e $value como nomes de variáveis para manter a consistência.

@each $key, $value in $map {
  .section-#{$key} {
    background-color: $value;
  }
}
@each $key, $value in $map
  .section-#{$key}
    background-color: $value

Façam também questão de respeitar as seguintes diretrizes para preservar a legibilidade:

  • Colocar sempre uma linha em branco antes do @each;
  • Colocar sempre uma linha em branco antes da chaveta de fecho (}) a menos que a próxima linha seja uma chaveta de fecho (}).

For

O ciclo @for poderá ser útil quando combinado com as pseudo-classes :nth-* de CSS. À exceção destes cenários, é preferível um ciclo @each se têm de iterar sobre algo.

@for $i from 1 through 10 {
  .foo:nth-of-type(#{$i}) {
    border-color: hsl($i * 36, 50%, 50%);
  }
}
@for $i from 1 through 10
  .foo:nth-of-type(#{$i})
    border-color: hsl($i * 36, 50%, 50%)

Usem sempre $i como nome de variável para manter a convenção comum e a menos que tenham boa razão para tal, nunca usem a palavra-chave to: usem sempre through. Muitos programadores não sabem que Sass oferece esta variação; usá-la poderá levar a confusão.

Também não se esqueçam de respeitar as seguintes diretrizes para preservar a legibilidade:

  • Colocar sempre uma linha em branco antes do @each;
  • Colocar sempre uma linha em branco antes da chaveta de fecho (}) a menos que a próxima linha seja uma chaveta de fecho (}).

While

O ciclo @while não possui absolutamente nenhum uso real num projeto de Sass, especialmente porque não é possível quebrar o ciclo por dentro. Não usar.

Avisos e erros

Se há uma característica que é muitas vezes esquecida pelos programadores de Sass, é a capacidade de enviar dinamicamente avisos e erros. Por acaso, Sass vem com três diretivas personalizadas para imprimir conteúdo no standard output system (CLI, compilador…):

  • @debug;
  • @warn;
  • @error.

Vamos colocar o @debug de lado, uma vez que se destina claramente a fazer debug a SassScript, o que não é o nosso ponto aqui. Ficamos então com @warn and @error que são visivelmente idênticos, exceto que um para o compilador, enquanto que o outro não. Vocês podem adivinhar qual faz o quê.

Agora, há muito espaço num projecto de Sass para avisos e erros. Basicamente, qualquer mixin ou função à espera de um tipo ou argumento específico poderia lançar um error se algo desse errado, ou mostrar um aviso ao fazer uma suposição.

Leitura adicional

Avisos

Considerem esta função de Sass-MQ a tentar converter um valor em px para em, por exemplo:

@function mq-px2em($px, $base-font-size: $mq-base-font-size) {
  @if unitless($px) {
    @warn 'Assuming #{$px} to be in pixels, attempting to convert it into pixels.';
    @return mq-px2em($px + 0px);
  } @else if unit($px) == em {
    @return $px;
  }

  @return ($px / $base-font-size) * 1em;
}
@function mq-px2em($px, $base-font-size: $mq-base-font-size)
  @if unitless($px)
    @warn 'Assuming #{$px} to be in pixels, attempting to convert it into pixels.'
    @return mq-px2em($px + 0px)
  @else if unit($px) == em
    @return $px

  @return ($px / $base-font-size) * 1em

Se o valor fornecido não possuir unidade, a função assume que o valor está expresso em píxeis. Neste ponto, uma suposição poderá ser arriscada, pelo que o utilizador deverá ser avisado que o software fez algo que poderá ser considerado inesperado.

Erros

Erros, ao contrário de avisos, previnem o compilador de ir mais além. Basicamente, interrompem o compilador e mostram uma mensagem no output stream tal como o stack trace, o que é útil para debugging. Por causa disto, erros deverão ser lançados quando não há mais forma do programa continuar a correr. Sempre que possível, tentem antes contornar o problema e mostrar um aviso em seu lugar.

Como exemplo, digamos que querem construir uma função getter para aceder a valores de um mapa específico. Poderiam lançar um erro se a chave pedida não existir no mapa.

/// Mapa dos z-indexes, reunindo todas as camadas Z da aplicação
/// @access private
/// @type Map
/// @prop {String} key - Nome da camada
/// @prop {Number} value - Valor de Z mapeado à chave (key)
$z-indexes: (
  'modal': 5000,
  'dropdown': 4000,
  'default': 1,
  'below': -1,
);

/// Obtém um valor de z-index através do nome de uma camada
/// @access public
/// @param {String} $layer - Nome da camada
/// @return {Number}
/// @require $z-indexes
@function z($layer) {
  @if not map-has-key($z-indexes, $layer) {
    @error 'There is no layer named `#{$layer}` in $z-indexes. '
         + 'Layer should be one of #{map-keys($z-indexes)}.';
  }

  @return map-get($z-indexes, $layer);
}
/// Mapa dos z-indexes, reunindo todas as camadas Z da aplicação
/// @access private
/// @type Map
/// @prop {String} key - Nome da camada
/// @prop {Number} value - Valor de Z mapeado à chave (key)
$z-indexes: ('modal': 5000, 'dropdown': 4000, 'default': 1, 'below': -1,)

/// Obtém um valor de z-index através do nome de uma camada
/// @access public
/// @param {String} $layer - Nome da camada
/// @return {Number}
/// @require $z-indexes
@function z($layer)
  @if not map-has-key($z-indexes, $layer)
    @error 'There is no layer named `#{$layer}` in $z-indexes. '
         + 'Layer should be one of #{map-keys($z-indexes)}.'

  @return map-get($z-indexes, $layer)

Ferramentas

O que um pré-processador de CSS tão popular como Sass tem de bom é que vem com todo um ecossistema de frameworks, plugins, bibliotecas e ferramentas. Após 8 anos de existência, estamos cada vez mais perto do ponto onde tudo o que pode ser escrito em Sass foi escrito em Sass.

No entanto, o meu conselho seria para reduzir o número de dependências ao estritamente necessário. Gerir dependências é uma espécie de inferno do qual não querem fazer parte. Além disso, há pouca ou nenhuma necessidade de dependências externas no que toca a Sass.

Compass

Compass é a framework principal de Sass por aí. Desenvolvida por Chris Eppstein, um dos designers principais do Sass, não a vejo a perder popularidade drasticamente durante uns tempos, se querem a minha opinião.

Mesmo assim, já não uso Compass, a razão principal sendo que abranda muito processamento de Sass. Sass em Ruby é bastante lento por si só, então adicionar mais Ruby e Sass por cima não ajuda de verdade.

De verdade, usamos muito pouco de toda a framework. Compass é enorme. Mixins de compatibilidade inter-browser é apenas a ponta do icebergue. Funções matemáticas, auxiliares de images, spriting… Há muito que pode ser feito com esta grande peça de software.

Infelizmente, isto tudo é apenas agradável, mas não essencial. Uma exceção poderia ser feita do construtor de sprites, que é realmente bom, mas Grunticon e Grumpicon dão para a despesa, e têm o benefício de poderem ser integrados no building process.

De qualquer forma, eu não proíbo o uso de Compass embora também não o recomende, especialmente visto que não é compatível com LibSass (mesmo que esforços tenham sido desenvolvidos nessa direção). Se se sentem melhor em usá-la, é justo, mas não acho que tirarão muito dela no final do dia.

Sass em Ruby está de momento a sofrer otimizações notáveis que são especificamente orientadas a estilos altamente baseados em lógica, com muitas funções e mixins. Estas mudanças deverão melhorar dramaticamente o seu desempenho, até ao ponto onde Compass e outras frameworks já não abrandarão Sass.

Leitura adicional

Sistemas de grelhas

Não usar um sistema de grelhas não é opção, agora que Responsive Web Design é omnipresente. Para manter designs consistentes e sólidos em todos os tamanhos, usamos um tipo de grelha para dispor os elementos. Para evitar ter que programar estas grelhas uma e outra vez, algumas mentes brilhantes tornaram estas reutilizáveis.

Deixem-me ser direto: não sou grande fã de sistemas de grelhas. Claro que vejo o potencial, mas penso que a maior parte deles são completamente exagerados e são maioritariamente usados para desenhar colunas vermelhas num fundo branco em apresentações totós de designers. Quando foi a última vez que que pensaram graças-a-Deus-que-tenho-esta-ferramenta-para-construir-esta-grelha-de-2-5-3.1-π? Exatamente, nunca. Porque na maior parte dos casos, apenas querem a grelha comum de 12 colunas, nada de especial.

Se estão a usar uma framework de CSS para o vosso projecto como Bootstrap ou Foundation, é provável que já incluam um sistema de grelhas que eu recomendo que usem de forma a evitar ter que lidar com mais uma dependência.

Se não estiverem presos a um sistema de grelhas específico, gostarão de saber que há dois sistemas de grelhas topo de gama: Susy e Singularity. Ambos fazem muito mais do que alguma vez irão precisar, por isso podem escolher o que preferirem de entre os dois e terão a certeza que todos os vossos casos extremos—mesmo os mais rebuscados—estarão cobertos. Se me perguntarem, Susy possui uma comunidade ligeiramente melhor, mas é apenas a minha opinião.

Ou podem preferir algo mais casual, como csswizardry-grids. No final de contas, a escolha não terá muito impacto no vosso guia de estilo, por isso fica ao vosso critério.

Leitura adicional

SCSS-lint

Analisar código é muito importante. Normalmente, seguir as diretrizes de um guia de estilo ajuda a reduzir a quantidade de erros de qualidade de código, mas ninguém é perfeito e há sempre algo a melhorar. Por isso pode-se dizer que fazer linting a código é tão importante quanto documentá-lo.

SCSS-lint é uma ferramenta importante para vos ajudar a manter os vossos ficheiros SCSS limpos e legíveis. É completamente personalizável e fácil de integrar com as vossas ferramentas.

Felizmente, as recomendações de SCSS-lint são bastante parecidas com as descritas neste documento. De forma a configurar SCSS-lint de acordo com as Sass Guidelines, recomendo a seguinte configuração:

linters:

  BangFormat:
    enabled: true
    space_before_bang: true
    space_after_bang: false

  BemDepth:
    enabled: true
    max_elements: 1

  BorderZero:
    enabled: true
    convention: zero

  ChainedClasses:
    enabled: false

  ColorKeyword:
    enabled: true

  ColorVariable:
    enabled: false

  Comment:
    enabled: false

  DebugStatement:
    enabled: true

  DeclarationOrder:
    enabled: true

  DisableLinterReason:
    enabled: true

  DuplicateProperty:
    enabled: false

  ElsePlacement:
    enabled: true
    style: same_line

  EmptyLineBetweenBlocks:
    enabled: true
    ignore_single_line_blocks: true

  EmptyRule:
    enabled: true

  ExtendDirective:
    enabled: false

  FinalNewline:
    enabled: true
    present: true

  HexLength:
    enabled: true
    style: short

  HexNotation:
    enabled: true
    style: lowercase

  HexValidation:
    enabled: true

  IdSelector:
    enabled: true

  ImportantRule:
    enabled: false

  ImportPath:
    enabled: true
    leading_underscore: false
    filename_extension: false

  Indentation:
    enabled: true
    allow_non_nested_indentation: true
    character: space
    width: 2

  LeadingZero:
    enabled: true
    style: include_zero

  MergeableSelector:
    enabled: false
    force_nesting: false

  NameFormat:
    enabled: true
    convention: hyphenated_lowercase
    allow_leading_underscore: true

  NestingDepth:
    enabled: true
    max_depth: 1

  PlaceholderInExtend:
    enabled: true

  PrivateNamingConvention:
    enabled: true
    prefix: _

  PropertyCount:
    enabled: false

  PropertySortOrder:
    enabled: false

  PropertySpelling:
    enabled: true
    extra_properties: []

  PropertyUnits:
    enabled: false

  PseudoElement:
    enabled: true

  QualifyingElement:
    enabled: true
    allow_element_with_attribute: false
    allow_element_with_class: false
    allow_element_with_id: false

  SelectorDepth:
    enabled: true
    max_depth: 3

  SelectorFormat:
    enabled: true
    convention: hyphenated_lowercase
    class_convention: '^(?:u|is|has)\-[a-z][a-zA-Z0-9]*$|^(?!u|is|has)[a-zA-Z][a-zA-Z0-9]*(?:\-[a-z][a-zA-Z0-9]*)?(?:\-\-[a-z][a-zA-Z0-9]*)?$'

  Shorthand:
    enabled: true

  SingleLinePerProperty:
    enabled: true
    allow_single_line_rule_sets: false

  SingleLinePerSelector:
    enabled: true

  SpaceAfterComma:
    enabled: true

  SpaceAfterPropertyColon:
    enabled: true
    style: one_space

  SpaceAfterPropertyName:
    enabled: true

  SpaceAfterVariableColon:
    enabled: true
    style: at_least_one_space

  SpaceAfterVariableName:
    enabled: true

  SpaceAroundOperator:
    enabled: true
    style: one_space

  SpaceBeforeBrace:
    enabled: true
    style: space
    allow_single_line_padding: true

  SpaceBetweenParens:
    enabled: true
    spaces: 0

  StringQuotes:
    enabled: true
    style: single_quotes

  TrailingSemicolon:
    enabled: true

  TrailingZero:
    enabled: true

  TransitionAll:
    enabled: false

  UnnecessaryMantissa:
    enabled: true

  UnnecessaryParentReference:
    enabled: true

  UrlFormat:
    enabled: false

  UrlQuotes:
    enabled: true

  VariableForProperty:
    enabled: false

  VendorPrefixes:
    enabled: true
    identifier_list: base
    include: []
    exclude: []

  ZeroUnit:
    enabled: true

Se pretendem ligar o SCSS-lint no build process de Grunt, gostarão de saber que há um plugin de Grunt para tal chamado grunt-scss-lint.

Além disso, se estão à procura de uma aplicação boa que funcione com SCSS-lint e afins, o pessoal da Thoughtbot (Bourbon, Neat…) estão a trabalhar na Hound.

Leitura adicional

Longo Demais; Não Li

Resumindo, queremos:

  • Indentação de dois (2) espaços, nada de tabulações (tabs);
  • Linhas com largura de 80 carateres;
  • CSS multi-linha devidamente escrito;
  • Uso significativo de espaços em branco;
  • Strings e URLs entre aspas (simples);
  • Não usar zeros (0) não significativos à direita da vírgula, mas obrigatórios à esquerda;
  • Cálculos entre parêntises;
  • Nada de números mágicos;
  • Cores expressas em palavras > HSL > RGB > hexadecimal;
  • Listas separadas com vírgulas;
  • Não usar vírgulas no fim de listas (visto que são linha única);
  • Vírgula no fim de mapas;
  • Não encadear seletores exceto para pseudo-classes e pseudo-elementos;
  • Nomenclatura delineada por hífens;
  • Comentários extensivos;
  • Comentários da API ao estilo de SassDoc
  • Uso limitado de @extend;
  • Mixins simples;
  • O mínimo de ciclos possível, não usar @while;
  • Número reduzido de dependências;
  • Uso significativo de avisos e erros.
De volta ao topo