Cree experiencias web memorables: un conjunto de herramientas CSS moderno
Me encanta el hecho de que CSS finalmente esté recuperando el control de las interacciones visuales, cuidando el estilo, la animación y la accesibilidad exactamente como debería ser. Hoy en día, las capacidades nativas del navegador nos permiten acercar el trabajo pesado del hilo principal de JavaScript a la GPU. Al permitir que el motor del navegador optimice el rendimiento interno, podemos ahorrar energía y potencia de procesamiento mientras creamos código robusto, accesible e independiente de bibliotecas externas que pueden quedar obsoletas en el futuro.
Contamos con 3D, tecnología de diseño moderna, trazados de recorte, transformaciones, propiedades personalizadas, animaciones con desplazamiento, transiciones de visualización, @property – Podemos animar casi cualquier cosa, Incluso altura automática!
Por supuesto, existe SVG, que no es nuevo pero nos permite construir sitios web completos a través de ilustraciones y animaciones. Tomemos el siguiente ejemplo: es responsivo, liviano, accesible y funciona principalmente con CSS Grid + SVG.
Incluso podemos construir un videojuego completo Incluye UI usando solo SVG:
Lo que sigue no es una guía completa del CSS moderno, sino más bien algunas decisiones técnicas obstinadas que hago cuando quiero que mi sitio se sienta vibrante y memorable. Hay muchas maneras de crear una experiencia inolvidable. A veces es tan simple como un formulario que se puede completar sin problemas. Pero lo que me interesa aquí es el extremo expresivo del espectro.
Movimiento como comunicación: define tu intención
Antes de entrar en los aspectos técnicos, quiero aclarar algunas cosas: No deberíamos mover las cosas sólo porque podemos.
Todo puede comunicarse y nuestras animaciones no son una excepción. Debemos tomarnos el tiempo para diseñar acciones que respalden el mensaje que queremos transmitir de modo que nuestras intenciones sean estrictamente limitadas pero no exageradas.
Este es el enfoque que utilizo al planificar el diseño y la animación de mi sitio web.
Imaginemos que estamos desarrollando un proyecto. Actividades en la naturaleza con especial atención a las setas.. El lenguaje de diseño cambia completamente en función de la “ambiente”: hay un mundo de diferencia entre vender una “rave de hongos psicodélicos” y un “retiro espiritual de hongos” centrado en la medicina ancestral.
Cada decisión de diseño se comunica entre sí. Me gusta crear lo que yo llamo lista de palabras clave Definir mi intención y alcance. Por ejemplo, podría dividir las cosas en diferentes opciones:
Opción A: Evento psicodélico
- Efectos visuales: color, saturación, alto contraste, ilustración, distorsión
- mover: Rápido, loco, impredecible, cambiante, rítmico, en bucle sincronizado, hipnótico
- Sentir: divertido, caótico, enérgico, emocionante, sorprendente
- formato: Funk, “rock psicológico”
- Referencia de estilo: Arte pop, Op Art de los años 60 y 70, Folletos de carnaval
- acción: Bailar
- Características adicionales: Emojis, películas (p. ej. Miedo y asco en Las Vegas)
Opción B: Retiro espiritual
- Efectos visuales: Tonos tierra, tonos neutros, desaturación, fotografías intensas, naturales, en blanco
- mover: Lento, suave, orgánico, respirable, paralaje sutil, desplazamiento suave.
- Sentir: Tranquilo, calmado, introspectivo, contemplativo, seguro.
- formato: Serif elegante, sans serif simple, espaciado amplio, claro y fácil de leer
- Referencia de estilo: Diseño escandinavo, Japón wabi sabiestética de salud/spa, libros de botánica
- acción: respirar
- Características adicionales: Sonidos curativos, películas (p. ej. comer orar amar)
Este es un ejercicio que utilizo para guiar mis decisiones de diseño y animación. Estas listas de verificación me ayudarán a elegir qué propiedades CSS planeo usar y cómo usarlas. Incluso los comparto con los clientes y elegimos una dirección juntos.
Digamos que elegimos la opción A y miramos algunos ejemplos de lo que considero los elementos básicos para crear una experiencia de usuario memorable.
Animación de texto dividido
Estas animaciones son populares porque Complemento GSAP SplitText. Divide el texto por caracteres (o palabras, o líneas, si lo prefieres) para que podamos crear efectos de texto interesantes, como animaciones escalonadas.
<h1 class="reveal-text">
<span style="--i:0">H</span>
<span style="--i:1">O</span>
<span style="--i:2">L</span>
<span style="--i:3">A</span>
</h1>
Este método envuelve cada letra en “Hola” dentro de un lapso. A partir de ahí, a cada tramo se le aplica un estilo en línea, con propiedades personalizadas que indexan los tramos de forma secuencial. cuando sibling-index() Las funciones reciben un amplio soporte del navegador.
Pero actualmente, cada valor de atributo personalizado actúa como un multiplicador, aumentando animation-delaycada tramo es asombroso. En este caso, nos desvanecemos a medida que cada personaje asciende.
.reveal-text span {
animation: slideUp 0.6s ease-out forwards;
animation-delay: calc(var(--i) * 0.1s);
display: inline-block;
opacity: 0;
transform: translateY(3rem);
}
@keyframes slideUp {
to {
opacity: 1;
transform: translateY(0);
}
}
La accesibilidad es la parte complicada aquí. El instinto es ocultar todos los tramos personales mediante tecnología de asistencia. aria-hidden="true" Y agrega versiones visualmente ocultas de palabras completas para lectores de pantalla:
<h1>
<span class="sr-only">HOLA</span>
<span aria-hidden="true" class="reveal-text">
<span style="--i:0">H</span>
<span style="--i:1">O</span>
<span style="--i:2">L</span>
<span style="--i:3">A</span>
</span>
</h1>
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
Pero tenga en cuenta: este modo no garantiza una buena experiencia con todos los lectores de pantalla. Adrian Roselli prueba SplitText de GSAP De ocho combinaciones de lector de pantalla y navegador, se encontró que funcionaba correctamente sólo en dos de ellas. Si utiliza esta tecnología, pruébela con tecnología de asistencia real.
Si este riesgo parece demasiado alto, existe un riesgo muy La alternativa inteligente de Preethi Vale la pena saber utilizar letter-spacing propiedad. Acepta valores negativos y colapsa los caracteres, ocultándolos sin tocar el DOM. animarlo de nuevo 0 Y obtienes una pantalla similar sin la sobrecarga de acceso.
gran cosa es Pseudo selector como este ::nth-letter Coloque un solo glifo directamente desde CSS ::first-letter Selecciona el primer personaje. Pero lamentablemente no ::nth-letter…al menos por ahora.
Recordar Respetar las preferencias de ejercicio de los usuarios. En cada animación:
@media (prefers-reduced-motion: reduce) {
.reveal-text span {
animation: none; /* or a softer animation */
}
}
Empecemos:
Cuando tenemos mucho texto y diferentes animaciones que queremos aplicar, es posible que no se escale demasiado. Para el evento psicodélico me gustaría intentar usar Sonrisapero es detallado. Aquí está el código para animar dos letras individualmente:
<svg role="img" aria-label="TODOS LOS HONGOS" viewBox="0 0 1366 938.96">
<title>TODOS LOS HONGOS</title>
<g aria-hidden="true">
<text transform="rotate(-9.87 2181.107 -1635.1)" opacity="0">T
<animate attributeName="dy" values="100; -20; 0" keyTimes="0; 0.8; 1" dur="0.4s" begin="0s" fill="freeze"/>
<animate attributeName="opacity" from="0" to="1" dur="0.01s" begin="0s" fill="freeze"/>
</text>
<text transform="rotate(-8.92 2372.854 -2084.755)" opacity="0">O
<animate attributeName="dy" values="100; -20; 0" keyTimes="0; 0.8; 1" dur="0.4s" begin="0.1s" fill="freeze"/>
<animate attributeName="opacity" from="0" to="1" dur="0.01s" begin="0.1s" fill="freeze"/>
</text>
<!-- rest of letters... -->
</g>
</svg>
añadir role="img" y un <title> llegar <svg>y envuelve cada letra en <g aria-hidden="true">. Esto proporciona una etiqueta clara para que la lean los lectores de pantalla. Funciona bien en algunas combinaciones pero mal en otras, por lo que si el texto es crítico, no lo animes.
Este es el código completo. Es más fácil escribir cuando tienes inteligencia artificial haciéndolo por ti:
Para textos más largos, una biblioteca como GSAP puede brindarle más control, pero los riesgos de accesibilidad que analizamos anteriormente también se aplican, con resultados inconsistentes en todos los lectores de pantalla:
<h1>
<span class="splitfirst">Todos los hongos son</span>
<span class="splitlast">mágicos</span>
</h1>
const splitFirst = SplitText.create('.splitfirst', {
type: "chars",
});
const splitLast = SplitText.create('.splitlast', {
type: "chars, lines",
mask: "lines"
});
const tween = gsap.timeline()
.from(splitFirst.chars, {
xPercent: 100,
stagger: 0.1,
opacity: 0,
duration: 1,
})
.from(splitLast.chars, {
yPercent: 100,
stagger: 0.1,
opacity: 0,
duration: 1,
});
Este sería un gran enfoque para la opción B si elegimos esta ruta. Vea cuán “tranquilas” se sienten las cosas cuando el texto desaparece.
Enmascaramiento y recorte
este clip-path y mask Las propiedades nos permiten ocultar partes de un elemento, pero funcionan de manera completamente diferente. sastrería es una decisión binaria: el píxel es completamente visible o desaparece por completo, lo que lo convierte en la opción correcta para formas geométricas limpias como polígonos, círculos o rutas SVG, y donde el navegador puede optimizar la representación de manera más eficiente. enmascaramientoPor otro lado, el uso de valores de canal luma o alfa: el blanco se muestra, el negro se oculta y todo lo intermedio da como resultado una transparencia parcial. Esto la convierte en una herramienta para bordes suaves, desvanecimientos degradados y texturas irregulares. Recuerde que si tiene formas vectoriales muy complejas, utilice mask vector de relación clip-path. Sarah Drasner tiene un buen artículo sobre cuándo tiene más sentido utilizar uno sobre el otro.
Nuestro proyecto es un caso de uso muy claro. clip-path. Tenemos un círculo que comienza con clip-path: circle(0%)lo que hace que el elemento sea invisible (el radio del círculo de recorte es cero). Durante la duración de la animación, se expande a circle(100%)a medida que el círculo crece hacia afuera desde su centro, revela completamente el elemento. Mientras tanto, entramos y salimos de opacity.
#rainbow, #floor, #mushroom, #flores {
opacity: 0;
animation: maskAnim 2s ease-in forwards;
}
@keyframes maskAnim {
0%, 1% {
clip-path: circle(0%);
opacity: 1;
}
100% {
clip-path: circle(100%);
opacity: 1;
}
}
notas: este 1% El fotograma clave es para garantizar que el navegador se inicie. clip-path interpolado desde circle(0%) en lugar de cualquier valor que el elemento ya pueda tener. Sin él, algunos navegadores saltarán inesperadamente al principio. Una alternativa más limpia es utilizar animation-fill-mode: both porque bloquea el elemento en su from El estado antes de que comience la animación.
A partir de ahí, aplicamos la misma animación a diferentes grupos SVG en la ilustración:
<g id="rainbow">...</g>
<g id="floor">...</g>
<g id="mushroom">...</g>
<g id="flowers">...</g>
¿Qué tan psicodélico es esto? !
Las animaciones basadas en desplazamiento son excelentes porque podemos conectar el progreso de la animación con el desplazamiento del usuario, en lugar de la típica línea de tiempo de ejecución y parada.
Podemos usarlo para crear movimientos sutiles, un tanto “psicodélicos”, como ligeros efectos de paralaje. En este caso, podemos hacer que los objetos más cercanos al usuario se muevan más rápido que los objetos más alejados.
Aquí está el CSS completo:
#estrellas, #arcoiris, .text-line, #fecha, #arco, #flores, #dir, #piso, #barras {
animation: moveUp both;
animation-timeline: view();
}
@keyframes moveUp {
from { transform: translateY(var(--offset)); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
#estrellas { --offset: 10vh; }
#arcoiris { --offset: 20vh; }
#fecha { --offset: 45vh; }
#arco { --offset: 50vh; }
#dir { --offset: 50vh; }
#flores { --offset: 65vh; }
#piso { --offset: 85vh; }
#barras { --offset: 90vh; }
este animation-timeline: view() Indica que cuando el usuario se desplaza a un elemento que ingresa al puerto de desplazamiento, la animación debe comenzar inmediatamente y completarse por completo cuando el elemento se desplaza fuera de la vista. Para hacer que los objetos se muevan a diferentes velocidades, los colocamos en diferentes desplazamientos usando indexación. --offset Propiedades personalizadas como hicimos antes para dividir el texto.
transformación 3D
Este es más complicado y debemos prestar mucha atención al rendimiento. herramientas como esta disposición Puede ayudar con el ascensor porque tiene un vóxel y generador de terreno Completamente construido usando CSS 3D. Cuando se repone, puede llegar más lejos. Funciona CSSun motor de vóxeles completo que representa cuboides 3D y transforma usando solo capas de cuadrícula CSS sin la complejidad de Canvas o WebGL.
Combinemos algunos efectos de desplazamiento y 3D. Es lo que respalda las ideas de “hipnosis” y “baile” en la lista de palabras clave de la Opción A. Echa un vistazo a esto:
aquí yo uso perspective atributo, y luego envuelve todos los elementos secundarios dentro de la escena en el espacio 3D transform-style: preserve-3d. De esta manera, todos los elementos de la subimagen se rotan y trasladan a lo largo del eje de profundidad (o eje z).
Conectemos esto a una animación basada en desplazamiento usando transform: rotateY:
.scene {
perspective: 1200px;
}
.img-wrapper {
transform-style: preserve-3d;
animation: rotateImg linear;
animation-timeline: scroll();
> img {
transform: rotateY(270deg) translate3d(0, 50px, var(--distance));
}
> img:nth-child(2) {
transform: rotateY(180deg) translate3d(0, 50px, var(--distance));
}
}
/* etc. */
@keyframes rotateImg {
to { transform: rotateY(360deg); }
}
Cursor personalizado
cursor Probablemente una de las propiedades CSS menos utilizadas. tener Muchos tipos de cursor Podemos usar aunque hay Definitivamente tengo una opinión sobre hasta dónde puede llegar esto..
Podemos usar esto para procesar imágenes de modo que cuando el usuario pase el mouse sobre diferentes contenedores, se muestren diferentes cursores en diferentes contenedores. Yo personalmente usaría imágenes SVG y PNG para admitir la transparencia, aunque esta propiedad admite cualquier imagen rasterizada.
Vale la pena señalar que el tamaño del cursor varía según el navegador: Firefox limita los cursores personalizados a 32×32 píxeles, mientras que Chrome admite un máximo de 128×128 píxeles. La mayoría de los navegadores se niegan a mostrar (o reducir el tamaño) cursores de más de 32×32 píxeles en pantallas de alto DPI (retina). Mantener el cursor en 32×32px es la opción más segura para mantener la coherencia.
Por ejemplo:
.box1 {
cursor: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAZCAMAAAD63NUrAAAACVBMVEX///8AAAD///9+749PAAAAAXRSTlMAQObYZgAAAFZJREFUeNqdzksKwDAIAFHH+x+6lIYOVPOhs5OHJnES/5UkYKEkU7xjijSIm50iFh4fAXgYDd/yumVVRSwsqq/nRA3xVK0oo06d5U6DpQZ7PV7lMxH7LkaQAbYFwryzAAAAAElFTkSuQmCC),auto;
}
Incluso podemos configurar múltiples alternativas para garantizar la más amplia compatibilidad con el navegador:
body {
cursor: url('path-to-image.png'), url('path-to-image-2.svg'), url('path-to-image-3.jpeg'), auto;
}
Si bien esto es genial, debemos tener en cuenta la accesibilidad para cambiar el comportamiento web predeterminado como este. Podría resultar interesante aplicar cursores personalizados a elementos muy específicos en lugar de hacerlo al por mayor.
Bonificación: posicionamiento del ancla
Una cosa más antes de terminar. he estado jugando Posicionamiento del ancla CSSinspirado en Kevin Powell presenta. En lugar de adjuntar un pseudoelemento a cada elemento, podemos usar esto para adjuntar un solo pseudoelemento al elemento actualmente sobreelevado. En otras palabras, creamos un elemento y lo anclamos a un elemento flotante, como una tarjeta destacada:
Esto abre posibilidades interesantes, como poder cambiar los estados de desplazamiento entre tarjetas. En este caso estoy usando linear() Características Consigue un rebote natural con la ayuda de guía de flexibilización.
en conclusión
Las barreras técnicas para crear experiencias en línea memorables han desaparecido en gran medida. Espero que todo lo que hemos cubierto aquí le dé una idea de hasta dónde podemos llegar con las funciones CSS modernas que eliminan por completo la necesidad de JavaScript adicional. Tenemos más posibilidades que nunca sin los complejos gastos técnicos del pasado.
Entonces, en lugar de preguntar, ¿Es esto posible?la pregunta más importante es: ¿Este movimiento cuenta una mejor historia? Si es así, por favor envíe. Utilice estas herramientas no porque pueda, sino porque le ayudan a contar una historia mejor, accesible y eficaz.
Por supuesto, todo lo aquí son sólo algunas formas de lograrlo. Pero, ¿cuáles son algunas experiencias inolvidables en tu trabajo? ¿O qué viste en otros sitios web?