アバター
stimeo--avatar
ユーザー画像を表示し、読み込みに失敗したらフォールバックへ切り替える。
stimeo--avatar コントローラに専用の APG パターンはなく、非テキストコンテンツの指針(WCAG 1.1.1)に従う。内部 <img> の load / error を監視し、画像が失敗したときや src 未指定のときは利用側が用意したフォールバックへ切り替え、読み込み中 / 成功 / 失敗を data-state で公開する。アクセシブル名はコンテナ(role="img" + aria-label)が一元管理し、<img> とフォールバックは aria-hidden にするため、どちら側が見えていても支援技術は名前を一度だけ読み上げる。ライブラリは振る舞いのみを提供し、形状・サイズ・色は Playground が所有する。イニシャル生成や色割り当てはスコープ外。
<%# Markup for the avatar demo.
The accessible name is owned solely by the container (role="img" + aria-label);
the inner <img> and fallback use aria-hidden="true" to avoid double announcement.
The library only detects load/error and toggles display (data-state); shape,
color, and size are drawn by demo.css. %>
<div class="avatar-demo">
<%# 1. An avatar that loads successfully. The src is a self-contained data-URI SVG that
draws a person silhouette (a picture, not letters) so the "loaded image" state reads
clearly different from the initials fallback below. %>
<figure class="avatar-demo__item">
<span
class="avatar"
data-controller="stimeo--avatar"
role="img"
aria-label="<%= t("components.avatar.demo.name_ok") %>"
data-stimeo--avatar-src-value="data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20width='96'%20height='96'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%20x1='0'%20y1='0'%20x2='0'%20y2='1'%3E%3Cstop%20offset='0'%20stop-color='%236366f1'/%3E%3Cstop%20offset='1'%20stop-color='%234338ca'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect%20width='96'%20height='96'%20fill='url(%23g)'/%3E%3Ccircle%20cx='48'%20cy='38'%20r='17'%20fill='%23eef2ff'/%3E%3Cpath%20d='M20%2084%20a28%2028%200%200%201%2056%200%20Z'%20fill='%23eef2ff'/%3E%3C/svg%3E">
<img
class="avatar__image"
alt=""
aria-hidden="true"
data-stimeo--avatar-target="image"
data-action="load->stimeo--avatar#onLoad error->stimeo--avatar#onError">
<span
class="avatar__fallback"
aria-hidden="true"
hidden
data-stimeo--avatar-target="fallback"
>AU</span>
</span>
<figcaption><%= t("components.avatar.demo.caption_ok") %></figcaption>
</figure>
<%# 2. An avatar that fails to load (nonexistent path → fallback initials). The browser
logs an expected 404 for the missing image — that is the point: it triggers the error
→ fallback path the library handles. %>
<figure class="avatar-demo__item">
<span
class="avatar"
data-controller="stimeo--avatar"
role="img"
aria-label="<%= t("components.avatar.demo.name_fallback") %>"
data-stimeo--avatar-src-value="/this-image-does-not-exist.jpg">
<img
class="avatar__image"
alt=""
aria-hidden="true"
data-stimeo--avatar-target="image"
data-action="load->stimeo--avatar#onLoad error->stimeo--avatar#onError">
<span
class="avatar__fallback"
aria-hidden="true"
hidden
data-stimeo--avatar-target="fallback"
>JD</span>
</span>
<figcaption><%= t("components.avatar.demo.caption_fallback") %></figcaption>
</figure>
</div>
/* Presentation CSS for avatar. The library only detects load/error and toggles
data-state / hidden, so shape, color, size, and the fallback look are all here. */
.avatar-demo {
display: flex;
gap: 2rem;
flex-wrap: wrap;
}
.avatar-demo__item {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
margin: 0;
/* Cap the column width so a longer caption wraps under the avatar (centered)
instead of stretching the item and breaking the row layout. */
max-width: 12rem;
font-size: 0.85rem;
color: var(--color-text-muted);
text-align: center;
}
.avatar-demo__item figcaption {
text-wrap: balance;
}
/* The container owns the circular clip; the inner image / fallback are layered inside. */
.avatar {
position: relative;
display: inline-grid;
place-items: center;
width: 96px;
height: 96px;
border-radius: 50%;
overflow: hidden;
background: var(--surface-subtle);
box-shadow: 0 0 0 2px var(--surface-subtle), 0 1px 4px rgba(15, 23, 42, 0.2);
}
.avatar__image {
width: 100%;
height: 100%;
object-fit: cover;
}
/* Fallback initials. Visible only once the hidden attribute is removed. */
.avatar__fallback {
font-size: 2rem;
font-weight: 600;
color: var(--white);
background: var(--slate-500);
width: 100%;
height: 100%;
display: grid;
place-items: center;
}
/* data-state is set by the library. Example: a subtle pulse while loading. */
.avatar[data-state="loading"] {
animation: avatar-pulse 1.2s ease-in-out infinite;
}
@keyframes avatar-pulse {
50% {
opacity: 0.6;
}
}
@media (prefers-reduced-motion: reduce) {
.avatar[data-state="loading"] {
animation: none;
}
}
このデモに固有の消費側 JS はありません(挙動はコントローラが担います)。
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。 共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--avatar"
ターゲット
| 名前 | 説明 | 属性 |
|---|---|---|
image
必須
|
読み込み成否を監視し、成功時に表示する<img>要素。 |
data-stimeo--avatar-target="image" |
fallback
|
画像失敗時やsrc未指定時に表示する代替要素。 | data-stimeo--avatar-target="fallback" |
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
src
|
<img>に適用する画像ソース。指定時はこれが優先される(既定は空で、マークアップ側のsrcを尊重)。 |
data-stimeo--avatar-src-value |
アクション
| 名前 | 説明 | アクション |
|---|---|---|
onError
|
画像読み込み失敗時に代替へ切り替え、errorイベントを発火する。 | stimeo--avatar#onError |
onLoad
|
画像の読み込み成功時に画像を表示する。 | stimeo--avatar#onLoad |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
error
|
画像読み込みが失敗すると発火する。detailに試行したsrcを含む。 | stimeo--avatar:error |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
data-state |
コンテナ | "loading" / "loaded" / "error"。 |
hidden |
画像 / フォールバック | 表示している側のみ可視。 |