跳转到内容

UI 基础组件——实地指南

如果你曾看着一个现代应用心想”那个东西到底叫什么?“——这个页面就是为你准备的。

你使用的每一个界面,都是由数量惊人地少的一组可复用积木搭建而成的,它们被称为 UI 基础组件(UI primitives)。它们有自己的名字。有时候名字还挺奇怪。但一旦你看懂了背后的隐喻,就再也无法视而不见了。

En Parlant~ 使用 Mantine,一个 React 组件库,开箱即用地提供了所有这些组件。以下是你的实地指南。


Toast 是一种从屏幕边缘弹出的小通知,停留几秒钟,然后滑走。就像一片面包从烤面包机里弹出来——只不过重力方向反了,它最终会自己缩回去。

在 En Parlant~ 中,toast 出现在右下角(通过 Mantine 的 @mantine/notifications),用于以下场景:

  • “数据库加载成功”——
  • “TTS 语音未配置”——
  • “无法连接到 Lichess”——,然后你会有点小难过

你从来没有主动召唤过它们。它们自己出现,传达信息,然后离开。它们是 UI 世界里的信鸽。

Card 是一个带边框或阴影的矩形容器,将相关内容聚合在一起。想象一张放在桌上的索引卡——它有明确的边界,承载一件事,你可以拿起来移动它。

在 En Parlant~ 中,你会看到卡片用于:

  • 对局预览,在数据库浏览器中(GameCard)——棋手姓名、结果、日期
  • 棋盘位置编辑器EditingCard)——设置自定义局面时翻开的那张卡片
  • 数据库条目,在数据库页面——每个数据库在网格中都有自己的一张卡片

Card 有一个近亲叫 Paper,基本上就是一张上过艺术学校的卡片。相同的理念——一个带边框或阴影的容器——但语义含义更少。Paper 只是说”我是一个平面。“它作为分析面板和数据库信息展示的背景出现。

En Parlant~ 目前没有使用,但值得了解:drawer 是一个从屏幕边缘滑入的面板,就像一个书桌抽屉。(就是这样。这就是全部的隐喻。UI 设计师那天非常直白。)


Modal 是一个弹出对话框,强制要求你的注意力。它会将背景灰化,让你在做其他任何事之前必须先处理它。它是 UI 模式中的小孩子——“现在立刻看我!”

En Parlant~ 在真正需要你关注的时刻使用模态框:

  • “确定要清除所有数据吗?“ClearDataModal)——带有复选框,让你选择要摧毁哪些数据
  • “有更新可用”UpdateModal)——下载时带有进度条
  • “导入一盘对局”ImportModal)——粘贴 PGN、输入 FEN,或拖入链接
  • “创建新开局库”CreateRepertoireModal)——命名、配置、开始
  • “关于 En Parlant~“About)——版本信息、致谢,通常的后台通行证

模态框的意思是:“这是一次对话。我们现在就要进行。“

Dialog 是模态框的礼貌表亲。在 Mantine 中,它们本质上是同一个组件,但在 UI 设计理论中,对话框更小、更聚焦——一个问题和一个回答。“保存更改?“是 / 否。完成。

En Parlant~ 使用 ConfirmModal 来处理这类快速的是非选择时刻,比如确认你是否要放弃未保存的更改。


Tooltip 是当你将鼠标悬停在某个东西上时出现的小标签。它就是那个在你一脸困惑时低声告诉你答案的热心陌生人。

Tooltip 在 En Parlant~ 中无处不在。几乎悬停在任何图标按钮上你都会看到一个:

  • 悬停在 ↻ 按钮上 → “重新加载”
  • 悬停在被截断的文件路径上 → 显示完整路径
  • 悬停在标注符号上 → 该符号的含义
  • 悬停在分析面板中的一步棋上 → 弹出一个棋盘缩略预览

Tooltip 是最原始的”无需阅读手册”。

Popover 是读过研究生的 tooltip。它不只是显示一个文本标签,而是可以包含任何内容——按钮、图片,甚至一个迷你棋盘。

