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 时,应用到子头像(及溢出计数)的变体 |
max | number | - | 最多渲染的头像子项数量;省略则显示全部 |
isGrid | boolean | false | 是否使用无叠放的换行网格布局 |
overlap | 'clip' | 'ring' | 'clip' | 叠放样式:新月裁切(默认)或 box-shadow 描边;isGrid 时忽略 |
className | string | - | 附加 CSS 类 |
children | React.ReactNode | - | 要组合的 Avatar 组件(以及可选的 AvatarGroup.Count) |
AvatarGroup.Count
由 Avatar + Avatar.Fallback 组合而成,用于溢出指示。不受 max 截断。
| Prop | 类型 | 默认值 | 描述 |
|---|---|---|---|
children | React.ReactNode | - | 计数内容(例如 +3) |
size | 'sm' | 'md' | 'lg' | - | 覆盖组的尺寸 |
color | 'default' | 'accent' | 'success' | 'warning' | 'danger' | - | 覆盖组的颜色 |
variant | 'default' | 'soft' | - | 覆盖组的变体 |
className | string | - | 附加 CSS 类 |
注意事项
- AvatarGroup 通过 React Context 仅向直接 Avatar 子组件传递
size、color与variant - 设置
max会截断可见头像并根据剩余子项自动计数;省略则显示全部 - 已知总量(例如来自服务端)时,渲染显式的
AvatarGroup.Count— 它不受max截断,并会抑制自动计数。请用Count替代已移除的 v2total isGrid会禁用叠放,改为换行网格布局overlap默认为"clip"(新月裁切);使用"ring"可改为实线描边;与isGrid同时使用时无效- 默认不加
role="group"— 当叠放有语义(指派人、在看的人)时,自行加上role="group",并配合aria-label或aria-labelledby