Figma 全局通知中心与消息收件箱(Notification Center & Activity Inbox Architecture)组件架构实战:从铃铛红点徽标联动(Unread Badge)、多分类选项卡切换、消息卡片类型拓扑与审批流 CTA 到 Variables 状态机与 Dev Mode 前端 Radix UI / Novu 代码交付

2026 年 11 月 13 日

一、引言与架构全景:为什么现代 SaaS 必须建立体系化通知中心? #

在现代 B2B SaaS 平台、企业协同办公套件(如 Slack、飞书、Notion、Linear)以及复杂管理控制台中,全局通知中心(Notification Center / Activity Inbox) 是联结系统事件、协同互动与用户待办任务的核心信息分发枢纽。无论是设计稿被团队成员添加批注、代码合并请求等待审核、工单审批流流转,还是重要系统告警,用户都依赖右上角的铃铛入口实时捕获动态并做出响应。

然而,在大量设计团队的原型与组件库中,通知中心往往存在以下几大通病:

  1. 轻提示与持久化消息混淆:未能清晰划分即时消失的临时浮层(Toast / Snackbar)与需要持久保留的收件箱(Inbox)。研发盲目套用弹窗或轻提示,导致用户刷新页面后重要待办消息彻底丢失;
  2. 卡片形态单一无差别:将所有消息简化为“头像 + 一行正文”,无法应对带有复杂操作诉求的业务场景(例如需要直接在通知内点击“同意”或“驳回”的审批卡片,或者包含代码差异与上下文引用的批注卡片);
  3. 未读状态机与角标脱节:导航栏的未读红点(Unread Badge)、弹窗内部未读行高亮、单条标记已读与“全部标为已读”缺乏统一的状态机变量联动,导致原型交互缺乏说服力;
  4. 前端协议与无障碍缺失:未对齐现代消息中间件协议(如 Novu、Knock),缺乏屏幕朗读器(aria-live="polite")焦点流转规范,增加前端二次重构成本。

为了彻底规范通知中心的设计与研发交付,我们梳理出如下四层架构拓扑:

+------------------------------------------------------------------------+
|      Figma 全局通知中心(Notification Center Architecture)四层拓扑结构   |
+------------------------------------------------------------------------+
| 1. 导航栏触发器与角标联动层 (Navigation Trigger & Badge Layer)           |
|    - 铃铛图标容器 (Bell Icon: Default / Active / Hover / Muted)        |
|    - 绝对定位红点角标 (Absolute Badge: Unread Dot / 1~99 Count / 99+)  |
+------------------------------------------------------------------------+
| 2. 浮动收件箱面板容器层 (Inbox Popover Canvas & Viewport Layer)        |
|    - 三段式布局结构 (Header Title, Tabs Filter Bar, Footer View-All)   |
|    - 视口尺寸约束 (Width: 380px, Max-H: 520px, Auto Layout Hug/Scroll) |
|    - 业务缺省容错态 (Empty State 插画与文字引导, 骨架屏 Skeleton)       |
+------------------------------------------------------------------------+
| 3. 原子消息卡片变体矩阵层 (Atomic Notification Card Component Set)       |
|    - 业务类型多态 (System Announcement, Comment Mention, Task Approval)|
|    - 阅读状态区分 (Unread 蓝点高亮态 vs Read 浅灰次级态)                |
|    - 悬停浮动快捷手柄 (Hover Quick Action: Mark as Read, Delete)       |
+------------------------------------------------------------------------+
| 4. 语义 Tokens 与状态机层 (Semantic Tokens & Variables State Machine)   |
|    - 浅色/暗黑主题 Tokens (Surface Popover, Card Unread Tint, Border)  |
|    - 动态数值与布尔变量绑定 (unreadCount, activeTab, isPopoverOpen)     |
+------------------------------------------------------------------------+

二、导航栏触发器与未读红点徽标(Trigger & Badge)联动设计 #

通知中心的入口通常位于全局导航栏(Top Navigation)右上角。该组件由基础操作图标与绝对定位的角标图层组合而成。

1. 触发器容器与绝对定位角标配置 #

在 Figma 中配置铃铛触发器:

  • 底座图标按钮(Icon Button):
    • 尺寸:36px × 36px(内边距 8px),内部放置 20px 矢量铃铛图标(Bell Icon);
    • 悬停(Hover)背景填充浅灰圆角底色(R: 8px);
    • 激活(Active/Opened)变体可设为品牌浅蓝底色,图标呈实心深色态;
  • 未读角标(Unread Badge)绝对定位法则:
    • 角标图层开启 Absolute position(绝对定位),锚定在右上角;
    • 偏移量设定:Top: 2px, Right: 2px,避免遮挡铃铛主体轮廓;
    • 角标形态提供三种变体:
      • Dot:8px × 8px 纯红实心圆点,用于弱感知场景;
      • Count:高度 16px,最小宽度 16px,左右内边距 PX: 4px,圆角 999px(Pill 胶囊形),显示数值 1~99;
      • Overflow:固定显示 99+,宽度自动延伸。
  • 高对比防粘连描边:
