Saltar al contenido principal
Leyendo: Elementos dinámicos avanzados
Apuntes de clase

Clase 29 · Elementos dinámicos avanzados

Cinco elementos interactivos que se construyen solo con HTML y CSS: ventana popup, tabs, menú emergente, carrusel de imágenes y acordeón. Cada uno termina con el resultado en funcionamiento.

PDF · Popup PDF · Tabs PDF · Menú emergente PDF · Carrusel PDF · Acordeón

02 · Tabs

Tabs (pestañas)

Ver y ocultar secciones con botones, igual que el popup: mismo ID en el enlace y en la sección.

Las pestañas funcionan con la misma idea que el popup: cada botón apunta a un ID y la sección con ese ID se muestra gracias a :target.

02 · Tabs

HTML

Tres botones y tres secciones independientes.

Creamos 3 botones (dentro de un div para darles margen inferior) y 3 secciones independientes.

.tab1–.tab3: añadimos clases a las secciones para darles tamaño, color de fondo y espaciado.

!!! El ID del href del botón y el ID de las secciones deben coincidir.

index.html · botones
<!-- BOTONES -->
<div style="margin-bottom:24px;">
  <a class="open-btn" href="#info1">tab1</a>
  <a class="open-btn" href="#info2">tab2</a>
  <a class="open-btn" href="#info3">tab3</a>
</div>
index.html · secciones
<!-- TAB 1 -->
<div id="info1" class="tab1">
  Info TAB1
</div>

<!-- TAB 2 -->
<div id="info2" class="tab2">
  Info TAB2
</div>

<!-- TAB 3 -->
<div id="info3" class="tab3">
  Info TAB3
</div>
02 · Tabs

CSS

Ocultamos con display: none y mostramos con display: block.

Para ver/ocultar vamos a utilizar:

  • display: none; → ocultar.
  • display: block; → visualizar.

En la clase .tab1 ocultamos mediante display: none;. En la pseudoclase .tab1:target damos visibilidad mediante display: block;.

!!! Utilizamos display en lugar de visibility: hidden porque display: none no ocupa espacio; visibility: hidden oculta el contenido, pero el espacio que deja se queda en blanco.

style.css
.tab1 {
  width: 750px;
  height: 400px;
  background-color: aqua;
  border-radius: 12px;
  padding: 32px;
  display: none;
}
.tab1:target {
  display: block;
}
02 · Tabs

Animar tabs

Con display no hay transición posible: usamos @keyframes.

Cuando utilizamos display para visualizar/ocultar, para animar las tabs tenemos que recurrir a una animación mediante @keyframes. Añadimos la clase animacion a la sección:

index.html
<!-- TAB 1 -->
<div id="info1" class="tab1 animacion">
  Info TAB1
</div>
style.css
@keyframes anim {
  0% {
    transform: translateY(50px);
  }
  100% {
    transform: translateY(0px);
  }
}
.animacion {
  animation: anim;
  animation-duration: 2s;
  animation-iteration-count: 1;
}
02 · Tabs

Tabs en funcionamiento

Código completo con las tres pestañas: pulsa tab1, tab2 o tab3.

En el código completo agrupamos .tab1, .tab2, .tab3 para no repetir los estilos comunes y dejamos solo el color de fondo en cada una.

index.html
<!-- BOTONES -->
<div style="margin-bottom:24px;">
  <a class="open-btn" href="#info1">tab1</a>
  <a class="open-btn" href="#info2">tab2</a>
  <a class="open-btn" href="#info3">tab3</a>
</div>

<!-- TAB 1 -->
<div id="info1" class="tab1 animacion">
  Info TAB1
</div>

<!-- TAB 2 -->
<div id="info2" class="tab2 animacion">
  Info TAB2
</div>

<!-- TAB 3 -->
<div id="info3" class="tab3 animacion">
  Info TAB3