在 En Parlant~ 中,当你悬停在引擎分析中的某一步棋上时,popover 会渲染该位置的棋盘预览。它是一个 tooltip,但自带了视觉辅助工具。

Menu 是当你点击(或右键点击)某个东西时出现的操作列表。从计算机诞生之初你就在使用它们——文件、编辑、视图。你懂的。

En Parlant~ 有两种风味:

  • 顶部栏菜单——经典的 文件 / 编辑 / 视图 菜单栏,使用 Mantine 的 Menu 组件构建。快捷键列在右侧。一种老到已经可以领退休金的设计模式。
  • 上下文菜单——在对局树中右键点击一步棋,你会看到”提升变着”、“从此处删除”和”复制走法序列”等选项。这些使用 mantine-contextmenu 库来增加额外的精致感。

菜单有自己的子词汇:Menu.Target(你点击的东西)、Menu.Dropdown(出现的列表)、Menu.Item(每个选项)和 Menu.Divider(那条表示”现在是不同区域了”的细线)。


它们是布局界的花生酱与果冻——天生一对。

  • Stack 将元素垂直排列——一个叠在另一个上面。就像摞书。
  • Group 将元素水平排列——并排放置。就像在架子上排列棋子。

它们各自出现在 100 多个文件中。它们是整个应用中最常用的组件。每当东西排列整齐时,背后都有 Stack 或 Group 在负责。

Flex 是 Stack 和 Group 的瑞士军刀表亲。当元素需要换行、拉伸、收缩、反转或其他不那么规矩的行为时,Flex 会以对 CSS Flexbox 模型的完全控制介入。

用于分析面板的走法列表——那些像文本一样换行流动的走法序列?那就是 Flex 在做重活。

Box 是一个戴着高礼帽的 <div>。它本身什么都不做——只是一个通用容器。但它接受 Mantine 的样式属性,这使它成为”我需要在这个东西外面包一层东西”时极其趁手的包装器。

Box 出现在 150 多个文件中。它是组件库的万能胶带。

功能完全如其名。将某个东西放在正中央。水平居中。垂直居中。两者兼有。搞定。

出现在空状态中——当没有关联账号、没有加载数据库、还没有数据时。一个大图标,一条消息,Center 确保它们完美居中。没人喜欢偏心的空状态。

一个简单的网格。你告诉它要几列,它就把你的卡片排成整齐的网格,并随窗口大小自动重排。数据库页面用它在响应式网格中排列数据库卡片。

一个在内容溢出时可以滚动的容器。你可能觉得这应该是免费的——用 CSS overflow: auto 确实某种程度上是——但 ScrollArea 添加了样式化的滚动条、惯性滚动和溢出检测。它包裹着对局树面板、分析行,以及任何内容可能超出其容器高度的地方。

AppShell 是总布局——整个应用的骨架。它定义了头部在哪里、侧边栏在哪里、主要内容区域在哪里。其他一切都嵌套在它里面。

你只使用它一次。它是房子的地基。

Portal 是一个神奇的虫洞。它将一个组件渲染到 DOM 树中其正常位置之外的地方——通常是文档的根部。这就是弹出层和棋盘预览如何避免被带有 overflow: hidden 的父容器裁切的。

你永远看不到 Portal 本身。你只能看到它的效果。它是 UI 组件中的幕后工作人员。


一个可点击的矩形,里面有文字。这个你认识。在 Mantine 中,按钮有多种变体(variants)

  • filled——纯色填充,高强调。“点我,我很重要。”
  • outline——只有边框。“我是一个选项,但没有压力。”
  • subtle——若隐若现。“如果你需要我,我就在这里。”
  • default——中性的中间地带。

En Parlant~ 在 150 多个文件中使用了所有这些变体。最常见的模式:模态框底部的一个 Group,放着两个按钮——“取消”(subtle)和”确认”(filled)。经典。