+-------------------------------------------------------------------------+
|                  Bell Trigger 与 Absolute Position 角标推导             |
+-------------------------------------------------------------------------+
|     +-------------------------+                                         |
|     |  Top Nav Bar            |                                         |
|     |                         |                                         |
|     |        [ ( 🔔 ) ] (Top: 2px, Right: 2px)                          |
|     |            \                      |                               |
|     |             +--- Absolute Badge: ( 3 ) Red Pill                   |
|     +-------------------------------------------------------------------+

三、通知中心浮动面板(Inbox Popover)三段式架构 #

当用户点击铃铛时,通知面板以浮动覆盖层(Popover Overlay)形式展开。其尺寸通常推荐固定宽度 380px,最大高度 520px。

1. 三段式结构剖析 #

  • 顶部操作栏(Header Bar):
    • 高度 48px,水平内边距 PX: 16px;
    • 左侧为主标题“通知”(Heading 3,16px SemiBold)与未读总数灰色胶囊标牌(如 (3));
    • 右侧包含“全部标为已读(Mark all read)”文本按钮与消息设置(Settings ⚙️)图标;
  • 选项卡过滤导轨(Tabs Filter Bar):
    • 采用紧凑型分段控制器或下划线 Tabs:全部(All)、未读(Unread)、提及我的(@Mentions)、待办审批(Tasks);
    • 切换选项卡时,下方列表进行条件过滤。标签页设计细节可参考 Figma 标签页与分段控制器架构实战;
  • 消息滚动视口(Scroll Viewport):
    • Auto Layout 纵向排列,宽度设为 Fill container;
    • 开启原型纵向滚动(Vertical Scrolling),保证内容超出时顶部与底部操作栏吸顶/吸底固定;
  • 底部导流栏(Footer Bar):
    • 高度 40px,居中放置“查看全部历史通知”超链接,点击可跳转至全屏独立消息管理大厅。

2. 缺省空状态(Empty State)架构 #

当当前选项卡下没有消息时,列表区域动态展示空状态插槽:

  • 包含 80px 浅灰矢量收件箱插画;
  • 文案分级:主文案“太棒了,所有消息都已处理完”,副文案“新动态将在此第一时间通知您”;
  • 保持整体高度与最小阅读舒适度平衡,具体布局规范可参考 Figma 空状态与结果反馈系统组件架构实战。

四、原子消息卡片(Notification Card)四维变体矩阵设计 #

通知卡片(Notification Card)是承载单条事件的最小完整交互单元。在 Figma 中建立名为 _Notification / Card-Item 的主组件,具备如下变体维度:

1. 四类典型业务卡片形态对照表 #

卡片类型(Type) 视觉核心构成 典型业务场景与内容 快捷操作(Actions)
基础系统公告 (System) 品牌/系统图标(24px),主次双行标题 平台版本发布、安全防护预警、扣费通知 “查看详情”外部链接
协同评论提及 (Mention) 成员头像(32px)+ 动作说明 + 引用框 “Alex 在设计系统规范中提及了你” “一键回复”浮动输入框
待办审批流 (Approval) 申请人头像 + 工单单号 + 审批摘要 “财务报销工单审批”、“项目上线发布审批” “同意(Approve)”、“驳回(Reject)”双 CTA
任务风险预警 (Warning) 橙黄/红色三角感叹号 + 剩余到期倒计时 “SSL 证书将于 3 天后过期”、“集群负载过高” “立即处理”警示按钮

2. 阅读状态视觉编码规则 #

  • 未读态(Unread):
    • 卡片左上角绝对定位一个 6px × 6px 的品牌蓝高亮实心圆点;
    • 卡片背景填充带有极浅品牌色微调的浅蓝底色(rgba(59, 130, 246, 0.04));
    • 主标题文本为加粗深色(Text/Primary);
  • 已读态(Read):
    • 隐藏未读蓝点;
    • 背景恢复为纯白或面板底色(透明背景);
    • 标题降级为次级灰度文本(Text/Secondary);
  • 悬停快捷手柄(Hover Actions):
    • 当鼠标 Hover 在单条卡片上时,卡片右上角的时间戳(如 10m 前)暂时淡出,取而代之浮现“标为已读(勾选图标)”与“删除该通知(垃圾桶图标)”两个微型快捷按钮,实现极速整理。
