No que estamos trabalhando
Cada instrutor aqui no Tuts+ tem sua própria página de arquivo. Vamos recriar uma lista de tutoriais como esta, usando nossa própria marcação. Em seguida, implementaremos um componente que filtrará as postagens com base nas categorias às quais pertencem.
Aqui está nosso projeto final:
Vamos construir!
1. Comece com a marcação HTML
Começamos identificando as categorias de filtros em nosso componente. Neste exemplo, usaremos sete filtros:
All
CSS
JavaScript
jQuery
WordPress
Slider
fullPage.js
Para fazer isso, primeiro definimos sete botões de opção que agrupamos sob o categories
palavra-chave. Por padrão, o primeiro botão de opção está marcado:
1 |
type="radio" id="All" name="categories" value="All" checked>
|
2 |
type="radio" id="CSS" name="categories" value="CSS">
|
3 |
type="radio" id="JavaScript" name="categories" value="JavaScript">
|
4 |
type="radio" id="jQuery" name="categories" value="jQuery">
|
5 |
type="radio" id="WordPress" name="categories" value="WordPress">
|
6 |
type="radio" id="Slider" name="categories" value="Slider">
|
7 |
type="radio" id="fullPage.js" name="categories" value="fullPage.js">
|
Em seguida, criamos uma lista ordenada que contém os rótulos relacionados aos botões de opção mencionados.
Lembre-se de que associamos um botão de opção a um rótulo definindo seu id
valor igual ao da etiqueta for
valor:
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
A seguir, montamos outra lista ordenada que inclui os elementos que queremos filtrar (nossos cartões). Cada um dos elementos filtrados terá um personalizado data-category
atributo cujo valor é uma lista de filtros separados por espaços em branco:
1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
No nosso caso, os elementos filtrados serão postagens. Portanto, a marcação que usaremos para descrever uma postagem junto com sua meta (título, imagem, categorias) fica assim:
1 |
|
2 |
|
3 |
href="" target="_blank">
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
href="">...
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
href="" target="_blank">...
|
16 |
|
17 |
|
18 |
|
19 |
|
Com a marcação pronta, vamos voltar nossa atenção para os estilos necessários.
2. Defina os estilos
Primeiro ocultamos visualmente os botões de opção:
1 |
input[type="radio"] { |
2 |
position: absolute; |
3 |
left: -9999px; |
4 |
}
|
Em seguida, adicionamos alguns estilos aos filtros:
1 |
:root { |
2 |
--black: #1a1a1a; |
3 |
--white: #fff; |
4 |
--green: #49b293; |
5 |
}
|
6 |
|
7 |
.filters { |
8 |
text-align: center; |
9 |
margin-bottom: 2rem; |
10 |
}
|
11 |
|
12 |
.filters * { |
13 |
display: inline-block; |
14 |
}
|
15 |
|
16 |
.filters label { |
17 |
padding: 0.5rem 1rem; |
18 |
margin-bottom: 0.25rem; |
19 |
border-radius: 2rem; |
20 |
min-width: 50px; |
21 |
line-height: normal; |
22 |
cursor: pointer; |
23 |
transition: all 0.1s; |
24 |
}
|
25 |
|
26 |
.filters label:hover { |
27 |
background: var(--green); |
28 |
color: var(--white); |
29 |
}
|
Layout de grade CSS
Continuamos especificando alguns estilos para os elementos filtrados. Mais importante ainda, usamos CSS Grid para organizá-los de forma diferente dependendo do tamanho da tela:
1 |
:root { |
2 |
--black: #1a1a1a; |
3 |
--white: #fff; |
4 |
--green: #49b293; |
5 |
}
|
6 |
|
7 |
.posts { |
8 |
display: grid; |
9 |
grid-gap: 1.5rem; |
10 |
grid-template-columns: repeat(4, 1fr); |
11 |
}
|
12 |
|
13 |
.posts .post { |
14 |
background: #fafafa; |
15 |
border: 1px solid rgba(0, 0, 0, 0.1); |
16 |
}
|
17 |
|
18 |
.posts .post-title { |
19 |
font-size: 1.3rem; |
20 |
}
|
21 |
|
22 |
.posts .post-title:hover { |
23 |
text-decoration: underline; |
24 |
}
|
25 |
|
26 |
.posts figcaption { |
27 |
padding: 1rem; |
28 |
}
|
29 |
|
30 |
.posts .post-categories { |
31 |
margin-bottom: 0.75rem; |
32 |
font-size: .75rem; |
33 |
}
|
34 |
|
35 |
.posts .post-categories * { |
36 |
display: inline-block; |
37 |
}
|
38 |
|
39 |
.posts .post-categories li { |
40 |
margin-bottom: 0.2rem; |
41 |
}
|
42 |
|
43 |
.posts .post-categories a { |
44 |
padding: 0.2rem 0.5rem; |
45 |
border-radius: 1rem; |
46 |
border: 1px solid; |
47 |
line-height: normal; |
48 |
background: all 0.1s; |
49 |
}
|
50 |
|
51 |
.posts .post-categories a:hover { |
52 |
background: var(--green); |
53 |
color: var(--white); |
54 |
}
|
55 |
|
56 |
@media screen and (max-width: 900px) { |
57 |
.posts { |
58 |
grid-template-columns: repeat(3, 1fr); |
59 |
}
|
60 |
}
|
61 |
|
62 |
@media screen and (max-width: 650px) { |
63 |
.posts { |
64 |
grid-template-columns: repeat(2, 1fr); |
65 |
}
|
66 |
}
|
Adicionando os estilos de filtragem
A ideia aqui é surpreendentemente simples. Cada vez que clicamos em um filtro, apenas os elementos filtrados correspondentes (posts) deverão aparecer. Para implementar essa funcionalidade, usaremos uma combinação dos seguintes recursos CSS:
Quando clicamos no All
filtrar, todas as postagens que possuem um data-category
atributo aparecerá:
1 |
[value="All"]:checked ~ .posts [data-category] { |
2 |
display: block; |
3 |
}
|
Quando clicamos em qualquer outra categoria de filtro, apenas as postagens alvo ficarão visíveis:
1 |
[value="CSS"]:checked ~ .posts .post:not([data-category~="CSS"]), |
2 |
[value="JavaScript"]:checked ~ .posts .post:not([data-category~="JavaScript"]), |
3 |
[value="jQuery"]:checked ~ .posts .post:not([data-category~="jQuery"]), |
4 |
[value="WordPress"]:checked ~ .posts .post:not([data-category~="WordPress"]), |
5 |
[value="Slider"]:checked ~ .posts .post:not([data-category~="Slider"]), |
6 |
[value="fullPage.js"]:checked ~ .posts .post:not([data-category~="fullPage.js"]) { |
7 |
display: none; |
8 |
}
|
Por exemplo, desde que cliquemos no Slider
categoria de filtro, apenas as postagens que pertencem à Slider
a categoria ficará visível.



