跳到內容

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~ 在真正需要你專注的時刻使用 modal:

  • 「確定要清除所有資料嗎?」ClearDataModal)——附有核取方塊讓你選擇哪些資料要清除
  • 「有可用更新」UpdateModal)——下載時顯示進度條
  • 「匯入對局」ImportModal)——貼上 PGN、輸入 FEN 或拖放連結
  • 「建立新開局庫」CreateRepertoireModal)——命名、設定、開始
  • 「關於 En Parlant~」About)——版本資訊、致謝名單,一般的幕後通行證

Modal 說的是:「這是一段對話。我們現在就要進行。」

Dialog 是 modal 的有禮貌表親。在 Mantine 中,它們基本上是同一個元件,但在 UI 設計理論中,dialog 更小、更聚焦——一個簡單的問答。「要儲存變更嗎?」是 / 否。結束。

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 樹中正常位置以外的地方——通常是文件的根節點。這就是 popover 和棋盤預覽如何避免被設有 overflow: hidden 的父容器裁切的方式。

你永遠看不到 Portal 本身。你只看到它的效果。它是 UI 元件中的幕後工作人員。


一個可點擊的帶有文字的矩形。你認識它。在 Mantine 中,按鈕有不同的樣式變體(variants)

  • filled ——實心色彩,高強調。「點我,我很重要。」
  • outline ——只有邊框。「我是個選項,但別有壓力。」
  • subtle ——幾乎看不見。「需要的時候我在。」
  • default ——中性的折衷方案。

En Parlant~ 在 150 多個檔案中使用了所有這些變體。最常見的模式:在 modal 底部的一排 Group 按鈕——「取消」(subtle)和「確認」(filled)。經典。

ActionIcon 是一個放棄了文字標籤、全心擁抱圖示生活方式的按鈕。它緊湊、方正,依靠圖示(加上 tooltip)來傳達用途。

工具列按鈕、面板控制按鈕、關閉按鈕——都是 ActionIcon。它們是提供操作的最節省空間的方式。

Switch 是一個看起來像迷你電燈開關的切換控制。往左撥、往右撥。開。關。

在設定中,開關控制二元選擇:

  • 聲音開/關
  • TTS 語音朗讀啟用/停用
  • 深色模式 / 淺色模式

這個物理比喻好到連你的祖父母都能理解。

Slider 是一條帶有把手的軌道,你可以前後拖動來選擇一個值。就像一個被壓扁的音量旋鈕。

En Parlant~ 使用滑桿的場景:

  • 字體大小 ——拖動調整
  • 音量 ——拖動調整(當然)
  • CPU 核心數 ——引擎應該使用多少核心?
  • 雜湊記憶體 ——引擎的雜湊表要用多少 RAM?

還有一個 RangeSlider,帶有兩個把手用於選擇範圍——用來按照 Elo 等級分(例如 2000–2200)和時間區間篩選對局。

一排按鈕,同一時間只能選擇其中一個。就像汽車收音機的預設頻道——按下一個,其他的就彈起來。

用於切換:

  • 分析圖表上的厘兵值評估 vs. 勝/和/負機率
  • 不同的走步記譜格式
  • 引擎分析行數

你認識這些。Checkbox(核取方塊)用於「勾選所有適用的選項」,Select(下拉選單)用於「從列表中選一個」。它們出現在整個應用程式的設定、篩選器和表單中。清除資料的 modal 有一組特別令人滿足的核取方塊,讓你可以選擇自己的毀滅方式

一個在你輸入時建議補全內容的文字輸入框。資料庫瀏覽器中的棋手搜尋使用了它——開始輸入名字,匹配的棋手就會出現在下方。

FileInput 在點擊時開啟檔案選擇器——在匯入 modal 中用於載入 PGN 檔案。DateInput 彈出日曆以選擇日期——用於按日期篩選對局。還有 MonthPickerInputYearPickerInput,用於不需要那麼精確的時候(Lichess 資料庫按月篩選、Masters 資料庫按年篩選)。


一個從左到右填充的水平條,顯示某件事進行到哪裡了。用於應用程式更新(下載進度),以及——更有創意地——用於在分析面板中顯示勝/和/負機率。三個彩色區段(白、灰、黑)按比例填充進度條。它是一個偽裝成進度指示器的迷你長條圖。

Loader 是一個旋轉動畫,表示「我正在處理,等我一下。」在資料庫載入、棋手統計資料擷取中,或任何非同步操作進行時出現。

Skeleton 是一個模仿尚未載入內容形狀的灰色佔位符。不是空白畫面或旋轉指示器,而是顯示卡片和文字即將出現位置的幽靈輪廓。它是載入狀態的建築藍圖——「真正的家具已經在路上了。」

資料庫頁面在卡片網格載入時使用骨架屏。比單獨使用旋轉指示器優雅得多。

一層半透明的遮罩覆蓋在正在載入的元件上。分析圖表使用了這個——你仍然可以透過遮罩看到圖表,但它被調暗了,上面還有一個旋轉指示器。「我還在這裡,只是在更新。」


Badge 是一個小型彩色標籤——就像會議上的名牌。在 En Parlant~ 中,SpeedBadge 元件使用徽章以色彩編碼顯示時間控制:

  • 🩷 粉紅色 — UltraBullet
  • 🔴 紅色 — Bullet
  • 🟠 橘色 — Blitz
  • 🟢 綠色 — Rapid
  • 🔵 藍色 — Classical
  • 🟣 紫色 — Correspondence

每個徽章都小巧、色彩鮮明,一眼就能傳達對局的時間模式。

keyboard 的縮寫。以那種特徵性的凸起按鍵樣式顯示鍵盤按鍵。在按鍵綁定設定中用來顯示 Ctrl + Z 之類的內容。它是用 CSS 重現你鍵盤上實體按鍵的效果。令人愉悅的擬物設計。


一條分隔區段的水平線。就這樣。它是一條線。但它是一條有樣式的線,而且可以選擇性地在中間放一個文字標籤——就像匯入 modal 中「貼上 PGN」和「輸入 FEN」之間的「— 或 —」分隔線。

超過三十個檔案使用了 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。

一旦你學會了這些名字,就會開始到處看到它們。每個應用程式。每個網站。同樣的二十幾個基礎元件,以千百種不同的方式重新組合。

一路往下拆,全是樂高積木。