Conceptos de diseño web
Base de técnicas de diseño web y scroll interactivo, para recordarlas de un vistazo.
Scroll interactivo
- Smooth scroll Lenis Scroll con inercia y suavizado que hace que la página se sienta fluida y premium. Cimiento de casi todo efecto de scroll; da esa sensación de mantequilla. Visto en: lenis.darkroom.engineering, la web de la propia librería, movida con Lenis. Pídeselo a tu agente: Añade smooth scroll con Lenis a esta página. Instálalo, arráncalo en el punto de entrada y engancha su bucle a requestAnimationFrame. No toques el CSS de scroll-behavior ni rompas los enlaces de ancla: comprueba que un enlace a #seccion sigue llevando ahí. Respeta prefers-reduced-motion apagando la inercia.
- Parallax CSS · JS Capas que se mueven a distinta velocidad al hacer scroll, creando sensación de profundidad. Heros inmersivos y storytelling visual. Pídeselo a tu agente: Haz que el fondo de esta sección se mueva más lento que el contenido al hacer scroll. Usa transform: translate3d y nunca top ni background-position, que fuerzan recálculo. Que la amplitud sea de pocos píxeles, no de media pantalla, y que se apague con prefers-reduced-motion.
- Scroll Snap CSS El scroll encaja automáticamente en cada sección o slide (scroll-snap-type). Presentaciones a pantalla completa, carruseles y secciones tipo diapositiva. Visto en: stripe.com, los carruseles de su portada encajan con scroll-snap. Pídeselo a tu agente: Convierte estas secciones en diapositivas que encajen al hacer scroll, con CSS puro: scroll-snap-type en el contenedor y scroll-snap-align en cada hija. Usa proximity y no mandatory si el contenido de una sección puede ser más alto que la pantalla, porque si no se queda gente sin poder leer el final.
- Scroll-driven animations CSS scroll()/view() Animaciones atadas al progreso del scroll, ya nativas en CSS sin necesidad de JS. Barras de progreso, reveals y parallax ligero con 0 KB. Visto en: stripe.com, anima con animation-timeline, sin JavaScript de scroll. Pídeselo a tu agente: Ata esta animación al progreso del scroll con CSS nativo: animation-timeline con scroll() o view(), sin JavaScript. Deja una alternativa para los navegadores que no lo soportan con @supports (animation-timeline: view()), y que sin soporte el elemento se vea igual pero quieto.
- Scroll reveal IntersectionObserver Elementos que aparecen o se animan al entrar en el viewport. Revelar secciones al bajar; con stagger (retardo escalonado) queda muy elegante. Visto en: awwwards.com, las tarjetas aparecen al entrar en pantalla, con IntersectionObserver y no con un listener de scroll. Pídeselo a tu agente: Haz que estos bloques aparezcan al entrar en pantalla, con IntersectionObserver y no con un listener de scroll. Que cada elemento se anime una sola vez y luego deje de observarse. Importante: el contenido tiene que estar en el HTML y visible por defecto, y que la animación solo lo esconda si hay JavaScript, para que no desaparezca en un buscador.
- Sticky / Pin CSS · GSAP Un elemento se pega en pantalla mientras el resto del contenido sigue avanzando. Stacking cards (tarjetas que se apilan), demos fijas y capítulos. Visto en: vercel.com, once elementos que se quedan pegados al bajar. Pídeselo a tu agente: Deja esta columna pegada mientras la de al lado sigue subiendo, con position: sticky y top. Antes de dar por hecho que funciona, comprueba que ningún ancestro tiene overflow hidden, auto o scroll: es lo que rompe sticky el noventa por ciento de las veces, y encima no da ningún error.
- Horizontal scroll GSAP Desplazamiento lateral controlado por el scroll vertical. Galerías, líneas de tiempo y portfolios. Pídeselo a tu agente: Convierte esta sección en un desplazamiento lateral movido por el scroll vertical. Que el contenedor se quede pegado y las tarjetas se muevan con transform. Deja salida: cuando la tira se acaba, la página tiene que seguir bajando con normalidad, y en móvil vale más un carrusel con scroll-snap que secuestrar el scroll.
- Marquee CSS · JS Cinta de texto o logos que se repite en bucle infinito. Tiras de logos de clientes y titulares en movimiento. Visto en: vercel.com, la tira de logos que no para de correr. Pídeselo a tu agente: Monta una cinta de logos que corra en bucle sin cortes. Duplica el contenido y anima el conjunto con transform: translateX en un @keyframes lineal, que es lo que hace que el salto no se vea. Que se pare al pasar el ratón por encima y con prefers-reduced-motion. Nada de la etiqueta marquee, que está muerta.
- Scrollytelling JS Contar una historia guiada por el scroll, con texto y visuales sincronizados. Reportajes interactivos, secciones de 'cómo funciona', datos narrados. Pídeselo a tu agente: Monta una historia guiada por el scroll: una visual que se queda fija a un lado y pasos de texto al otro que la van cambiando. Detecta el paso activo con IntersectionObserver. Cada paso tiene que entenderse leído solo, sin la visual, porque es lo que va a ver quien llegue desde un buscador o con el JavaScript caído.
- Page transitions View Transitions · barba.js Transición animada al navegar entre páginas o vistas. Que la navegación se sienta de app, sin recargas bruscas. Pídeselo a tu agente: Añade una transición al navegar entre páginas con la View Transitions API del navegador, no con una librería. Envuelve el cambio en document.startViewTransition y comprueba antes que existe. Sin soporte, la navegación tiene que seguir siendo instantánea y correcta: la transición es un extra, nunca un requisito.
Puntero y micro-interacciones
- Tilt 3D JS · CSS Un elemento se inclina en 3D siguiendo al ratón (perspective + rotateX/Y). Tarjetas de producto, avatares y elementos destacados. Pídeselo a tu agente: Haz que esta tarjeta se incline en 3D siguiendo al ratón. El contenedor lleva perspective y la tarjeta rotateX y rotateY calculados desde la posición del cursor dentro de ella. Suaviza el retorno al salir y limita el giro a unos 8 grados: más que eso marea y deforma el texto. En táctil no lo montes, que no hay cursor y el listener solo gasta.
- Hover magnético JS Un botón atrae el cursor moviéndose ligeramente hacia él. CTAs y botones que invitan a pulsar. Pídeselo a tu agente: Haz que este botón se acerque un poco al cursor cuando el ratón ronda cerca. Calcula la distancia al centro y muévelo con transform, como mucho unos 10px, volviendo a su sitio al salir. El área de detección puede ser mayor que el botón, pero el ÁREA PULSABLE no se mueve con él: si el botón huye del dedo, no lo pulsa nadie.
- Cursor personalizado (lerp) JS Cursor a medida que sigue al ratón con suavizado (interpolación lineal). Marcas creativas y portfolios con personalidad. Pídeselo a tu agente: Sustituye el cursor por un punto que lo sigue con retardo, interpolando su posición en cada frame con requestAnimationFrame. Ojo con tres cosas: esconder el cursor del sistema deja sin pistas a quien no ve bien, así que hazlo solo en punteros finos con @media (pointer: fine); no lo montes en táctil; y déjalo desactivado con prefers-reduced-motion.
- Micro-interacciones CSS Pequeños feedbacks en hover, focus y click (escala, color, sombra). Hacen la interfaz viva y agradable; refuerzan cada acción. Visto en: apple.com, cada transición lleva su curva a medida, no la de fabrica. Pídeselo a tu agente: Dale respuesta a estos controles al pasar por encima, al enfocarlos y al pulsarlos: un cambio pequeño de escala, color o sombra, con su propia curva de easing y no la de fábrica. Que el estado de FOCO se vea tanto como el de hover, porque quien navega con teclado solo tiene ese. Nada por encima de 200ms: una micro-interacción lenta se siente como lag.
- Cursor blend mix-blend-mode Cursor o formas que invierten el color de lo que hay debajo. Efecto llamativo en portfolios y marcas creativas. Pídeselo a tu agente: Haz que este cursor a medida invierta el color de lo que tiene debajo, con mix-blend-mode: difference. Comprueba antes que ningún ancestro tenga un backdrop-filter ni un filter, porque cualquiera de los dos crea un contexto de apilado y el blend deja de ver el fondo. Y mira que el texto siga legible por debajo, que es lo que se rompe con esto.
- Spotlight / glow radial-gradient · JS Un resplandor que sigue al cursor dentro de una tarjeta o botón. Da vida a las cards (se usa en esta misma web al pasar el ratón). Pídeselo a tu agente: Que estas tarjetas enciendan un resplandor que sigue al cursor. Guarda la posición del ratón en dos variables CSS desde JavaScript y pinta el brillo con un radial-gradient que las use, así el trabajo por frame es escribir dos números y no recalcular estilos. Un solo listener en el contenedor, no uno por tarjeta. El brillo va detrás del contenido y con pointer-events: none.
Estética y efectos
- Glassmorphism backdrop-filter Efecto de cristal esmerilado: fondo borroso y translúcido. Barras, tarjetas y menús flotantes modernos. Visto en: tailwindcss.com, las capas translúcidas de su interfaz. Pídeselo a tu agente: Dale a este panel aspecto de cristal esmerilado: fondo semitransparente y backdrop-filter con blur y algo de saturate. Dos avisos que cambian el resultado: backdrop-filter crea contexto de apilado, así que si dentro hay algo con position: fixed dejará de cubrir la pantalla; y el texto encima de un fondo que se mueve tiene que seguir cumpliendo 4,5:1, así que sube la opacidad hasta que lo cumpla.
- Neumorphism box-shadow Superficies con sombras suaves que parecen relieve de plástico. Botones y controles con estilo táctil (ojo con el contraste). Pídeselo a tu agente: Dale a estos controles el relieve de neumorfismo, con dos box-shadow (una clara arriba a la izquierda y una oscura abajo a la derecha) del MISMO color que el fondo. Antes de aceptarlo, mide el contraste: este estilo suele dejar los bordes por debajo de 3:1 y entonces no se ve dónde se puede pulsar. Si no llega, añade un borde o un cambio de fondo al enfocarlo.
- Aurora / mesh gradient CSS Degradados orgánicos y difusos, a menudo animados, de fondo. Fondos vivos sin recargar (como el de esta misma web). Visto en: astro.build, el fondo son gradientes superpuestos, no una imagen. Pídeselo a tu agente: Monta un fondo de aurora: varios radial-gradient de colores superpuestos y desenfocados, animándose despacio. Hazlo con CSS y transform, sin canvas y sin imágenes. Anima solo transform y opacity, nunca background-position, que repinta toda la superficie. Que el ciclo dure decenas de segundos y se pare con prefers-reduced-motion.
- Clip-path y máscaras CSS Revelar o recortar contenido con formas (clip-path, mask). Reveals con estilo, texto que se descubre y transiciones de imagen. Visto en: vercel.com, recorta y difumina bordes con máscaras CSS, sin imágenes. Pídeselo a tu agente: Recorta esta sección con una forma usando clip-path, o difumina su borde con mask-image y un gradiente. Dos cosas: lo recortado sigue existiendo para un lector de pantalla, así que si sobra de verdad quítalo del DOM y no solo de la vista; y clip-path corta también el foco visible de los controles que queden en el borde, así que déjales margen.
- View Transitions CSS/JS API Transición animada nativa entre dos estados o páginas. Cambios de tema y navegación suave (la usa el toggle de tema de aquí). Visto en: astro.build, el paso entre páginas lo hace la API del navegador. Pídeselo a tu agente: Anima el cambio entre estos dos estados con la View Transitions API: envuelve la actualización del DOM en document.startViewTransition y comprueba antes que existe. Para que un elemento viaje de un estado a otro dale el mismo view-transition-name en los dos, y que ese nombre sea ÚNICO en la página: dos elementos con el mismo nombre a la vez cancelan la transición entera.
- Skeleton loaders CSS Bloques con la forma de lo que va a venir, mientras carga el contenido real. Dos versiones: quieto o con un brillo que lo recorre. Percepción de rapidez; evitan la pantalla en blanco y el salto de maquetación al llegar. Visto en: github.com, el hueco de lo que carga tiene la forma de lo que va a venir. Pídeselo a tu agente: Mientras carga, enseña un esqueleto con la forma de lo que va a venir, no un spinner: bloques del tamaño real del título, del texto y de la imagen. Dame las dos versiones, la fija y la del brillo que recorre, y usa la fija cuando haya muchos bloques a la vez, porque cada uno animado es una capa que el navegador repinta sin parar. Que ocupe EXACTAMENTE el mismo espacio que el contenido final, porque si no el salto al llegar cuenta como CLS. Apaga el brillo en prefers-reduced-motion, y márcalo con aria-hidden más un aria-busy en el contenedor.
- Tipografía cinética GSAP SplitText Animar el texto por letras o palabras (aparecer, deslizar, distorsionar). Titulares con impacto e intros de marca. Visto en: gsap.com, su propia portada carga SplitText, el plugin que parte el texto en letras. Pídeselo a tu agente: Anima este titular letra a letra al entrar. Parte el texto en spans y desplázalos con un pequeño retardo escalonado. Importante para que no se rompa nada: el titular tiene que seguir siendo UNA frase para quien la escuche, así que pon el texto completo en un aria-label del contenedor y aria-hidden en los trozos. Sin JavaScript, el titular se ve entero y quieto.
- Noise / grain SVG · CSS Textura de grano sutil sobre fondos y degradados. Da un acabado cálido y analógico, menos plano. Visto en: linear.app, una capa de grano encima del color para quitarle el plástico. Pídeselo a tu agente: Añade una capa de grano sutil sobre este fondo para quitarle el aspecto plástico. Genera la textura con un SVG de feTurbulence embebido como data URI, no con una imagen que haya que descargar. Ponla en un pseudoelemento con pointer-events: none, opacidad muy baja (0.03 a 0.06) y por debajo del contenido: si el texto pierde nitidez, te has pasado.
- Preloader JS Pantalla o barra de carga inicial mientras se prepara la web. Cubrir la carga de recursos pesados, con mesura (nunca eternos). Pídeselo a tu agente: Monta una pantalla de carga inicial que se va cuando la página está lista. Y ponle un techo de tiempo: si a los dos segundos no ha terminado, se quita igual. Un preloader sin límite convierte cualquier fallo de red en una pantalla en blanco eterna. Que el contenido esté en el HTML detrás, no dentro del preloader, para que un buscador lo lea aunque el JavaScript no corra.
Rendimiento y buenas prácticas
- Animar transform/opacity GPU Animar solo transform y opacity, nunca top/left/width/height. Es lo único que la GPU compone barato, dando 60fps sin tirones (jank). Visto en: apple.com, mueve con transform y no con top o left, que obliga a recalcular. Pídeselo a tu agente: Revisa las animaciones de este archivo y pásalas a transform y opacity. Cualquier cosa que anime top, left, width, height o margin obliga al navegador a recalcular la maquetación en cada frame; el mismo movimiento con translate no toca la maquetación. Enséñame qué has cambiado y por qué, y comprueba en el panel de rendimiento que ya no hay recálculos por frame.
- will-change CSS Avisa al navegador de qué propiedad va a cambiar para que prepare una capa. Úsalo con criterio: en exceso consume memoria. Visto en: framer.com, avisa al navegador de lo que va a moverse, 258 veces en su portada. Pídeselo a tu agente: Avisa al navegador de lo que va a moverse en este elemento con will-change, y quítalo cuando la animación termine. La regla que se salta todo el mundo: will-change permanente en muchos elementos crea una capa de composición por cada uno y se come la memoria de vídeo, sobre todo en móvil. Es una pista para un momento concreto, no un atributo que se deja puesto.
- Core Web Vitals Métrica LCP (<2,5s), INP (<200ms) y CLS (<0,1): las métricas de experiencia de Google. Si un efecto las empeora, baja su intensidad o pásalo a CSS nativo. Visto en: developer.mozilla.org, marca qué imagen carga primero para no hundir el LCP. Pídeselo a tu agente: Mide los Core Web Vitals de esta página y arréglalos por orden de impacto. Para el LCP, encuentra qué elemento es el más grande de la primera pantalla y dale fetchpriority="high", quitándole cualquier lazy loading. Para el CLS, reserva sitio con width y height o aspect-ratio en imágenes, iframes y anuncios. Dime los números antes y después, no solo lo que cambiaste.
- prefers-reduced-motion CSS media Preferencia del sistema para reducir animaciones (accesibilidad). Respétala para no marear; o, si tu marca lo decide, ofrece una válvula para apagar el movimiento. Visto en: stripe.com, ciento diez reglas que respetan a quien pide menos movimiento. Pídeselo a tu agente: Repasa este proyecto y respeta prefers-reduced-motion en todas las animaciones, las de CSS y las de JavaScript. Reducir NO es apagarlo todo y dejar la interfaz muerta: los cambios de opacidad y los fundidos cortos se quedan; lo que se quita es el desplazamiento grande, el parallax, el zoom y el giro, que son los que marean de verdad.
- Progressive enhancement Método Que el contenido funcione y se lea sin JS; el efecto va por encima. Robustez: si el JS falla, la web sigue siendo usable. Visto en: apple.com, la página sigue diciendo algo con el JavaScript apagado. Pídeselo a tu agente: Comprueba qué queda de esta página con el JavaScript desactivado y arregla lo que se caiga. El contenido y la navegación tienen que funcionar sin JavaScript; los efectos van encima. En concreto: nada de texto que solo exista tras montar, los enlaces son etiquetas <a> con su href de verdad y los formularios tienen action y method. Dime qué se rompía antes de tocarlo.
- Responsive / mobile-first CSS Diseñar primero para móvil y escalar hacia arriba. La mayoría del tráfico es móvil; los efectos deben ir a 60fps también ahí. Visto en: framer.com, sirve una imagen distinta según el ancho de la pantalla. Pídeselo a tu agente: Pasa este CSS a mobile-first: los estilos base son los del móvil y las media queries solo añaden hacia arriba con min-width. Comprueba a 320px de ancho, que es donde se rompe todo, y que ningún bloque desborde en horizontal. Para las imágenes, srcset con varios tamaños y sizes: servir la de escritorio a un móvil es la forma más habitual de hundir el LCP.
- Lazy loading loading="lazy" Cargar imágenes y recursos solo cuando se acercan al viewport. Acelera la carga inicial y ahorra datos. Visto en: clerk.com, noventa y siete imágenes que esperan a hacer falta. Pídeselo a tu agente: Pon loading="lazy" en las imágenes y los iframes de esta página, pero NO en los de la primera pantalla: la imagen principal cargada de forma perezosa retrasa el LCP, que es justo lo contrario de lo que se busca. Añade width y height a todas para que reserven su sitio, y decoding="async" a las que no sean críticas.
- Debounce y throttle JS Limitar cuántas veces se ejecuta una función en eventos frecuentes (scroll, resize, input). Evita trabajo de más y mantiene la fluidez. Pídeselo a tu agente: Limita cuántas veces se ejecuta esto en un evento que dispara sin parar, y elige bien cuál de los dos: DEBOUNCE si solo importa el final (una búsqueda mientras se escribe, un resize); THROTTLE si hace falta ir viendo el progreso (scroll, movimiento del ratón). Y si es scroll o movimiento, lo que quieres casi seguro es requestAnimationFrame en vez de un temporizador.
- requestAnimationFrame JS Sincronizar animaciones JS con el refresco de pantalla del navegador. Animaciones suaves a 60fps sin saturar el hilo principal. Visto en: vercel.com, engancha sus animaciones al reloj del navegador. Pídeselo a tu agente: Mueve esta animación a requestAnimationFrame, para que corra sincronizada con el refresco de la pantalla en vez de con un setInterval. Calcula el avance con el tiempo que llega en el callback y no con un incremento fijo, o irá al doble de velocidad en una pantalla de 120Hz. Cancela el bucle al desmontar con cancelAnimationFrame, que si no sigue corriendo invisible.
CSS moderno
- Container queries @container Estilos según el tamaño del contenedor, no de la ventana. Componentes de verdad reutilizables que se adaptan a su hueco. Visto en: vercel.com, su CSS lleva noventa y seis consultas de contenedor. Pídeselo a tu agente: Haz que este componente se adapte al ancho de SU CONTENEDOR y no al de la ventana, con container queries. Declara container-type: inline-size en el padre y usa @container en el hijo. Ojo con container-type: size, que exige alto explícito y colapsa el elemento; para casi todo lo que quieres, inline-size es el correcto.
- Fluid typography clamp() Tamaños de fuente que escalan suavemente entre un mínimo y un máximo. Tipografía que luce bien de móvil a pantalla grande sin saltos. Visto en: vercel.com, los tamaños se calculan con clamp, sin saltos. Pídeselo a tu agente: Pasa esta escala tipográfica a tamaños fluidos con clamp(), para que crezca sola entre el móvil y el escritorio sin saltos por breakpoint. Usa una unidad relativa en la parte que escala (rem con vw) y no vw a secas, porque con vw puro el texto deja de responder al zoom del navegador y eso incumple accesibilidad. Comprueba que a 200% de zoom sigue creciendo.
- :has() CSS El 'selector padre': estilar un elemento según lo que contiene. Lógica de estilos antes imposible sin JS. Visto en: vercel.com, lo usa más de cien veces para reaccionar al hijo. Pídeselo a tu agente: Usa :has() para estilar este elemento según lo que contiene, en vez de añadir una clase desde JavaScript. Sirve para el padre de un input inválido, la tarjeta que lleva imagen o el formulario con un campo enfocado. Dos avisos: :has() no es perezoso y un selector muy amplio se evalúa mucho, así que acótalo; y comprueba que sin soporte la página sigue siendo usable.
- Cascade layers @layer Organizar la prioridad del CSS en capas para evitar guerras de especificidad. Mantener el CSS ordenado en proyectos grandes. Visto en: stripe.com, ordena su CSS en capas en vez de pelear con la especificidad. Pídeselo a tu agente: Organiza este CSS en capas con @layer para acabar con la guerra de especificidad. Declara el orden de las capas UNA vez y al principio (reset, base, componentes, utilidades), porque ese orden es lo que manda y no dónde esté escrita cada regla. Y ten claro que lo que queda FUERA de toda capa gana a todo lo que está dentro, que es justo al revés de lo que parece.
- color-mix() CSS Mezclar dos colores directamente en CSS. Generar tonos, hovers y transparencias desde un color base (se usa mucho en esta web). Visto en: tailwindcss.com, más de mil colores mezclados en su hoja de estilos. Pídeselo a tu agente: Genera estos tonos con color-mix() en vez de escribir cada hex a mano: el hover, el borde y el estado desactivado salen del color base mezclado con el fondo o con transparent. Trabaja en oklch y no en srgb, que en srgb las mezclas pasan por grises sucios. Al terminar comprueba el contraste, que un tono derivado puede quedarse por debajo de 4,5:1 sin que se note.
- Subgrid CSS grid Que un elemento hijo herede las líneas de la cuadrícula del padre. Alinear tarjetas y listas de forma perfecta. Visto en: vercel.com, rejillas hijas alineadas con la de su padre. Pídeselo a tu agente: Alinea estas tarjetas entre sí con subgrid, para que sus títulos, textos y pies queden en la misma línea aunque tengan distinto contenido. La tarjeta declara grid-template-rows: subgrid y ocupa varias filas del padre con grid-row: span N. Es la forma de arreglarlo sin alturas fijas ni JavaScript midiendo, que es como se hacía antes y se rompía siempre.
- Logical properties CSS Propiedades por eje lógico (inline/block) en vez de left/right/top/bottom. Soporte automático de idiomas de derecha a izquierda (RTL). Visto en: stripe.com, márgenes en lógico, para que el sitio pueda girar de dirección. Pídeselo a tu agente: Cambia en este CSS las propiedades físicas por las lógicas: margin-inline en vez de margin-left y margin-right, padding-block en vez de top y bottom, inset-inline-start en vez de left. Con eso la maquetación se da la vuelta sola en árabe o hebreo sin escribir una regla aparte. Empieza por los espaciados y los bordes, que es donde está el noventa por ciento.