Logotipo do CataBits

CataBits

A máquina movida à bits

Folhas de Estilo com SASS/SCSS

Por André Luferat em 29/04/2024.

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.

Folhas de Estilo com SASS/SCSS

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:

style.sass
style.scss
style.css
// 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:

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:

model.html
model.scss
model.js
<!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);
    });
};

Ver exemplo online

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 biblioteca sass.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.

Comentários