ActionIcon 是一个放弃了文字标签、全身心投入图标生活方式的按钮。它紧凑、方形,依靠图标(加上一个 tooltip)来传达其用途。

工具栏按钮、面板控件、关闭按钮——全都是 ActionIcon。它们是提供操作的最节省空间的方式。

Switch 是一个看起来像小灯开关的切换控件。往左拨,往右拨。开。关。

在设置中,开关控制二元选择:

  • 声音 开/关
  • TTS 旁白 启用/禁用
  • 深色模式 / 浅色模式

这个物理隐喻好到连你的祖父母都能理解。

Slider 是一条带有可拖动手柄的轨道,用来选择一个值。就像一个被压扁的音量旋钮。

En Parlant~ 使用滑块用于:

  • 字体大小——拖动调整
  • 音量——拖动调整(当然)
  • CPU 核心数——引擎应使用多少核心?
  • 哈希内存——引擎的哈希表应使用多少内存?

还有一个 RangeSlider,带有两个手柄用于选择范围——用于按 Elo 等级分筛选对局(例如 2000–2200)以及时间段筛选。

一行按钮,同一时间只能选中一个。就像汽车收音机的预设按钮——按下一个,其他的就弹出来。

用于在以下选项之间切换:

  • 分析图表上的厘兵评估 vs. 胜/和/负百分比
  • 不同的走法记谱格式
  • 引擎分析行数

Checkbox 与 Select(复选框与下拉选择)

Section titled “Checkbox 与 Select(复选框与下拉选择)”

这些你都认识。Checkbox 用于”勾选所有适用的”,Select 下拉菜单用于”从列表中选一个”。它们出现在整个应用的设置、筛选和表单中。清除数据模态框有一组特别令人满足的复选框,你可以在其中选择自己的毁灭方式

一个在你输入时建议补全内容的文本输入框。数据库浏览器中的棋手搜索使用了它——开始输入名字,匹配的棋手就会出现在下方。

FileInput 与 DateInput(文件输入与日期输入)

Section titled “FileInput 与 DateInput(文件输入与日期输入)”

FileInput 在点击时打开文件选择器——用于导入模态框中加载 PGN 文件。DateInput 弹出一个日历用于选择日期——用于按日期筛选对局。还有 MonthPickerInputYearPickerInput,用于不需要那么精确的场景(Lichess 数据库按月筛选,Masters 数据库按年筛选)。


一个从左到右填充的水平条,显示某件事进行到哪了。用于应用更新(下载进度),以及——更具创意地——在分析面板中显示胜/和/负概率。三个彩色段(白、灰、黑)按比例填充进度条。它是一个伪装成进度指示器的迷你条形图。

Loader 是一个旋转动画,意思是”我正在处理,等我一下。“在数据库加载、棋手统计数据获取中,或任何异步操作进行时出现。

Skeleton 是一个灰色占位符,模拟尚未加载完成的内容形状。不是空白屏幕或转圈,而是你看到卡片和文字将要出现位置的幽灵轮廓。它是加载状态的建筑蓝图——“真正的家具正在路上。”

数据库页面在卡片网格加载时使用骨架屏。比单独一个转圈优雅得多。

一层半透明的覆盖层,在组件加载时铺在上面。分析图表使用了它——你仍然可以看到遮罩后面的图表,但它变暗了,上面还有一个转圈。“我还在,只是在更新。“


Badge 是一个小的彩色标签——就像会议上的名牌。在 En Parlant~ 中,SpeedBadge 组件使用徽章来显示带颜色编码的时间控制:

  • 🩷 粉色 — UltraBullet(超超快棋)
  • 🔴 红色 — Bullet(超快棋)
  • 🟠 橙色 — Blitz(快棋)
  • 🟢 绿色 — Rapid(快速)
  • 🔵 蓝色 — Classical(慢棋)
  • 🟣 紫色 — Correspondence(通信棋)

每个徽章都小巧、多彩,让人一眼就能看出对局速度。