+-------------------------------------------------------------------------+
|                  审批流卡片(Actionable Approval)布局结构               |
+-------------------------------------------------------------------------+
| [●] (Avatar)  林思远 提交了《2026 Q4 服务器扩容预算审批》      15m ago  |
|               金额: ¥48,500 | 关联项目: 云原生迁移平台                  |
|               +--------------------------------------------------+     |
|               |  [ 驳回 (Ghost) ]      [ 同意扩容 (Primary CTA) ] |     |
|               +--------------------------------------------------+     |
+-------------------------------------------------------------------------+

五、Variables 变量绑定与暗黑模式语义 Tokens 体系 #

通知中心涉及多层背景色重叠、边框层次与状态微调,必须通过 Design Tokens 规范 与 Figma Variables 变量 进行系统化驱动。

1. 通知中心专用语义 Tokens 体系表 #

Token 变量名称 浅色模式(Light Mode) 暗黑模式(Dark Mode) 作用域与设计语义说明
inbox.surface.popover #FFFFFF #1E1E1E 通知浮层面板主背景色
inbox.border.subtle #E5E7EB #2E2E2E 面板轮廓与分割线细边框
inbox.card.unread.bg #F0F7FF #172338 未读消息卡片微蓝浅底色
inbox.card.unread.dot #2563EB #60A5FA 未读状态左侧指示圆点
inbox.card.hover.bg #F9FAFB #262626 卡片悬停时的浅灰反馈底色
inbox.badge.bg #EF4444 #F87171 导航栏未读数大红胶囊底色
inbox.badge.text #FFFFFF #FFFFFF 未读角标文字纯白色
inbox.quote.bg #F3F4F6 #2A2A2A 评论提及引文灰色背景框
inbox.quote.border #D1D5DB #3D3D3D 评论引文左侧边框线条

2. Variables 变量状态机联动 #

在 Figma 中配置 Component/Notification 集合:

  • unreadCount(Number):控制铃铛角标显示的数字,当用户点击“全部标为已读”时,通过交互逻辑将该数值赋值为 0;
  • hasUnread(Boolean):与角标图层的显隐绑定(当 unreadCount > 0 时显现);
  • activeTab(String):驱动当前高亮的分类选项卡(all / unread / mention);
  • isPopoverOpen(Boolean):控制弹出层的打开与关闭。

六、Dev Mode 现代前端代码交付:从 Figma 到 Radix UI / Novu #

在向工程团队移交设计稿时,利用 Figma Dev Mode 呈现标准的数据契约与组件化代码。

1. 现代化消息数据契约(JSON AST / Novu Schema) #

{
  "id": "notif_892314",
  "actor": {
    "name": "林思远",
    "avatar": "https://assets.example.com/avatar/lin.png"
  },
  "type": "WORKFLOW_APPROVAL",
  "title": "提交了审批申请",
  "summary": "《2026 Q4 服务器扩容预算审批》",
  "status": "UNREAD",
  "createdAt": "2026-11-13T09:20:00Z",
  "actions": [
    { "label": "驳回", "actionKey": "REJECT", "variant": "secondary" },
    { "label": "同意扩容", "actionKey": "APPROVE", "variant": "primary" }
  ]
}

2. 生产级 React + Radix UI Popover + Tailwind CSS 交付代码 #

import React, { useState } from 'react';
import * as Popover from '@radix-ui/react-popover';
import { Bell, CheckCheck, Settings } from 'lucide-react';

interface NotificationItem {
  id: string;
  sender: string;
  content: string;
  time: string;
  isRead: boolean;
  type: 'mention' | 'approval' | 'system';
}

