Capítulo 30
Pseudo-elementos e interação CSS
4 min de leitura 698 palavras 2 gravuras
Esta é o capítulo 30 de 52 do volume Aprenda a Web. O projeto-fio é a Oficina Costeira, um coletivo fictício de design em João Pessoa. O exemplo está em exemplos/30-pseudo-elementos/. Nada aqui exige JavaScript. Em cada capítulo: leia, copie, mude uma coisa, preveja, recarregue e confira.
O que você vai aprender
- Ligar
::beforee::aftercomcontent. - Usar pseudo-elementos para marcador e aspas.
- Separar
:hoverde:focus-visible. - Abrir um painel com
:targetoudetails. - Manter o foco visível em cerca de 3 px.
- Recusar interação que só funciona no mouse.
Dois pedaços escondidos
Todo elemento tem ::before e ::after. Eles nascem vazios. Sem content, não aparecem. O valor pode ser um espaço, um texto ou "".
.cartao::before {
content: "";
}
.cartao::after {
content: "Oficina";
display: inline-block;
padding: 0.25em 0.5em;
background: #0b4f6c;
color: #fff;
}
Os pseudo-elementos ficam dentro do pai. position: absolute neles mede o ancestral relative.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Detalhe — Oficina Costeira</title>
<link rel="stylesheet" href="assets/stylesheets/main.css">
</head>
<body>
<article class="cartao">
<h1>Encontro na orla</h1>
<p>Ateliê fictício na Praia do Jacaré, João Pessoa.</p>
</article>
</body>
</html>
Copie. Mude content do ::after para "Jacaré". Preveja o selo. Recarregue.
Erro comum: esquecer content. A regra de cor e tamanho existe, mas nada desenha.
Marcadores e aspas
A bolinha padrão de ul é difícil de pintar. Tire list-style e coloque o marcador no ::before.
.oficinas {
list-style: none;
padding: 0;
}
.oficinas li::before {
content: "·";
display: inline-block;
margin-right: 0.35em;
color: #c45c26;
}
Citação com aspas curvas, sem span extra:
blockquote {
position: relative;
}
blockquote::before {
content: "“";
font-size: 3rem;
color: #c45c26;
position: absolute;
left: -0.4em;
top: 0;
}
O texto da citação continua no HTML. A aspa é enfeite. Quem não vê CSS ainda lê a frase.
Hover não é foco
:hover reage ao ponteiro. Teclado não passa por cima. :focus-visible reage quando o foco precisa aparecer — em geral Tab.
a {
color: #0b4f6c;
}
a:hover {
color: #c45c26;
}
a:focus-visible,
button:focus-visible {
outline: 3px solid #c45c26;
outline-offset: 3px;
}
Não remova o outline sem substituto. O retângulo de 3 px é o combinado deste volume.
:focus-visible evita o anel quando a pessoa clica com o mouse e o navegador julga que o anel não ajuda. O Tab continua óbvio.
Mude outline-offset para 0. Preveja o anel colado no texto. Recarregue. Volte para 3px.

Abrir sem script
:target estiliza o elemento cujo id está no hash da URL.
<p><a href="#aviso">Ler o aviso</a></p>
<p id="aviso" class="aviso">Encontro fictício. Nada é enviado a um servidor.</p>
.aviso:target {
outline: 3px solid #c45c26;
background: #fff;
}
Clique no link. A URL ganha #aviso. O parágrafo destaca. Voltar tira o alvo.
details e summary abrem um bloco nativo, com teclado.
<details>
<summary>Horário do encontro</summary>
<p>Sábado fictício, 9h, Praia do Jacaré.</p>
</details>
Não use checkbox escondido para fingir um botão. O foco some e o estado não tem nome acessível. Prefira details ou um link :target.
Erro comum: div clicável só com :hover para “abrir o menu”. Sem Tab, sem Enter, sem leitor. Interação de verdade precisa de foco e teclado.
Enfeite, não conteúdo
content não substitui um título. Texto importante fica no HTML. Pseudo-elemento serve para marca, ícone decorativo ou aspa.
attr(data-selo) copia um atributo para content. Continua sendo visual. O leitor pode ignorar. Se a informação importa, escreva no p ou no span.
.cartao::after {
content: attr(data-selo);
}
<article class="cartao" data-selo="Vagas fictícias">
<h2>Oficina de cartaz</h2>
</article>
Leia o HTML sem CSS. Se a frase essencial sumir, ela estava no lugar errado.
Ciclo completo na página
Abra o exemplo. Tab até o primeiro link. Confira o anel. Passe o mouse: a cor muda, o anel do Tab não precisa aparecer. Ative um details. Recarregue: o details volta fechado, o :target só permanece se a URL ainda tiver o hash.
Estreite a janela. Os pseudo-elementos não devem cobrir o texto. Se o ::before absoluto invadir o título, aumente padding-left no pai.

Mude o content do marcador para "→". Preveja a lista. Recarregue.
Atividades
- O que falta para um
::beforeaparecer na tela? - Qual pseudo-classe desenha o contorno quando a pessoa usa Tab, sem depender de
:hover? - Por que um menu só com
:hovernadivfalha no teclado? - Qual elemento HTML abre e fecha conteúdo sem JavaScript e sem checkbox?
- O selo “Vagas fictícias” pode viver só em
content? Justifique.
Respostas comentadas
- A propriedade
content, mesmo que seja"". Sem ela o pseudo-elemento não gera caixa. :focus-visible, comoutlinede cerca de 3 px e deslocamento.divnão entra na ordem de Tab.:hovernão existe no teclado. Não há nome de botão.detailscomsummary. Alternativa: link interno e:target.- Não, se a informação importa.
contenté visual. Coloque o texto no HTML.
Referências
O arquivo completo
Referência do exemplo 30-pseudo-elementos, reproduzida aqui para que você possa conferir sem sair da página.
assets/javascript/simulacao.js
const form=document.querySelector("#simulacao");if(form){form.querySelector("button[type=submit]").disabled=false;form.addEventListener("submit",event=>{event.preventDefault();document.querySelector("#retorno").textContent="Simulação concluída. Nada foi enviado ou guardado.";});}