1 · Banner “cookies” accesible y persistente
Objetivo: dialog, atributos ARIA y localStorage
.
cookies.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Banner de cookies</title>
<style>
body{font-family:system-ui;margin:0}
dialog{position:fixed;inset:0 auto 0 0;width:100
dialog[open]{animation:slide .4s}
@keyframes slide{from{transform:translateY(100
button{margin-left:1rem;padding:.4rem 1rem;border:none;border-radius:4px;cursor:pointer}
</style>
</head>
<body>
<header><h1>Mi Web</h1></header>
<dialog id="banner" aria-label="Aviso de cookies">
<span>Usamos cookies para mejorar la experiencia.</span>
<div>
<button id="ok">Aceptar</button>
<button id="no">Rechazar</button>
</div>
</dialog>
<script>
const banner = document.getElementById('banner');
if(localStorage.getItem('cookies')===null) banner.show();
document.getElementById('ok').onclick = ()=>{localStorage.setItem('cookies','yes');banner.close()};
document.getElementById('no').onclick = ()=>{localStorage.setItem('cookies','no');banner.close()};
</script>
</body>
</html>
2 · Tabla filtrable con la pseudoclase :is()
Objetivo: selectores modernos y estilo condicional sin JS.
tabla‑filtrable.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Filtra por estado</title>
<style>
body{font-family:system-ui;margin:2rem}
label{margin-right:.8rem}
table{border-collapse:collapse;margin-top:1rem;width:320px}
th,td{border:1px solid #999;padding:.3rem;text-align:left}
/* Oculta filas cuyo data-state ≠ radio marcado */
tbody tr{display:table-row}
input[name=estado]:checked ~ table tbody tr:not([data-state="todos"]) {display:none}
input[name=estado="activos"]:checked ~ table tbody tr[data-state="activo"]{display:table-row}
input[name=estado="pendientes"]:checked ~ table tbody tr[data-state="pendiente"]{display:table-row}
</style>
</head>
<body>
<!-- Controles -->
<label><input type="radio" name="estado" value="todos" checked>Todos</label>
<label><input type="radio" name="estado" value="activos">Activos</label>
<label><input type="radio" name="estado" value="pendientes">Pendientes</label>
<!-- Datos -->
<table>
<thead><tr><th>Tarea</th><th>Estado</th></tr></thead>
<tbody>
<tr data-state="activo"><td>Diseñar logo</td><td>Activo</td></tr>
<tr data-state="pendiente"><td>Prototipo móvil</td><td>Pendiente</td></tr>
<tr data-state="activo"><td>Reunión cliente</td><td>Activo</td></tr>
</tbody>
</table>
</body>
</html>
3 · Ventana modal centrada solo con CSS
Objetivo: práctica de :target
, Flexbox y propiedades backdrop-filter
.
modal.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Modal CSS puro</title>
<style>
body{font-family:system-ui;margin:0}
/* Capa semitransparente */
.overlay{position:fixed;inset:0;background:#0008;backdrop-filter:blur(2px);display:none;justify-content:center;align-items:center}
.overlay:target{display:flex}
.box{background:#fff;border-radius:8px;padding:2rem;max-width:300px;text-align:center}
a.btn{display:inline-block;margin:1rem 0;padding:.4rem 1rem;background:#4f46e5;color:#fff;border-radius:4px;text-decoration:none}
</style>
</head>
<body>
<a href="#modal" class="btn">Abrir modal</a>
<div id="modal" class="overlay" role="dialog" aria-modal="true">
<div class="box">
<h2>¡Hola!</h2>
<p>Este modal se cierra sin JS.</p>
<a href="#" class="btn">Cerrar</a>
</div>
</div>
</body>
</html>
4 · Calendario mini con CSS Grid
Objetivo: practicar grid-template-areas
y variables personalizables.
calendario.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Calendario mayo 2025</title>
<style>
:root{--h:#4f46e5;--w:120px}
body{font-family:system-ui;display:flex;justify-content:center}
.cal{display:grid;grid-template-columns:repeat(7,var(--w));grid-auto-rows:80px;border:2px solid var(--h)}
.cal > *{display:flex;align-items:center;justify-content:center;border:1px solid #ddd}
.head{background:var(--h);color:#fff;font-weight:700}
</style>
</head>
<body>
<div class="cal">
<!-- Encabezados -->
<div class="head">Lun</div><div class="head">Mar</div><div class="head">Mié</div>
<div class="head">Jue</div><div class="head">Vie</div><div class="head">Sáb</div><div class="head">Dom</div>
<!-- Días mayo 2025 empieza jueves -->
<div></div><div></div><div></div> <!-- espacio Lun‑Mié -->
<!-- 1 → 31 -->
<!-- Fila 1 -->
<div>1</div><div>2</div><div>3</div><div>4</div>
<!-- Fila 2 -->
<div>5</div><div>6</div><div>7</div><div>8</div><div>9</div><div>10</div><div>11</div>
<!-- Fila 3 -->
<div>12</div><div>13</div><div>14</div><div>15</div><div>16</div><div>17</div><div>18</div>
<!-- Fila 4 -->
<div>19</div><div>20</div><div>21</div><div>22</div><div>23</div><div>24</div><div>25</div>
<!-- Fila 5 -->
<div>26</div><div>27</div><div>28</div><div>29</div><div>30</div><div>31</div><div></div>
</div>
</body>
</html>
5 · Comparador de imágenes “antes/después”
Objetivo: usar <input type="range">
y clip-path
para crear un “slider” visual sin librerías externas.
comparador.html
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Comparador imagen</title>
<style>
body{font-family:system-ui;display:flex;flex-direction:column;align-items:center;margin:2rem}
.wrap{position:relative;width:480px;height:320px;overflow:hidden}
.wrap img{position:absolute;inset:0;width:100
.after{clip-path:inset(0 50
input[type=range]{width:480px;margin-top:1rem}
</style>
</head>
<body>
<h1>Antes / Después</h1>
<div class="wrap">
<img src="after.jpg" alt="Después" class="after">
<img src="before.jpg" alt="Antes">
</div>
<input type="range" min="0" max="100" value="50" aria-label="Deslizador comparación">
<script>
const range=document.querySelector('input[type=range]');
const after=document.querySelector('.after');
range.addEventListener('input',e=>{
const v=100-e.target.value; // invertido para clip‑path
after.style.clipPath=`inset(0 ${v}
});
</script>
</body>
</html>
🔍 Cómo practicar con esta ronda
- Comprende qué técnica nueva usa cada ejemplo (dialog‑&‑localStorage, filtros con atributos,
:target
, Grid, clip‑path…). - Adapta: cambia colores, añade más filas, ajusta la transición; observa qué rompe y cómo lo arreglas.
- Valida y perfila la accesibilidad revisando tabindex, etiquetas ARIA y contraste.
- Versiona: súbelos a un repositorio aparte (“ejercicios‑3”) y crea ramas para cada modificación.