export const NotificationCenter: React.FC = () => {
  const [unreadCount, setUnreadCount] = useState<number>(3);
  const [notifications, setNotifications] = useState<NotificationItem[]>([
    {
      id: '1',
      sender: '林思远',
      content: '在《核心设计系统 2.0》中提及了你并请求代码评审',
      time: '10m 前',
      isRead: false,
      type: 'mention',
    },
    {
      id: '2',
      sender: '系统通知',
      content: '安全审计告警:发现 1 处跨域策略风险需及时确认',
      time: '1h 前',
      isRead: false,
      type: 'system',
    },
  ]);

  const handleMarkAllRead = () => {
    setNotifications((prev) => prev.map((item) => ({ ...item, isRead: true })));
    setUnreadCount(0);
  };

  return (
    <Popover.Root>
      <Popover.Trigger asChild>
        <button
          className="relative flex items-center justify-center w-9 h-9 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500"
          aria-label={`通知中心,共 ${unreadCount} 条未读消息`}
        >
          <Bell className="w-5 h-5 text-gray-700 dark:text-gray-300" />
          {unreadCount > 0 && (
            <span className="absolute top-1 right-1 flex items-center justify-center min-w-[16px] h-4 px-1 text-[10px] font-bold text-white bg-red-500 rounded-full border-2 border-white dark:border-gray-900">
              {unreadCount > 99 ? '99+' : unreadCount}
            </span>
          )}
        </button>
      </Popover.Trigger>

      <Popover.Portal>
        <Popover.Content
          className="w-[380px] bg-[var(--inbox-surface-popover)] rounded-xl shadow-2xl border border-[var(--inbox-border-subtle)] z-50 overflow-hidden"
          sideOffset={8}
          align="end"
        >
          {/* 头部操作区 */}
          <div className="flex items-center justify-between px-4 py-3 border-b border-gray-100 dark:border-gray-800">
            <div className="flex items-center gap-2">
              <span className="font-semibold text-gray-900 dark:text-gray-100 text-sm">通知</span>
              {unreadCount > 0 && (
                <span className="px-1.5 py-0.5 text-xs bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-300 rounded font-medium">
                  {unreadCount} 条新动态
                </span>
              )}
            </div>
            <div className="flex items-center gap-3">
              <button
                onClick={handleMarkAllRead}
                className="flex items-center gap-1 text-xs text-gray-500 hover:text-blue-600 dark:hover:text-blue-400"
              >
                <CheckCheck className="w-3.5 h-3.5" />
                全部已读
              </button>
              <button className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-200">
                <Settings className="w-4 h-4" />
              </button>
            </div>
          </div>

          {/* 消息滚动列表 */}
          <div className="max-h-[420px] overflow-y-auto divide-y divide-gray-100 dark:divide-gray-800">
            {notifications.map((item) => (
              <div
                key={item.id}
                className={`p-3.5 transition-colors hover:bg-[var(--inbox-card-hover-bg)] ${
                  !item.isRead ? 'bg-[var(--inbox-card-unread-bg)]' : ''
                }`}
              >
                <div className="flex items-start gap-2.5">
                  {!item.isRead && (
                    <span className="w-2 h-2 mt-1.5 rounded-full bg-[var(--inbox-card-unread-dot)] shrink-0" />
                  )}
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center justify-between text-xs mb-1">
                      <span className="font-medium text-gray-900 dark:text-gray-100">{item.sender}</span>
                      <span className="text-gray-400">{item.time}</span>
                    </div>
                    <p className="text-xs text-gray-600 dark:text-gray-300 line-clamp-2 leading-relaxed">
                      {item.content}
                    </p>
                  </div>
                </div>
              </div>
            ))}
          </div>
        </Popover.Content>
      </Popover.Portal>
    </Popover.Root>
  );
};

3. W3C WCAG 2.2 辅助功能(a11y)标准 #

  • 实时播报区域:未读消息卡片流声明 role="region" 与 aria-live="polite",使得新增通知到达时不打断用户当前屏幕阅读;
  • 键盘焦点流动:ESC 键可立即关闭通知面板并将焦点重置回铃铛触发按钮;Tab 键可在卡片内部的动作按钮(同意/驳回)与标为已读图标之间无障碍流转。

七、常见排障方案与避坑实战 #

在通知中心组件的实际搭建与项目交付中,常发生以下三大高频故障,可对照 Figma 常见报错排障手册 进行快速定位:

  1. 浮动面板在最右侧边缘被裁切或超出视口:
    • 故障现象:当铃铛按钮紧贴屏幕右上角时,宽度为 380px 的面板展开后有 50px 伸出了屏幕右侧边界;
    • 根治方案:在 Figma 原型设置中,使用 Open Overlay 并选择对齐方式为 Top Right,或在工程交付中声明 Floating UI 的 collisionPadding 与 flip 逻辑,确保浮层右对齐锚定;
  2. 长标题导致卡片布局被破坏坍塌:
    • 故障现象:某条通知的标题极长,未换行直接横向撑开,导致时间戳与右侧删除按钮被挤出卡片;
    • 根治方案:必须将正文文本图层的宽度强制设为 Fill container,并在文本排印设置中开启 Auto Height 或在 CSS 交付中标注 -webkit-line-clamp: 2(多行溢出省略);
  3. 未读角标数字过长被截断:
    • 故障现象:当未读数量达到三位数(如 128)时,红点内部文字被吞噬或椭圆变形;
    • 根治方案:在 Figma 角标图层中,宽度切勿写死为绝对数字,必须设为 Hug contents,且配置最小宽度(Min width: 16px)与水平内边距(PX: 4px),或利用组件变体直接拦截为 99+。

通过本文详述的触发器红点规范、四类业务卡片矩阵以及现代 Popover 代码架构,团队不仅能在 Figma 中建立结构严密的通知收件箱原型,更能够无缝衔接工程侧的现代通知协议,为产品提供高效可靠的用户触达体验。

❓ 常见问题与答疑 (FAQ)

高频疑难解析与避坑指南
A

完全不需要。绝大多数核心功能在免费 Starter 计划中均可完整体验与实操练习,无需额外付费购买付费团队版。