Vale ressaltar que em nossos estilos acima ao invés do [att~=val]
sintaxe, poderíamos igualmente ter usado a [att*=val]
sintaxe. Esta é a aparência dessa mudança sutil:
1 |
[value="CSS"]:checked ~ .posts .post:not([data-category*="CSS"]), |
2 |
[value="JavaScript"]:checked ~ .posts .post:not([data-category*="JavaScript"]), |
3 |
[value="jQuery"]:checked ~ .posts .post:not([data-category*="jQuery"]), |
4 |
[value="WordPress"]:checked ~ .posts .post:not([data-category*="WordPress"]), |
5 |
[value="Slider"]:checked ~ .posts .post:not([data-category*="Slider"]), |
6 |
[value="fullPage.js"]:checked ~ .posts .post:not([data-category*="fullPage.js"]) { |
7 |
display: none; |
8 |
}
|
Explicação rápida do seletor CSS
O que exatamente esse seletor está dizendo?
A primeira parte [value="CSS"]:checked
procura por botões de opção marcados com um valor específico (“CSS” neste caso).
Depois disso, o til (~) é o que hoje chamamos de “seletor de irmão subsequente”. Ele seleciona elementos que possuem o mesmo pai do elemento anterior, mesmo que eles não sigam imediatamente na marcação. Então ~ .posts .post
procura o elemento .posts .post
que compartilha o mesmo pai que a entrada de rádio verificada.
Para ser ainda mais específico, :not([data-category~="CSS"])
refina nosso seletor para apenas aqueles .post
elementos que fazem não tenha um data-category
atributo que contém um valor de CSS
em algum lugar dentro de uma lista separada por espaço.
Em seguida, aplica-se uma display: none;
a quaisquer elementos que correspondam a esses critérios.
É um seletor bastante complexo, embora seja perfeitamente lógico. Em termos de linguagem humana, você pode descrevê-lo como:
“Quando o rádio com valor “CSS” for verificado, encontre quaisquer elementos irmãos subsequentes que não contenham “CSS” em sua lista de categorias de dados e oculte-os.”
Última parte do estilo
Como última etapa, adicionamos uma regra que destaca a categoria do filtro ativo:
1 |
:root { |
2 |
--black: #1a1a1a; |
3 |
--white: #fff; |
4 |
--green: #49b293; |
5 |
}
|
6 |
|
7 |
[value="All"]:checked ~ .filters [for="All"], |
8 |
[value="CSS"]:checked ~ .filters [for="CSS"], |
9 |
[value="JavaScript"]:checked ~ .filters [for="JavaScript"], |
10 |
[value="jQuery"]:checked ~ .filters [for="jQuery"], |
11 |
[value="WordPress"]:checked ~ .filters [for="WordPress"], |
12 |
[value="Slider"]:checked ~ .filters [for="Slider"], |
13 |
[value="fullPage.js"]:checked ~ .filters [for="fullPage.js"] { |
14 |
background: var(--green); |
15 |
color: var(--white); |
16 |
}
|
3. Acessibilidade
Essa filtragem é facilmente acessível por padrão; graças à forma nativa como os botões de opção e rótulos funcionam, podemos filtrar nossos itens com as teclas do teclado. Primeiro pressione o Guia para mover o foco para o botão de opção marcado. Em seguida pressione o Teclas de seta para mover o foco e a seleção para os outros botões de opção. Experimente você mesmo:
Dito isto, não prestámos muita atenção à acessibilidade, por isso pode haver outros aspectos que precisam de ser melhorados.
4. Extra: Filtrando com o :has()
Seletor
Vamos agora reconstruir este componente de alternância somente CSS aproveitando as vantagens do moderno :has()
Pseudoclasse CSS. Este seletor flexível nos permite estilizar elementos que atendam às condições definidas dentro do :has()
função. Neste caso, não usaremos o :not()
e ~
Seletores CSS.
Primeiro, ocultamos todas as postagens:
1 |
.posts .post { |
2 |
display: none; |
3 |
}
|
Depois disso, substituímos as regras de filtragem por estas:
1 |
/*CUSTOM VARIABLES HERE*/
|
2 |
|
3 |
*:has([value="All"]:checked) .filters [for="All"], |
4 |
*:has([value="CSS"]:checked) .filters [for="CSS"], |
5 |
*:has([value="JavaScript"]:checked) .filters [for="JavaScript"], |
6 |
*:has([value="jQuery"]:checked) .filters [for="jQuery"], |
7 |
*:has([value="WordPress"]:checked) .filters [for="WordPress"], |
8 |
*:has([value="Slider"]:checked) .filters [for="Slider"], |
9 |
*:has([value="fullPage.js"]:checked) .filters [for="fullPage.js"] { |
10 |
background: var(--green); |
11 |
color: var(--white); |
12 |
}
|
13 |
|
14 |
*:has([value="All"]:checked) .posts [data-category], |
15 |
*:has([value="CSS"]:checked) .posts [data-category~="CSS"], |
16 |
*:has([value="JavaScript"]:checked) .posts [data-category~="JavaScript"], |
17 |
*:has([value="jQuery"]:checked) .posts [data-category~="jQuery"], |
18 |
*:has([value="WordPress"]:checked) .posts [data-category~="WordPress"], |
19 |
*:has([value="Slider"]:checked) .posts [data-category~="Slider"], |
20 |
*:has([value="fullPage.js"]:checked) .posts [data-category~="fullPage.js"] { |
21 |
display: block; |
22 |
}
|
Vamos isolar um deles e traduzi-lo em termos da linguagem humana.
Considere este:
1 |
*:has([value="CSS"]:checked) .posts [data-category~="CSS"] { |
2 |
display: block; |
3 |
}
|
Podemos descrevê-lo assim:
Verifique se existe algum seletor (pai) que inclua um botão de opção marcado com o valor “CSS”. Se for esse o caso, encontre todos os posts descendentes cujo atributo “categoria de dados” contém a palavra-chave “CSS” e exiba-os.
Ou assim:
Combine e exiba as postagens cujo atributo “categoria de dados” contém a palavra-chave “CSS” e são filhos de qualquer elemento pai que inclua um botão de opção marcado com o valor “CSS”.
Aqui está a demonstração resultante, como lembrete:
Conclusão
É isso aí, pessoal! Com apenas algumas regras CSS e alguma marcação estruturada, conseguimos construir duas variações de um componente de filtragem baseado em CSS totalmente funcional.
Espero que você tenha gostado deste exercício e que ele tenha ajudado a expandir sua compreensão sobre seletores CSS.
Como sempre, obrigado pela leitura!