Skip to content
Lenso UI

AvatarGroup 头像组

以叠放或网格展示多个头像,并支持溢出计数

用法

import { AvatarGroup, Avatar } from '@lenso/ui';

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

"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { Avatar, AvatarGroup } from "@lenso/ui";import { users } from "./users";
export function Basic() {  return (    <AvatarGroup>      {users.slice(0, 4).map((user) => (        <Avatar key={user.id}>          <Avatar.Image alt={user.name} src={user.image} />          <Avatar.Fallback>            {user.name              .split(" ")              .map((name) => name[0])              .join("")}          </Avatar.Fallback>        </Avatar>      ))}    </AvatarGroup>  );}

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

组件结构

import { AvatarGroup, Avatar } from '@lenso/ui';
export default () => (  <AvatarGroup>    <Avatar>      <Avatar.Image />      <Avatar.Fallback />    </Avatar>    <AvatarGroup.Count /> {/* 可选的显式子组件 */}  </AvatarGroup>);

示例

最大数量

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

"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { Avatar, AvatarGroup } from "@lenso/ui";import { users } from "./users";
export function Max() {  return (    <AvatarGroup max={3}>      {users.map((user) => (        <Avatar key={user.id}>          <Avatar.Image alt={user.name} src={user.image} />          <Avatar.Fallback>            {user.name              .split(" ")              .map((n) => n[0])              .join("")}          </Avatar.Fallback>        </Avatar>      ))}    </AvatarGroup>  );}

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

自定义计数

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

"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { Avatar, AvatarGroup } from "@lenso/ui";import { users } from "./users";
export function Count() {  const total = 12;  return (    <AvatarGroup size="sm">      {users.slice(0, 3).map((user) => (        <Avatar key={user.id}>          <Avatar.Image alt={user.name} src={user.image} />          <Avatar.Fallback>            {user.name              .split(" ")              .map((n) => n[0])              .join("")}          </Avatar.Fallback>        </Avatar>      ))}      <AvatarGroup.Count>+{total - 3}</AvatarGroup.Count>    </AvatarGroup>  );}

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

尺寸

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

"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { Avatar, AvatarGroup } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { s } from "../card/display.stylex";import { users } from "./users";
export function Sizes() {  const sizes = [    { size: "sm", label: "Small" },    { size: "md", label: "Medium (default)" },    { size: "lg", label: "Large" },  ] as const;  return (    <div {...stylex.props(s.centeredColumn6)}>      {sizes.map(({ size, label }) => (        <div key={size} {...stylex.props(s.centeredColumn2)}>          <p {...stylex.props(s.textSm, s.muted)}>{label}</p>          <AvatarGroup size={size}>            {users.slice(0, 4).map((user) => (              <Avatar key={user.id}>                <Avatar.Image alt={user.name} src={user.image} />                <Avatar.Fallback>                  {user.name                    .split(" ")                    .map((n) => n[0])                    .join("")}                </Avatar.Fallback>              </Avatar>            ))}          </AvatarGroup>        </div>      ))}    </div>  );}

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

网格

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

"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import { Avatar, AvatarGroup } from "@lenso/ui";import { users } from "./users";
export function Grid() {  return (    <AvatarGroup isGrid max={5}>      {users.map((user) => (        <Avatar key={user.id}>          <Avatar.Image alt={user.name} src={user.image} />          <Avatar.Fallback>            {user.name              .split(" ")              .map((n) => n[0])              .join("")}          </Avatar.Fallback>        </Avatar>      ))}    </AvatarGroup>  );}

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

叠放样式(Overlap)

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

