name: motion-and-hover-polish description: Proporciona una interfaz con un tempo compartido y un pequeño vocabulario de microinteracciones de hover y estado: duraciones escalonadas (tap/pop/hover/card/layout) para CSS y Motion, intercambios con hand-off, filas cuyo resaltado crece, flechas finales, empujones de chevron, elevaciones de tarjeta sin translate, desvanecimientos en bordes de scroll, e intercambios de icono/etiqueta para botones de copia y alternancia. Útil cuando las animaciones se sienten inconsistentes, las duraciones son números mágicos dispersos, los hovers solo cambian de color, o un producto se siente plano o sin terminar. Se activa con: duración de animación, temporización de transición, estado hover, microinteracción, pulido, feedback de botón de copia, icono de menú para cerrar, flecha atrás, hover de tarjeta, desvanecimiento de scroll, tokens de movimiento, duraciones de Framer Motion. license: MIT
Niveles de movimiento y recetas de hover
Dos ideas, probadas en 17 aplicaciones prototipo:
- Las duraciones son una escala, no un número por componente. El ojo lee un tempo compartido como un solo producto. Las cosas más pequeñas se mueven más rápido.
- Los hovers mueven la geometría, suavemente. Un resaltado que crece dentro de la fila, una flecha que llega, un chevron que se inclina hacia donde va, una etiqueta que rueda hacia su siguiente estado. Los hovers que solo cambian de color se leen como un interruptor que se acciona.
Archivos
references/recipes.css: Tokens@themey recetas@utilityde Tailwind v4.references/timing.ts: Los mismos niveles para Motion y WAAPI.references/swap.tsx:IconSwapyLabelSwap(React).
Niveles
| Nivel | ms | Para |
|---|---|---|
tap | 100 | un icono que cambia, una pulsación |
pop | 150 | apertura de un menú, popover o tooltip |
hover | 200 | una fila o control respondiendo al puntero |
card | 300 | una tarjeta elevándose, una lista asentándose, un cambio de página |
layout | 500 | un panel colapsando, la página refluyendo |
- Las entradas se desaceleran (
cubic-bezier(0.22,1,0.36,1)). Las salidas aceleran y se ejecutan un nivel más corto. - Cuando dos cosas intercambian lugares, la que sale se va de una vez y la que
entra comienza
delay-handoff(50ms) después. - Los interpolaciones de valores (un contador, un relleno de barra, el dibujo de una línea de gráfico) pueden durar más: el ojo sigue los datos.
- Mantén los springs deliberados.
Migración: asigna cada duración ad-hoc al nivel más cercano.
| Valor ad-hoc | Nivel |
|---|---|
| 0.12 | tap |
| 0.15–0.18 | pop |
| 0.2–0.24 | hover |
| 0.28–0.35 | card |
| 0.4–0.5 | layout |
Espera que la temporización en pantalla cambie en menos de 20ms en casi todas partes.
Recetas (una por elemento)
hover-grow(filas de navegación, filas de enlaces):- El resaltado es un
::beforeque comienza insertado (3px por 6px) y transparente, luego se expande hasta la fila completa mientras se desvanece. - También se muestra para
:focus-visible,data-state=openyaria-current=page. - Los diseños cuadrados o brutalistas (tipo Carbon) deben mantener un resaltado plano en su lugar.
- El resaltado es un
trail(filas de enlaces, "Explorar planes"):- Un glifo final a media escala y transparente hasta que el padre
.groupse pasa el cursor o se enfoca, creciendo desde su borde delantero. trail-out(crece desde la esquina inferior izquierda) másarrow-up-rightmarcan los enlaces que salen de la aplicación.
- Un glifo final a media escala y transparente hasta que el padre
nudge/nudge-back(chevrones, "Ver todo", enlaces de retorno): una inclinación de 2px hacia el destino.lift(tarjetas clickeables):- Un paso de superficie más claro y un paso de sombra más profundo, durante
el nivel
card. - Nunca usar translate: una tarjeta que salta bajo el cursor mueve lo que se está apuntando.
- Un paso de superficie más claro y un paso de sombra más profundo, durante
el nivel
scroll-fade(paneles desplazables):- Desvanecimientos de borde basados en máscara, solo en el lado que aún puede desplazarse. Se impulsa por la propia línea de tiempo de desplazamiento del contenedor y no necesita color de fondo.
- Los navegadores no compatibles simplemente no muestran desvanecimiento.
IconSwap(menú a cerrar, copiar a verificado, sol a luna):- El glifo saliente se encoge, se inclina −22° y se desvanece con una aceleración.
- El entrante sigue después del retardo de hand-off.
- Ambos permanecen montados, por lo que se revierte limpiamente a medio vuelo.
LabelSwap("Copiar enlace" a "Copiado", "Contraer" a "Expandir"):- Ambas etiquetas se sientan en una celda de cuadrícula, por lo que no hay salto de ancho.
- La etiqueta oculta se vuelve
invisiblemediantetransition-discrete, así que la tecnología de asistencia solo escucha la actual.
Trampas
scroll-fadeen un elemento que se anima a sí mismo (menú Radix, contenido de popover o select) reemplaza la animación de salida de ese elemento. Radix espera una salida que nunca termina, y el menú permanece abierto. Pon el desvanecimiento en un hijo desplazable dentro.scroll-fadetambién enmascara bordes y sombras. No lo pongas en una tarjeta con borde; ponlo en el desplazador interior.- Las utilidades de
duration-*personalizadas en Tailwind v4 también deben establecer--tw-duration, o una utilidadtransition-[…]en el mismo elemento gana dependiendo del orden de ordenación. - Los pasos de tipo nombrados que llevan una altura de línea
(
--text-meta--line-height) desplazan cada línea convertida en un píxel al reemplazar tamaños arbitrarios que heredaban el interlineado del padre. Da a los pasos intermedios ninguna altura de línea propia. - Protege los hovers detrás de
@media (hover: hover). Muestra el mismo estado en:focus-visible, y respetaprefers-reduced-motion(Motion:<MotionConfig reducedMotion="user">).