Curso de CSS / Organizar arquivos, aninhamento e camadas de cascata

CSS fácil de manter separa valores compartilhados, estilos base, componentes e ajustes condicionais de acordo com suas funções. O aninhamento nativo delimita seletores relacionados, enquanto camadas de cascata ordenam explicitamente grupos de regras e introduzem suas próprias regras de precedência.

Reduza duplicações e atribua responsabilidades aos arquivos

Primeiro, procure por duplicatas da mesma cor e espaçamento, seletores obsoletos e regras de botão quase idênticas. Valores com a mesma função são agrupados em variáveis, e diferentes botões dividem regras comuns e diferenças necessárias. Pode ser melhor manter dois componentes independentes em vez de criar condições complexas para eliminar toda a duplicação. Ao agrupar valores comuns, verifique o escopo e as condições de herança da variável em Variáveis: var(), escopo, herança e fallback.

/* Before cleanup: Same value repeated in multiple places */
.lesson-button { padding: .65rem 1rem; color: #166b92; }
.notice-button { padding: .65rem 1rem; color: #166b92; }

/* After organizing: sharing common shapes and roles */
.button { padding: .65rem 1rem; color: var(--accent); }
.button.text-alert { color: #9d2632; }

A comparação acima é um código parcial explicando a direção da limpeza. Uma classe de botão comum também deve ser anexada ao HTML real e, se você apenas renomear o CSS e deixar o HTML como está, a aparência pode desaparecer. Todo o exercício abaixo executa essa configuração organizada em arquivos e camadas.

arquivo responsabilidade Julgamento ao revisar
tokens.css Cores comuns e espaçamento É um valor compartilhado por vários componentes?
base.css Fonte, margem padrão, exibição em foco Esse é o comportamento padrão para todo o documento?
layout.css Área de conteúdo e layout É a relação posicional dos componentes?
components.css Forma e estado dos cartões e botões Está ligado a um único componente?
utilities.css Pequenas classes auxiliares explícitas Isso é uma exceção que se aplicará apenas em alguns lugares?

Para documentos pequenos, pode bastar organizar seções com comentários dentro de um único arquivo. Considere separar arquivos quando várias pessoas modificam componentes diferentes ou quando o escopo de reutilização aumenta. O nome do arquivo é um separador para navegação, e apenas dividir o arquivo não elimina conflitos de seletores. Caminhos de conexão HTML e cascatas também devem ser gerenciados.

Distinga o pai e os descendentes no aninhamento

O aninhamento CSS nativo é interpretado diretamente pelo navegador. Isso é diferente da forma como preprocessadores como Sass geram CSS diferente durante a fase de build. O código abaixo coloca o título do .card próximo ao estado do próprio card.

.card {
  & h2 { color: var(--accent); }
  &:hover { border-color: var(--accent); }
}

& h2 representa o h2 dentro do card, e &:hover representa o hover do próprio card. Se você inserir um espaço como & :hover, o resultado será diferente porque ele seleciona o descendente em hover. Também distinga entre &.warning, que seleciona uma classe auxiliar do mesmo elemento, e & .warning, que seleciona descendentes.

/* The simple overlap above is expanded */
.card h2 { color: var(--accent); }
.card:hover { border-color: var(--accent); }

Com este seletor de pai único, o aninhamento pode ser comparado diretamente com as regras expandidas. Uma lista de pais separada por vírgulas pode fazer com que a especificidade do seletor aninhado dependa do seletor mais específico nesta lista. Portanto, aninhamentos complexos não são simples concatenações de strings. Evite profundidade desnecessária que vincule o CSS a uma hierarquia HTML rígida. Para mais detalhes, veja o guia de uso de aninhamento MDN, aninhamento e explicação de prioridade.

Verifique o suporte ao aninhamento do navegador que você precisa suportar e, se precisar de um ambiente que não ofereça suporte, escolha o CSS expandido como acima ou uma ferramenta de build apropriada. Só porque você usa uma nova sintaxe, não presuma que ela se aplica a todos os seus visitantes.

Compare as camadas da cascata antes da especificidade

Para declarações normais da mesma origem de autor, as declarações fora de camadas têm precedência sobre as declarações em camadas. Entre camadas, a que aparece depois na ordem estabelecida tem precedência. Assim, uma classe de uma camada posterior pode vencer um ID de uma camada anterior. Quando origem, importância e camada são iguais, a comparação prossegue com herança, especificidade e cascata, proximidade de escopo quando aplicável e ordem das declarações.

@layer tokens, base, layout, components, utilities;
@layer base { #layer-target { color: #183044; } }
@layer utilities { .text-alert { color: #9d2632; } }

O botão de exemplo corresponde a ambos os seletores. Entre as declarações normais deste exemplo, a camada utilities vem depois, então a cor vermelha se aplica. Prever o resultado apenas porque um botão tem uma prioridade de ID alta pode estar errado. A ordem das camadas é determinada pela ordem em que seus nomes são registrados pela primeira vez, e mesmo que você abra a mesma camada novamente mais tarde, essa camada não será movida para trás.

As declarações !important têm precedência sobre declarações gerais, e a ordem das camadas para declarações importantes é invertida. Declarações com !important em camadas anteriores vencem as de camadas posteriores; declarações importantes em camadas também vencem as importantes fora de camadas, na comparação limitada à mesma origem de autor. Este exemplo é limitado a comparar camadas de CSS do mesmo autor e suas propriedades. Ele não se expande para outras condições da cascata, como estilos de usuário/navegador, declarações inline e transições em execução, dizendo: “Você só precisa olhar para as camadas.” Você pode verificar as condições completas no documento de camadas MDN.

Crie o HTML e cinco arquivos CSS

Crie a estrutura abaixo em uma pasta vazia. Todos os arquivos devem ser salvos como UTF-8. Como o index.html usa o caminho relativo styles/..., o arquivo CSS deve estar na subpasta styles. Você pode abrir o index.html em um navegador sem precisar de um servidor separado ou biblioteca externa. Se não conseguir ler o arquivo, compare a localização do arquivo e o endereço de conexão com Aplicar estilos inline, internos e externos.

stylesheet-project/
  index.html
  styles/
    tokens.css
    base.css
    layout.css
    components.css
    utilities.css

index.html contém as tags link e classes efetivamente usadas. O botão de comparação não é um botão para executar uma ação, mas serve para fins de prática para verificar a cor do texto aplicada e o foco.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>CSS files and layers by responsibility</title>
  <link rel="stylesheet" href="styles/tokens.css">
  <link rel="stylesheet" href="styles/base.css">
  <link rel="stylesheet" href="styles/layout.css">
  <link rel="stylesheet" href="styles/components.css">
  <link rel="stylesheet" href="styles/utilities.css">
</head>
<body>
  <main class="demo">
    <h1>Organizing a small stylesheet</h1>
    <article class="card">
      <h2>Scope nested rules to the card</h2>
      <p>The card heading uses the accent color. Compare it with the heading outside the card.</p>
      <button class="button" type="button">Regular button</button>
    </article>
    <section><h2>Heading outside the card</h2><p>Use the default font color.</p></section>
    <button id="layer-target" class="button text-alert" type="button">Layer comparison button</button>
  </main>
</body>
</html>

styles/tokens.css registra a ordem das camadas primeiro e declara valores comuns. Outro arquivo abre uma camada com o mesmo nome para adicionar regras a ela.

@layer tokens, base, layout, components, utilities;
@layer tokens {
  :root { --ink: #183044; --accent: #166b92; --space-card: 1.25rem; }
}

styles/base.css define a aparência padrão do documento e o indicador de foco. Regras de ID são dispositivos implementados para comparar o impacto das camadas na prioridade.

@layer base {
  *, *::before, *::after { box-sizing: border-box; }
  body { margin: 0; padding: 1rem; color: var(--ink); font-family: sans-serif; line-height: 1.6; }
  h1 { font-size: 1.5rem; } h2 { font-size: 1.2rem; }
  button { font: inherit; cursor: pointer; }
  :focus-visible { outline: 3px solid #164f73; outline-offset: 3px; }
  #layer-target { color: #183044; }
}

styles/layout.css gerencia o espaçamento entre áreas de conteúdo e seções.

@layer layout {
  .demo { display: grid; gap: 1rem; max-width: 44rem; margin-inline: auto; }
  .demo > h1 { margin-block: 0 .5rem; }
  .demo > button { justify-self: start; }
}

styles/components.css gerencia cards e botões. A cor do título se aplica apenas ao interior do card, e a borda muda quando você passa o mouse sobre o card ou foca no botão dentro dele.

@layer components {
  .card {
    padding: var(--space-card); border: 2px solid #90a4b2; border-radius: .5rem;
    & h2 { margin-top: 0; color: var(--accent); }
    &:hover, &:focus-within { border-color: var(--accent); }
  }
  .button {
    padding: .65rem 1rem; color: var(--accent); background: #eaf4fa;
    border: 1px solid #166b92; border-radius: .3rem;
  }
}

styles/utilities.css gerencia uma classe auxiliar para o botão de comparação. Declarações gerais neste arquivo têm precedência sobre regras de ID/botão em camadas anteriores.

@layer utilities {
  .text-alert { color: #9d2632; }
}

Teste arquivos, aninhamento e camadas separadamente

  1. Na tela principal, certifique-se de que o título do cartão e o botão geral estejam azuis, o título fora do cartão esteja na cor padrão e o botão de comparação esteja vermelho. Use a tecla Tab para ver se o botão tem um indicador de foco.
  2. Comente apenas a regra & h2 dentro do cartão em components.css. Os títulos dos cartões também retornam às suas cores padrão. Após a confirmação, descomente-a.
  3. Vamos adicionar a classe text-alert a ambos os botões no HTML. Os botões dentro do cartão também ficarão vermelhos. Depois remova apenas a classe text-alert do botão interno para restaurá-lo.
  4. No final do components.css, adicione apenas .button { color: #28633b; } fora do bloco @layer. Ambos os botões ficarão verdes. Isso resulta em declarações gerais fora da camada precedendo a ordem das camadas. Após a confirmação, exclua a nova regra.
  5. Adicione !important apenas à declaração de cor do ID em base.css. O botão de comparação muda para a cor padrão. Nesse estado, se você adicionar !important à declaração de cor de utilities.css, a cor básica da camada anterior será mantida. Após a comparação, remova os dois !important.
  6. Apenas remova temporariamente o link do components.css do HTML. As formas dos componentes do cartão e do botão desaparecem. Reconecte o caminho ao seu estado original e atualize para restaurá-lo.

Diagnostique estilos ausentes

Verifique separadamente problemas de separação de arquivos e conflitos de seletor. Primeiro, olhe em Network ou Sources nas ferramentas de desenvolvedor do seu navegador para ver se o arquivo foi realmente carregado, depois selecione o elemento para ver se as regras correspondem em Styles e se alguma declaração foi sobrescrita. Se você não vir o arquivo, verifique o caminho relativo, nome do arquivo e extensão, e se você não vir a regra, verifique a classe e o suporte à sintaxe.

Mesmo que haja uma regra correspondente, se ocorrer um risco, compare a camada, !important, a especificidade do seletor, a proximidade de escopo quando aplicável e a ordem. Se as variáveis comuns não estiverem conforme o esperado, também observe o escopo da declaração da variável. Após a separação, é útil para a manutenção reinspecionar a tela principal e foco/passagem do mouse, e corrigir as divisões de responsabilidade e seletores que causaram conflitos em vez de continuar a adicionar declarações importantes desnecessárias.