</div>
style.css
.tab1, .tab2, .tab3 {
  width: 750px;
  height: 400px;
  border-radius: 12px;
  padding: 32px;
  display: none;
}
.tab1 {
  background-color: aqua;
}
.tab2 {
  background-color: #ffd9a0;
}
.tab3 {
  background-color: #f8c1de;
}
.tab1:target, .tab2:target, .tab3:target {
  display: block;
}
@keyframes anim {
  0% {
    transform: translateY(50px);
  }
  100% {
    transform: translateY(0px);
  }
}
.animacion {
  animation: anim;
  animation-duration: 2s;
  animation-iteration-count: 1;
}
Resultado: tabs
04 · Carrusel de imágenes

Carrusel (slider de imágenes)

Un carrusel sin JavaScript: una galería en fila, un contenedor que oculta lo que sobra y enlaces que se desplazan a cada imagen.

Vamos a construirlo en nueve pasos numerados. La idea: colocamos las imágenes una al lado de otra, mostramos solo una (con overflow: hidden) y los puntos de navegación son enlaces a los ID de cada imagen.

04 · Carrusel de imágenes

Contenedor y galería

Pasos 1, 2 y 3.

1. Contenedor: creamos el contenedor padre container, le damos un tamaño fijo (600×400) y le asignamos un borde para poder visualizarlo.

2. Galería: creamos el contenedor gallery, que es el que llevará las imágenes (con display: flex).

3. Las imágenes tienen que tener el mismo tamaño que el contenedor principal.

index.html
<div class="container">
  <div class="gallery">
    <img src="img1.svg">
    <img src="img2.svg">
    <img src="img3.svg">
    <img src="img4.svg">
  </div>
</div>
style.css
.container {
  border: 1px solid black;
  width: 600px;
  height: 400px;
}

.gallery {
  display: flex;
}

.gallery img {
  width: 600px;
  height: 400px;
}
04 · Carrusel de imágenes

Ocultar el contenido desbordado e ID de las imágenes

Pasos 4 y 5.

4. Ocultar el contenido desbordado: ocultamos lo que se sale del contenedor principal con overflow: hidden;.

5. ID de las imágenes: asignamos un id diferente a cada imagen. Nos servirá para poder desplazarnos entre las imágenes.

index.html
<div class="container">
  <div class="gallery">
    <img id="img1" src="img1.svg">
    <img id="img2" src="img2.svg">
    <img id="img3" src="img3.svg">
    <img id="img4" src="img4.svg">
  </div>
</div>
style.css
.container {
  border: 4px solid black;
  width: 600px;
  height: 400px;
  overflow: hidden;
}
04 · Carrusel de imágenes

Contenedor de dots y dots de navegación

Pasos 6 y 7.

6. Contenedor de dots: creamos un contenedor para los «dots» de navegación. Este contenedor es flex para alinear horizontalmente los puntos.

7. Dots: cada dot es una etiqueta <a>; para dar la forma de círculo damos en CSS la misma anchura y altura, asignamos un color de fondo y border-radius para hacerlo circular.

IMPORTANTE: el ID de la imagen tiene que coincidir con el href del enlace (dot).

index.html
<div id="dots">
  <a class="dot" href="#img1"></a>
  <a class="dot" href="#img2"></a>
  <a class="dot" href="#img3"></a>
  <a class="dot" href="#img4"></a>
</div>
style.css
#dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
}
.dot {
  width: 15px;
  height: 15px;
  border-radius: 50px;
  background-color: rgb(169, 169, 169);
}
04 · Carrusel de imágenes

Centrar el carrusel y los dots

Paso 8.

8. Para centrar verticalmente el carrusel y los dots creamos un contenedor superior que lo englobe todo y centramos con flex columna.

index.html
<div class="column">
  <div class="container">
    <div class="gallery">
      <img id="img1" src="img1.svg">
      <img id="img2" src="img2.svg">
      <img id="img3" src="img3.svg">
      <img id="img4" src="img4.svg">
    </div>
  </div>

  <div id="dots">
    <a class="dot" href="#img1"></a>
    <a class="dot" href="#img2"></a>
    <a class="dot" href="#img3"></a>
    <a class="dot" href="#img4"></a>
  </div>
</div>
style.css
.column {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 10px;
}
04 · Carrusel de imágenes

Suavizar el movimiento del scroll horizontal

