CSS, caixa e layout

Seletores, cascata, herança, medidas, fluxo, posicionamento, flexbox, grid e desenho responsivo.

TLDR

Perguntar sobre esta página

ChatGPTClaudePerplexityGrokGeminiCopiar e abrirDeepSeekCopiar e abrir

No catálogo, .livro > h2 { color: navy; } dá cor aos títulos que são filhos diretos dos cartões. O seletor escolhe os elementos e a declaração muda uma propriedade. Quando duas regras atingem o mesmo título com cores diferentes, a cascata decide qual se aplica. Esta página mostra como prever essa decisão, medir caixas e construir o layout.

Seletores

A folha externa é ligada pelo link no head do HTML. Também podes escrever regras num elemento style ou declarações no atributo style; a folha externa permite reutilizar regras entre páginas. Uma regra contém um seletor e declarações propriedade: valor, separadas por ponto e vírgula.

Uma regra como .livro > h2 { color: navy; } escolhe os h2 filhos diretos de um elemento com classe livro. O espaço escolhe descendentes a qualquer profundidade. A vírgula separa seletores, não acrescenta uma condição.

SeletorElementos selecionados
article.livroarticle com a classe livro
[name="q"]Elementos com esse atributo e valor
.livro pParágrafos descendentes de .livro
.livro > pParágrafos filhos diretos
h2 + pO p imediatamente a seguir a um h2, com o mesmo pai
h2 ~ pTodos os irmãos p posteriores a um h2
li:first-childli que é o primeiro elemento filho
li:first-of-typeO primeiro li entre os irmãos
li:nth-child(2n)li numa posição par entre todos os elementos filhos
input:checkedControlos marcados
a:hover, a:focus-visibleLigações sob o apontador ou com foco visível
p::first-letter, p::beforeUma parte ou um pseudo-elemento do p

Desenha a árvore antes de responder. Uma section contém h2, p e p como filhos diretos. O primeiro p é o segundo filho e o primeiro do seu tipo. Por isso corresponde a p:first-of-type, mas não a p:first-child, que exigiria ser o primeiro filho. O índice de :nth-child começa em 1 e conta elementos, não nós de texto.

[href^="https://"] testa o início de um atributo; [href$=".pdf"] testa o fim; [href*="livro"] procura uma parte. [class~="livro"] procura uma palavra numa lista separada por espaços, enquanto [lang|="pt"] aceita pt ou um valor que comece por pt-. p.ativo seleciona o próprio parágrafo com a classe; p .ativo seleciona um descendente. Uma pseudo-classe, como :checked, testa um elemento; um pseudo-elemento, como ::before, representa uma parte gerada, normalmente com content.

:not(.esgotado) exclui uma condição; :is(.livro, .revista) reúne alternativas; .livro:has(button) escolhe o elemento pela existência de um descendente que corresponde. Em seletores aninhados, & representa o seletor exterior; numa pergunta de seleção, desenha pais e irmãos antes de contar especificidade.

Cascata e herança

Aqui vamos comparar declarações normais do autor, sem camadas:

#catalogo .preco {
  color: red;
} /* (1, 1, 0) */
ul#catalogo li span.preco {
  color: green;
} /* (1, 1, 3) */

Ambas atingem o mesmo span.preco. Resolve por etapas. Primeiro confirma que as duas declarações estão no mesmo nível de origem, importância e camada. Depois compara o trio (IDs, classes/atributos/pseudo-classes, elementos/pseudo-elementos) da esquerda para a direita: (1, 1, 0) contra (1, 1, 3).

O empate nas duas primeiras posições resolve-se na terceira, por isso a segunda ganha. Daqui sai uma regra prática: dez classes não ultrapassam um identificador, porque a comparação decide na primeira coluna onde houver diferença. * e combinadores não acrescentam especificidade.

:where(...) tem especificidade zero. :is, :not e :has usam a maior especificidade entre os argumentos. Um estilo em linha tem precedência sobre as regras normais da folha do autor; !important altera a importância, não a especificidade. Declarações importantes do utilizador podem ultrapassar as do autor. Não resumas a cascata a “o seletor mais comprido ganha”.

@layer base, componentes;
@layer base {
  #catalogo p {
    color: red;
  }
}
@layer componentes {
  p {
    color: blue;
  }
}

Para declarações normais, a camada posterior ganha antes de contar especificidade, por isso o parágrafo fica azul. Regras normais sem camada ficam acima das normais com camada. A prioridade das camadas inverte-se para declarações importantes.

Herança é outro mecanismo. color e propriedades de texto costumam herdar; margem, borda e padding não. Uma declaração diretamente no filho vence um valor herdado, mesmo que o seletor no pai tenha muitos IDs. inherit pede o valor calculado do pai; initial pede o valor inicial da propriedade; unset herda se a propriedade é herdada e usa o inicial nos restantes casos.

