Fundação
Motion
O Spinlist usa movimento mínimo e intencional. Toda animação tem um propósito — feedback de toque, carregamento ou transição de estado. Toque nos exemplos abaixo.
Active state
Toda superfície tocável recua levemente ao toque. Feedback imediato sem animação de saída.
Kind of Blue
Miles Davis
Botões primários (CTA)
active:scale-[0.96] active:opacity-80
transition: transform 75ms, opacity 75ms
Ações secundárias / chips
active:opacity-75
transition: opacity 75ms
List rows
active:bg-[#111]
transition: background 0.1s
Global (index.css)
button {
transition: transform 75ms ease,
opacity 75ms ease;
}
Transição de scroll
Header do Disco Detalhe: fundo e título fazem fade ao passar o threshold da capa.
scrollY < threshold
Dark Side of the Moon
scrollY > threshold
Dark Side of the Moon
Transição
transition-all duration-200
Propriedades animadas
background (transparent → #0f0f0f)
border-bottom (none → 1px solid #1a1a1a)
opacity do título (0 → 1)
background dos botões pill
(rgba(0,0,0,0.6) → transparent)
Threshold
COVER_HEIGHT − 80px
Heart burst
Aparece ao dar duplo toque na capa do Feed. Roda uma vez e desaparece.
Keyframe
@keyframes heart-burst {
0% { scale(0); opacity:1 }
40% { scale(1.5); opacity:1 }
70% { scale(1.1); opacity:1 }
100% { scale(1.1); opacity:0 }
}
animation: heart-burst 0.8s ease forwards
Gatilho
Duplo toque em < 300ms
Apenas quando não está curtido
Ícone
w-24 h-24 fill="#C8F53C"
drop-shadow-lg
Shimmer (Skeleton)
Varre da esquerda para direita infinitamente enquanto o conteúdo carrega.
Keyframe
@keyframes shimmer {
0% { translateX(-100%) }
100% { translateX(100%) }
}
animation: shimmer 1.4s ease-in-out infinite
Gradiente
linear-gradient(90deg,
transparent 0%,
rgba(255,255,255,0.04) 50%,
transparent 100%)
Toast entrada / saída
Aparece em 0,25s e sai com fade + escala 220ms antes de ser removido do DOM.
Entrada (toast-in)
Saída (toast-out)
Entrada
@keyframes toast-in {
from { opacity:0; scale(0.96); translateY(8px) }
to { opacity:1; scale(1); translateY(0) }
}
duration: 0.25s ease
Saída
@keyframes toast-out {
from { opacity:1; scale(1); translateY(0) }
to { opacity:0; scale(0.96); translateY(6px) }
}
duration: 0.22s ease · dispara 220ms
antes de remover o nó do DOM
Haptic feedback
Vibração do dispositivo reforça ações sem saída visual. Usa navigator.vibrate() (Android + alguns PWAs). iOS ignora silenciosamente.
Utility
// src/utils/haptic.ts
type HapticType =
'light' | 'medium' | 'success' | 'error'
export function haptic(type = 'light') {
try { navigator.vibrate?.(PATTERNS[type]) }
catch { /* noop */ }
}
Onde usar
haptic('light') → useLike toggleLike
haptic('light') → BottomNav tabs
haptic('light') → FAB actions
haptic('medium') → toggleFollow
Input focus
Toque no campo abre com glow sutil em verde. Border-color e box-shadow transitam juntos.
Sem foco
Com foco
CSS global (index.css)
input:focus, textarea:focus {
box-shadow: 0 0 0 3px
rgba(200, 245, 60, 0.07);
transition:
border-color 0.15s ease,
box-shadow 0.15s ease;
}
Border no focus
rgba(200, 245, 60, 0.5)
(definido via Tailwind focus:border-[...]
ou CSS override)
Resumo de durações
| Animação | Duração | Easing | Onde |
|---|---|---|---|
| Active press | 75ms | ease | Todos os botões (global CSS) |
| List row press | 100ms | ease | List items |
| Toast entrada | 250ms | ease | ToastProvider |
| Toast saída | 220ms | ease forwards | ToastProvider (3,3s após exibir) |
| Input focus glow | 150ms | ease | Todos os inputs |
| Tab dot indicator | 300ms | ease | Bottom nav tab ativa |
| Header scroll | 200ms | ease | Disco Detalhe |
| Heart burst | 800ms | ease forwards | Feed card |
| Shimmer | 1.4s | ease-in-out ∞ | Skeleton |