Boas práticas com Next.Js e Sass

Marcelo Silva
5 min readMay 21, 2023

--

Houve mudanças na forma como iniciamos nossos projetos React e a própria documentação do mesmo recomenda o uso do Next.Js para a criação de novos projetos e com essas mudanças muitos desenvolvedores que estavam habituados com o desenvolvimento React tiveram que utilizar o Vite ou o Next.

Construir aplicações React é bem diferente quando se comparada ao Next, aprendemos sobre Server components e Client components além de outras formas de estilização, este artigo se trata disso. Estilizações com Sass.

Habituado a utilizar React é comum usarmos o styled components para definir todo o nosso design system globalmente e consumi-los em cada pagina ou componente que construimos e é possível utiliza-lo com Next, porém como forma de aprender algo novo procurei aplicar boa parte daquilo que ja temos com o styled components com Sass, mas fique tranquilo não vamos recriar a roda será apenas dicas de como deixar o seu Sass mais limpo e fácil de utiliza-lo.

Classes

A primeira coisa que me incomodou foi aquele amontoado de classes em uma tag HTML.

<div className='content section-animation'>
<h1 className='text headline-plain-text headline-text-wrapper'>Hello</h1>
<button className='sign-in sign-in-text-style'>Entrar</button>
</div>

Me pareceu que estava retrocedendo aquele modelo antigo que criávamos HTML e CSS, e é claro que iria se tornar muito difícil encontrar classes especificas no olho, além de visualmente não ficar nada agradável.

E quando precisarmos aplicar alguma condicional ? “caso o botão for pressionado altere a cor do titulo”, imagino que ficaria mais ou menos assim:

<div className='content section-animation'>
<h1
className={`text headline-plain-text headline-text-wrapper ${
buttonPressed
? 'headline-plan-text-dark'
: 'headline-plan-text-yellow'
}`}>
Hello
</h1>
<button
type='button'
onClick={onPress}
className='sign-in sign-in-text-style'>
Entrar
</button>
</div>

Para ajudar na visualização e condicionais criei um método chamado injectClass

Com o uso dele deixamos o nosso código mais limpo:

import style from './home.module.sass'; // Primeiro importamos o modulo sass
import { injectClass } from '~/utils'; // Helper

const classes = injectClass(style); // Passamos o style para o inject.

Nosso código anterior ficará assim:

  <div {...classes(['content', 'section-animation'])}>
<h1
{...classes(['text', 'headline-plain-text', 'headline-text-wrapper'], {
condition: buttonPressed,
whenIsFalse: ['headline-plan-text-yellow'],
whenIsTrue: ['headline-plan-text-dark'],
})}>
Hello
</h1>
<button
type='button'
onClick={onPress}
{...classes(['sign-in', 'sign-in-text-style'])}>
Entrar
</button>
</div>

Além de cada uma das classes se tornar um item em um array o segundo parâmetros do método classes se torna uma condicional onde possui o condition do tipo boleano e os outros dois parâmetros whenIsFalse, whenIsTrue do tipo array que recebem outras classes.

Código do Inject

type InjectClass = {
condition: boolean;
whenIsTrue: string[];
whenIsFalse: string[];
};

export const parsedClass = (classes: string[]) => classes.join(' ');

export const mapClass = (style: Record<string, string>, classes: string[]) => {
return classes.map(it => style[it] || it);
};

export const injectClass = (style: Record<string, string>) => {
return (classes: string[], arg?: InjectClass) => {
const conditionClass = arg?.condition
? arg?.whenIsTrue
: arg?.whenIsFalse || [];

const mappedNormalClass = mapClass(style, classes);
const mappedConditionClass = mapClass(style, conditionClass);

return {
className: parsedClass([...mappedNormalClass, ...mappedConditionClass]),
};
};
};

Se olharmos no quesito quantidade de código escrito é claro que não valeria muito a pena adotar esse formato, porém de acordo com que algumas aplicações crescem e o seu nível de complexidade exigir novas classes e condicionais. Utilizar o inject seria uma solução para deixar seu código mais limpo, e esteticamente agradável.

Definição de Design System