Medidas e modelo de caixa

Uma caixa tem conteúdo, padding, border e margin. Com box-sizing: content-box, a largura declarada mede o conteúdo. Se temos width: 240px, padding de 12px em cada lado e border de 2px:

largura ateˊ aˋ borda=240+2×12+2×2=268 px.\text{largura até à borda}=240+2\times12+2\times2=268\text{ px}.

Com margem de 10px em cada lado, a ocupação horizontal é 288px. Com border-box, os mesmos 240px já incluem padding e borda, e o conteúdo tem 212px. A margem continua fora dos 240px.

No diagrama, confirma onde ficam os extremos de width: 240px: em content-box ficam nas extremidades do conteúdo; em border-box, nas extremidades exteriores da borda. As margens ficam fora de width nos dois casos.

As abreviaturas seguem os lados da caixa. padding: 4px aplica 4px aos quatro lados. padding: 4px 8px dá 4px em cima e em baixo e 8px nos lados; padding: 4px 8px 12px dá 4px em cima, 8px nos lados e 12px em baixo. Com quatro valores, a ordem é cima, direita, baixo, esquerda. max-width limita uma largura flexível; overflow: auto permite deslocar conteúdo que não cabe, sem o apagar.

Margens verticais de blocos em fluxo normal podem colapsar. Duas margens positivas de 16px e 24px dão 24px entre os blocos, não 40px. Essa regra não se aplica da mesma forma aos itens flex e grid. Padding não colapsa.

rem usa o tamanho de letra da raiz. em em font-size usa o tamanho do pai; nas outras propriedades, usa o tamanho de letra do próprio elemento. Segue o exemplo com raiz a 16px e pai a 20px. Um filho com font-size: 2em tem 40px, porque dobra os 20px do pai.

Nesse filho, padding: 1em mede 40px, pois usa a letra do próprio elemento. Já padding: 1rem mede 16px, pois volta à raiz. Percentagens dependem da propriedade e da caixa de referência. vw e vh dependem do viewport.

Fluxo e posicionamento

Um bloco participa normalmente numa sequência vertical. Um elemento inline participa nas linhas de texto, com regras de largura e altura diferentes. inline-block participa na linha e aceita dimensões de caixa. display: none retira a caixa do layout; visibility: hidden conserva o espaço.

positionEfeito
staticFluxo normal; top e left não deslocam a caixa
relativeDesloca visualmente e conserva o lugar original
absoluteSai do fluxo; posiciona-se pela caixa de referência, frequentemente o antepassado posicionado mais próximo
fixedSai do fluxo e, no caso habitual, usa o viewport
stickyPermanece no fluxo e fica preso ao limite definido durante o scroll

Se o botão Reservar tiver position: relative; top: 8px, aparece 8px abaixo do seu lugar, mas os elementos seguintes conservam as posições. O espaço original continua reservado.

Se o mesmo botão tiver position: absolute; bottom: 0 num cartão com position: relative, fica no fundo desse cartão e deixa de reservar espaço no fluxo. O texto pode então ficar por baixo do botão. Um antepassado transformado também pode estabelecer essa referência, incluindo para fixed. z-index compara níveis dentro de contextos de empilhamento; um filho não salta arbitrariamente acima de um contexto exterior.

Para texto à volta de uma imagem, usa float: left na imagem e clear: both no bloco que deve recomeçar abaixo; para grelhas e alinhamento, usa flexbox ou grid.

Flexbox e grid

Flexbox dispõe itens principalmente num eixo. flex-direction define o eixo principal; justify-content alinha nesse eixo e align-items no transversal. flex-wrap permite novas linhas. flex: 1 1 200px dá crescimento 1, redução 1 e base de 200px, não uma largura fixa garantida.

Grid define linhas e colunas. 1fr reparte espaço disponível, depois de considerar outras medidas e limites. minmax(0, 1fr) permite uma coluna reduzir sem o mínimo automático do conteúdo a forçar o layout. gap cria intervalos entre itens. grid-column: 1 / 3 vai da linha 1 à linha 3 e ocupa duas colunas. grid-template-areas dá nomes a zonas retangulares.

HTML / CSS / JavaScript
Código a editar
HTML
CSS e JavaScript
CSS
JavaScript
Pré-visualização

A preparar a pré-visualização…

Consola
Sem mensagens.

O catálogo começa com uma coluna. A partir de 600px, a media query cria três. Dentro de cada cartão, flexbox organiza título, texto e botão em coluna; margin-top: auto absorve o espaço livre antes do botão, por isso os botões alinham quando os cartões têm a mesma altura.

Cor, variáveis e movimento

