玻璃提示条

banner · glass-apple · 灵感来源:GOV.UK notification banner / Polaris banner / USWDS alert

常驻不自动消失,× 手动关闭

有 3 个组件待复核

这一批改的是浮层定位。

已保存

复用卡

Notification Banner · 玻璃提示条 · 提示条 / Glass

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

做一个 Notification Banner(玻璃提示条):
- 页面级状态:常驻、不自动消失,处理完或手动关闭才走
- warning 用 role=alert,info / success 用 role=status
- 结构三段:图标 + 标题(发生了什么)+ 正文(该怎么做)+ 可选动作,动作只有一个
- 严重度三档,错误与警告共用琥珀色
- 可关时按钮带 aria-label=关闭提示:{标题}
用这些 CSS 变量:--color-info-bd --color-info-bg --color-info-fg --color-success-bd --color-success-bg --color-success-fg --color-text --color-text-muted --color-text-subtle --color-warning-bd --color-warning-bg --color-warning-fg --glass-inset
灵感参考:GOV.UK notification banner / Polaris banner / USWDS alert

参数(CSS 变量)

--color-info-bd --color-info-bg --color-info-fg --color-success-bd --color-success-bg --color-success-fg --color-text --color-text-muted --color-text-subtle --color-warning-bd --color-warning-bg --color-warning-fg --glass-inset

必缺状态

(无必缺状态)

整页场景

安装(一条链接)

参考(本站可核对)

调研笔记

玻璃提示条

  • 页面级状态,不是 toast:常驻、不自动消失,用户处理完或手动关闭才走(决策 17)
  • 语义按严重度分:warning 用 role="alert"(打断读屏),info / success 用 role="status"(礼貌播报)
  • 结构固定三段:图标 + 标题(一句话说清发生了什么)+ 正文(该怎么做)+ 可选动作;动作只有一个,放正文之后
  • 严重度只有三档:错误与警告共用琥珀色——和输入框的错误描边同一支色,不再开第四种语义
  • 可关时按钮要带 aria-label="关闭提示:{标题}",只有关闭按钮的图标本身没有可读文本
  • 材质签名:色底本来就半透明,再加模糊让它落在玻璃层上;inset 高光表示它是浮在页面上的一层

实现

banner/glass-apple
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)]',
    box: 'bg-[var(--color-info-bg)] border-[var(--color-info-bd)]',
  },
  success: {
    icon: CircleCheck,
    role: 'status' as const,
    fg: 'text-[var(--color-success-fg)]',
    box: 'bg-[var(--color-success-bg)] border-[var(--color-success-bd)]',
  },
  warning: {
    icon: TriangleAlert,
    role: 'alert' as const,
    fg: 'text-[var(--color-warning-fg)]',
    box: 'bg-[var(--color-warning-bg)] border-[var(--color-warning-bd)]',
  },
};

/** 玻璃提示条:色底本身是半透明的,加模糊让它落在玻璃层上;图标与标题取语义色 */
export function GlassAppleBanner({
  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-xl
        border backdrop-blur-xl shadow-[var(--glass-inset)]
        ${t.box} ${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/glass-apple
状态
done
最后提交
2026-11-01
风格轴
glass-apple
依赖 token
--color-info-bd --color-info-bg --color-info-fg --color-success-bd --color-success-bg --color-success-fg --color-text --color-text-muted --color-text-subtle --color-warning-bd --color-warning-bg --color-warning-fg --glass-inset
在工作台打开 源码与 notes 都在 src/components/banner/glass-apple/