Skip to content
Lenso UI

Skeleton 骨架屏

Skeleton 用于展示加载状态,并预览组件的预期形状。

用法

import { Skeleton } from '@lenso/ui';

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Skeleton } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  panel: {    width: 250,    display: "flex",    flexDirection: "column",    gap: 20,    borderRadius: "var(--radius-lg)",    backgroundColor: "transparent",    padding: 16,    boxShadow: "var(--shadow-panel)",  },  text: { display: "flex", flexDirection: "column", gap: 12 },  picture: { height: 128, borderRadius: "var(--radius-lg)" },  short: { height: 12, width: "60%", borderRadius: "var(--radius-lg)" },  long: { height: 12, width: "80%", borderRadius: "var(--radius-lg)" },  shortest: { height: 12, width: "40%", borderRadius: "var(--radius-lg)" },});export function Basic() {  return (    <div {...stylex.props(styles.panel)}>      <Skeleton xstyle={styles.picture} />      <div {...stylex.props(styles.text)}>        <Skeleton xstyle={styles.short} />        <Skeleton xstyle={styles.long} />        <Skeleton xstyle={styles.shortest} />      </div>    </div>  );}export default Basic;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

示例

文本内容

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Skeleton } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  text: { width: "100%", maxWidth: 448, display: "flex", flexDirection: "column", gap: 12 },  line: { height: 16, borderRadius: "var(--radius)" },  full: { width: "100%" },  five: { width: "83.333333%" },  four: { width: "66.666667%" },  half: { width: "50%" },});export function TextContent() {  return (    <div {...stylex.props(styles.text)}>      <Skeleton xstyle={[styles.line, styles.full]} />      <Skeleton xstyle={[styles.line, styles.five]} />      <Skeleton xstyle={[styles.line, styles.four]} />      <Skeleton xstyle={[styles.line, styles.full]} />      <Skeleton xstyle={[styles.line, styles.half]} />    </div>  );}export default TextContent;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

用户资料

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Skeleton } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  row: { display: "flex", alignItems: "center", gap: 12 },  avatar: { height: 40, width: 40, flexShrink: 0, borderRadius: "50%" },  text: { flex: 1, display: "flex", flexDirection: "column", gap: 8 },  name: { height: 12, width: 144, borderRadius: "var(--radius-lg)" },  detail: { height: 12, width: 96, borderRadius: "var(--radius-lg)" },});export function UserProfile() {  return (    <div {...stylex.props(styles.row)}>      <Skeleton xstyle={styles.avatar} />      <div {...stylex.props(styles.text)}>        <Skeleton xstyle={styles.name} />        <Skeleton xstyle={styles.detail} />      </div>    </div>  );}export default UserProfile;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

列表项

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Skeleton } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  list: { width: "100%", maxWidth: 384, display: "flex", flexDirection: "column", gap: 16 },  row: { display: "flex", alignItems: "center", gap: 12 },  avatar: { height: 40, width: 40, flexShrink: 0, borderRadius: "var(--radius-lg)" },  text: { flex: 1, display: "flex", flexDirection: "column", gap: 8 },  full: { height: 12, width: "100%", borderRadius: "var(--radius)" },  short: { height: 12, width: "80%", borderRadius: "var(--radius)" },});export function List() {  return (    <div {...stylex.props(styles.list)}>      {Array.from({ length: 3 }, (_, index) => (        <div key={index} {...stylex.props(styles.row)}>          <Skeleton xstyle={styles.avatar} />          <div {...stylex.props(styles.text)}>            <Skeleton xstyle={styles.full} />            <Skeleton xstyle={styles.short} />          </div>        </div>      ))}    </div>  );}export default List;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

网格

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Skeleton } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  grid: {    display: "grid",    width: "100%",    maxWidth: 576,    gridTemplateColumns: "repeat(3, minmax(0, 1fr))",    gap: 16,  },  bone: { height: 96, borderRadius: "var(--radius-xl)" },});export function Grid() {  return (    <div {...stylex.props(styles.grid)}>      <Skeleton xstyle={styles.bone} />      <Skeleton xstyle={styles.bone} />      <Skeleton xstyle={styles.bone} />    </div>  );}export default Grid;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

