Pulido de UI
El pulido proviene de un montón de pequeños detalles que se acumulan. Esta skill es la referencia sobre cuáles vale la pena tener y qué valores toman.
Al revisar, ralentiza la interfaz. Lo que se siente extraño al 10% de velocidad es lo que está sutilmente mal a velocidad normal.
Mantén la biblioteca de componentes del proyecto, los tokens y la densidad, y ajusta su lenguaje de movimiento, excepto cuando una regla a continuación prescriba una interacción exacta.
Cada duración, curva, escala y desenfoque a continuación es un valor específico, no un rango para aproximar. cubic-bezier(0.2, 0, 0, 1) no es cubic-bezier(0.4, 0, 0.2, 1), y 0.96 no es 0.95. Usa lo que está escrito.
El ajuste de texto, el renderizado de fuentes, los números tabulares y el espaciado de texto pertenecen a better-typography. Las áreas táctiles, el enfoque, el soporte de teclado, ARIA y el movimiento reducido pertenecen a better-accessibility. La agrupación, el espaciado de secciones, los puntos de interrupción y el RTL espacial pertenecen a better-layout.
Radio de borde concéntrico
Radio exterior = radio interior + relleno. Los radios no coincidentes en elementos anidados es lo más común que hace que una interfaz se sienta fuera de lugar. Las recetas de radio, sombra y contorno están en surfaces.md.
Alineación óptica sobre geométrica
Cuando la centración geométrica se ve incorrecta, alinea ópticamente. Los botones con iconos, triángulos de reproducción e iconos asimétricos necesitan un ajuste manual.
Sombras para elevación, bordes para estructura
Donde un borde existe solo para crear profundidad, prefiere valores de box-shadow transparentes superpuestos. Mantén los bordes que comunican estructura o estado: divisores, separadores y estados seleccionados o enfocados.
Animaciones interrumpibles
Usa transiciones CSS para cambios de estado interactivos, porque pueden interrumpirse a mitad de la animación. Reserva los keyframes para secuencias escenificadas que se ejecutan una vez.
Dividir y escalonar animaciones de entrada
Para una entrada escenificada poco frecuente donde la secuencia comunica jerarquía, divide el contenido en bloques semánticos y escalónalos por ~100ms. Animar un contenedor obtiene menos por el mismo costo. Deja las interacciones de alta frecuencia sin escalonar. Consulta enter-exit.md.
Animaciones de salida sutiles
Usa un translateY fijo pequeño en lugar de altura completa. Las salidas deben ser más suaves que las entradas. Usa ease-out para ambas direcciones.
Animaciones de iconos contextuales
Anima los iconos con opacity, scale y blur en lugar de alternar visibilidad. Usa exactamente estos valores: escala 0.25 a 1, opacidad 0 a 1, desenfoque 4px a 0px.
Con una biblioteca de movimiento (motion o framer-motion en package.json), usa la ruta de importación de esa biblioteca, o las importaciones cercanas donde ambas existan. Usa transition: { type: "spring", duration: 0.3, bounce: 0 }. El rebote siempre es 0.
Sin una, mantén ambos iconos en el DOM con uno posicionado absolutamente, y funde-cruzado con cubic-bezier(0.2, 0, 0, 1). Eso te da entrada y salida sin dependencia. Ambas recetas están en icon-transitions.md.
Contornos de imágenes
Da a las imágenes un contorno de 1px con baja opacidad para una profundidad consistente. Negro puro en modo claro (oklch(0 0 0 / 0.1)), blanco puro en modo oscuro (oklch(1 0 0 / 0.1)). Nunca un casi-negro como pizarra o zinc, y nunca un neutro con tinte. Un contorno teñido capta la superficie subyacente y se lee como suciedad en el borde de la imagen.
Escala al presionar
Un scale(0.96) al hacer clic da al botón una retroalimentación táctil. Siempre 0.96; cualquier valor por debajo de 0.95 se siente exagerado. Añade una prop static para desactivarlo donde el movimiento distraiga. Consulta recetas para CSS, Tailwind y Motion.
Omitir animación al cargar la página
Usa initial={false} en AnimatePresence para mantener las animaciones de entrada fuera del primer renderizado. Verifica que deje intactas las entradas intencionales de la página.
Suprimir transiciones al cambiar de tema
Un cambio de tema altera el color, fondo, borde y sombra en casi todos los elementos a la vez. Cada transición en esas propiedades se dispara junto y el cambio se mancha en lugar de encajar. Inyecta *,*::before,*::after{transition:none !important}, fuerza un reflujo, luego elimínalo en el siguiente frame. Consulta la receta.
Transicionar solo lo que cambia
Nombra siempre las propiedades exactas: transition-property: scale, opacity. transition-transform de Tailwind cubre transform, translate, scale, rotate.
Usa will-change con moderación
Solo para transform, opacity y filter, que la GPU puede componer. Nunca will-change: all. Añádelo cuando veas tartamudeo en el primer frame, no antes. Consulta performance.md.
Ajustar el trazo del icono al peso del texto
Un icono junto a un texto lleva el peso óptico del texto: trazo de 1.5px junto a texto regular (400), 2px junto a seminegrita (600). Un peso de trazo por conjunto de iconos y una biblioteca de iconos por superficie. El tamaño y el volteo RTL están en icons.md.
Un SVG, recoloreado por estado
Los iconos usan currentColor y toman los estados hover, seleccionado y deshabilitado del color y opacidad CSS, nunca de activos separados. El contorno es la variante predeterminada; el relleno marca el estado activo.
Moderación del movimiento
Da retroalimentación instantánea a las interacciones de alta frecuencia, o una transición de 150ms o menos en opacidad y color. Una animación personalizada allí cobra su costo de atención en cada disparador.
Cada cambio de estado animado también necesita una señal estática: color, un icono o una etiqueta. El movimiento nunca es el único canal de retroalimentación.
Antes de terminar
| Error | Solución |
|---|---|
| Los iconos se ven descentrados | Ajusta ópticamente con relleno, o corrige el SVG |
| Entrada o salida escenificada discordante | Escalona entradas poco frecuentes; mantén salidas sutiles |
| El cambio de tema funde-cruzado toda la página | Deshabilita transiciones para el intercambio, fuerza un reflujo, restablece en el siguiente frame |
transition: all en elementos | Especifica propiedades exactas |
| Tartamudeo de animación en el primer frame | Añade will-change: transform (con moderación) |
| Icono de trazo fino junto a texto en negrita | Ajusta el ancho del trazo al peso del texto |
Informe
Severidad. HIGH rompe una interacción, hace que el movimiento sea inutilizable, o deja un cambio de estado visible solo mientras se ejecuta la animación. MEDIUM es una inconsistencia visible en superficies, iconos o movimiento. LOW es pulido aislado.
Verificación. Sin navegador: cada estado que define el componente, es decir, hover, focus, active, loading y empty, más las duraciones y curvas de movimiento leídas del código. Con uno: recorre cada estado y reproduce el movimiento al 10% de velocidad en el panel de Animaciones del navegador. Reporta cada verificación que no pudiste realizar como No verificado.
Formato. Agrupa los hallazgos bajo el principio que viola cada uno, ordenados por severidad, una fila por causa raíz listando cada ubicación donde aparece:
| Severidad | Ubicación | Antes | Después | Por qué |
|---|
Ubicación es ruta/al/archivo:línea. Por qué nombra el principio y el impacto en el usuario.
Termina con Bloqueo cuando quede algún HIGH, Aprobar en caso contrario, dejando el resto en la tabla como trabajo pendiente. Nunca Aprobar una cobertura que no inspeccionaste. Sin nada que reportar, indica "No se encontraron hallazgos procesables de pulido de UI" e informa la verificación.