SCALE — Build Lab
UI部品 · REACT COMPONENT

アバター(イニシャル/画像)

CATEGORYUI部品 TYPEReact Component EFFORT30〜60分 DIFFICULTY
PRIMARY CODE
tsx
function colorFromName(name: string): string {
  let h = 0;
  for (let i = 0; i < name.length; i++) h = ((h << 5) - h) + name.charCodeAt(i);
  return `hsl(${Math.abs(h) % 360}, 60%, 55%)`;
}

export function Avatar({ name, src, size = 32 }: {
  name: string; src?: string; size?: number;
}) {
  const initial = name.charAt(0).toUpperCase();
  return (
    <span aria-label={name} title={name} style={{
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      width: size, height: size, borderRadius: '50%',
      background: src ? 'transparent' : colorFromName(name),
      color: '#fff', fontSize: size * 0.4, fontWeight: 600,
      overflow: 'hidden', flexShrink: 0,
    }}>
      {src ? <img src={src} alt={name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> : initial}
    </span>
  );
}

// グループ表示
export function AvatarGroup({ users, max = 4, size = 32 }: {
  users: { name: string; src?: string }[]; max?: number; size?: number;
}) {
  const display = users.slice(0, max);
  const more = users.length - max;
  return (
    <div style={{ display: 'inline-flex' }}>
      {display.map((u, i) => (
        <div key={i} style={{ marginLeft: i === 0 ? 0 : -size * 0.3, border: '2px solid #08080a', borderRadius: '50%' }}>
          <Avatar {...u} size={size} />
        </div>
      ))}
      {more > 0 && <div style={{
        marginLeft: -size * 0.3, width: size, height: size, borderRadius: '50%',
        background: 'rgba(255,255,255,.1)', color: '#fff', display: 'inline-flex',
        alignItems: 'center', justifyContent: 'center', fontSize: size * 0.35,
        border: '2px solid #08080a',
      }}>+{more}</div>}
    </div>
  );
}
前提条件
Tailwind CSS v4TypeScript 5
USE CASES
  • 任意のダッシュボードに組み込み

アバター(イニシャル/画像)

:LiTarget: 用途

画像 or イニシャル表示のアバター。色は名前から自動算出。

:LiSparkle: 特徴

  • 画像/イニシャル切替
  • 名前から色算出
  • サイズ可変
  • グループ表示

:LiCode: コード(コピペ用)

function colorFromName(name: string): string {
  let h = 0;
  for (let i = 0; i < name.length; i++) h = ((h << 5) - h) + name.charCodeAt(i);
  return `hsl(${Math.abs(h) % 360}, 60%, 55%)`;
}

export function Avatar({ name, src, size = 32 }: {
  name: string; src?: string; size?: number;
}) {
  const initial = name.charAt(0).toUpperCase();
  return (
    <span aria-label={name} title={name} style={{
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      width: size, height: size, borderRadius: '50%',
      background: src ? 'transparent' : colorFromName(name),
      color: '#fff', fontSize: size * 0.4, fontWeight: 600,
      overflow: 'hidden', flexShrink: 0,
    }}>
      {src ? <img src={src} alt={name} style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> : initial}
    </span>
  );
}

// グループ表示
export function AvatarGroup({ users, max = 4, size = 32 }: {
  users: { name: string; src?: string }[]; max?: number; size?: number;
}) {
  const display = users.slice(0, max);
  const more = users.length - max;
  return (
    <div style={{ display: 'inline-flex' }}>
      {display.map((u, i) => (
        <div key={i} style={{ marginLeft: i === 0 ? 0 : -size * 0.3, border: '2px solid #08080a', borderRadius: '50%' }}>
          <Avatar {...u} size={size} />
        </div>
      ))}
      {more > 0 && <div style={{
        marginLeft: -size * 0.3, width: size, height: size, borderRadius: '50%',
        background: 'rgba(255,255,255,.1)', color: '#fff', display: 'inline-flex',
        alignItems: 'center', justifyContent: 'center', fontSize: size * 0.35,
        border: '2px solid #08080a',
      }}>+{more}</div>}
    </div>
  );
}

:LiHandPointer: 使い方

対象プロジェクトに該当ファイルをコピーして、props を流し込むだけ。

:LiAlertCircle: 注意事項

  • 依存パッケージを忘れず追加