单次闪烁

同步的 shimmer 效果会一次性扫过所有骨架元素。在父容器上应用 skeleton--shimmer 类,并将子骨架的 animationType 设为 "none"。

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Skeleton } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  grid: {    display: "grid",    width: "100%",    maxWidth: 576,    gridTemplateColumns: "repeat(3, minmax(0, 1fr))",    gap: 16,    borderRadius: "var(--radius-xl)",    backgroundColor: "transparent",  },  bone: { height: 96, borderRadius: "var(--radius-xl)" },});export function SingleShimmer() {  return (    <Skeleton xstyle={styles.grid}>      <Skeleton animationType="none" xstyle={styles.bone} />      <Skeleton animationType="none" xstyle={styles.bone} />      <Skeleton animationType="none" xstyle={styles.bone} />    </Skeleton>  );}export default SingleShimmer;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

动画类型

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Skeleton } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const styles = stylex.create({  grid: {    display: "grid",    width: "100%",    maxWidth: 576,    gridTemplateColumns: {      default: "1fr",      "@media (min-width: 640px)": "repeat(2, minmax(0, 1fr))",      "@media (min-width: 1024px)": "repeat(3, minmax(0, 1fr))",    },    gap: 24,  },  column: { display: "flex", flexDirection: "column", gap: 8 },  label: {    fontSize: 12,    lineHeight: "16px",    color: "var(--muted)",    overflow: "hidden",    textOverflow: "ellipsis",    whiteSpace: "nowrap",  },  panel: {    display: "flex",    flexDirection: "column",    gap: 12,    borderRadius: "var(--radius-lg)",    backgroundColor: "transparent",    padding: 16,    boxShadow: "var(--shadow-panel)",  },  picture: { height: 80, borderRadius: "var(--radius-lg)" },  short: { height: 12, width: "60%", borderRadius: "var(--radius-lg)" },  long: { height: 12, width: "80%", borderRadius: "var(--radius-lg)" },});export function AnimationTypes() {  return (    <div {...stylex.props(styles.grid)}>      {(["shimmer", "pulse", "none"] as const).map((animationType) => (        <div key={animationType} {...stylex.props(styles.column)}>          <p {...stylex.props(styles.label)}>            {animationType === "shimmer" ? "Shimmer" : animationType === "pulse" ? "Pulse" : "None"}          </p>          <div {...stylex.props(styles.panel)}>            <Skeleton animationType={animationType} xstyle={styles.picture} />            <Skeleton animationType={animationType} xstyle={styles.short} />            <Skeleton animationType={animationType} xstyle={styles.long} />          </div>        </div>      ))}    </div>  );}export default AnimationTypes;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

自定义样式

Tailwind CSS

此预览复用英文版适配,不代表中文源示例已完成本地实现。

"use client";// Adapted from HeroUI v3.2.6 (Apache-2.0).import { Skeleton } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";const shine = stylex.keyframes({  from: { translate: "-100% 0" },  to: { translate: "100% 0" },});const styles = stylex.create({  panel: {    width: 250,    display: "flex",    flexDirection: "column",    gap: 20,    borderRadius: "var(--radius-xl)",    border: "1px solid color-mix(in oklab, var(--border) 80%, transparent)",    backgroundColor: "var(--surface)",    padding: 16,    boxShadow:      "0 1px 2px 0 rgb(0 0 0 / .05), 0 0 0 1px light-dark(rgb(0 0 0 / .05), rgb(255 255 255 / .1))",  },  text: { display: "flex", flexDirection: "column", gap: 12 },  bone: {    borderRadius: "var(--radius-lg)",    backgroundColor: "light-dark(oklch(92.2% 0 0 / .9), oklch(26.9% 0 0 / .9))",    "::after": {      position: "absolute",      inset: 0,      width: "100%",      height: "100%",      content: '""',      translate: "-100% 0",      backgroundImage:        "linear-gradient(120deg, transparent 10%, light-dark(rgb(255 255 255 / .3), rgb(255 255 255 / .1)) 45%, light-dark(rgb(255 255 255 / .1), rgb(255 255 255 / .04)) 55%, transparent 90%)",      animationName: { default: shine, "@media (prefers-reduced-motion: reduce)": "none" },      animationDuration: "3s",      animationTimingFunction: "ease-in-out",      animationIterationCount: "infinite",      pointerEvents: "none",    },  },  picture: { height: 128 },  short: { height: 12, width: "60%" },  long: { height: 12, width: "80%" },  shortest: { height: 12, width: "40%" },});export function CustomStyles() {  return (    <div {...stylex.props(styles.panel)}>      <Skeleton xstyle={[styles.bone, styles.picture]} />      <div {...stylex.props(styles.text)}>        <Skeleton xstyle={[styles.bone, styles.short]} />        <Skeleton xstyle={[styles.bone, styles.long]} />        <Skeleton xstyle={[styles.bone, styles.shortest]} />      </div>    </div>  );}export default CustomStyles;