Paso 9.

9. El carrusel funciona, pero el cambio de imágenes lo hace de golpe. Para suavizarlo aplicamos scroll-behavior: smooth; en el contenedor padre.

style.css
.container {
  border: 4px solid black;
  width: 600px;
  height: 400px;
  overflow: hidden;
  scroll-behavior: smooth;
}
04 · Carrusel de imágenes

Carrusel en funcionamiento

Código completo y resultado: pulsa los puntos.

En el ejemplo las imágenes son ilustraciones img1.svg–img4.svg; en tu ejercicio usa las tuyas (.jpg) con el mismo tamaño que el contenedor.

index.html
<div class="column">
  <div class="container">
    <div class="gallery">
      <img id="img1" src="img1.svg">
      <img id="img2" src="img2.svg">
      <img id="img3" src="img3.svg">
      <img id="img4" src="img4.svg">
    </div>
  </div>

  <div id="dots">
    <a class="dot" href="#img1"></a>
    <a class="dot" href="#img2"></a>
    <a class="dot" href="#img3"></a>
    <a class="dot" href="#img4"></a>
  </div>
</div>
style.css
.container {
  border: 4px solid black;
  width: 600px;
  height: 400px;
  overflow: hidden;
  scroll-behavior: smooth;
}
.gallery {
  display: flex;
}
.gallery img {
  width: 600px;
  height: 400px;
}
#dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: 16px;
}
.dot {
  width: 15px;
  height: 15px;
  border-radius: 50px;
  background-color: rgb(169, 169, 169);
}
.column {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: 10px;
}
Resultado: carrusel de imágenes
05 · Acordeón

Acordeón

Secciones que se abren y se cierran al hacer clic, con HTML puro.

El acordeón no necesita :target ni JavaScript: el navegador ya trae las etiquetas <details> y <summary>.

05 · Acordeón

<details> y <summary>

Las dos etiquetas de un acordeón.

<details>: etiqueta que hace referencia al elemento del acordeón; abre y cierra al hacer clic.

<summary>: hace referencia al título o la sección que se ve cuando el acordeón está cerrado.

index.html
<details>
  <summary>Título cabecera</summary>
  Información dentro del acordeon
</details>
05 · Acordeón

Estado inicial del acordeón

Por defecto todos los <details> están cerrados.

Por defecto todos los elementos <details> están cerrados, pero podemos querer que uno en concreto esté abierto desde el inicio. Para ello simplemente ponemos el parámetro open dentro de la etiqueta: <details open>.

index.html
<details open>
  <summary>Título cabecera</summary>
  Información dentro del acordeon
</details>

<details>
  <summary>Título cabecera</summary>
  Información dentro del acordeon
</details>
05 · Acordeón

Acordeón desasociado y asociado

Con name abrimos uno y se cierran los demás.

Acordeón desasociado: cada <details> funciona de forma independiente; si tengo 2, se abren y cierran sin tener en cuenta lo que hace el otro.

Acordeón asociado: si queremos que, al abrir un <details>, se cierren el resto, tenemos que asociarlos. Para ello introducimos el parámetro name="nombre". Todos los <details> tienen que tener el mismo nombre para que estén asociados.

index.html · desasociado
<details open>
  <summary>Título cabecera</summary>
  Información dentro del acordeon
</details>

<details>
  <summary>Título cabecera</summary>
  Información dentro del acordeon
</details>
index.html · asociado
<details open name="acordeon">
  <summary>Título cabecera</summary>
  Información dentro del acordeon
</details>

<details name="acordeon">
  <summary>Título cabecera</summary>
  Información dentro del acordeon
</details>
05 · Acordeón

CSS del acordeón

Estilo para details y para details summary.

details: añadimos tamaño, color de fondo…

details summary: añadimos estilo al texto del summary.

!! margin-bottom: 16px; genera una separación respecto al siguiente details.

style.css · details
details {
  background-color: #f6f8fa;
  width: 80%;
  margin-bottom: 16px;
  border-radius: 8px;
  border: 1px solid #d8e0e9;
  color: #364f6b;
  padding: 16px;
}
style.css · summary
details summary {
  font-weight: 400;
  font-size: 18px;
  padding: 1rem;
}
05 · Acordeón

