Por que a estrutura de pastas decide a escalabilidade
Em projetos reais, a maior parte do “custo” do CSS não está em escrever novas regras, mas em encontrar onde mexer sem quebrar o restante. Uma estrutura de pastas bem definida reduz o tempo de busca, diminui duplicação e cria limites claros entre estilos globais, componentes e variações por contexto. O objetivo aqui é transformar o repositório em um mapa: ao bater o olho, você sabe onde um arquivo deve nascer, onde uma alteração deve acontecer e quais importações são permitidas.
Estruturar pastas não é apenas estética. É uma forma de impor disciplina: modularização (cada parte com responsabilidade clara), limites (o que pode depender do quê) e padrões de importação (ordem e regras para compilar o CSS). Quando essas três coisas estão alinhadas, o time consegue crescer o código sem “efeitos colaterais” e sem criar um emaranhado de overrides.
Princípios práticos para modularização
1) Um arquivo deve ter um motivo único para mudar
Se um arquivo mistura estilos de layout de página, estilos de um componente e ajustes de tema, ele terá múltiplos gatilhos de mudança e ficará instável. Prefira arquivos pequenos e coesos, onde o nome do arquivo descreve exatamente o que está dentro.
2) Modularize por domínio de UI, não por tecnologia
Em vez de separar “tudo que é botão” em um arquivo gigante, modularize por componente ou por “unidade de interface” que o usuário reconhece. Um botão pode ter variações, mas ainda é um componente. Já um “header” pode conter logo, navegação e busca; isso costuma ser um componente composto (ou um conjunto de componentes).
3) Evite arquivos “catch-all”
Arquivos como misc.scss, temp.css, fixes.scss viram depósitos de exceções. Se algo é temporário, crie um processo para remover; se é permanente, dê um nome que reflita a intenção (por exemplo, _accessibility.scss para utilitários de acessibilidade, ou _print.scss para estilos de impressão).
Continue em nosso aplicativo
Você poderá ouvir o audiobook com a tela desligada, ganhar gratuitamente o certificado deste curso e ainda ter acesso a outros 5.000 cursos online gratuitos.
ou continue lendo abaixo...Baixar o aplicativo
4) Prefira composição a sobrescrita
Uma estrutura de pastas saudável incentiva que você componha estilos (por exemplo, usando classes utilitárias e variações explícitas) em vez de criar “cascatas de correção” em arquivos posteriores. Isso se conecta diretamente aos limites e ao padrão de importação: quanto mais previsível a ordem, menos você depende de “ganhar no final”.
Limites: como evitar dependências perigosas
Limites são regras de dependência entre módulos. Sem limites, qualquer arquivo pode importar qualquer outro, e o projeto vira uma teia difícil de refatorar. Com limites, você consegue responder perguntas como: “um componente pode depender de estilos de página?” (idealmente não) ou “um componente pode importar tokens?” (sim).
Tipos comuns de limites
- Limite de camada: arquivos de componentes não importam arquivos de páginas; páginas podem compor componentes.
- Limite de responsabilidade: um arquivo de layout define grid/spacing macro; um componente não deve redefinir o grid global.
- Limite de dependência: tokens e configurações podem ser usados por todos; já overrides contextuais devem ser raros e explícitos.
- Limite de escopo: estilos globais (ex.: tipografia base) não devem conter regras específicas de um componente.
Esses limites precisam ser reforçados por convenções de pastas e por um padrão de importação que torne violações óbvias. Se para “consertar” um componente você precisa importar um arquivo de página dentro dele, isso é um sinal de que o componente não está realmente encapsulado ou que a variação deveria ser modelada como modificador/variante.
Convenções de arquivos: nomes, granularidade e previsibilidade
Nomenclatura de arquivos
Escolha um padrão e aplique em todo o projeto. Exemplos comuns:
- kebab-case:
button-group.scss,user-card.scss - prefixos por tipo:
c-button.scss(component),o-grid.scss(object),u-spacing.scss(utility) - pastas por tipo e arquivos sem prefixo:
components/button.scss,utilities/spacing.scss
O importante é que o nome do arquivo ajude a localizar e entender o papel dele sem abrir o conteúdo. Em times grandes, isso reduz revisões e evita duplicação.
Granularidade: um componente por arquivo (na maioria dos casos)
Como regra prática, mantenha um componente em um arquivo. Se o componente for muito grande, quebre em subpartes internas, mas preserve um ponto de entrada claro. Exemplo: components/card/ com card.scss, card-header.scss, card-footer.scss, e um arquivo agregador que importa os demais.
Arquivos “index” (barrels) para controlar importações
Um padrão útil é ter um _index.scss (ou index.scss) por pasta, que centraliza as importações daquele diretório. Isso cria um limite natural: outros módulos importam o index, não arquivos internos arbitrários. Assim, você controla o que é público vs. privado.
// components/_index.scss
@forward "button";
@forward "card";
@forward "modal";Se você não usa @forward/@use (Sass moderno), pode fazer com @import, mas o ideal é migrar para o modelo moderno para reduzir vazamento de variáveis/mixins e tornar dependências explícitas.
Estrutura de pastas recomendada (exemplo aplicável)
A seguir está uma estrutura que funciona bem para projetos com Sass/SCSS, mas a lógica também se aplica a CSS puro com um bundler (Vite/Webpack/Parcel) ou com PostCSS. O foco é: separar o que é global do que é modular, e tornar a ordem de compilação previsível.
styles/
00-settings/
_tokens.scss
_theme.scss
_breakpoints.scss
01-tools/
_mixins.scss
_functions.scss
02-generic/
_reset.scss
_box-sizing.scss
03-elements/
_typography.scss
_links.scss
_forms.scss
04-objects/
_container.scss
_grid.scss
_media.scss
05-components/
button/
_button.scss
_button-group.scss
_index.scss
card/
_card.scss
_index.scss
_index.scss
06-utilities/
_spacing.scss
_display.scss
_visually-hidden.scss
_index.scss
pages/
home.scss
checkout.scss
vendors/
_datepicker.scss
main.scssObservações práticas:
- settings concentra configurações e tokens consumidos por todo o resto.
- tools contém mixins e funções (sem gerar CSS diretamente, quando possível).
- generic/elements são globais e previsíveis, com baixo risco de “efeito dominó”.
- objects define padrões estruturais reutilizáveis (layout sem “skin”).
- components é onde vive a maior parte do trabalho do dia a dia.
- utilities são classes pequenas e diretas, com alta reutilização.
- pages é opcional e deve ser usado com parcimônia: estilos específicos de uma tela, sem virar um “segundo sistema”.
- vendors isola CSS de terceiros para não contaminar o restante.
Padrões de importação: ordem, previsibilidade e isolamento
Um único ponto de entrada
Defina um arquivo principal (por exemplo, main.scss) como ponto de entrada do build. Ele deve apenas orquestrar importações, evitando regras diretas. Isso facilita auditoria: a ordem está toda em um lugar.
// main.scss
@use "00-settings/tokens";
@use "00-settings/theme";
@use "01-tools/mixins";
@use "02-generic/reset";
@use "02-generic/box-sizing";
@use "03-elements/typography";
@use "04-objects/container";
@use "05-components/index";
@use "06-utilities/index";Se houver CSS por página, você pode ter entradas separadas (ex.: home.scss, checkout.scss) que importam a base e adicionam apenas o necessário. Isso reduz CSS entregue em páginas específicas, quando o pipeline permite code-splitting.
Regra de ouro: importações sempre “para baixo”
Para manter limites, adote a regra: módulos mais específicos podem depender de módulos mais genéricos, mas não o contrário. Na prática:
- tokens/settings não importam nada.
- tools podem importar settings.
- generic/elements podem importar settings/tools.
- objects podem importar settings/tools.
- components podem importar settings/tools/objects/utilities (com cautela).
- pages podem importar componentes e utilitários, mas componentes não importam pages.
Quando essa regra é quebrada, surgem dependências circulares e “efeitos mágicos” (um componente muda porque uma página importou algo depois). O padrão de importação deve impedir isso.
Evite importação “profunda” (deep imports)
Deep import é quando alguém importa um arquivo interno de outro módulo, pulando o ponto de entrada. Exemplo ruim: @use "05-components/button/button" a partir de uma página, em vez de importar 05-components/index ou o button/_index.scss. Isso cria acoplamento com a estrutura interna e dificulta refatorações.
Uma convenção simples: só é permitido importar arquivos index (ou arquivos explicitamente marcados como públicos). O restante é considerado privado do módulo.
Importação de vendors e isolamento
CSS de bibliotecas externas deve ficar isolado em vendors/ e ser importado em um ponto controlado. Se você precisa sobrescrever estilos de vendor, faça isso em um arquivo de integração, por exemplo:
// vendors/_datepicker.scss (código do vendor)
// integrations/_datepicker-overrides.scss (seu código)
.datepicker { /* ajustes necessários */ }Assim você evita misturar “código de terceiros” com “código do produto” e mantém claro o que pode ser atualizado sem conflito.
Passo a passo: migrando um projeto existente para uma estrutura modular
Passo 1: inventário do CSS atual
Liste os arquivos existentes e categorize por intenção: global (reset/tipografia), layout (grid/containers), componentes (botões, cards, modais), utilitários (helpers), páginas (ajustes específicos) e vendors. Não tente reorganizar tudo de uma vez; o objetivo é criar um plano incremental.
Passo 2: crie a árvore de pastas e o ponto de entrada
Adicione a pasta styles/ (ou adapte à sua estrutura atual) e crie main.scss como orquestrador. Mesmo que, no início, ele importe apenas um arquivo legado, você já estabelece o “caminho oficial” de compilação.
// main.scss (início de migração)
@use "legacy";Passo 3: mova primeiro o que é mais estável
Comece por arquivos globais e estáveis (reset, box-sizing, tipografia base). Eles raramente dependem de componentes e tendem a ter menos risco de regressão. Ao mover, ajuste importações e garanta que o build continue gerando o mesmo CSS (ou o mais próximo possível).
Passo 4: extraia utilitários e objetos reutilizáveis
Procure padrões repetidos (ex.: classes de espaçamento, display, alinhamento) e mova para utilities/. Procure padrões de layout (ex.: container, grid, media object) e mova para objects/. Isso reduz duplicação e prepara o terreno para componentes mais limpos.
Passo 5: migre componentes por prioridade de uso
Escolha os componentes mais usados (botão, input, card) e migre um por vez. Para cada componente:
- Crie uma pasta do componente (se fizer sentido) e um arquivo principal.
- Garanta que o componente não dependa de estilos de página.
- Se houver variações, mantenha-as no mesmo módulo (por exemplo, em arquivos internos) e exponha via
_index.scss.
// styles/05-components/button/_button.scss
.c-button { /* base */ }
.c-button--primary { /* variação */ }
// styles/05-components/button/_index.scss
@forward "button";Passo 6: crie arquivos de página apenas para exceções reais
Quando uma tela precisa de ajustes específicos, crie um arquivo em pages/. Mas imponha uma regra: arquivo de página só pode conter estilos “de composição” (layout da página, espaçamento entre seções, posicionamento de componentes) e pequenas exceções. Se o arquivo de página começa a definir “como o componente é por dentro”, isso indica que a variação deveria virar uma variante do componente.
// pages/checkout.scss
@use "../main";
.checkout {
display: grid;
gap: 24px;
}
.checkout__summary {
position: sticky;
top: 16px;
}Padrões de arquivo dentro de um componente
Estrutura interna sugerida
Para componentes complexos, uma estrutura interna consistente ajuda muito:
components/modal/
_modal.scss
_modal-header.scss
_modal-footer.scss
_modal-animations.scss
_index.scssRegras práticas:
- _modal.scss contém a base e a API pública do componente (classes principais e variações).
- Arquivos auxiliares existem para manter legibilidade, mas não devem ser importados diretamente por outros módulos.
- _index.scss é o único ponto de entrada público do componente.
O que deve (e não deve) estar no arquivo do componente
- Deve: estilos do bloco e elementos internos, variações explícitas, estados (hover/focus/disabled), e dependências diretas de tokens/mixins.
- Não deve: regras globais (ex.:
body,h1), ajustes específicos de uma página, e “correções” para outros componentes.
Padrões de importação com Sass moderno: @use e @forward
Se você usa Sass, prefira @use e @forward para tornar dependências explícitas e evitar vazamento global. Um padrão comum:
@forwardem arquivosindexpara expor módulos.@usenos consumidores para importar com namespace.
// 00-settings/_tokens.scss
$space-1: 4px;
$space-2: 8px;
// 06-utilities/_spacing.scss
@use "../00-settings/tokens" as t;
.u-mt-1 { margin-top: t.$space-1; }
.u-mt-2 { margin-top: t.$space-2; }Esse padrão evita que variáveis virem “globais invisíveis” e ajuda a entender de onde vem cada dependência.
Regras de convivência entre módulos: padrões que evitam retrabalho
1) Um módulo não deve “consertar” outro
Se um componente A precisa de um override para funcionar dentro do componente B, isso geralmente indica que:
- o componente A está com estilos muito opinativos (deveria ser mais neutro), ou
- o componente B deveria oferecer uma variante/slot/estado para acomodar o caso.
Em termos de pastas, isso significa: evite criar arquivos como components/modal/_fix-button.scss. Em vez disso, ajuste o botão para suportar a variação necessária, ou crie uma variante do botão.
2) Exceções devem ser nomeadas e rastreáveis
Quando uma exceção é inevitável (por exemplo, integração com vendor ou uma limitação de markup), crie um arquivo específico e nomeie de forma explícita:
integrations/_legacy-header.scssintegrations/_vendor-select-overrides.scss
Isso evita que exceções se espalhem por componentes e páginas sem controle.
3) Padrão de ordenação dentro do arquivo
Além da ordem de importação, padronize a ordem interna das regras para facilitar leitura e revisão. Exemplo de ordem:
- comentário curto de propósito (opcional)
- bloco base
- elementos internos
- modificadores/variações
- estados
- media queries (ou agrupadas por breakpoint, conforme padrão do time)
.c-card { /* base */ }
.c-card__title { /* elemento */ }
.c-card--featured { /* variação */ }
.c-card.is-loading { /* estado */ }
@media (min-width: 768px) { .c-card { /* responsivo */ } }Checklist operacional para o time (aplicável em PRs)
- O arquivo novo está na pasta correta (settings/tools/generic/elements/objects/components/utilities/pages/vendors)?
- O nome do arquivo descreve claramente o conteúdo e segue o padrão do projeto?
- Existe um ponto de entrada (
index) para a pasta, evitando deep imports? - O módulo respeita limites: não importa páginas, não cria regras globais indevidas, não depende de ordem “mágica”?
- As importações seguem a ordem definida no
main.scss(ou nos entrypoints por página)? - Não foi criado um arquivo “misc/temp/fixes” sem justificativa?