Tive uma ótima experiência usando Next.js para gerenciar projetos complexos de front-end. Next.js é opinativo sobre como organizar o código JavaScript, mas não tem opiniões internas sobre como organizar CSS.
Depois de trabalhar dentro da estrutura, descobri uma série de padrões organizacionais que Acredito que ambos estejam em conformidade com as filosofias orientadoras do Next.js e exercitem as melhores práticas de CSS. Neste artigo, construiremos um site (uma casa de chá!) Juntos para demonstrar esses padrões.
Observação : você provavelmente não precisará de experiência anterior com Next.js, embora seria bom ter um conhecimento básico do React e estar aberto para aprender algumas novas técnicas de CSS.
Escrevendo CSS “antiquado”
Ao examinar o Next.js pela primeira vez, podemos ficar tentados a considerar usando algum tipo de biblioteca CSS-in-JS. Embora possa haver benefícios dependendo do projeto, CSS-in-JS apresenta muitas considerações técnicas. Requer o uso de uma nova biblioteca externa, que aumenta o tamanho do pacote. CSS-in-JS também pode ter um impacto no desempenho, causando renderizações e dependências adicionais no estado global.
Leitura recomendada : “ Os custos de desempenho invisíveis das bibliotecas CSS-in-JS modernas em aplicativos React ) ”Por Aggelos Arvanitakis
Além disso, o objetivo de usar uma biblioteca como Next.js é renderizar ativos estaticamente sempre que possível, por isso não faz muito sentido escrever JS que precise ser executado no navegador para gerar CSS.
Há algumas questões que devemos considerar ao organizar o estilo no Next.js:
Como podemos nos ajustar às convenções/melhores práticas de a estrutura?
Como podemos equilibrar as preocupações de estilo”globais”(fontes, cores, layouts principais e assim por diante) com as”locais”(estilos relativos a componentes individuais)?
A resposta que encontrei para a primeira pergunta é simplesmente escreva o bom e velho CSS . O Next.js não apenas oferece suporte para fazer isso sem configuração adicional; também produz resultados de alto desempenho e estáticos.
Para resolver o segundo problema, utilizo uma abordagem que pode ser resumida em quatro partes:
Tokens de design Estilos globais Classes de utilitários Estilos de componentes
Agradeço a ideia de Andy Bell de CUBE CSS (“Composição, Utilidade, Bloco, Exceção”) aqui. Se você nunca ouviu falar desse princípio organizacional antes, recomendo verificar seu site oficial ou recurso do Smashing Podcast . Um dos princípios que tiraremos do CUBE CSS é a ideia de que devemos abraçar ao invés de temer a cascata de CSS. Vamos aprender essas técnicas aplicando-as a um projeto de site.
Primeiros passos
Estaremos construindo uma loja de chá porque, bem, o chá é saboroso. Começaremos executando yarn create next-app para fazer um novo projeto Next.js. Em seguida, removeremos tudo no diretório de estilos/(é todo código de amostra).
Observação : se você quiser acompanhar o projeto concluído, pode verificá-lo aqui .
Design de tokens
Em praticamente qualquer configuração CSS , há um benefício claro em armazenar todos os valores compartilhados globalmente em variáveis . Se um cliente pede uma cor para mudar, implementar a mudança é uma linha em vez de uma grande confusão de localizar e substituir. Consequentemente, uma parte importante da configuração CSS do Next.js será o armazenamento de todos os valores de todo o site como tokens de design.
Usaremos propriedades personalizadas CSS integradas para armazenar esses tokens. (Se você não está familiarizado com esta sintaxe, pode verificar “ Um Guia de Estratégia Para CSS Custom Properties ”.) Devo mencionar que (em alguns projetos) optei por usar variáveis SASS/SCSS para esta finalidade. Não encontrei nenhuma vantagem real, então geralmente só incluo o SASS em um projeto se achar que preciso de outros recursos do SASS (mix-ins, iteração, importação de arquivos e assim por diante). As propriedades personalizadas do CSS, por outro lado, também funcionam com a cascata e podem ser alteradas com o tempo, em vez de compilar estaticamente. Então, por hoje, vamos continuar com CSS simples .
Em nosso diretório styles/, vamos fazer um novo arquivo design_tokens.css:
: root {–green: # 3FE79E;–dark: # 0F0235;-esbranquiçado: # F5F5F3;–space-sm: 0,5 rem;–space-md: 1rem;–space-lg: 1.5rem;–font-size-sm: 0,5 rem;–font-size-md: 1rem;–font-size-lg: 2rem; }
Claro, esta lista pode e vai crescer com o tempo. Depois de adicionar esse arquivo, precisamos pular para nosso arquivo pages/_app.jsx, que é o layout principal de todas as nossas páginas, e adicionar:
import’../styles/design_tokens.css’
Gosto de pensar nos tokens de design como a cola que mantém a consistência em todo o projeto. Faremos referência a essas variáveis em uma escala global, bem como em componentes individuais, garantindo uma linguagem de design unificada.
Estilos globais
A seguir, vamos adicionar uma página ao nosso site! Vamos pular para o arquivo pages/index.jsx (esta é nossa página inicial). Excluiremos todos os clichês e adicionaremos algo como:
export default function Home () {return
Chás calmantes
Bem-vindo à nossa maravilhosa casa de chá.
Estamos abertos desde 1987 e atendemos clientes com chás oolong escolhidos a dedo.
}
Infelizmente, vai parecer bem simples, então vamos definir alguns estilos globais para o básico elementos , por exemplo tags
. (Gosto de pensar nesses estilos como “padrões globais razoáveis”.) Podemos substituí-los em casos específicos, mas eles são um bom palpite sobre o que desejaremos se não o fizermos.
Vou colocar isso no arquivo styles/globals.css (que vem por padrão do Next.js):
*, *:: before, *:: after {box-sizing: border-box; } corpo {cor: var (-branco sujo); cor de fundo: var (-escuro); } h1 {cor: var (-verde); tamanho da fonte: var (-tamanho da fonte-lg); } p {tamanho da fonte: var (-tamanho da fonte-md); } p, artigo, seção {altura da linha: 1,5; }: foco {contorno: 0,15 rem tracejado var (-esbranquiçado); contorno-deslocamento: 0,25 rem; } principal: foco {esboço: nenhum; } img {largura máxima: 100%; }
Claro, esta versão é bastante básica, mas meu arquivo globals.css geralmente não acaba precisando ficar muito grande. Aqui, eu estilizo elementos HTML básicos (cabeçalhos, corpo, links e assim por diante). Não há necessidade de envolver esses elementos em componentes React ou adicionar classes constantemente apenas para fornecer um estilo básico.
Eu também incluo quaisquer redefinições de estilos de navegador padrão . Ocasionalmente, terei algum estilo de layout para todo o site para fornecer um “rodapé fixo”, por exemplo, mas eles só pertencem aqui se todas as páginas compartilharem o mesmo layout. Caso contrário, ele precisará ter o escopo dentro de componentes individuais.
Sempre incluo algum tipo de: estilo de foco para indicar claramente os elementos interativos para usuários de teclado quando focados. É melhor torná-lo parte integrante do DNA de design do site!
Agora, nosso site está começando a tomar forma:
Classes de utilitários
Uma área em que nossa página inicial certamente poderia melhorar é que o texto atualmente sempre se estende para os lados da tela, portanto, vamos limitar sua largura. Precisamos desse layout nesta página, mas imagino que também possamos precisar dele em outras páginas. Este é um ótimo caso de uso para uma classe de utilitário!
Tento usar classes de utilitário com moderação ao invés de apenas como um substituto para escrever CSS. Meus critérios pessoais para quando faz sentido adicionar um a um projeto são:
Eu preciso repetidamente; Ele faz uma coisa bem; Ele se aplica a uma variedade de componentes ou páginas diferentes.
Acho que este caso atende a todos os três critérios, então vamos criar um novo arquivo CSS styles/utilities.css e adicionar:
.lockup {max-width: 90ch; margem: 0 automático; }
Então, vamos adicionar import’../styles/utilities.css’às nossas páginas/_app.jsx. Finalmente, vamos mudar a tag
Agora, nossa página está se formando ainda mais. Como usamos a propriedade max-width, não precisamos de consultas de mídia para tornar nosso layout responsivo para dispositivos móveis. E, como usamos a unidade de medida ch-que equivale a cerca da largura de um caractere-nosso dimensionamento é dinâmico para o tamanho da fonte do navegador do usuário.
Como nosso site crescer, podemos continuar adicionando mais classes de utilitários. Eu adoto uma abordagem bastante utilitária aqui: se eu estiver trabalhando e descobrir que preciso de outra classe para uma cor ou algo, eu a adiciono. Eu não adiciono todas as classes possíveis-isso aumentaria o tamanho do arquivo CSS e tornaria meu código confuso. Às vezes, em projetos maiores, gosto de dividir as coisas em estilos/utilitários/diretório com alguns arquivos diferentes; depende das necessidades do projeto.
Podemos pensar nas classes de utilitários como nosso kit de ferramentas de comandos de estilo repetidos e comuns que são compartilhados globalmente. Eles ajudam a evitar que reescrevamos constantemente o mesmo CSS entre diferentes componentes.
Estilos de componentes
Concluímos nossa página inicial por enquanto, mas ainda precisamos construir uma parte do nosso site: a loja online. Nosso objetivo aqui será exibir uma grade de cartões com todos os chás que queremos vender , portanto, precisaremos adicionar alguns componentes ao nosso site.
Vamos começar por adicionando uma nova página em pages/shop.jsx:
export default function Shop () {return
Shop Our Teas
}
Então, vamos precisar de alguns chás para mostrar. Incluiremos um nome, uma descrição e uma imagem (no diretório público/) para cada chá:
const teas=[{nome:”Oolong”, descrição:”Um chá parcialmente fermentado.”, Imagem:”/oolong.jpg”},//…]
Observação : este não é um artigo sobre busca de dados , então pegamos o caminho mais fácil e definimos um array no início do arquivo.
Em seguida, precisaremos definir um componente para exibir nosso chás. Vamos começar criando um diretório de componentes/(Next.js não faz isso por padrão). Então, vamos adicionar um diretório de componentes/TeaList. Para qualquer componente que acabe precisando de mais de um arquivo, geralmente coloco todos os arquivos relacionados dentro de uma pasta. Isso evita que nossos componentes/pasta fiquem inavegáveis.
Agora, vamos adicionar nosso arquivo de componentes/TeaList/TeaList.jsx:
import TeaListItem de’./TeaListItem’const TeaList=(props)=> {const {teas}=props return
- {teas.map (tea=>
} exportação padrão TeaList
O objetivo deste componente é iterar sobre nossos chás e mostrar um item da lista para cada um, então agora vamos definir nossos componentes/TeaList/TeaListItem.jsx componente:
import Image from’next/image’const TeaListItem=(adereços)=> {const {tea}=retorno de adereços
{tea.name}
{tea.description}
} exportar TeaListItem padrão
Observe que estamos usando o componente de imagem integrado do Next.js. Defino o atributo alt para uma string vazia porque as imagens são puramente decorativas neste caso; queremos evitar sobrecarregar os usuários de leitores de tela com longas descrições de imagens aqui.
Finalmente, vamos criar um arquivo components/TeaList/index.js, para que nossos componentes sejam fáceis de importar externamente:
import TeaList from’./TeaList’import TeaListItem from’./TeaListItem’export {TeaListItem} export default TeaList
E então, vamos juntar tudo adicionando import TeaList from../components/TeaList e um
Colocando estilo com componentes por meio de módulos CSS
Vamos começar estilizando nossos cartões (o Componente TeaListLitem). Agora, pela primeira vez em nosso projeto, vamos querer adicionar um estilo que seja específico para apenas um componente. Vamos criar um novo arquivo components/TeaList/TeaListItem.module.css.
Você pode estar se perguntando sobre o módulo na extensão do arquivo. Este é um Módulo CSS . Next.js suporta módulos CSS e inclui alguns boa documentação sobre eles. Quando escrevemos um nome de classe a partir de um módulo CSS, como.TeaListItem, ele será automaticamente transformado em algo mais parecido com. TeaListItem_TeaListItem__TFOk_ com um monte de caracteres extras adicionados. Conseqüentemente, podemos usar qualquer nome de classe que quisermos sem nos preocupar se ele entrará em conflito com outros nomes de classe em outro lugar em nosso site.
Outra vantagem dos módulos CSS é o desempenho. Next.js inclui um recurso de importação dinâmica . next/dynamic nos permite carregar componentes lentamente, de forma que seu código só seja carregado quando necessário, em vez de aumentar o tamanho do pacote inteiro. Se importarmos os estilos locais necessários para componentes individuais, os usuários também podem carregar lentamente o CSS para componentes importados dinamicamente . Para grandes projetos, podemos escolher carregar lentamente partes significativas de nosso código e apenas carregar o JS/CSS mais necessário antecipadamente. Como resultado, geralmente acabo criando um novo arquivo de Módulo CSS para cada novo componente que precisa de um estilo local.
Vamos começar adicionando alguns estilos iniciais ao nosso arquivo:
.TeaListItem {display: flex; direção flexível: coluna; lacuna: var (-espaço-sm); cor de fundo: var (-color, var (-off-white)); cor: var (-escuro); raio da borda: 3px; sombra da caixa: 1px 1px 1px rgba (0, 0, 0, 0,1); }
Então, podemos importar o estilo de./TeaListItem.module.css em nosso componente TeaListitem. A variável de estilo vem como um objeto JavaScript, para que possamos acessar este estilo de classe.TeaListItem.
Observação : o nome de nossa classe não precisa estar em maiúscula. Descobri que uma convenção de nomes de classes em maiúsculas dentro dos módulos (e em minúsculas fora) diferencia os nomes de classes locais dos globais visualmente.
Então, vamos pegar nossa nova classe local e atribuí-la ao
Você pode estar se perguntando sobre a linha da cor de fundo (ou seja, var (-color, var (-off-white)) ;). O que este trecho significa é que, por padrão, o fundo será nosso valor–off-white. Mas, se definirmos uma propriedade personalizada–color em um cartão, ela substituirá e escolherá esse valor.
A princípio, queremos que todos os nossos cartões sejam–desbranquiçados, mas podemos querer alterar o valor de cartas individuais mais tarde. Isso funciona de forma muito semelhante aos adereços em React. Podemos definir um valor padrão, mas criar um slot onde podemos escolher outros valores em circunstâncias específicas. Então, eu encorajo-nos a pensar nas propriedades personalizadas CSS, como a versão CSS dos adereços .
O estilo ainda não ficará bem porque queremos ter certeza de que as imagens permanecerão dentro seus recipientes. O componente Image do Next.js com o prop layout=”fill”obtém a posição: absoluto; do framework, para que possamos limitar o tamanho colocando em um contêiner com position: relative ;.
Vamos adicionar uma nova classe ao nosso TeaListItem.module.css:
.ImageContainer {position: relativo; largura: 100%; altura: 10em; estouro: oculto; }
E então vamos adicionar className={styles.ImageContainer} no
Finalmente, queremos adicionar um pouco de preenchimento nas laterais do texto, então vamos adicionar uma última classe e contar com as variáveis de espaçamento que configuramos como tokens de design:
.Title {padding-left: var (-espaço-sm); padding-right: var (-espaço-sm); }
Podemos adicionar esta classe ao
Combinando estilo global e local
A seguir, queremos que nossos cartões sejam exibidos em um layout de grade. Nesse caso, estamos apenas na fronteira entre os estilos local e global. Certamente poderíamos codificar nosso layout diretamente no componente TeaList. Mas, eu também poderia imaginar que ter uma classe de utilitário que transforma uma lista em um layout de grade poderia ser útil em vários outros lugares.
Vamos pegar a abordagem global aqui e adicionar um nova classe de utilitário em nossos styles/utilities.css:
.grid {list-style: none; display: grade; colunas de modelo de grade: repetir (preenchimento automático, minmax (var (-min-item-width, 30ch), 1fr)); lacuna: var (-espaço-md); }
Agora, podemos adicionar a classe.grid a qualquer lista e obteremos um layout de grade responsivo automaticamente. Também podemos alterar a propriedade personalizada–min-item-width (por padrão, 30ch) para alterar a largura mínima de cada elemento.
Nota : Lembre-se de pensar nas propriedades personalizadas como adereços! Se esta sintaxe parecer desconhecida, você pode verificar “ Grade CSS com resposta intrínseca com minmax () And min () ”de Chris Coyier.
Como escrevemos esse estilo globalmente, não é necessário adicionar className=”grid”em nosso componente TeaList. Mas, digamos que queremos unir esse estilo global com alguma loja local adicional. Por exemplo, queremos trazer um pouco mais da “estética do chá” e fazer com que todos os outros cartões tenham um fundo verde. Tudo o que precisamos fazer é criar um novo arquivo components/TeaList/TeaList.module.css:
.TeaList>: nth-child (even) {–color: var (-green); }
Lembra-se de como criamos uma propriedade personalizada–color em nosso componente TeaListItem? Bem, agora podemos configurá-lo em circunstâncias específicas. Observe que ainda podemos usar seletores filhos em módulos CSS, e não importa se estamos selecionando um elemento com estilo dentro de um módulo diferente. Portanto, também podemos usar nossos estilos de componentes locais para afetar os componentes filhos. Este é um recurso e não um bug, pois nos permite tirar proveito da cascata CSS ! Se tentássemos replicar esse efeito de outra maneira, provavelmente acabaríamos com algum tipo de sopa de JavaScript em vez de três linhas de CSS.
Então, como podemos manter a classe.grid global em nosso Componente TeaList ao mesmo tempo em que adiciona a classe.TeaList local? É aqui que a sintaxe pode ficar um pouco funky, porque temos que acessar nossa classe.TeaList fora do módulo CSS, fazendo algo como style.TeaList.
Uma opção seria usar a interpolação de string para obter algo como:
Neste pequeno caso, isso pode ser bom o suficiente. Se estivermos misturando e combinando mais classes, acho que essa sintaxe faz meu cérebro explodir um pouco, então, às vezes, optarei por usar a biblioteca de nomes de classe. Nesse caso, terminamos com uma lista de aparência mais sensata:
Agora, terminamos nossa página da Loja, e fizemos nosso componente TeaList aproveitar os estilos global e local.
Um ato de equilíbrio
Agora construímos nossa casa de chá usando apenas CSS simples para lidar com o estilo. Você deve ter notado que não precisávamos perder muito tempo lidando com configurações personalizadas de Webpack, instalando bibliotecas externas e assim por diante. Isso é por causa dos padrões que usamos funcionam com Next.js fora da caixa. Além disso, eles incentivam as melhores práticas de CSS e se adaptam naturalmente à arquitetura do framework Next.js.
Nossa organização CSS consistia em quatro peças principais:
tokens de design, estilos globais , Classes utilitárias, Estilos de componentes.
Conforme continuamos construindo nosso site, nossa lista de tokens de design e classes de utilitários aumentará. Qualquer estilo que não faz sentido adicionar como uma classe de utilitário, podemos adicionar em estilos de componentes usando módulos CSS. Como resultado, podemos encontrar um equilíbrio contínuo entre as preocupações de estilo locais e globais. Também podemos gerar código CSS intuitivo e de alto desempenho que cresce naturalmente junto com nosso site Next.js.