É muito comum em aplicações definirmos o seu design system antes de iniciar o desenvolvimento, definindo cores, espaçamentos, tamanhos e radius. No styled-components temos o Provider, que adiciona essas configurações globalmente e pode ser acessadas via props na própria estilização, algo como:

const Button = styled.button`
color: ${props.colors.button.primary}`;
`;

Com Sass podemos fazer isso utilizando quatro recursos, variáveis, colors, funções e map, resultando em:

@use '~/theme/theme.module'

.button
color: theme.getColor(black)

Então você pode criar um módulo específico para cores, espaçamentos entre outras configurações e consumir de forma bem simples ao estilizar sua aplicação.

Com cores podemos alterar a sua opacidade

@use '~/theme/theme.module'

.button
color: theme.getColor(black, 65%)

Olhando em detalhes como essa função é implementada, primeiro temos o nosso modulo Sass com as cores.

colors.module.sass

$colors: (white: #FAFAFA,yellow: #FFE47C,black: #222222)

Em seguida nosso módulo theme

theme.module.sass

@use 'sass:map'
@use 'sass:color'
@use './colors.module'

@function getColor($color, $lightness: 0%)
@return color.adjust(map.get(colors.$colors, $color), $lightness: $lightness)

Se você conhece bem o Sass sabe que todos esses “Módulos embutidos” são do próprio Sass, como o map, colors e function.

Seguindo esse mesmo modelo é possível aplica-lo a espaçamentos, radius e sizes.

Sizes

Complementando a seção de Design System podemos também utilizar os operadores do Sass para unidades de medida dentro da nossa aplicação, um exemplo bem simples é a conversão de px para rem, apenas para fins de exemplo nossa função ficaria assim:

@function unity($value)
@return #{$value * 0.0625}rem

Para utilizar na estilização ficaria algo como:

@use '~/theme/theme.module'

.button
color: theme.getColor(black, 65%)
width: theme.unity(80)
height: theme.unity(48)

Media Query

Media query é essencial para transformar nossas aplicações responsivas e geralmente utilizamos definindo a @mediadiretamente em nosso código para cada dimensões que buscamos atender, repetir as diversas dimensões toda vez que necessário é bem complicado ainda mais quando atendemos mobile, tablet e desktop.

Geralmente o uso se da assim:

.button
color: theme.getColor(black, 65%)

// Mobile
@media (min-width: 328px) and (max-width: 768px)
width: theme.unity(40)
height: theme.unity(28)

// Tablet
@media (min-width: 768px) and (max-width: 1024)
width: theme.unity(60)
height: theme.unity(38)

// Desktop
@media min-width: 1024)
width: theme.unity(80)
height: theme.unity(48)

Já imaginou repetir isso inúmeras vezes em toda a sua aplicação ? e para piorar e se mudar alguma regra na media query de mobile ? teria que alterar em cada uma dessas declarações.

Com os módulos embutidos do Sass podemos construir algo bem simples para evitar repetição e transformar a declaração mais enxutas e caso for necessário mudar alguma regra, será alterado em um único ponto.

Mudando a declaração anterior

@use '~/theme/theme.module'
@use '~/theme/md.module'

.button
color: theme.getColor(black, 65%)

@include md.Mobile
width: theme.unity(40)
height: theme.unity(28)

@include md.Tablet
width: theme.unity(60)
height: theme.unity(38)

@include md.Desktop
width: theme.unity(80)
height: theme.unity(48)

Para quem ainda não domina o intervalo de media query fica muito mais fácil olhar e saber exatamente para qual tipo de device estamos aplicando nosso estilo.

Agora olhando nosso modulo md (Media)

md.module.sass

@use 'sass:map'

$mobile: 328px
$tablet: 768px
$desktop: 1024px

@mixin Mobile
@media (min-width: $mobile) and (max-width: $tablet)
@content

@mixin Tablet
@media (min-width: $tablet) and (max-width: $desktop)
@content

@mixin Desktop
@media (min-width: $desktop)
@content

Algo diferente dos exemplos anteriores foi o uso do mixins que permite a criação de blocos de código que podem ser reutilizados alem do uso do @content que recebe o conteúdo do bloco.

Essas são apenas algumas dicas que me ajudam muito no dia a dia, espero que seja útil para você também.

Grande abraço

--

--

No responses yet