Local adaptation source above. Derived from HeroUI v3.2.6 source.

全局 CSS

若要自定义组件类,可使用 @layer components 指令。了解更多。

@layer components {  /* Base skeleton styles */  .skeleton {    @apply bg-surface-secondary/50; /* Change base background */  }
  /* Shimmer animation gradient */  .skeleton--shimmer:before {    @apply viasurface; /* Change shimmer gradient color */  }
  /* Pulse animation */  .skeleton--pulse {    @apply animate-pulse opacity-75; /* Customize pulse animation */  }
  /* No animation variant */  .skeleton--none {    @apply opacity-50; /* Style for static skeleton */  }}

全局动画配置

可在全局 CSS 中通过 --skeleton-animation 变量为所有 Skeleton 组件设置默认动画类型:

/* 全局 CSS 文件 */:root {  /* 可选值:shimmer、pulse、none */  --skeleton-animation: pulse;}
/* 也可为浅色/深色主题设置不同值 */.light, [data-theme="light"] {  --skeleton-animation: shimmer;}
.dark, [data-theme="dark"] {  --skeleton-animation: pulse;}

在单个组件上指定 animationType 属性会覆盖此全局设置。

样式参考

HeroUI 遵循 BEM 方法论,确保组件变体与状态可复用且易于自定义。

全局动画配置

你可以通过在应用中定义 --skeleton-animation CSS 变量,为所有 Skeleton 设置默认动画类型:

/* In your global CSS file */:root {  /* Possible values: shimmer, pulse, none */  --skeleton-animation: pulse;}
/* You can also set different values for light/dark themes */.light, [data-theme="light"] {  --skeleton-animation: shimmer;}
.dark, [data-theme="dark"] {  --skeleton-animation: pulse;}

在单个组件上指定 animationType 时,会覆盖上述全局设置。

CSS 类

Skeleton 使用以下 CSS 类(查看源码样式):

基础类 [!toc]

.skeleton - 包含背景与圆角等基础骨架样式

动画变体类 [!toc]

  • .skeleton--shimmer - 添加带渐变效果的闪烁动画(默认)
  • .skeleton--pulse - 使用 Tailwind 的 animate-pulse 添加脉冲动画
  • .skeleton--none - 无动画的静态骨架

动画

Skeleton 支持三种动画类型,视觉效果各不相同:

闪烁动画 [!toc]

闪烁效果会在骨架元素上移动渐变:

.skeleton--shimmer:before {  @apply animate-skeleton via-surface-3 absolute inset-0 -translate-x-full         bg-gradient-to-r from-transparent to-transparent content-[''];}

闪烁动画在主题中通过以下方式定义:

@theme inline {  --animate-skeleton: skeleton 2s linear infinite;
  @keyframes skeleton {    100% {      transform: translateX(200%);    }  }}

脉冲动画 [!toc]

脉冲动画使用 Tailwind 内置的 animate-pulse 工具类:

.skeleton--pulse {  @apply animate-pulse;}

无动画 [!toc]

用于不需要任何动画的静态骨架:

.skeleton--none {  /* No animation styles applied */}

API 参考

Skeleton

Prop类型默认值描述
animationType"shimmer" | "pulse" | "none""shimmer" 或 CSS 变量Skeleton 的动画类型;也可通过 --skeleton-animation CSS 变量进行全局配置
classNamestring-额外的 CSS 类名

相关组件