跳到正文
册页

展示

Avatar - 头像

一个人,画成一个圆;图片没到之前先显示缩写。

用法

TSX
import { Avatar } from '@misoto22/folio'

说明

一个人,画成一个圆。

包了 Radix,这样占位内容只在图片确实加载失败或还在加载时才出现——自己手写的 onError 切换,会在缓存回答之前的每一次渲染里都闪一下首字母。

可访问名称挂在根节点上,不在图片上。图片上的 alt 只有在 src 也在时才进得了 DOM,而首字母是 aria-hidden 的,所以一个没有照片的头像,不管 alt 写得多仔细,播报出来什么都没有——而一份照片可有可无的用户列表里,到处都是这种头像。

结构

Avatar anatomy
部件说明
圆必填Radix 的根节点:--stone 之上一条 --rule 细线,overflow 隐藏,尺寸是三个固定方块之一——28px、36px 或 48px。它带着 role="img",并把 alt 当作自己的名字,因为它是这里唯一一个始终会渲染的元素。
图片只在给了 src 时才渲染,object-cover,所以一张竖幅照片是被裁进这个圆,而不是被拉变形。它自己的 alt 是故意留空的——为这个人命名的是外面那个圆,在这里再来一个名字,就是同一个人被念了两遍。
缩写Radix 的兜底:等宽、大写、--ink-3-aa,并且 aria-hidden。图片加载期间和加载失败之后由 Radix 显示它,所以它不会像手写的 onError 替换那样每次渲染都闪一下。

实践建议

推荐

  • alt 是写给这个人的,不是写给这一行的:无论照片会不会到,那个圆都带着它,所以一份照片可有可无的列表,播报的是每一个人,而不是每隔一个人。
  • 传一份自己站得住的缩写:fallback 是必填的,因为可选的那一半是图片,而真实列表里大多数行渲染出来的都是这个元素,不是照片。
  • 让 Radix 来管这次替换,不要去动 onError——它只在图片确实失败之后才切到兜底,正是这一点让缩写不会在缓存回答之前先闪出一帧。

避免

  • 头像不是那个控件。sm 是 28px,md 是 36px,都低于 44px 的指针目标下限(WCAG 2.5.8),而根节点是一个没有任何东西让它可聚焦的 <span>——把 onClick 挂上去做出来的账号菜单,既够不到又太小,两样一起占了。
  • props 里特意没有 children:进这个圆的只有图片和兜底两样东西,而根节点是 overflow-hidden 的,所以放进去的在线状态点,会被那条把它变圆的边界裁掉。

示例

sizes

三个固定的方块——28px、36px、48px——背后都没有图片,而一份真实列表里大多数行渲染出来就是这个样子。fallback 之所以必填,正是这个理由:它是那个永远都在的一半,图片才是可选的那个。这几个尺寸没有一个是控件,而且其中两个还低于一个控件所需的 44px 指针目标。

a name beside it

让头像变得可读的那种排布:名字就印在它旁边。这里的 alt 是刻意留空的,因为旁边的文字已经说清楚这是谁了——而且 alt 只有在 src 到位时才会进到 DOM 里,缩写字母又是 aria-hidden 的,所以一个没有照片的人,这个圆圈里根本没有任何无障碍文本,无论 alt 写得多用心。

  • Henry ChenDesign systems
  • Rui WangInfrastructure

inside the control

一个把头像装在里面的账户控件,而不是在圆圈上挂一个 onClick。根节点是一个没人让它可聚焦的 span,而 sm 是 28px,低于 44px 的指针下限——所以一个被接成菜单触发器的头像,同时既键盘够不着、又太小。按钮带来了目标区域、焦点环和无障碍名称;圆圈还是一张图。

无障碍

  • alt 命名的是那个圆本身,以 role="img" 的形式,所以一个没有照片的头像照样说得出自己是谁。
  • 旁边已经印了名字时,空的 alt 才是对的:那时这个圆干脆不取任何 role,而不是取一个没有名字的 role,于是它整个离开无障碍树。
  • 缩写是 aria-hidden 的——挨着一个根节点已经给出的名字念出来,它们只是噪音。