拟态头像

avatar · neumorphic · 灵感来源:Neumorphism(同色浮起的圆形)

三档尺寸(无图时取名字)

张三李四王小明

状态点:颜色之外还有读屏文案

Ada Lovelace在线Alan Turing忙碌Grace Hopper离线

图片加载失败回落首字母

Lin

复用卡

Avatar with fallback · 拟态头像 · 头像 / Neumorphic

规格 · 术语提示词(给 AI 用)

做一个 Avatar with fallback(拟态头像):
- 同一份 props(name / src / size / status)
- 同色底 + 外投影,没有边框;投影只在外圈,不糊住图片
- 首字母回落与状态读屏文案与玻璃头像一致
用这些 CSS 变量:--color-success-fg --color-surface --color-text-muted --color-text-subtle --color-warning-fg --neuro-bg --neuro-dark --neuro-light --text-2xs --text-xs
灵感参考:Neumorphism(同色浮起的圆形)

参数(CSS 变量)

--color-success-fg --color-surface --color-text-muted --color-text-subtle --color-warning-fg --neuro-bg --neuro-dark --neuro-light --text-2xs --text-xs

必缺状态

(无必缺状态)

整页场景

(尚未接进整页场景)

安装(一条链接)

调研笔记

拟态头像

  • 与玻璃头像同一份 props(name / src / size / status)
  • 同色底 + 外投影,没有边框;图片裁进圆形时投影仍在外圈,不糊住人脸
  • 首字母回落与状态点读屏文案与玻璃头像一致
  • 限制:白色之外的同色面板才能读出凸起,彩色背景上改用极简

实现

avatar/neumorphic
import { useState } from 'react';

interface Props {
  name: string;
  src?: string;
  size?: 'sm' | 'md' | 'lg';
  status?: 'online' | 'busy' | 'offline';
  className?: string;
}

const SIZE = {
  sm: 'size-6 text-[length:var(--text-2xs)]',
  md: 'size-8 text-[length:var(--text-xs)]',
  lg: 'size-11 text-sm',
};

const STATUS = {
  online: 'bg-[var(--color-success-fg)]',
  busy: 'bg-[var(--color-warning-fg)]',
  offline: 'bg-[var(--color-text-subtle)]',
};

const STATUS_LABEL = {
  online: '在线',
  busy: '忙碌',
  offline: '离线',
};

/** 中文在小尺寸只留姓,lg 才放两个字;西文取首字母缩写 */
function initials(name: string, size: 'sm' | 'md' | 'lg') {
  const parts = name.trim().split(/\s+/);
  if (parts.length > 1) return (parts[0][0] + parts[1][0]).toUpperCase();
  if (/[\u4e00-\u9fa5]/.test(name)) return size === 'lg' ? name.slice(0, 2) : name.slice(0, 1);
  return name.slice(0, 2).toUpperCase();
}

export function NeumorphicAvatar({ name, src, size = 'md', status, className = '' }: Props) {
  const [broken, setBroken] = useState(false);
  const showImage = src && !broken;

  return (
    <span className={`relative inline-flex shrink-0 ${className}`}>
      <span
        className={`
          ${SIZE[size]} rounded-full overflow-hidden relative
          inline-flex items-center justify-center
          bg-[var(--neuro-bg)] border border-transparent
          shadow-[2px_2px_4px_var(--neuro-dark),-2px_-2px_4px_var(--neuro-light)]
          text-[var(--color-text-muted)] font-medium
        `}
      >
        <span aria-hidden="true" className="absolute inset-0 grid place-items-center">
          {initials(name, size)}
        </span>
        {showImage && (
          <img
            src={src}
            alt=""
            className="absolute inset-0 size-full object-cover"
            onError={() => setBroken(true)}
          />
        )}
        <span className="sr-only">{name}</span>
      </span>

      {status && (
        <>
          <span
            className={`
              absolute -bottom-0 -right-0 size-2 rounded-full
              ring-2 ring-[var(--color-surface)]
              ${STATUS[status]}
            `}
            aria-hidden="true"
          />
          <span className="sr-only">{STATUS_LABEL[status]}</span>
        </>
      )}
    </span>
  );
}

元信息

标识
avatar/neumorphic
状态
done
最后提交
2026-11-01
风格轴
neumorphic
依赖 token
--color-success-fg --color-surface --color-text-muted --color-text-subtle --color-warning-fg --neuro-bg --neuro-dark --neuro-light --text-2xs --text-xs
在工作台打开 源码与 notes 都在 src/components/avatar/neumorphic/