360 Secure SkillHub
360 安全技能中心

Loading State Patterns

核心功能包含基础骨架、多类内容布局骨架(卡片、表格等)、主题适配骨架、渐进加载组件、进度条、流式内容指示器等,附完整React+CSS可复用代码,支持自定义样式动画。典型场景:构建内容区加载占位、流式内容微光效果、渐进式加载体验、主题化加载UX。输出直接可用的组件代码,可修改类名、属性适配自身设计系统。

wpank
wpank
热度 56.04
最近更新 2026-05-11 16:01:18
分类:开发依赖:纯提示词应用场景:代码生成

安装技能

复制下面的话术发给你的 Agent,它会通过 secure-skills 检索并安装该技能。

执行命令安装此 skill: npx secure-skills-cli add loading-state-patterns
Metadata
name
loading-state-patterns
model
fast
description
Patterns for skeleton loaders, shimmer effects, and loading states that match design system aesthetics. Covers skeleton components, shimmer animations, and progressive loading. Use when building polished loading experiences. Triggers on skeleton, loading state, shimmer, placeholder, loading animation.

Loading State Patterns

Build loading states that feel intentional and match your design system aesthetic.


When to Use

  • Building skeleton loaders for content areas
  • Need shimmer effects for streaming content
  • Want progressive loading experiences
  • Building premium loading UX

Pattern 1: Skeleton Base

import { cn } from '@/lib/utils';

interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement> {
  shimmer?: boolean;
}

export function Skeleton({ className, shimmer = true, ...props }: SkeletonProps) {
  return (
    <div
      className={cn(
        'rounded-md bg-muted',
        shimmer && 'animate-shimmer bg-gradient-to-r from-muted via-muted-foreground/10 to-muted bg-[length:200%_100%]',
        className
      )}
      {...props}
    />
  );
}

CSS Animation

@keyframes shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

.animate-shimmer {
  animation: shimmer 1.5s ease-in-out infinite;
}

Pattern 2: Content Skeleton Layouts

Card Skeleton

export function CardSkeleton() {
  return (
    <div className="rounded-lg border bg-card p-4 space-y-3">
      <div className="flex items-center gap-3">
        <Skeleton className="size-10 rounded-full" />
        <div className="space-y-1.5 flex-1">
          <Skeleton className="h-4 w-1/3" />
          <Skeleton className="h-3 w-1/4" />
        </div>
      </div>
      <Skeleton className="h-20 w-full" />
      <div className="flex gap-2">
        <Skeleton className="h-8 w-20" />
        <Skeleton className="h-8 w-20" />
      </div>
    </div>
  );
}

Table Row Skeleton

export function TableRowSkeleton({ columns = 4 }: { columns?: number }) {
  return (
    <tr>
      {Array.from({ length: columns }).map((_, i) => (
        <td key={i} className="p-3">
          <Skeleton className="h-4 w-full" />
        </td>
      ))}
    </tr>
  );
}

Metric Skeleton

export function MetricSkeleton() {
  return (
    <div className="space-y-2">
      <Skeleton className="h-3 w-16" />
      <Skeleton className="h-8 w-24" />
    </div>
  );
}

Pattern 3: Design System Skeleton

Match skeleton to your aesthetic:

// For retro-futuristic theme
export function CyberSkeleton({ className, ...props }: SkeletonProps) {
  return (
    <div
      className={cn(
        'rounded-md bg-tone-cadet/30',
        'animate-pulse-glow',
        'border border-tone-cyan/10',
        className
      )}
      {...props}
    />
  );
}

// CSS
@keyframes pulse-glow {
  0%, 100% { opacity: 0.4; box-shadow: 0 0 0 0 rgba(var(--tone-cyan), 0); }
  50% { opacity: 0.6; box-shadow: 0 0 8px 0 rgba(var(--tone-cyan), 0.1); }
}

Pattern 4: Progressive Loading

Show content as it loads:

interface ProgressiveLoadProps {
  isLoading: boolean;
  skeleton: React.ReactNode;
  children: React.ReactNode;
}

export function ProgressiveLoad({
  isLoading,
  skeleton,
  children,
}: ProgressiveLoadProps) {
  return (
    <div className="relative">
      {isLoading ? (
        skeleton
      ) : (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          transition={{ duration: 0.2 }}
        >
          {children}
        </motion.div>
      )}
    </div>
  );
}

Pattern 5: Streaming Content Indicator

For AI/LLM content that streams:

export function StreamingIndicator() {
  return (
    <div className="flex items-center gap-1">
      {[0, 1, 2].map((i) => (
        <motion.div
          key={i}
          className="size-1.5 rounded-full bg-primary"
          animate={{ scale: [1, 1.3, 1], opacity: [0.5, 1, 0.5] }}
          transition={{
            duration: 0.8,
            repeat: Infinity,
            delay: i * 0.15,
          }}
        />
      ))}
    </div>
  );
}

Pattern 6: Loading Progress Bar

interface LoadingProgressProps {
  progress?: number; // 0-100, undefined = indeterminate
}

export function LoadingProgress({ progress }: LoadingProgressProps) {
  const isIndeterminate = progress === undefined;

  return (
    <div className="h-1 w-full bg-muted overflow-hidden rounded-full">
      <div
        className={cn(
          'h-full bg-primary transition-all duration-300',
          isIndeterminate && 'animate-indeterminate'
        )}
        style={!isIndeterminate ? { width: `${progress}%` } : undefined}
      />
    </div>
  );
}

// CSS
@keyframes indeterminate {
  0% { transform: translateX(-100%); width: 50%; }
  100% { transform: translateX(200%); width: 50%; }
}

.animate-indeterminate {
  animation: indeterminate 1.5s ease-in-out infinite;
}

Pattern 7: Skeleton Grid

export function GridSkeleton({ 
  count = 6, 
  columns = 3 
}: { 
  count?: number; 
  columns?: number;
}) {
  return (
    <div 
      className="grid gap-4" 
      style={{ gridTemplateColumns: `repeat(${columns}, 1fr)` }}
    >
      {Array.from({ length: count }).map((_, i) => (
        <CardSkeleton key={i} />
      ))}
    </div>
  );
}

Related Skills


NEVER Do

  • Use gray skeletons on dark themes — Match your surface colors
  • Skip shimmer animation — Static blocks look broken
  • Make skeletons exact size — Slight size variation is natural
  • Forget aspect ratios — Images need consistent skeleton ratios
  • Show skeleton forever — Add timeout fallbacks for errors

Quick Reference

// Basic skeleton
<Skeleton className="h-4 w-full" />

// Avatar skeleton
<Skeleton className="size-10 rounded-full" />

// Text lines
<div className="space-y-2">
  <Skeleton className="h-4 w-3/4" />
  <Skeleton className="h-4 w-1/2" />
</div>

// Card skeleton
<div className="p-4 space-y-3">
  <Skeleton className="h-6 w-1/3" />
  <Skeleton className="h-20 w-full" />
</div>