Cor pode usar nomes, hexadecimal, rgb ou hsl. O fundo pertence à caixa definida por background; background-size: cover pode cortar a imagem para cobrir a área. A fonte deve ter uma família de recurso, e line-height sem unidade acompanha o tamanho da letra.

:root {
  --cor-livro: #345;
}
.livro {
  border-color: var(--cor-livro);
}
button {
  transition: transform 150ms ease;
}
button:hover {
  transform: translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
  button {
    transition: none;
  }
}

Uma custom property participa na cascata e costuma herdar. transform muda o desenho da caixa sem reservar um novo lugar no fluxo. transition interpola uma alteração de propriedade; não inicia por si uma sequência repetida.

Testa o layout com textos longos, zoom e largura pequena; uma media query responde ao ambiente, mas não corrige uma largura mínima que já não cabe.

Repartir o espaço com fr

Uma grelha tem 720px de largura de conteúdo, sem padding nem borda, e três colunas: 120px, uma fração e duas frações. Os dois intervalos entre colunas medem 30px cada:

.grelha {
  display: grid;
  width: 720px;
  grid-template-columns: 120px minmax(0, 1fr) minmax(0, 2fr);
  gap: 30px;
}

Primeiro retiramos o que não é repartido: a coluna fixa e os dois gaps. Restam 720 - 120 - 2×30 = 540px. As frações somam 3, por isso uma unidade vale 540 / 3 = 180px.

ParteLargura
Coluna fixa120px
1fr180px
2fr360px
Dois gaps60px
Total720px

Não dividas a largura inteira por três: fr reparte o espaço que sobra. Com conteúdo de 480px e as restantes medidas iguais, sobram 300px e as colunas fracionárias passam a 100px e 200px.

minmax(0, ...) fixa em zero o mínimo dos dois tracks flexíveis neste cálculo. Com o mínimo automático de um simples 1fr, um conteúdo longo pode impor uma largura maior e mudar a distribuição; os itens também precisam de tratamento para não transbordarem. O exemplo de largura fixa serve para calcular, não para substituir o layout responsivo do catálogo.

Um item com grid-column: 2 / 4 atravessa as colunas 2 e 3 e o gap entre elas. No caso de 720px, ocupa 180 + 30 + 360 = 570px. As linhas delimitam o intervalo; o espaço entre os tracks faz parte do item que os atravessa.

Exercícios

Selecionar e decidir a cor

Prevê a cor de B. Considera regras normais do autor sem camadas e sem outras declarações.

<section id="zona">
  <ul class="lista">
    <li>A</li>
    <li class="ativo">B</li>
  </ul>
</section>
#zona {
  color: blue;
}
.lista li {
  color: green;
}
section .lista > li.ativo {
  color: red;
}
.lista li:first-child + li {
  color: inherit;
}

Pista

A regra no section fornece herança; as restantes atingem diretamente B.

Mais uma pista

As duas últimas têm (0,2,2). A última declara inherit.

Solução

B fica azul. Segue as etapas. Primeiro separa herança de declaração direta: #zona declara azul diretamente na secção; o ul herda esse azul. Depois compara as regras que atingem B diretamente. A segunda tem (0,1,1). A terceira e a quarta têm (0,2,2), por isso a quarta ganha pela posição e declara inherit. B herda então o azul do ul, que por sua vez herdou de #zona. A especificidade do avô não compete diretamente com as declarações do neto.

Confere a tua resposta:

  • Contei as duas especificidades empatadas.
  • Expliquei inherit a partir do ul azul.

Erros frequentes

Não comparar a especificidade do pai com declarações diretas do filho.

Voltar à explicação
Medir uma caixa

Uma caixa tem width 180px, padding 8px de cada lado, border 3px de cada lado e margin 5px de cada lado. Calcula a largura até à borda e a ocupação com margens em content-box, e repete em border-box.

Pista

Padding e border somam-se ao conteúdo em content-box.

Mais uma pista

Em border-box, width já inclui padding e border, mas não margin.

Solução

Em content-box, soma ao conteúdo: 180 + 2x8 + 2x3 = 202px até à borda, e 202 + 2x5 = 212px com margens. Em border-box, os 180px já incluem padding e borda: 180px até à borda e 190px com margens. O conteúdo mede 180 - 16 - 6 = 158px. O colapso de margens verticais não altera estas contas horizontais.

Confere a tua resposta:

  • Obtive 202/212 em content-box.
  • Obtive 180/190 e conteúdo 158 em border-box.

Erros frequentes

Não somar padding e border de novo ao width de border-box.

Voltar à explicação
Distinguir filho e irmão

Num ul há quatro li, com classes primeiro, segundo, terceiro e quarto. O segundo tem um span filho. Quais li escolhem li.primeiro + li, li.primeiro ~ li e li > span? Justifica.

