拟态提示条

banner · neumorphic · 灵感来源:GOV.UK notification banner / USWDS alert

底板保持同色,语义只染图标与标题

有 3 个组件待复核

这一批改的是浮层定位。

已保存

复用卡

Notification Banner · 拟态提示条 · 提示条 / Neumorphic

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

做一个 Notification Banner(拟态提示条):
- 同一份 props 与 role 分配
- 底板同色挤出,语义色只染图标与标题——上色就不是拟态了
- 少了底色通道,严重度靠图标形状 + 文案承担,暗色下更弱
- 零边框,分层全靠外双阴影
用这些 CSS 变量:--color-info-fg --color-success-fg --color-text --color-text-muted --color-text-subtle --color-warning-fg --neuro-bg --neuro-dark --neuro-light
灵感参考:GOV.UK notification banner / USWDS alert

参数(CSS 变量)

--color-info-fg --color-success-fg --color-text --color-text-muted --color-text-subtle --color-warning-fg --neuro-bg --neuro-dark --neuro-light

必缺状态

(无必缺状态)

整页场景

(尚未接进整页场景)

安装(一条链接)

参考(本站可核对)

调研笔记

拟态提示条

  • 同一份 props 与语义口径(三档严重度、role 分配、结构三段),见玻璃那支的 notes
  • 材质签名:底板必须同色挤出,所以语义色只染图标与标题,不给底板上色——上色就不是拟态了
  • 代价要写清:少了底色这条通道,严重度只能靠图标形状 + 文案承担,暗色下更弱; 所以这一支不适合放在需要一眼分辨对错的位置,那种场合用玻璃或极简
  • 零边框:分层全部来自外双阴影

实现

banner/neumorphic
import { useState, type ReactNode } from 'react';
import { CircleCheck, Info, TriangleAlert, X } from 'lucide-react';
import { Icon } from '~/components/Icon';

interface Props {
  /** 严重度:错误与警告共用一支琥珀色(和输入框的错误描边同色) */
  tone?: 'info' | 'success' | 'warning';
  title: string;
  description?: ReactNode;
  /** 一个动作:链接或按钮,放正文之后 */
  action?: ReactNode;
  /** 可关:常驻不等于不能收,但绝不自动消失(决策 17) */
  dismissible?: boolean;
  className?: string;
}

const TONE = {
  info: { icon: Info, role: 'status' as const, fg: 'text-[var(--color-info-fg)]' },
  success: { icon: CircleCheck, role: 'status' as const, fg: 'text-[var(--color-success-fg)]' },
  warning: { icon: TriangleAlert, role: 'alert' as const, fg: 'text-[var(--color-warning-fg)]' },
};

/**
 * 拟态提示条:面板必须是同色挤出的,所以语义色只染图标与标题,
 * 不给底板上色——上色就不是拟态了。严重度靠图标形状 + 文案承担。
 */
export function NeumorphicBanner({
  tone = 'info',
  title,
  description,
  action,
  dismissible = false,
  className = '',
}: Props) {
  const [hidden, setHidden] = useState(false);
  const t = TONE[tone];

  if (hidden) return null;

  return (
    <div
      role={t.role}
      className={`
        flex items-start gap-3 px-4 py-3.5 rounded-2xl border border-transparent
        bg-[var(--neuro-bg)]
        shadow-[5px_5px_12px_var(--neuro-dark),-5px_-5px_12px_var(--neuro-light)]
        ${className}
      `}
    >
      <Icon icon={t.icon} size={16} className={`shrink-0 mt-0.5 ${t.fg}`} />
      <div className="min-w-0 flex-1">
        <p className={`text-sm font-semibold ${t.fg}`}>{title}</p>
        {(description || action) && (
          <div className="mt-1 text-sm text-[var(--color-text-muted)] leading-relaxed">
            {description}
            {action && <span className="mt-2 block">{action}</span>}
          </div>
        )}
      </div>
      {dismissible && (
        <button
          type="button"
          onClick={() => setHidden(true)}
          aria-label={`关闭提示:${title}`}
          className="press-feedback shrink-0 grid place-items-center size-6 -mr-1 rounded-full
            text-[var(--color-text-subtle)] hover:text-[var(--color-text)] cursor-pointer"
        >
          <Icon icon={X} size={14} />
        </button>
      )}
    </div>
  );
}

元信息

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