Diseño Apple
Respuesta Inicial
Cuando esta skill se invoca por primera vez sin una pregunta específica, responda solo con:
Estoy listo para ayudarlo a construir interfaces fluidas de estilo Apple en la web, mi conocimiento proviene de las charlas de diseño de la WWDC de Apple, traducidas 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 usted. Este conocimiento proviene de las charlas de diseño de la WWDC de Apple — principalmente Designing Fluid Interfaces (WWDC 2018) — destiladas y traducidas para la plataforma web (CSS, Pointer Events, requestAnimationFrame, bibliotecas de muelles 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 impulso hacia adelante y puede ser agarrada e invertida en cualquier instante. Los muelles 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, sucede 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 impulso, resisten en los límites y pueden redirigirse a mitad del movimiento. Todo lo que sigue es una forma de acercarse a eso.
Apple enmarca el diseño como el 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 "cae en picado". La respuesta es la base sobre la que se construye todo lo demás.
- Responda 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. - Sea vigilante con 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 interfaz de usuario 1:1 con el puntero durante todo el recorrido — nunca anime solo cuando el gesto termina.
/* La retroalimentación vive en la presió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 instantáneamente 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. - Rastree 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; // respetar dónde agarraron
// ...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 el usuario agarra de nuevo debe seguir el dedo — no terminar de cerrarse primero y luego reabrirse.
- Nunca bloquee la entrada durante una transición.
- Siempre anime desde el valor de presentación (actual), nunca el valor objetivo. Al interrumpir, lea la transformación en vivo del elemento en pantalla y comience 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 muelles animan desde el valor actual por defecto, que es exactamente lo que necesita la interrupción. - Cuando un gesto se invierte, combine 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 muelles que transportan la velocidad a través de un re-direccionamiento lo evitan. (Esto es lo que las animaciones aditivas de iOS hacen de forma nativa; en la web, elija una biblioteca de muelles que re-direccione desde la velocidad actual).
- Descomponga el movimiento 2D en muelles X e Y independientes. Un solo muelle en una distancia 2D se desincroniza cuando X e Y tienen velocidades diferentes.
4. Comportamiento sobre animación — use muelles
"Piense en la animación como una conversación entre usted y el objeto, no algo prescrito por la interfaz."
Una animación preprogramada y de duración fija no puede responder a nuevas entradas. Un muelle puede — una nueva entrada simplemente cambia el objetivo, y el movimiento se mantiene continuo. Recurra a los muelles 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 amigables para el diseñador. Piense en estos:
- Relación de amortiguación — controla el sobrepaso.
1.0= amortiguación crítica, sin rebote, asentamiento suave.< 1.0= sobrepasa y oscila. 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 muelle no tiene una duración fija; su tiempo de asentamiento surge de los parámetros.
Valores predeterminados:
- Comience la mayoría de las IU 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 llevaba impulso (un movimiento rápido, un lanzamiento, una liberación de arrastre). El sobrepaso en un menú que simplemente apareció se siente mal; el sobrepaso 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 muelle bounce + duration se asigna estrechamente a la amortiguación + respuesta de Apple. Un estilo seguro de casa es usar muelles de damping: 1.0 en todas partes por defecto; reserve el rebote para interacciones físicas e impulsadas por momento.
import { animate } from 'motion';
// Valor predeterminado con amortiguación crítica (sin sobrepaso)
animate(el, { y: 0 }, { type: 'spring', bounce: 0, duration: 0.4 });
// Interacción de momento — un poco de rebote, solo porque un movimiento rápido lo precedió
animate(el, { y: target }, { type: 'spring', bounce: 0.2, duration: 0.4 });
5. Transferencia de velocidad — la costura 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 costura 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 muelle. Algunas APIs de muelle quieren una velocidad relativa — normalícela por la distancia restante al objetivo:
velocidadRelativa = velocidadDelGesto / (valorObjetivo − valorActual)
Ejemplo: elemento en y=50, objetivo y=150 (100px por recorrer), dedo moviéndose a 50px/s → velocidad inicial del muelle = 50 / 100 = 0.5. Framer Motion / Motion toman la velocidad absoluta en px/s directamente (opción velocity), por lo que generalmente le pasa el valor sin procesar.
6. Proyección de momento — animar hacia donde el gesto se dirige
"Tome una entrada pequeña y haga una salida grande."
No se 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 movimiento rápido se sienta como si lanzara el elemento.
La función de proyección exacta de Apple (del código de muestra de Designing Fluid Interfaces):
// decelerationRate ≈ 0.998 para 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 desde la proyección
animateSpringTo(target, { velocity: releaseVelocity }); // luego transferir velocidad (§5)
Nota: la fórmula física de libro de texto v²/(2·decel) no es lo que Apple envía — use la forma de decaimiento exponencial anterior. Este es el comportamiento estándar en buenas hojas inferiores y carouseles (Vaul, Embla).
7. Consistencia espacial — caminos simétricos, 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 descartarse hacia la derecha. Entrar desde la derecha / salir por abajo se siente desconectado y confuso.
- Ancle las interacciones a su origen. Un menú, popover u hoja debe originarse desde el elemento que lo activó — establezca
transform-originen el activador, 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 los popovers que escalan desde su activador, no desde su centro). - Refleje la facilitación en las transiciones reversibles para que el camino de salida coincida con el camino de retorno (use puntos de control cúbicos-bezier inversos para las dos direcciones).
8. Insinuar en la dirección del gesto
Los humanos predicen un estado final a partir de una trayectoria. Los fotogramas intermedios deben 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 que interpolen ciegamente hacia él.
9. Efecto de caucho (Rubber-banding) — límites suaves
En un límite, 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 tocar-abajo (instantáneo), confirme al tocar-arriba. Agregue ~10px de histéresis/relleno de acierto alrededor del objetivo, y permita cancelar arrastrando hacia afuera y de regreso.
- 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 de tipo
swipeleft) — 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 individuales; solo pague ese costo donde el doble toque realmente exista.
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 posicional por fotograma por debajo del umbral de percepción para evitar el estroboscopio.
- Para movimientos muy rápidos, un sutil desenfoque de movimiento / estiramiento codifica la velocidad y se lee mejor que una raya dura y nítida.
requestAnimationFramees el reloj sincronizado con la pantalla de la web (Apple usaCADisplayLink). Anime solo propiedades amigables para 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 foco. En la web, aproxime con backdrop-filter.
- Construya barras de 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 consumen 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: desenfoque más fuerte + una sombra más profunda que los chips pequeños. Considere la sombra consciente del contexto — más pesada sobre contenido ocupado/texto para la separación, más clara sobre fondos planos.
- Atenuar para enfocar, separar para mantener el flujo. Una tarea modal combina 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 interrumpa. Para hojas apiladas, atenúe progresivamente y empuje hacia atrás cada capa principal.
- La vitalidad 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. Ponga el color en una capa sólida, no en 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 interfaz de usuario flotante realmente se superpone 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 simple desvanecimiento de opacidad.
.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 atrapa el material */
}
13. Retroalimentación multimodal — movimiento + sonido + hápticos
Tres reglas para combinar sentidos (de Designing Audio-Haptic Experiences):
- Causalidad — debe ser obvio qué causó la retroalimentación. Actívela en el evento causal real (el interruptor volteándose, el elemento encajando en su lugar), y empareje su carácter con la fisicalidad de la acción.
- Armonía — lo visual, el sonido y lo háptico deben activarse en el mismo fotograma. La latencia entre ellos destruye la ilusión. No deje que un …
[truncado para traducción]