Atualizando o SASS do Computaria
Após o Manipulando query string para melhor permitir compartilhar uma página carregada dinamicamente acabei atualizando no meu sistema a gem com o SASS. E isso teve um efeito um tanto quanto inesperado: o SASS começou a disparar novos warnings.
Primeiro warning: darken
A primeira reclamação que me subiu fortemente foi em relação ao darken. Ele
sugeria usar outra alternativa, simplesmente trocar por color.adjust:
$grey-color-dark: darken($grey-color, 15%); // original
$grey-color-dark: color.adjust($color, $lightness: -15%); // sugestão
Mas ao usar ele dava um erro:
Error: There is no module with the namespace "color".
╷
40 │ $grey-color-dark: color.adjust($grey-color, $lightness: -25%);
Mas então, o que seria isso?
Bem, descobri que é porque agora o SASS está indo para um caminho mais modular.
E eu não chamei o módulo color. Como faço pra usar o módulo? Chamo com
@use! Como é um módulo bem padrão, ele é prefixado com sass:, então vamos
chamar o módulo color do sass?
@use "sass:color";
$grey-color-dark: color.adjust($grey-color, $lightness: -25%);
Perfeito, aparentemente funcionou! Efetivamente o SASS reclamou a mesma coisa
pro lighten, então tal qual foi pro darken, só fazer um ajuste de
lighteness da cor. Como é para ficar mais clara, o ajuste é positivo, já que
o ajuste para dicar mais escuro era negativo:
@use "sass:color";
$grey-color-dark: color.adjust($grey-color, $lightness: -25%);
$grey-color-light: color.adjust($grey-color, $lightness: 40%);
Ok, como vemos se tá dando certo? Abrindo o _site/css/main.css! Sim, eu
valido o artefato final e profit!
No começo eu realmente fiz isso, ao ponto de comparar com o artefato do blog
disponível na web em
https://computaria.gitlab.io/blog/css/main.css
(inclusive tinha mudanças que o diff não estava pegando direito, então fiz na
mão o meu diferencial).
Mas tem um jeito muito melhor! Posso usar o comando @debug!
Então, vamos verificar se a cor está de acordo com o que eu tinha no blog já compilado! Pegar um trecho aqui do SCSS:
blockquote {
color: $grey-color;
border-left: 4px solid $grey-color-light;
/* ... */
}
/* da web */
blockquote {
color: #828282;
border-left: 4px solid #e8e8e8;
/* .. */
}
/* gerado local */
blockquote {
color: #828282;
border-left: 4px solid #e8e8e8;
/* .. */
}
Muito bem, o lighten foi dominado. E para o darken?
.site-header {
border-top: 5px solid c.$grey-color-dark;
/* .. */
}
/* original */
.site-header {
border-top: 5px solid #424242;
/* .. */
}
/* gerado local */
.site-header {
border-top: 5px solid rgb(66.25, 66.25, 66.25);
/* .. */
}
Ok, não foi o resultado que eu queria. O $lightness: -15% resultou em um
número quebrado. Qual diferença um 0.25 faria em um olho humano em um
intervalo que vai de [0, 255)? Nenhuma. Mas eu queria resolver isso. E já que
eu vou ajeitar pro darken por conta de uma coincidência de valores, vou
aproveitar e fazer pro lighten também! Vamos começar a testar hipóteses com
@debug?
Vamos lá, e se eu arredondar o valor? Eu tenho o módulo math e após declarar
que vou usá-lo tenho acesso a math.round. Mas pra isso vou precisar acessar
o vermelho, o verde e o vermelho da cor… e, bem? temos color.channel!
Aqui, o color.channel você passa a cor e de que canal quer tirar a
propriedade. Como eu quero tirar o vermelho de $grey-color-dark:
@debug color.channel($grey-color-dark, "red");
E isso imprimiu Debug: 66.25. Ok, progresso. Arredondar esse valor:
@debug math.round(color.channel($grey-color-dark, "red"));
Arredondado bonitinho: Debug: 66. Qual o próximo passo? Criar uma nova cor!
Mas como faço isso? Bem, vou tentar colocar o vermelho, o verde e o azul dentro
da função rgb e ver no que dá… criei uma variável $grey-j só para ficar
mais fácil manipular e eu conseguir ler o que estou imprimindo:
@use "sass:math";
/* ... */
$grey-j: rgb(
math.round(color.channel($grey-color-dark, "red")),
math.round(color.channel($grey-color-dark, "green")),
math.round(color.channel($grey-color-dark, "blue"))
);
@debug $grey-j;
E o resultado foi um decepcionante Debug: rgb(66, 66, 66). Não obtive o que
eu deseja, que era o equivalente #424242. E se… e se eu usar a função
color.change para criar uma nova cor?
Vou continuar usar o $grey-j para os testes, mas vou por o valor original
criado pelo ajuste de escurecimento em uma variável raw, digamos assim.
$grey-color-dark-raw:
$grey-color: #828282;
$grey-color-dark-raw: color.adjust($grey-color, $lightness: -25%);
$grey-j: color.change($grey-color-dark-raw,
$red: math.round(color.channel($grey-color-dark-raw, "red")),
$green: math.round(color.channel($grey-color-dark-raw, "green")),
$blue: math.round(color.channel($grey-color-dark-raw, "blue"))
);
@debug $grey-j;
E com isso obtive Debug: #424242! Uhulll!!!
A versão sem o código de debug ficou assim:
$grey-color: #828282;
$grey-color-light-raw: color.adjust($grey-color, $lightness: 40%);
$grey-color-dark-raw: color.adjust($grey-color, $lightness: -25%);
$grey-color-dark: color.change($grey-color-dark-raw,
$red: math.round(color.channel($grey-color-dark-raw, "red")),
$green: math.round(color.channel($grey-color-dark-raw, "green")),
$blue: math.round(color.channel($grey-color-dark-raw, "blue"))
);
$grey-color-light: color.change($grey-color-light-raw,
$red: math.round(color.channel($grey-color-light-raw, "red")),
$green: math.round(color.channel($grey-color-light-raw, "green")),
$blue: math.round(color.channel($grey-color-light-raw, "blue"))
);
Segundo warning: @import
Bem, o SASS reclamou também do @import. Não devo usá-lo porque o SASS começou
o processo de remoção disso. Antes eu tinha o arquivo main.scss assim:
@charset "utf-8";
@use "sass:color"; // para lidar com o escurecer de cores
@use "sass:math"; // para usar o round
// Our variables
$base-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
$base-font-size: 16px;
/** outras variáveis */
@mixin media-query($device) {
@media screen and (max-width: $device) {
@content;
}
}
// Import partials from `sass_dir` (defaults to `_sass`)
@import
"base",
"layout",
"syntax-highlighting"
;
A propósito, a crítica ao @import você pode encontrar na documentação
aqui. Como o @import
funcionava na prática?
Bem, na prática era como se os arquivos _base.scss, _layout.scss e
_syntax-highlighting.scss fossem, nessa exata ordem porque é a ordem que
aparecem no @import, fossem concatenados no final do main.scss. Então todas
as declarações feitas em main.scss estão disponíveis para os arquivos
importados.
Então precisamos usar o @use no lugar, né? Bem, não. Para começar, cada
@use é próprio, não posso colocar como no @import lá em cima que eram todos
juntos separados por vírgula, isso dá erro:
@use
"base",
"layout",
"syntax-highlighting"
;
Então façamos para cada um desses individualmente? Ficaria assim mais ou menos:
@charset "utf-8";
@use "sass:color"; // para lidar com o escurecer de cores
@use "sass:math"; // para usar o round
// Our variables
$base-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
$base-font-size: 16px;
/** outras variáveis */
@mixin media-query($device) {
@media screen and (max-width: $device) {
@content;
}
}
// Import partials from `sass_dir` (defaults to `_sass`)
@use "base";
@use "layout";
@use "syntax-highlighting";
Só que isso também dá pane!
Error: @use rules must be written before any other rules.
╷
52 │ @use "syntax-highlighting";
Hmmm, então vou precisar mudar as coisas de canto. Os @use vão precisar
surgir antes das outras coisas…
Bem, se eu vou por o @use antes de declarar as variáveis, hora de refatorar
e colocar minhas variáveis em um novo lugar, né?
Refatorando: variáveis em lugar comum
Primeira coisa que eu fiz foi mudar o main.scss para ficar assim:
@charset "utf-8";
// Import partials from `sass_dir` (defaults to `_sass`)
@use "base";
@use "layout";
@use "syntax-highlighting";
E o miolo dele eu coloquei em um arquivo _common.scss. Sem nenhum segredo
aqui.
Mas isso implicou algumas coisas… por exemplo, como usar essas variáveis em
outros lugares. Começando pelo _base.scss:
a {
color: $brand-color;
text-decoration: none;
&:visited {
color: color.adjust($brand-color, $lightness: -15%);
}
&:hover {
color: $text-color;
text-decoration: underline;
}
}
Aqui ele reclama pois não conhece nenhuma dessas variáveis. Ao simplesmente
chamar @use "common" também não funciona, pois o módulo está em outro
namespace. Como eu resolveria isso? Bem, a primeira opção seria usar o nome do
módulo, que nem foi feito para color:
@use "sass:color";
@use "common";
// ...
a {
color: common.$brand-color;
text-decoration: none;
&:visited {
color: color.adjust(common.$brand-color, $lightness: -15%);
}
&:hover {
color: common.$text-color;
text-decoration: underline;
}
}
Mas, sinceramente? Ficou feio. Se eu pudesse importar o módulo como c… E,
bem, eu posso sim fazer isso:
@use "sass:color";
@use "common" as c;
// ...
a {
color: c.$brand-color;
text-decoration: none;
&:visited {
color: color.adjust(c.$brand-color, $lightness: -15%);
}
&:hover {
color: c.$text-color;
text-decoration: underline;
}
}
E eu também posso importar no escopo local, que não necessitaria de mais mudanças:
@use "sass:color";
@use "common" as *;
// ...
a {
color: $brand-color;
text-decoration: none;
&:visited {
color: color.adjust($brand-color, $lightness: -15%);
}
&:hover {
color: $text-color;
text-decoration: underline;
}
}
Ok, preciso adequar também outros cantos de uso, como os media-query. Por
exemplo:
.wrapper {
max-width: -webkit-calc(#{c.$content-width} - (#{c.$spacing-unit} * 2));
max-width: calc(#{c.$content-width} - (#{c.$spacing-unit} * 2));
margin-right: auto;
margin-left: auto;
padding-right: c.$spacing-unit;
padding-left: c.$spacing-unit;
@extend %clearfix;
// perceba a alteração do media-query, agora é um elemento de `common.scss`
@include c.media-query(c.$on-laptop) {
max-width: -webkit-calc(#{c.$content-width} - (#{c.$spacing-unit}));
max-width: calc(#{c.$content-width} - (#{c.$spacing-unit}));
padding-right: calc(c.$spacing-unit / 2);
padding-left: calc(c.$spacing-unit / 2);
}
}
Estender algo que não existe?
Ok, tá vendo o @extend %clearfix? O %clearfix é o que o SASS chama de
“seletor placeholder”. O que isso significa? Bem, a classe .wrapper acima
compila para algo assim no blog (vai ser alterado após as alterações deste
post):
.footer-col-wrapper:after, .wrapper:after {
content: "";
display: table;
clear: both;
}
Em _layout.scss tenho a classe .footer-col-wrapper que também faz
@extend %clearfix. E em _base.scss tenho a declaração desse seletor:
/**
* Clearfix
*/
%clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
E, bem. Agora o %clearfix reside dentro do _base.scss. E como não está
sendo usado o @import que faz um append dos arquivos, o %clearfix não está
mais prontamente disponível. Qual minha primeira reação? “VAMOS REFATORAR!”
E eu coloco o seletor placeholder em _common.scss. Só que…
Error: The target selector was not found.
Use "@extend %clearfix !optional" to avoid this error.
╷
171 │ @extend %clearfix;
Ok, posso fazer isso. Eu coloco o !optional, mas a que custo? Eu descobri que
colocar o !optional resolve o problema de compilação, mas será que o
resultado é mesmo o esperado? Não sei, e fiquei com medo. Preferi contornar!
Eu não consegui de jeito nenhum com importação de módulo nomeado.
Tentei @extend c.%clearfix para o SASS reclamar de mim. Tentei algumas
variações na posiçãdo do %, mas nada deu certo…
E… se lembra que tem a importação de mesmo escopo? Então… tentei fazer
@use "common" as * agora só pra ver se ele pega o seletor placeholder. E qual
não foi minha surpresa quando isso funcionou!
Mas, não. Não quero isso. Parece demais com cheiro de gambiarra! E se no lugar
disso eu não tentasse usar como um mixin? Eu já tinha o exemplo do
media-query, e ele funcionava bem. Então transformei o clearfix, de um
seletor placeholder, em um mixin:
@mixin clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
E para usar, só chamar com @include do mixin, não o @extend do seletor.
.wrapper {
max-width: -webkit-calc(#{c.$content-width} - (#{c.$spacing-unit} * 2));
max-width: calc(#{c.$content-width} - (#{c.$spacing-unit} * 2));
margin-right: auto;
margin-left: auto;
padding-right: c.$spacing-unit;
padding-left: c.$spacing-unit;
@include c.clearfix;
@include c.media-query(c.$on-laptop) {
max-width: -webkit-calc(#{c.$content-width} - (#{c.$spacing-unit}));
max-width: calc(#{c.$content-width} - (#{c.$spacing-unit}));
padding-right: calc(c.$spacing-unit / 2);
padding-left: calc(c.$spacing-unit / 2);
}
}
Obtive um resultado distinto? Sim, obtive. O que antes ele soltava assim
.footer-col-wrapper:after, .wrapper:after {
content: "";
display: table;
clear: both;
}
Agora ele solta assim:
.wrapper:after {
content: "";
display: table;
clear: both;
}
/* ... */
/* muito distante */
/* ... */
.footer-col-wrapper:after {
content: "";
display: table;
clear: both;
}
Tinha outro seletor placeholder, %vertical-rhythm, que também mudou algumas
coisas. Antes era assim:
/**
* Set `margin-bottom` to maintain vertical rhythm
*/
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure,
%vertical-rhythm {
margin-bottom: calc($spacing-unit / 2);
}
E no _syntax_highlighting.scss o vertical-rhythm também era usado:
.highlight {
background: #fff;
@extend %vertical-rhythm;
// ...
}
Antes o resultado era assim:
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure,
.highlight {
margin-bottom: 15px;
}
/* ... */
.highlight {
background: #fff;
}
Ao alterar para tornar o %vertical-rhythm para um mixin ficou assim:
// common.scss
@mixin vertical-rhythm {
margin-bottom: calc($spacing-unit / 2);
}
// base.scss
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure {
@include c.vertical-rhythm;
// note aqui a inversão dos valores, antes era aqui que declarava
// vertical-rhythm, agora aqui apenas usa o vertical-rhythm, a fonte de
// verdade dele está em outro lugar
}
// syntax-highlighting.scss
.highlight {
background: #fff;
@include c.vertical-rhythm;
// ...
}
Agora o resultado gerou assim:
h1, h2, h3, h4, h5, h6,
p, blockquote, pre,
ul, ol, dl, figure {
margin-bottom: 15px;
}
/* ... */
.highlight {
background: #fff;
margin-bottom: 15px;
}
Pequeno ajuste no h1
Ok, eu só descobri essa recomendação enquanto escrevia esse trecho específico:
Apesar do padrão do HTM permitir usar múltiplos
<h1>na mesma página, isso não é considerado uma boa prática. Uma página deve em geral ter apenas um único elemento<h1>que descreva o seu conteúdo
Extraído da MozDev e traduzido por mim.
Original:
While using multiple
<h1>elements on one page is allowed by the HTML standard (as long as they are not nested), this is not considered a best practice. A page should generally have a single<h1>element that describes the content of the page (similar to the document’s<title>element).
Mas eu espalhei o uso indiscriminado de <h1> ao usar um único # para
representar títulos de seção.
Enfim, algo que sempre me incomodava era que o título das seções mais
importantes acabavem ficando menor do que <h2>. Aproveitei que estava
mexendo no _layout.scss e notei que estava faltando o <h1>. Então resolvi
consertar o meu incômodo. O trecho que me chamou atenção foi esse:
.post-content {
margin-bottom: c.$spacing-unit;
h2 {
font-size: 32px;
@include c.media-query(c.$on-laptop) {
font-size: 28px;
}
}
h3 {
font-size: 26px;
@include c.media-query(c.$on-laptop) {
font-size: 22px;
}
}
h4 {
font-size: 20px;
@include c.media-query(c.$on-laptop) {
font-size: 18px;
}
}
}
Olha que interessante! Todo aumento do nível o font-size diminui em 6 pontos.
Então, se eu vou diminuir um nível para um nível mais básico… eu aumento 6
pontos? Bem, vamos ver no que dá:
.post-content {
margin-bottom: c.$spacing-unit;
h1 {
font-size: 38px;
@include c.media-query(c.$on-laptop) {
font-size: 34px;
}
}
h2 {
font-size: 32px;
@include c.media-query(c.$on-laptop) {
font-size: 28px;
}
}
h3 {
font-size: 26px;
@include c.media-query(c.$on-laptop) {
font-size: 22px;
}
}
h4 {
font-size: 20px;
@include c.media-query(c.$on-laptop) {
font-size: 18px;
}
}
}
E sinceramente? Nem pareceu tão ruim assim, melhorou significativamente em
relação a o que se tinha antes. A próxima alteração vai ser para remover os
<h1> excessivos. Inclusive isso vai ajudar na hora de portar um conteúdo para
o dev.to. E isso foi um dos pontos que eu comentei no
Computaria no dev.to.