CSS cuando details está «open»

Funciona igual que un :hover.

Podemos asignar un CSS diferente cuando details y summary están abiertos (del mismo modo que cuando hacemos un hover):

details[open]: añadimos CSS diferente cuando está abierto (otro color de fondo, una sombra…).

details[open] summary: añadimos CSS diferente a summary cuando está abierto.

style.css
details[open] {
  background: #ffffff;
}
details[open] summary {
  font-weight: 600;
}
05 · Acordeón

Personalizar el icono desplegable

Ocultamos el triángulo por defecto y ponemos el nuestro con ::before.

  1. Asignar position: relative a details; después pondremos el icono en modo absoluto.
  2. Ocultar el icono que viene por defecto con list-style: none;.
  3. Mediante un pseudoelemento details summary::before: cambiamos el icono en content, el tamaño mediante font-size, la posición absolute y una transition para generar movimiento.
  4. Giramos el icono cuando details está abierto.

4.2 Como alternativa podemos poner otro icono diferente cuando esté abierto, en el content de ::before. El símbolo que ponemos en content lo copiamos de una página de símbolos, por ejemplo copychar.cc.

En Safari el triángulo se oculta con summary::-webkit-details-marker { display: none; }.

style.css · details
details {
  background-color: #f6f8fa;
  width: 80%;
  margin-bottom: 1rem;
  border-radius: 8px;
  border: 1px solid #d8e0e9;
  color: #364f6b;
  padding: 16px;
  position: relative;
}
style.css · summary
details summary {
  font-weight: 800;
  font-size: 18px;
  padding: 1rem;
  list-style: none;
}
style.css · icono
details summary::before {
  position: absolute;
  content: "👇";
  font-size: 1.75rem;
  top: 10px;
  right: 16px;
  transition: 0.3s;
}
style.css · girar al abrir
details[open] summary::before {
  transform: rotate(45deg);
  transition: 0.3s;
}
style.css · alternativa: otro icono al abrir
details[open] summary::before {
  content: "👆";
  font-size: 24px;
}
05 · Acordeón

Icono desde un archivo (método alternativo)

Cargamos la imagen con url() en lugar de pegar un símbolo.

Si queremos cargar un icono desde nuestro ordenador en lugar de pegar un símbolo, cargamos dentro de content la URL de la imagen/icono y le damos tamaño mediante width en lugar de font-size.

style.css
details summary::before {
  position: absolute;
  content: url("arrow.svg");
  width: 30px;
  right: 10px;
  top: 15px;
  transition: 0.3s;
}

details[open] summary::before {
  transform: rotate(90deg);
  transition: 0.3s;
}
05 · Acordeón

Acordeón en funcionamiento

Código completo y resultado: abre una sección y verás cómo se cierra la otra.

El ejemplo usa el acordeón asociado (name="acordeon") con el icono cargado desde arrow.svg.

index.html
<details open name="acordeon">
  <summary>Ver más información</summary>
  <h3>Nombre del sujeto</h3>
  <img src="img1.svg" width="120" alt="">
</details>

<details name="acordeon">
  <summary>Ver más información</summary>
  Información dentro del acordeon
</details>

<details name="acordeon">
  <summary>Ver más información</summary>
  Información dentro del acordeon
</details>
style.css
details {
  background-color: #f6f8fa;
  width: 80%;
  margin-bottom: 1rem;
  border-radius: 8px;
  border: 1px solid #d8e0e9;
  color: #364f6b;
  padding: 16px;
  position: relative;
}
details summary {
  font-weight: 800;
  font-size: 18px;
  padding: 1rem;
  list-style: none;
}
details[open] {
  background: #ffffff;
}
details[open] summary {
  font-weight: 600;
}
details summary::before {
  position: absolute;
  content: url("arrow.svg");
  width: 30px;
  right: 10px;
  top: 15px;
  transition: 0.3s;
}

details[open] summary::before {
  transform: rotate(90deg);
  transition: 0.3s;
}
Resultado: acordeón