"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.import type { ReactNode } from "react";import { Avatar, AvatarGroup } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { s } from "../card/display.stylex";
const stripes = stylex.keyframes({ to: { backgroundPosition: "24px 24px" } });const styles = stylex.create({  frame: { position: "relative", display: "inline-flex", borderRadius: 12, padding: 24 },  stripes: {    pointerEvents: "none",    position: "absolute",    inset: 0,    borderRadius: 12,    backgroundColor: "color-mix(in oklab, var(--danger) 6%, var(--surface))",    backgroundImage:      "repeating-linear-gradient(-45deg, transparent 0 10px, color-mix(in oklab, var(--danger) 12%, transparent) 10px 11px, transparent 11px 21px, color-mix(in oklab, var(--danger) 28%, transparent) 21px 22px)",    backgroundSize: "24px 24px",    animationName: stripes,    animationDuration: "2.8s",    animationTimingFunction: "linear",    animationIterationCount: "infinite",  },});
function StripeBackdrop({ children }: { children: ReactNode }) {  return (    <div {...stylex.props(styles.frame)}>      <div aria-hidden {...stylex.props(styles.stripes)} />      <div {...stylex.props(s.relativeForeground)}>{children}</div>    </div>  );}function DemoGroup({ overlap }: { overlap: "clip" | "ring" }) {  return (    <AvatarGroup overlap={overlap} size="lg">      <Avatar>        <Avatar.Image          alt="John"          src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/blue.jpg"        />        <Avatar.Fallback>JD</Avatar.Fallback>      </Avatar>      <Avatar>        <Avatar.Fallback>AB</Avatar.Fallback>      </Avatar>      <Avatar>        <Avatar.Image          alt="Emily"          src="https://heroui-assets.nyc3.cdn.digitaloceanspaces.com/avatars/purple.jpg"        />        <Avatar.Fallback>EC</Avatar.Fallback>      </Avatar>      <Avatar>        <Avatar.Fallback>SM</Avatar.Fallback>      </Avatar>      <AvatarGroup.Count>+2</AvatarGroup.Count>    </AvatarGroup>  );}export function Overlap() {  return (    <div {...stylex.props(s.startColumn6)}>      <div {...stylex.props(s.column2)}>        <p {...stylex.props(s.textSm, s.muted)}>clip</p>        <StripeBackdrop>          <DemoGroup overlap="clip" />        </StripeBackdrop>      </div>      <div {...stylex.props(s.column2)}>        <p {...stylex.props(s.textSm, s.muted)}>ring</p>        <StripeBackdrop>          <DemoGroup overlap="ring" />        </StripeBackdrop>      </div>    </div>  );}

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

自定义样式

Tailwind CSS

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

"use client";// Adapted from HeroUI v3.2.6 (e385ac202b2cdb94b1bf6fa76d32c31c8259cc5e), Apache-2.0.// oxlint-disable jsx-a11y/prefer-tag-over-role -- AvatarGroup is a div-based visual group, not a form fieldset.import { Person } from "@gravity-ui/icons";import { Avatar, AvatarGroup } from "@lenso/ui";import * as stylex from "@stylexjs/stylex";import { s } from "../card/display.stylex";import { users } from "./users";
const styles = stylex.create({  pill: {    display: "inline-flex",    alignItems: "center",    gap: 10,    borderRadius: 9999,    borderWidth: 1,    borderStyle: "solid",    borderColor: {      default: "color-mix(in oklab, var(--border) 70%, transparent)",      ':is([data-theme="dark"] *)': "color-mix(in oklab, var(--border) 80%, transparent)",    },    backgroundColor: {      default: "color-mix(in oklab, var(--surface) 95%, transparent)",      ':is([data-theme="dark"] *)': "color-mix(in oklab, var(--surface) 90%, transparent)",    },    paddingBlock: 4,    paddingRight: 12,    paddingLeft: 4,    boxShadow: {      default: "0 1px 2px 0 rgb(0 0 0 / 0.05), 0 0 0 1px rgb(0 0 0 / 0.04)",      ':is([data-theme="dark"] *)':        "0 1px 2px 0 rgb(0 0 0 / 0.05), 0 0 0 1px rgb(255 255 255 / 0.1)",    },  },  group: { "--avatar-group-overlap": "0.7rem", "--avatar-group-seam": "2px" },});
export function CustomStyles() {  return (    <div {...stylex.props(styles.pill)}>      <AvatarGroup        aria-label="Assignees"        xstyle={styles.group}        overlap="clip"        role="group"        size="sm"      >        {users.slice(0, 3).map((user) => (          <Avatar key={user.id}>            <Avatar.Image alt={user.name} src={user.image} />            <Avatar.Fallback>              {user.name                .split(" ")                .map((part) => part[0])                .join("")}            </Avatar.Fallback>          </Avatar>        ))}        <Avatar>          <Avatar.Fallback>            <Person {...stylex.props(s.icon4, s.shrink0)} />          </Avatar.Fallback>        </Avatar>        <AvatarGroup.Count>+3</AvatarGroup.Count>      </AvatarGroup>      <span {...stylex.props(s.textSm, s.medium, s.foreground)}>Assignees</span>    </div>  );}

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

全局 CSS

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

@layer components {  .avatar-group {    --avatar-group-overlap: 0.75rem;    --avatar-group-seam: 2px;  }
  .avatar-group__count {    @apply font-semibold;  }}

样式参考

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

CSS 类

AvatarGroup 组件使用以下 CSS 类(查看源码样式):

基础类 [!toc]

  • .avatar-group - 头像组基础容器
  • .avatar-group--grid - 网格布局修饰符(无叠放)
  • .avatar-group--clip - 新月裁切 / 透明缝(默认叠放)
  • .avatar-group--ring - 通过 --background 的实线 box-shadow 描边
  • .avatar-group__count - 溢出计数头像

叠放布局在兄弟元素之间使用负外边距(--avatar-group-overlap,默认 0.5rem)。clip 模式对非末子头像做遮罩,并光学微调 fallback 字形;ring 模式使用 --background 细线 box-shadow 描边。

API 参考

AvatarGroup

Prop类型默认值描述
size'sm' | 'md' | 'lg''md'当子 Avatar 未指定 size 时应用到子头像(及溢出计数);默认与 Avatar 一致
color'default' | 'accent' | 'success' | 'warning' | 'danger'-当子 Avatar 未指定 color 时,应用到子头像(及溢出计数)的颜色
variant'default' | 'soft'-当子 Avatar 未指定 variant 时,应用到子头像(及溢出计数)的变体
maxnumber-最多渲染的头像子项数量;省略则显示全部
isGridbooleanfalse是否使用无叠放的换行网格布局
overlap'clip' | 'ring''clip'叠放样式:新月裁切(默认)或 box-shadow 描边;isGrid 时忽略
classNamestring-附加 CSS 类
childrenReact.ReactNode-要组合的 Avatar 组件(以及可选的 AvatarGroup.Count)

AvatarGroup.Count

由 Avatar + Avatar.Fallback 组合而成,用于溢出指示。不受 max 截断。

Prop类型默认值描述
childrenReact.ReactNode-计数内容(例如 +3)
size'sm' | 'md' | 'lg'-覆盖组的尺寸
color'default' | 'accent' | 'success' | 'warning' | 'danger'-覆盖组的颜色
variant'default' | 'soft'-覆盖组的变体
classNamestring-附加 CSS 类

注意事项

  • AvatarGroup 通过 React Context 仅向直接 Avatar 子组件传递 size、color 与 variant
  • 设置 max 会截断可见头像并根据剩余子项自动计数;省略则显示全部
  • 已知总量(例如来自服务端)时,渲染显式的 AvatarGroup.Count — 它不受 max 截断,并会抑制自动计数。请用 Count 替代已移除的 v2 total
  • isGrid 会禁用叠放,改为换行网格布局
  • overlap 默认为 "clip"(新月裁切);使用 "ring" 可改为实线描边;与 isGrid 同时使用时无效
  • 默认不加 role="group" — 当叠放有语义(指派人、在看的人)时,自行加上 role="group",并配合 aria-label 或 aria-labelledby

相关组件