keyboard 的缩写。以那种标志性的凸起按键样式显示一个键盘按键。在快捷键设置中用来展示类似 Ctrl + Z 的内容。它是用 CSS 重现的你键盘上的实体按键。令人愉悦的拟物设计。


一条水平线,用来分隔不同区域。就是这样。它是一条线。但它是一条有样式的线,还可以在中间带一个文本标签——比如导入模态框中的”— OR —“分隔线,将”粘贴 PGN”和”输入 FEN”分开。

30 多个文件使用了 Divider。事实证明,很多东西需要被分隔。

Collapse 是一个可以平滑展开和收起的区域。点击展开,点击隐藏。数据库筛选选项使用了它——你并不总是需要看到每个筛选条件,所以它们藏在 Collapse 后面,等你需要时再展开。


一个完整的所见即所得文本编辑器,基于 TipTap 构建。加粗、斜体、列表,应有尽有。用于撰写对局注释——你为解释某步棋为何精妙或糟糕而添加的评论笔记。

这是唯一一个更像复合有机体而非单个细胞的基础组件。底层是 TipTap + ProseMirror + Mantine 样式。但从用户的角度看,它只是一个格式功能能正常工作的文本框。

来自 @mantine/charts(它封装了 Recharts),AreaChart 驱动着评估曲线图——分析面板底部那条起伏的波形线。中线以上的白色区域表示白方占优;下方的黑色区域表示黑方占优。

你可以点击图表上的任意一点跳转到对应的那步棋。交互式数据可视化遇上了国际象棋分析。它甚至有自定义 tooltip 和标记对局阶段的参考线(开局 → 中局 → 残局)。

通过 mantine-datatable 库——一个功能齐全的数据表格,支持排序、分页、行选择和筛选。驱动着数据库浏览器的对局列表、棋手列表和锦标赛列表。它是一个知道自己身在国际象棋应用中的电子表格。


有些基础组件本身不渲染任何可见内容,但让其他一切得以运作:

  • MantineProvider — 包裹整个应用,提供主题(颜色、深色模式、组件默认值)
  • Portal — 将渲染内容传送到 DOM 的另一个位置
  • useHotkeys — 一个 hook(不是组件),用于绑定键盘快捷键
  • useForm — 管理表单状态、验证和错误处理
  • useDebouncedValue — 等你停止输入后再触发搜索。一个有礼貌的 hook。

作为参考,以下是 En Parlant~ 中使用的每一类基础组件,按功能分组:

类别基础组件
布局AppShell, Box, Center, Flex, Group, Stack, SimpleGrid, ScrollArea, Portal
表面Card, Paper
覆盖层Modal, Tooltip, Popover, Menu, ContextMenu, LoadingOverlay
通知Toast(通过 @mantine/notifications
按钮Button, ActionIcon, CloseButton, ThemeIcon
输入TextInput, Textarea, NumberInput, Select, Autocomplete, Checkbox, Switch, Slider, RangeSlider, SegmentedControl, FileInput, DateInput, MonthPickerInput, YearPickerInput
数据展示Table, DataTable, Badge, Code, Kbd, Rating, Image
排版Text, Title, Anchor
进度Progress, Loader, Skeleton
结构Divider, Collapse
富内容RichTextEditor (TipTap), AreaChart (Recharts)
主题MantineProvider, createTheme, useColorScheme
HooksuseForm, useHotkeys, useToggle, useDebouncedValue, useClickOutside, useElementSize

所有组件均来自 Mantine v8 及其生态系统包:@mantine/core@mantine/notifications@mantine/dates@mantine/charts@mantine/form@mantine/tiptapmantine-datatablemantine-contextmenu


下次你看到一条小消息从屏幕角落滑上来——那是 toast。围绕对局预览的那个矩形——那是 card。内容加载前闪烁的灰色色块——那些是 skeleton。

一旦你学会了这些名字,你就会开始到处看到它们。每个应用。每个网站。同样的二十来个基础组件,以一千种不同的方式重新排列组合。

一路到底,全是乐高。