Pista

Os combinadores + e ~ ligam irmãos posteriores.

Solução

O primeiro seletor escolhe só o segundo li. O segundo escolhe segundo, terceiro e quarto. O terceiro não escolhe nenhum li: escolhe o span filho direto do segundo li. Combinadores descrevem relações, não mudam o tipo da seleção final.

Confere a tua resposta:

  • Distingui um irmão de todos os posteriores.
  • Identifiquei span como alvo do terceiro seletor.

Erros frequentes

Não dizer que li > span escolhe o pai li.

Voltar à explicação
Construir duas colunas

Escreve CSS para .catalogo com uma coluna abaixo de 700px e duas colunas iguais a partir de 700px, com gap: 16px. Cada .livro é um filho direto do catálogo e contém um título, um parágrafo e um botão, por essa ordem. Os textos têm comprimentos diferentes. Alinha os botões no fundo dos cartões da mesma linha, sem fixar a altura do texto.

Pista

Usa grid no catálogo e flex em coluna no cartão.

Mais uma pista

A media query pode mudar grid-template-columns; a margem automática absorve espaço antes do botão.

Solução

Uma solução é:

.catalogo {
  display: grid;
  grid-template-columns: 1fr;
  gap: 16px;
}
.livro {
  display: flex;
  flex-direction: column;
}
.livro button {
  margin-top: auto;
}
@media (min-width: 700px) {
  .catalogo {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

A grelha estica os cartões na linha por defeito. A margem automática aproveita o espaço vertical livre antes do botão.

Confere a tua resposta:

  • Defini a largura pequena e a condição >=700px.
  • Usei espaço automático antes do botão.

Erros frequentes

Não mudar apenas width dos cartões sem definir o layout, nem trocar o eixo principal pelo transversal.

Voltar à explicação
Descontar os espaços antes de repartir

Uma grelha tem largura de conteúdo 620px, sem padding nem borda, três colunas 80px minmax(0,1fr) minmax(0,3fr) e gap de 20px. Não há outras restrições de largura impostas pelos itens. Calcula os três tracks e a largura de um item com grid-column: 2 / 4. Repete os tracks quando a largura de conteúdo passa a 320px. Explica por que dividir 620 por quatro não dá a unidade fr.

Solução

Com três colunas, há dois gaps. Retiramos 80 + 2×20 = 120px antes de repartir. Sobram 500px e as frações somam 4: cada fr vale 125px. Os tracks são 80px, 125px e 375px.

O item entre as linhas 2 e 4 atravessa os dois tracks flexíveis e o gap interior: 125 + 20 + 375 = 520px.

Com conteúdo de 320px, sobram 320 - 120 = 200px, dando 50px por fr. Os tracks são 80px, 50px e 150px. Dividir a largura inteira incluiria novamente a coluna fixa e os gaps na distribuição. minmax(0,...) retira o mínimo automático que poderia fazer o conteúdo alterar estas contas.

Confere a tua resposta:

  • Obtive colunas 80, 125 e 375 com conteúdo 620.
  • Obtive 520 para o item entre as linhas 2 e 4.
  • Recalculei 80, 50 e 150 com conteúdo 320.
Voltar à explicação

À tua maneira

Aparência

Modo de cor
Tema
Cor de destaque

Leitura

Fonte de leitura

Álgebra, lógica e uma ideia de cada vez.

Código

Fonte do código

Navegação

Cursor de leitura, sem edição. h, j, k, l movem; / pesquisa nesta página. v seleciona caracteres; V seleciona linhas; y copia. Tab permite destacar ou comentar. Espaço ou Enter abre blocos recolhíveis. Nos cartões, h, j, k, l mudam o foco.

CSS personalizado

Ativa uma sugestão ou guarda o teu CSS. Só muda este navegador.

Usa --page, --text e --accent para acompanhar o tema.

Seletores e recuperação

.site-header, .prose, .course-card, [data-reading-history], [data-playground] e .page-actions.

Abre a aparência com a tecla vírgula ou no rodapé. Se o CSS esconder os controlos, abre esta página sem CSS personalizado.

Pesquisar

Escreve para pesquisar em todo o site.

para escolher · Enter para abrir · Esc para fechar

Copiar fórmula

A cópia automática não está disponível. Copia o LaTeX abaixo.

Pesquisar nesta página

Enter procura · n / N repetem · Esc cancela. Ctrl / Cmd K pesquisa todo o site.

Atalhos de teclado

Clica numa tecla para a mudar. Esc cancela. Backspace desativa.

PesquisarCtrl / Cmd K

Os atalhos não interferem enquanto escreves. Tab e Enter funcionam sempre.