Folhas de Estilo com SASS/SCSS
O HTML é uma estrutura fortemente hierárquica onde temos elementos parent (pais) e children (filhos) que são aninhados de forma a organizar a estrutura da página que estamos escrevendo.
Para exemplificar, observe o código fonte HTML5 abaixo:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>HTML5 Model</title>
</head>
<body>
<div id="wrap">
<header>Cabeçalho</header>
<nav>
<a href="#link1">Link 1</a>
<a href="#link2">Link 2</a>
<a href="#link3">Link 3</a>
</nav>
<main>
<article>Conteúdo</article>
<aside>Complemento</aside>
</main>
<footer>Rodapé</footer>
</div>
</body>
</html>
No código HTML fornecido, o elemento "raiz" é o elemento <html>
.
Ele é o primeiro elemento que envolve todo o código HTML e é o elemento pai de todos os outros elementos na página.
A estrutura começa no elemento <html>
e termina com o fechamento do mesmo elemento no final do código onde vemos </html>
...
O atributo
na primeira linha indica que o documento é um documento HTML5.
Não é uma tag HTML e sim um DTD (Document Type Definition) que informa para o navegador que está abrindo este documento que ele deve usar a "engine" HTML5 para processá-lo.
Usando indentação, podemos representar facilmente quem são os "pais" e "filhos" nessa árvore hierárquica:
html
head
meta { charset: "UTF-8" }
meta {
name: "viewport",
content: "width=device-width, initial-scale=1.0"
}
title [ "HTML5 Model" ]
body
div#wrap
header [ "CABEÇALHO" ]
nav {
a { href: #link1 } [ "Link 1" ]
a { href: #link2 } [ "Link 2" ]
a { href: #link3 } [ "Link 3" ]
}
main
article [ "CONTEÚDO" ]
aside [ "COMPLEMENTO" ]
footer [ "RODAPÉ" ]
Poderíamos ainda usar JSON, UML, XML é qualquer outra forma de representar estruturas hierárquicas que o código acima seria perfeitamente representado. Legal, não é?
CSS é plano
A segunda etapa "natural" na construção de uma página HTML é o CSS, e aí começa uma pequena confusão: a sintaxe do CSS é plana, sem hierarquia definida... Como vamos representar visualmente um mundo hierárquico usando um ambiente plano?
Essa "incompatibilidade" com a hierarquia HTML torna nossos códigos fonte CSS confusos, difíceis de debugar, extremamente repetitivos e trabalhosos para desenvolver. Apesar de poderoso, o CSS tem uma sintaxe "feia" para os padrões do desenvolvimento moderno.
De volta à rigidez da hierarquia
Pensando nisso e com falta do que fazer - Sempre assim, não é? - é que em 2006 o jovem Hampton Catlin criou o (a?) SASS, Syntactically Awesome Stylesheets que nada mais é do que a representação hierárquica do HTML aplicada às folhas de estilo. Temos assim, um "superset" que estende, atribui hierarquia, otimiza e, para completar, ainda traz um monte de novas "traquitanas" para as folhas de estilo.
Mas a coisa ainda não estava perfeita, então, no ano seguinte, Nathan Weizenbaum, outro com "Nada para fazer", fez o SCSS que tem as mesmas estruturas do SASS, mas com uma sintaxe totalmente compatível com o CSS original e que acabou se tornando "padrão" em diversas plataformas de desenvolvimento de aplicações Web.
Veja um exemplo de SASS e de SCSS para estilizar nossa página "HTML Model", juntamente com o CSS resultante:
// Variáveis
$pagewrap-width: 1080px
$default-font-family: arial, helvetica, sans-serif
$page-background: steelblue
// Todos os elementos
*
box-sizing: border-box
outline: none
// Elementos raiz
html, body, #wrap
position: relative
margin: 0
padding: 0
height: 100%
// Hierarquia do documento
html
font:
family: $default-font-family
size: 16px
body
background-color: $page-background
a
color: steelblue
text-decoration: none
&:hover
color: orangered
text-decoration: underline
#wrap
display: flex
flex-direction: column
max-width: $pagewrap-width
margin: auto
>header
background-color: #eee
padding: 1rem
font:
size: 2rem
weight: bold
>nav
background-color: #000
display: flex
justify-content: space-around
a
padding: 0.5rem 1rem
color: #fff
text:
decoration: none
transform: uppercase
&:hover
background-color: #ddd
color: #333
>main
flex-grow: 1
display: flex
flex-direction: column
>article
padding: 1rem
flex-grow: 1
background-color: #fff
>aside
padding: 0.5rem
background-color: #ddd
>footer
background-color: #000
color: #fff
padding: .5rem 1rem
text-align: center
@media screen and (min-width: 768px)
html
body
#wrap
>main
flex-direction: row
// Variáveis
$pagewrap-width: 1080px;
$default-font-family: arial, helvetica, sans-serif;
$page-background: steelblue;
// Todos os elementos
* {
box-sizing: border-box;
outline: none;
}
// Elementos raiz
html,
body,
#wrap {
position: relative;
margin: 0;
padding: 0;
height: 100%;
}
// Hierarquia do documento
html {
font: {
family: $default-font-family;
size: 16px;
}
body {
background-color: $page-background;
a {
color: steelblue;
text-decoration: none;
&:hover {
color: orangered;
text-decoration: underline;
}
}
#wrap {
display: flex;
flex-direction: column;
max-width: $pagewrap-width;
margin: auto;
>header {
background-color: #eee;
padding: 1rem;
font: {
size: 2rem;
weight: bold;
}
}
>nav {
background-color: #000;
display: flex;
justify-content: space-around;
a {
padding: 0.5rem 1rem;
color: #fff;
text: {
decoration: none;
transform: uppercase;
}
&:hover {
background-color: #ddd;
color: #333;
}
}
}
>main {
flex-grow: 1;
display: flex;
flex-direction: column;
>article {
padding: 1rem;
flex-grow: 1;
background-color: #fff;
}
>aside {
padding: 0.5rem;
background-color: #ddd;
}
}
>footer {
background-color: #000;
color: #fff;
padding: .5rem 1rem;
text-align: center;
}
}
}
}
@media screen and (min-width: 768px) {
html {
body {
#wrap {
>main {
flex-direction: row;
}
}
}
}
}
* {
box-sizing: border-box;
outline: none;
}
html,
body,
#wrap {
position: relative;
margin: 0;
padding: 0;
height: 100%;
}
html {
font-family: arial, helvetica, sans-serif;
font-size: 16px;
}
html body {
background-color: steelblue;
}
html body a {
color: steelblue;
text-decoration: none;
}
html body a:hover {
color: orangered;
text-decoration: underline;
}
html body #wrap {
display: flex;
flex-direction: column;
max-width: 1080px;
margin: auto;
}
html body #wrap>header {
background-color: #eee;
padding: 1rem;
font-size: 2rem;
font-weight: bold;
}
html body #wrap>nav {
background-color: #000;
display: flex;
justify-content: space-around;
}
html body #wrap>nav a {
padding: 0.5rem 1rem;
color: #fff;
text-decoration: none;
text-transform: uppercase;
}
html body #wrap>nav a:hover {
background-color: #ddd;
color: #333;
}
html body #wrap>main {
flex-grow: 1;
display: flex;
flex-direction: column;
}
html body #wrap>main>article {
padding: 1rem;
flex-grow: 1;
background-color: #fff;
}
html body #wrap>main>aside {
padding: 0.5rem;
background-color: #ddd;
}
html body #wrap>footer {
background-color: #000;
color: #fff;
padding: 0.5rem 1rem;
text-align: center;
}
@media screen and (min-width: 768px) {
html body #wrap>main {
flex-direction: row;
}
}
/*# sourceMappingURL=style.css.map */
Observe que a sintaxe do SASS usa indentação para designar os blocos e não tem caractere de fim de linha (;
), de forma similar ao Python.
Já o SCSS usa ";
" no final das linhas e é blocado com chaves ({...}
) da mesma forma que o CSS.
Se você prestou atenção aos códigos, deve ter percebido a hierarquia no SASS e no SCSS, similar a do HTML que vai ser estilizado. Já o CSS é plano, sendo necessário estilizar parte por parte.
Na verdade, o código CSS que está aí em cima, já está otimizado, isso porque ele não foi feito "na mão" como fiz o SASS. Ele é o resultado da transpilação do SASS, executada pelo aplicativo apropriado. Isso é necessário porque os navegadores ainda não reconhecem o SASS/SCSS como linguagem de estilos, apenas o CSS o é. Portanto, para usar SASS/SCSS é necessário "traduzir" as estruturas para "CSS" e incluí-la no código fonte para que seja reconhecida pelo navegador.
Isso pode ser visto lá no <head>
do HTML onde usamos:
<link rel="stylesheet" href="style.css">
Essa linha faz o link com a versão CSS das folhas de estilo, ou seja, do resultado da transpilação do SASS.
O mesmo vale para o SCSS. No caso, a versão de exemplo foi obtida à partir da transpilação da versão SASS original. O processo é similar ao que fazemos com o "TypeScript" que, para funcionar no navegador, precisa ser "transpilado" para JavaScript.
Além do SASS e do SCSS, existem outras "meta linguagens hierarquicas" para descrever folhas de estilo para Web. Um bom exemplo é o LESS. Todas tem sintaxe e estruturas similares como "@mixin", variáveis, "@extend", etc.
Transpilando...
À partir daqui vou trabalhar com SCSS que é a forma mais compatível com o CSS original, é mais fácil compreender e é mais usada no mercado, ok?
Estava tudo bem até que entrou essa tal de "transpilação", não é verdade? Mas calma! Não é complicado.
Dependendo do case, existem 4 formas de trabalhar com SASS que costumo usar:
Frameworks
Diversos frameworks disponíveis no mercado, principalmente para "front-end", convertem metalinguagens de estilos para CSS, como Angular, React, Next.js e por aí vai. Nesses, basta descrever seus estilos em SCSS, por exemplo, que, ao gerar a página HTML para o navegador, a coisa será feita como mágica!
Linha de comando
Existem diversos aplicativos que fazem a transpilação pelo terminal. Eu pessoalmente, uso o Sass Oficial, na versão JavaScript, que roda sobre o Node.js.
No site oficial tem todas as instruções, mas, basicamente:
- Tenha o Node.js instalado e atualizado;
- Instale o SASS globalmente, comandando no "Node.js command prompt" ou no terminal, se usa Linux ou Mac:
npm install -g sass
- Acesse a pasta raiz do projeto e abra um terminal - pode ser no próprio VSCode - e comande, por exemplo:
sass --watch css/style.scss css/style.css
- Isso vai converter, em tempo real, enquanto estiver desenvolvendo, "
style.scss
" para "style.css
", ambos na pasta "css
" do projeto. Entendeu?- Lembre-se de ajustar os caminhos para os arquivos!
- Se você já tem um SASS ou SCSS pronto e quer apenas gerar o CSS final, comande:
sass css/style.scss css/style.css
Biblioteca de Back-end
Dependendo da linguagem que você vai usar no back-end, desde que esteja construindo uma aplicação Web "full-stack", você pode usar uma biblioteca de transpilação em tempo real. Alguns exemplos:
- PHP → SassPHP
- Python → Sass/SCSS for Python
- Java → Java Maven LibSass
- ...
E, se você procurar, vai achar até para linguagens da era dos dinos... Comece por aqui.
Biblioteca Front-end
É uma forma menos recomendada, mas funciona bem e com poucas limitações. Você vai usar a biblioteca "sass.sync.js" que transpila de forma síncrona. Tem algumas documentações sobre o assunto, mas, basicamente, fiz tudo comentado nos códigos abaixo:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Importa a biblioteca "sass.sync.min.js" de um CDN -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/sass.js/0.11.01/sass.sync.min.js"></script>
<!-- Importa a função que processa o SCSS -->
<script src="model.js"></script>
<!-- Executa a função, passando o arquivo SCSS como parâmetro. -->
<script>
compileScss('model.scss');
// Você pode carregar outras folhas de estilo...
compileScss('example.scss');
</script>
<title>HTML5 Model</title>
</head>
<body>
<div id="wrap">
<header>Cabeçalho</header>
<nav>
<a href="#link1">Link 1</a>
<a href="#link2">Link 2</a>
<a href="#link3">Link 3</a>
</nav>
<main>
<article>Conteúdo</article>
<aside>Complemento</aside>
</main>
<footer>Rodapé</footer>
</div>
</body>
</html>
// Variáveis
$pagewrap-width: 1080px;
$default-font-family: arial, helvetica, sans-serif;
$page-background: steelblue;
// Todos os elementos
* {
box-sizing: border-box;
outline: none;
}
// Elementos raiz
html,
body,
#wrap {
position: relative;
margin: 0;
padding: 0;
height: 100%;
}
// Hierarquia do documento
html {
font: {
family: $default-font-family;
size: 16px;
}
body {
background-color: $page-background;
a {
color: steelblue;
text-decoration: none;
&:hover {
color: orangered;
text-decoration: underline;
}
}
#wrap {
display: flex;
flex-direction: column;
max-width: $pagewrap-width;
margin: auto;
>header {
background-color: #eee;
padding: 1rem;
font: {
size: 2rem;
weight: bold;
}
}
>nav {
background-color: #000;
display: flex;
justify-content: space-around;
a {
padding: 0.5rem 1rem;
color: #fff;
text: {
decoration: none;
transform: uppercase;
}
&:hover {
background-color: #ddd;
color: #333;
}
}
}
>main {
flex-grow: 1;
display: flex;
flex-direction: column;
>article {
padding: 1rem;
flex-grow: 1;
background-color: #fff;
}
>aside {
padding: 0.5rem;
background-color: #ddd;
}
}
>footer {
background-color: #000;
color: #fff;
padding: .5rem 1rem;
text-align: center;
}
}
}
}
@media screen and (min-width: 768px) {
html {
body {
#wrap {
>main {
flex-direction: row;
}
}
}
}
}
// Função para compilar SCSS e adicionar o resultado como um elemento <style> ao <head>
async function compileScss(scssFileUrl) {
const response = await fetch(scssFileUrl);
const file_scss = await response.text();
Sass.compile(file_scss, (result) => {
const styleElement = document.createElement('style');
styleElement.textContent = result.text;
document.head.appendChild(styleElement);
});
};
Observe que deixamos de carregar as folhas de estilo CSS e passamos a fazê-lo pela função JavaScript compileScss()
.
Mas, temos que observar alguns detalhes:
- É recomendado que os scripts estejam no <header> para diminuir os delays, mesmo assim, caso a conexão à Internet esteja lenta, poderemos observar a página sendo estilizada em tempo real;
- Para diminuir ainda mais o delay, hospede
sass.sync.min.js
no próprio site; - Por algum motivo, não consegui usar
@import
com a bibliotecasass.sync.js
. Se conseguir, me passa a dica!
Conclusão
Tenho certeza que, se você ainda não estiliza com SASS ou SCSS, é só começar que você vai "viciar"! Minha dica é começar por aqui.
E, se quiser trocar mais idéias sobre, use os comentários abaixo.