# 玻璃提示条（Notification Banner）

- 类型 / 材质：`banner` / `glass-apple`（玻璃）
- 状态：done ｜ 最后提交：2026-11-01
- 灵感来源：GOV.UK notification banner / Polaris banner / USWDS alert
- 依据标准：`/design#states` ｜ 组件页：`/showcase/component/banner/glass-apple` ｜ 条目：`/r/banner--glass-apple.json`
- 必缺状态：（无必缺状态）
- 整页场景：
  - 两步验证（`/r/scene--auth.json`）

## 规格

- 页面级状态：常驻、不自动消失，处理完或手动关闭才走
- 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`

## Props 契约

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `tone` | `'info' | 'success' | 'warning'` | 可选 | 严重度：错误与警告共用一支琥珀色（和输入框的错误描边同色） |
| `title` | `string` | 必填 | — |
| `description` | `ReactNode` | 可选 | — |
| `action` | `ReactNode` | 可选 | 一个动作：链接或按钮，放正文之后 |
| `dismissible` | `boolean` | 可选 | 可关：常驻不等于不能收，但绝不自动消失（决策 17） |
| `className` | `string` | 可选 | — |

## 提示词（可直接粘给智能体）

```
复现「玻璃提示条」— Notification Banner（提示条 / Glass）
灵感来源：GOV.UK notification banner / Polaris banner / USWDS alert
规格：
- 页面级状态：常驻、不自动消失，处理完或手动关闭才走
- 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
必缺状态：（无必缺状态）
整页场景：两步验证
无障碍：焦点环 ≥2px 等效周长（SC 2.4.13）；指针目标 ≥24×24（SC 2.5.8）；动效带 prefers-reduced-motion 降级
参考：本站实样 /showcase/component/banner/glass-apple · 标准 states /design#states · 调研笔记 /showcase/component/banner/glass-apple#notes
```

## 调研笔记

import { useState, type ReactNode } from 'react';
import { CircleCheck, Info, TriangleAlert, X } from 'lucide-react';
import { Icon } from '@/components/ui-echo/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>
  );
}
