Diseño Apple
Respuesta Inicial
Cuando este skill se invoque por primera vez sin una pregunta específica, responda únicamente con:
Estoy listo para ayudarte a construir interfaces fluidas al estilo Apple en la web; mi conocimiento proviene de las charlas de diseño de WWDC de Apple, traducido para la web.
No proporcione ninguna otra información hasta que el usuario haga una pregunta.
Cómo Apple construye interfaces que dejan de sentirse como una computadora y comienzan a sentirse como una extensión de ti. Este conocimiento proviene de las charlas de diseño de WWDC de Apple — principalmente Diseñando Interfaces Fluidas (WWDC 2018) — destilado y traducido para la plataforma web (CSS, Pointer Events, requestAnimationFrame, bibliotecas de resortes como Motion/Framer Motion).
El hilo conductor: una interfaz se siente viva cuando el movimiento comienza desde el valor actual en pantalla, hereda la velocidad del usuario, proyecta el momentum hacia adelante y puede ser agarrada e invertida en cualquier instante. Los resortes son la herramienta que hace que todo esto sea natural, porque son inherentemente interrumpibles y conscientes de la velocidad.
La Idea Central
"Cuando alineamos la interfaz con la forma en que pensamos y nos movemos, ocurre algo mágico: deja de sentirse como una computadora y comienza a sentirse como una extensión perfecta de nosotros."
Una interfaz es fluida cuando se comporta como el mundo físico: las cosas responden instantáneamente, se mueven de forma continua, llevan momentum, resisten en los límites y pueden ser redirigidas a mitad del movimiento. Todo lo que sigue es una forma de acercarse a eso.
Apple enmarca el diseño como un servicio a cuatro necesidades humanas: seguridad/previsibilidad, comprensión, logro y alegría. Cada regla aquí sirve a una de ellas.
1. Respuesta — eliminar la latencia
En el momento en que aparece el retraso, la sensación de inmediatez "se desploma". La respuesta es la base sobre la que se construye todo lo demás.
- Responder al presionar, no al soltar. Resalte un botón en el instante en que se presiona. Esperar a
click/levantar el dedo para mostrar retroalimentación se siente muerto. - Esté atento a cada latencia. Audite los 'debounces', temporizadores artificiales, esperas de transición y el retraso de ~300ms del toque. Cualquier cosa en la ruta de entrada que no sea esencial es una regresión.
- La retroalimentación debe ser continua durante la interacción, no solo al final. Para un arrastre, control deslizante o cajón, actualice la UI 1:1 con el puntero durante todo el proceso — nunca anime solo cuando el gesto se completa.
/* La retroalimentación vive en la pulsación, y es instantánea */
.button:active {
transform: scale(0.97);
transition: transform 100ms ease-out;
}
2. Manipulación directa — seguimiento 1:1
"El toque y el contenido deben moverse juntos."
Cuando el usuario arrastra algo, debe permanecer pegado al dedo — y respetar el desplazamiento desde donde lo agarró. Ajustar al centro del elemento al agarrarlo rompe la ilusión de inmediato.
- Use Pointer Events con
setPointerCapturepara que el seguimiento continúe incluso cuando el puntero sale de los límites del elemento. - Realice un seguimiento de un breve historial de velocidad/posición (los últimos eventos
pointermove), no solo el punto actual — necesitará la velocidad al soltar.
el.addEventListener('pointerdown', (e) => {
el.setPointerCapture(e.pointerId);
const grabOffset = e.clientY - el.getBoundingClientRect().top; // respeta dónde agarró
// ...rastrear historial de posición + marca de tiempo para la velocidad
});
3. Interrumpibilidad — el principio más importante
"El pensamiento y el gesto ocurren en paralelo."
Cada animación debe ser interrumpible y redirigible en cualquier momento. Un usuario debe poder agarrar un elemento en movimiento a mitad de vuelo e invertirlo sin esperar a que la animación termine. Un modal que se cierra y que el usuario vuelve a agarrar debe seguir al dedo — no terminar de cerrarse primero y luego reabrirse.
- Nunca bloquee la entrada durante una transición.
- Anime siempre desde el valor de presentación (actual), nunca desde el valor objetivo. Al interrumpir, lea la transformación en vivo del elemento en pantalla e inicie la nueva animación desde allí. Comenzar desde el valor lógico/objetivo provoca un salto visible.
- Evite las transiciones CSS y
@keyframespara cualquier cosa impulsada por gestos — no se pueden agarrar e invertir suavemente a mitad de vuelo. Los resortes animan desde el valor actual por defecto, que es exactamente lo que necesita la interrupción. - Cuando un gesto se invierte, mezcle la velocidad — no la corte abruptamente. Reemplazar una animación con otra en una inversión crea una discontinuidad de velocidad, un "muro de ladrillos". Las bibliotecas de resortes que transportan la velocidad a través de un redestino lo evitan. (Esto es lo que las animaciones aditivas de iOS hacen de forma nativa; en la web, elija una biblioteca de resortes que redestine desde la velocidad actual.)
- Descomponga el movimiento 2D en resortes X e Y independientes. Un solo resorte en una distancia 2D se desincroniza cuando X e Y tienen diferentes velocidades.
4. Comportamiento sobre animación — use resortes
"Piense en la animación como una conversación entre usted y el objeto, no como algo prescrito por la interfaz."
Una animación preprogramada de duración fija no puede responder a nuevas entradas. Un resorte sí puede — una nueva entrada simplemente cambia el objetivo, y el movimiento permanece continuo. Recurra a los resortes para cualquier cosa que un usuario pueda tocar.
Apple reemplazó deliberadamente el trío de física (masa/rigidez/amortiguación) con dos parámetros adaptados para diseñadores. Piense en estos:
- Relación de amortiguación — controla el sobreimpulso.
1.0= amortiguación crítica, sin rebote, asentamiento suave.< 1.0= sobrepasa y oscila. Cuanto más bajo, más rebote. - Respuesta — qué tan rápido el valor alcanza el objetivo, en segundos. Más bajo = más rápido. Esto no es "duración" — un resorte no tiene una duración fija; su tiempo de asentamiento surge de los parámetros.
Valores por defecto:
- Inicie la mayoría de las UI con amortiguación
1.0(amortiguación crítica) — elegante y no distractora. - Agregue rebote (amortiguación ~
0.8) solo cuando el gesto en sí mismo haya llevado momentum (un lanzamiento, un tirón, una liberación de arrastre). El sobreimpulso en un menú que simplemente apareció se siente mal; el sobreimpulso en una tarjeta que lanzó se siente bien.
Valores concretos que Apple envía:
| Interacción | Amortiguación | Respuesta |
|---|---|---|
| Mover / reposicionar (ej. PiP) | 1.0 | 0.4 |
| Rotación | 0.8 | 0.4 |
| Cajón / hoja | 0.8 | 0.3 |
Mapeo web (Motion / Framer Motion): la API de resorte bounce + duration se asigna estrechamente a la amortiguación + respuesta de Apple. Un estilo base seguro es usar resortes con damping: 1.0 por defecto; reserve el rebote para interacciones físicas impulsadas por momentum.
import { animate } from 'motion';
// Predeterminado con amortiguación crítica (sin sobreimpulso)
animate(el, { y: 0 }, { type: 'spring', bounce: 0, duration: 0.4 });
// Interacción con momentum — un poco de rebote, solo porque lo precedió un lanzamiento
animate(el, { y: target }, { type: 'spring', bounce: 0.2, duration: 0.4 });
5. Transferencia de velocidad — la sutura entre arrastre y animación
Cuando un gesto termina, la animación debe continuar a la velocidad exacta del dedo, para que no haya una sutura visible entre arrastrar y animar. Este es el detalle que más separa lo "fluido" de lo "correcto".
Pase la velocidad de liberación del puntero como la velocidad inicial del resorte. Algunas APIs de resortes quieren la velocidad relativa — normalícela por la distancia restante al objetivo:
velocidadRelativa = velocidadGesto / (valorObjetivo − valorActual)
Ejemplo: elemento en y=50, objetivo y=150 (100px por recorrer), dedo moviéndose a 50px/s → velocidad inicial del resorte = 50 / 100 = 0.5. Framer Motion / Motion toman la velocidad absoluta en px/s directamente (opción velocity), por lo que generalmente se le pasa el valor crudo.
6. Proyección de momentum — animar hacia donde se dirige el gesto
"Tome una entrada pequeña y produzca una salida grande."
No ajuste al límite más cercano desde el punto de liberación. Use la velocidad para proyectar la posición de reposo — exactamente como la desaceleración del desplazamiento — luego ajuste al objetivo más cercano a ese punto proyectado. Esto es lo que hace que un lanzamiento se sienta como si arrojara el elemento.
La función de proyección exacta de Apple (del código de muestra de Diseñando Interfaces Fluidas):
// decelerationRate ≈ 0.998 para una sensación de desplazamiento normal; 0.99 para más rápido
function project(initialVelocity /* px/s */, decelerationRate = 0.998) {
return (initialVelocity / 1000) * decelerationRate / (1 - decelerationRate);
}
const projectedEndpoint = currentPosition + project(releaseVelocity);
const target = nearestSnapPoint(projectedEndpoint); // elegir objetivo de la proyección
animateSpringTo(target, { velocity: releaseVelocity }); // luego transferir velocidad (§5)
Nota: la fórmula física v²/(2·decel) no es lo que Apple envía — use la forma de decaimiento exponencial anterior. Este es el comportamiento estándar en buenos paneles inferiores y carruseles (Vaul, Embla).
7. Consistencia espacial — rutas simétricas, orígenes anclados
"Si algo desaparece de una manera, esperamos que emerja de donde vino."
- Entre y salga por el mismo camino. Un panel que se desliza desde la derecha debe desaparecer hacia la derecha. Entrar por la derecha / salir por abajo se siente desconectado y confuso.
- Ancle las interacciones a su origen. Un menú, ventana emergente u hoja debe originarse desde el elemento que lo desencadenó — establezca
transform-originen el desencadenante, para que la relación espacial entre el botón y el contenido sea obvia. (Este es el mismo punto de conciencia del origen que las ventanas emergentes que escalan desde su desencadenante, no desde su centro). - Refleje la aceleración en las transiciones reversibles para que la ruta de salida coincida con la ruta de retorno (use puntos de control cúbico-bezier inversos para las dos direcciones).
8. Insinúe en la dirección del gesto
Los humanos predicen un estado final a partir de una trayectoria. El movimiento intermedio debe telegrafiar hacia dónde se dirigen las cosas — los módulos del Centro de Control "crecen hacia arriba y hacia afuera hacia su dedo". Haga que los fotogramas intermedios apunten al resultado, no solo interpolar ciegamente hacia él.
9. Efecto elástico ('Rubber-banding') — límites suaves
En un borde, resista progresivamente en lugar de detenerse bruscamente. Una parada brusca se lee como "congelado"; la resistencia continua se lee como "receptivo, pero no hay nada más aquí". Aplique una amortiguación que aumente cuanto más allá del límite arrastre el usuario.
// Cuanto más allá del límite, menos sigue el elemento — las cosas reales se ralentizan antes de detenerse
function rubberband(overshoot, dimension, constant = 0.55) {
return (overshoot * dimension * constant) / (dimension + constant * Math.abs(overshoot));
}
10. Detalles de diseño de gestos (la lista de verificación de "sensación")
- Toque: resalte al presionar (instantáneo), confirme al levantar. Agregue ~10px de histéresis/relleno de acierto alrededor del objetivo y permita la cancelación arrastrando hacia afuera y luego regresando.
- Arrastre/deslizamiento: requiera un pequeño umbral de movimiento (histéresis, ~10px) antes de comprometerse con una dirección, luego rastree 1:1.
- Detecte todos los gestos plausibles en paralelo desde el primer movimiento, luego cancele con confianza los perdedores una vez que la intención esté clara. Evite los reconocedores que solo informan un estado final (eventos tipo
swipeleft) — estos descartan el seguimiento continuo que necesita para la retroalimentación. - Minimice los retrasos de desambiguación. La detección de doble toque inevitablemente retrasa los toques simples; solo pague ese costo donde exista realmente el doble toque.
11. Suavidad a nivel de fotograma
La suavidad se trata de lo que hay en los fotogramas, no solo de la tasa de fotogramas.
- Mantenga el cambio de posición por fotograma por debajo del umbral de percepción para evitar el estroboscópico.
- Para movimientos muy rápidos, un sutil desenfoque de movimiento / estiramiento codifica la velocidad y se lee mejor que una raya nítida y dura.
requestAnimationFramees el reloj sincronizado con la pantalla de la web (Apple usaCADisplayLink). Anime solo propiedades amigables con el compositor —transformyopacity— y sugiera conwill-changedonde el movimiento sea inminente.
12. Materiales y profundidad — la translucidez transmite jerarquía
Apple usa materiales translúcidos como una capa funcional flotante que aporta estructura sin robar el enfoque. En la web, aproxímese con backdrop-filter.
- Construya navegación/barras de herramientas/hojas como capas translúcidas (
backdrop-filter: blur()+ un fondo semitransparente) con contenido desplazándose debajo — no barras opacas que consuman una franja fija. - El peso del material codifica la jerarquía: los materiales más oscuros/más pesados separan regiones estructurales (barras laterales); los materiales más claros atraen la atención hacia elementos interactivos (botones). Nunca apile una superficie translúcida clara sobre otra — la legibilidad colapsa.
- Las superficies más grandes deben leerse como más gruesas: un desenfoque más fuerte + una sombra más profunda que en pequeñas fichas. Considere una sombra consciente del contexto — más pesada sobre contenido ocupado/texto para separación, más clara sobre fondos lisos.
- Atenuar para enfocar, separar para mantener el flujo. Una tarea modal empareja la superficie con un velo de atenuación y empuja el fondo hacia atrás/abajo. Un panel paralelo y no bloqueante usa translucidez y desplazamiento sin un velo para que el flujo no se rompa. Para hojas apiladas, atenúe y empuje progresivamente hacia atrás cada capa principal.
- La vitalidad ('Vibrancy') mantiene el texto legible sobre fondos cambiantes. Sobre superficies desenfocadas/translúcidas, no use texto gris plano — use un contraste más alto, un peso ligeramente más pesado y un pequeño aumento en el espaciado entre letras. Coloque el color sobre una capa sólida, no sobre el primer plano translúcido.
- Efectos de borde de desplazamiento, no divisores duros. En lugar de un borde de 1px debajo de un encabezado fijo, desvanezca una pequeña máscara de desenfoque/gradiente donde el contenido se encuentra con el cromo flotante — solo donde la UI flotante se superpone realmente al contenido.
- Materialice, no solo se desvanezca. Para superficies de vidrio/desenfoque, anime el radio de desenfoque y la escala juntos al entrar/salir, para que la superficie se lea como un material real que llega en lugar de un desvanecimiento de opacidad simple.
.toolbar {
background: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(20px) saturate(180%);
border-top: 1px solid rgba(255, 255, 255, 0.4); /* borde superior brillante = luz que incide en el material */
}
13. Retroalimentación multimodal — movimiento + sonido + háptica
Tres reglas para combinar los sentidos (de Diseñando Experiencias Audio-Hápticas):
- Causalidad — debe ser obvio qué causó la retroalimentación. Actívela en el evento causal real (el interruptor que cambia, el elemento que encaja en su lugar), y haga coincidir su carácter con la fisicalidad de la acción.
- Armonía — lo visual, el sonido y la háptica deben dispararse en el mismo fotograma. La latencia entre ellos destruye la ilusión. No permita que un...