Loading...
Loading...
Compare original and translation side by side
| Wait Duration | Best Pattern | Use for |
|---|---|---|
| Short (< 1s) | Inline Spinner / Loader | Button actions, small updates, quick data fetches |
| Medium (1s – 3s) | Skeleton Screen | Cards, lists, dashboards, profile pages |
| Long (> 3s) | Determinate Progress Bar | File uploads, complex exports, heavy processing |
| Full Page | Staggered Entry / Animated Sections | Initial app load, hero sections, immersive transitions |
| 等待时长 | 最佳模式 | 适用场景 |
|---|---|---|
| 短(<1秒) | Inline Spinner / 加载器 | 按钮操作、小型更新、快速数据获取 |
| 中(1秒–3秒) | Skeleton骨架屏 | 卡片、列表、仪表盘、个人资料页 |
| 长(>3秒) | 确定性进度条 | 文件上传、复杂导出、重型处理任务 |
| 整页加载 | 交错入场/动画区块 | 应用初始加载、首页英雄区、沉浸式过渡 |
disabled@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner {
animation: spin 800ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
}disabled@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.spinner {
animation: spin 800ms cubic-bezier(0.4, 0, 0.2, 1) infinite;
}.skeleton {
background: var(--color-grey-100);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.5) 50%,
rgba(255, 255, 255, 0) 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}.skeleton {
background: var(--color-grey-100);
background-image: linear-gradient(
90deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0.5) 50%,
rgba(255, 255, 255, 0) 100%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}--color-grey-100grey-50--color-grey-100grey-50.section {
opacity: 0;
transform: translateY(10px);
animation: slide-up 400ms ease-out forwards;
}
/* Stagger by index */
.section:nth-child(1) { animation-delay: 100ms; }
.section:nth-child(2) { animation-delay: 200ms; }
.section:nth-child(3) { animation-delay: 300ms; }
@keyframes slide-up {
to { opacity: 1; transform: translateY(0); }
}.section {
opacity: 0;
transform: translateY(10px);
animation: slide-up 400ms ease-out forwards;
}
/* Stagger by index */
.section:nth-child(1) { animation-delay: 100ms; }
.section:nth-child(2) { animation-delay: 200ms; }
.section:nth-child(3) { animation-delay: 300ms; }
@keyframes slide-up {
to { opacity: 1; transform: translateY(0); }
}prefers-reduced-motionprefers-reduced-motion| Anti-pattern | Problem | Fix |
|---|---|---|
| A global spinner that blocks the whole app | High frustration, user cannot browse other areas | Use contextual loaders or skeletons |
| Skeletons that don't match the final layout | Massive layout shift (CLS) when data arrives | Match shapes and sizes exactly |
| Too many spinners on one page | Visual noise, feels like the whole app is broken | Group loading states into a single container skeleton |
| Faster-than-light skeletons | Shimmer animation that is too fast or high-contrast | Keep shimmer slow (1.5s+) and very subtle |
| 反模式 | 问题 | 解决方案 |
|---|---|---|
| 阻塞整个应用的全局Spinner | 用户体验极差,无法浏览其他区域 | 使用上下文加载器或骨架屏 |
| 与最终布局不匹配的骨架屏 | 数据加载完成时出现严重布局偏移(CLS) | 完全匹配形状和尺寸 |
| 单页存在过多Spinner | 视觉噪音,让用户感觉整个应用出问题 | 将加载状态整合到单个容器骨架中 |
| 过快的骨架屏动画 | 闪烁动画过快或对比度太高 | 保持闪烁动画缓慢(1.5秒以上)且非常细微 |