组件
骨架屏 Skeleton
在内容加载期间维持页面结构,降低布局跳动与等待的不确定感。
基础示例
Loading…
pnpm dlx wui@latest add @wui/skeletonimport * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const skeletonVariants = cva("shrink-0 bg-muted", {
variants: {
shape: {
default: "rounded-md",
circle: "rounded-full",
text: "h-4 rounded-sm",
},
animation: {
pulse: "animate-pulse motion-reduce:animate-none",
none: "",
},
},
defaultVariants: {
shape: "default",
animation: "pulse",
},
})
export interface SkeletonProps
extends React.ComponentProps<"div">,
VariantProps<typeof skeletonVariants> {
/** Corner treatment for the placeholder. @default "default" */
shape?: "default" | "circle" | "text"
/** Loading animation. @default "pulse" */
animation?: "pulse" | "none"
}
/** A neutral placeholder that preserves layout while content is loading. */
function Skeleton({
className,
shape = "default",
animation = "pulse",
...props
}: SkeletonProps) {
return (
<div
data-slot="skeleton"
data-shape={shape}
data-animation={animation}
aria-hidden="true"
className={cn(skeletonVariants({ shape, animation }), className)}
{...props}
/>
)
}
export interface SkeletonTextProps extends React.ComponentProps<"div"> {
/** Number of text rows. @default 3 */
lines?: number
/** Width of the final row. @default "70%" */
lastLineWidth?: React.CSSProperties["width"]
/** Loading animation shared by every row. @default "pulse" */
animation?: "pulse" | "none"
}
/** Generates a compact stack of text-shaped skeleton rows. */
function SkeletonText({
className,
lines = 3,
lastLineWidth = "70%",
animation = "pulse",
...props
}: SkeletonTextProps) {
return (
<div
data-slot="skeleton-text"
aria-hidden="true"
className={cn("grid w-full gap-2", className)}
{...props}
>
{Array.from({ length: lines }, (_, index) => (
<Skeleton
key={index}
shape="text"
animation={animation}
className="w-full"
style={index === lines - 1 ? { width: lastLineWidth } : undefined}
/>
))}
</div>
)
}
export { Skeleton, SkeletonText, skeletonVariants }
组件作用
Skeleton 在内容尚未到达时保留其大致形状与空间,适合首屏资料、列表、图片和文字区域。骨架布局应尽量接近真实内容,但不需要复刻每个细节;操作完成后的反馈应使用 Progress 或 Message,而不是 Skeleton。
组件属性
| Prop | Type | Default | Description |
|---|---|---|---|
| shape | "default" | "circle" | "text" | default | Corner treatment for the placeholder. |
| animation | "pulse" | "none" | pulse | Loading animation. |
shape="default | circle | text" 分别适合图片与容器、头像以及文字行。SkeletonText 通过 lines 快速生成多行占位,并使用 lastLineWidth 调整最后一行长度。
事件
骨架屏是非交互元素,不触发事件,并默认对辅助技术隐藏。加载区域应在父容器设置 aria-busy="true",并通过 aria-label 或邻近状态文本说明正在加载的内容。
拓展使用
内容布局
Loading…
用真实页面使用的 Grid 或 Flex 布局组合多个 Skeleton,可以在数据到达前稳定保留版面。设置 animation="none" 可关闭脉冲效果;系统启用“减少动态效果”时,动画也会自动停用。