Existem vários plugins para jQuery, React e até mesmo Angular para criar um carrossel de imagens, mas é possível criar utilizando apenas CSS puro.
Elementos de um carrossel

- Stage: A área disponível para o carrossel
- Item: O item visível atualmente pelo usuário
- Next/Prev Buttons: Os botões para passar aos items anteriores/próximos, que normalmente estão ocultos
Updates:
- 2021-01-25
- movido código para CodeSandbox
- revisão do texto do post
- grande redução no código fonte
Conceito
Vamos utilizar inputs do tipo radio, que estarão ocultos para os usuários, em conjunto com a tag label com o atributo for="", que ao ser clicada, automaticamente checa um radio que tenha o id correspondente.
Todos os input radio terão o mesmo atributo name, assim o próprio navegador se encarrega de desmarcar e marcar os radios sem a necessidade de JavaScript.
Estrutura HTML do carrossel de images
Utilizaremos 4 inputs do tipo rádio pra controlar 4 imagens no carrossel. Repare que o primeiro item não tem o botão “anterior” e o último não tem o botão “próximo”.
1<section class="carousel">
2 <input type="radio" name="carousel" id="carousel1" checked="checked" />
3 <input type="radio" name="carousel" id="carousel2" />
4 <input type="radio" name="carousel" id="carousel3" />
5 <input type="radio" name="carousel" id="carousel4" />
6
7 <main class="carousel__stage">
8 <aside class="carousel__item">
9 <img class="carousel__image" src="images/forest-1.jpg" />
10 <label for="carousel2" class="carousel__next carousel__control"></label>
11 </aside>
12
13 <aside class="carousel__item">
14 <label for="carousel1" class="carousel__prev carousel__control"></label>
15 <img class="carousel__image" src="images/sun-clouds.jpg" />
16 <label for="carousel3" class="carousel__next carousel__control"></label>
17 </aside>
18
19 <aside class="carousel__item">
20 <label for="carousel2" class="carousel__prev carousel__control"></label>
21 <img class="carousel__image" src="images/tree.jpg" />
22 <label for="carousel4" class="carousel__next carousel__control"></label>
23 </aside>
24
25 <aside class="carousel__item">
26 <label for="carousel3" class="carousel__prev carousel__control"></label>
27 <img class="carousel__image" src="images/flower.jpg" />
28 </aside>
29 </main>
30</section>
A primeira coisa que precisamos fazer é ocultar os inputs para o usuário.
Será utilizada a técnica de mover o elemento para fora do viewport, pois alguns navegadores tratam inputs “ocultos” como estando desabilitados.
Desta maneira o input fica invisível, mas ainda é um elemento válido para os navegadores.
1.carousel input {
2 position: absolute;
3 left: -10000px;
4}
Vamos deixar o carrossel responsivo:
1.carousel {
2 width: 80%;
3 height: 80%;
4 margin: 0 auto;
5}
6
7.carousel__stage {
8 overflow: hidden;
9 font-size: 0;
10 white-space: nowrap;
11 width: 100%;
12 height: 100%;
13 transition: text-indent 500ms;
14 position: relative;
15}
16
17.carousel__item {
18 display: inline-block;
19 text-indent: 0;
20 width: 100%;
21 height: 100%;
22}
23
24.carousel__image {
25 width: 100%;
26 height: 100%;
27 object-fit: cover;
28}
Posicionamento dos botões Próximo / Anterior. Repare que o elemento stage é quem tem posicionamento relative, então todos os botões serão alinhados a ele.
Como os botões terão sua visibilidade oculta por padrão, ao movimentarmos de um item para o outro, vamos exibir os botões corretamente, dando uma ilusão para o usuário que o botão não mudou.
1.carousel__control {
2 display: none;
3 position: absolute;
4 top: 50%;
5 color: #fff;
6 background: rgba(255, 255, 255, 0.3);
7 z-index: 1;
8 font-size: 1rem;
9 padding: 20px;
10 cursor: pointer;
11}
12
13.carousel__prev {
14 left: 0;
15}
16
17.carousel__prev::after {
18 content: ' << ';
19 display: inline-block;
20}
21
22.carousel__next {
23 right: 0;
24}
25
26.carousel__next::after {
27 content: ' >> ';
28 display: inline-block;
29}
Agora é aonde a mágica começa.
Será utilizado o seletor :checked do css para sabermos qual é o input radio que está selecionado no momento.
Utilizando a propriedade text-indent do CSS é possível movimentarmos o stage e dar a impressão de animação.
Nesta etapa temos que fazer um seletor para cada “estado” da animação:
1#carousel1:checked ~ .carousel__stage {
2 text-indent: 0;
3}
4
5#carousel2:checked ~ .carousel__stage {
6 text-indent: -100%;
7}
8
9#carousel3:checked ~ .carousel__stage {
10 text-indent: -200%;
11}
12
13#carousel4:checked ~ .carousel__stage {
14 text-indent: -300%;
15}
Para finalizar, vamos fazer a mágica de só mostrar os botões Próximo / Anterior referentes ao item atual no stage:
1#carousel1:checked ~ .carousel__stage .carousel__item:nth-child(1) .carousel__control,
2#carousel2:checked ~ .carousel__stage .carousel__item:nth-child(2) .carousel__control,
3#carousel3:checked ~ .carousel__stage .carousel__item:nth-child(3) .carousel__control,
4#carousel4:checked ~ .carousel__stage .carousel__item:nth-child(4) .carousel__control {
5 display: block;
6}
Resultado final
Você pode conferir o código fonte do exemplo aqui:
https://codesandbox.io/s/carousel-with-css-only-nshhx