第 6 篇2026-03-09

霧中前行, 第 6 篇: 可以上場了

「霧中前行」第六篇 — 一個關於開發 Ridgeline 的系列,Ridgeline 是為 Outward Bound Hong Kong 打造的補給決策引擎。第五篇講了從匿名飲食計數到具名參加者及自動替代的結構性重構。

倉庫

想像一下 OBHK 設施裡的補給室。架上是罐頭、乾貨、調味包。一個打包工作台。一部手機在某人手中 — 補給主任正在查看要訂什麼、多少、給哪個課程。

這就是軟件必須運作的地方。不是在辦公桌前對著螢幕。不是在會議室對著投影機。是在手機上,在倉庫裡,由一個打包遠征物資多年、根本不需要一個 app 來教他怎麼做事的人使用。

這個 app 必須靠著在試算表做不到的事情上更快來贏得一席之地 — scaling、飲食替代、跨供應商的成本匯總。如果它比一份打印出來的清單更慢、更笨拙、更難讀,那就注定失敗。

320 像素

最小的常見手機螢幕是 320px 寬。iPhone SE,仍然有人在用。Ridgeline 的每一頁都先為這個寬度設計(6eadcb7、d72c5a0)。

這意味著:

沒有橫向捲動。 多項目的行使用 flex-wrap。標籤、徽章、數據標示 — 任何可能溢出的東西 — 都換行到下一行。ADR-002 的規則:「永遠不要在可能超出螢幕寬度的內容上使用 inline-flex 而不加 flex-wrap。」

文字保持可讀。 任何面向使用者的內容最小為 20px — Tailwind 的 text-xl。不是 16px(網頁預設值),不是 18px(我們試過但在實際使用中覺得太小而否決了)。輸入欄位是 24px(text-2xl),因為小螢幕上的粗手指需要寬裕的觸控目標。唯一的例外是裝飾性的介面元素:導航列的大寫等寬品牌名、頁尾、數據提示標籤。這些必須在視覺上明顯區別於正文內容,這樣不會有人把它們誤認為需要閱讀的東西。

內容堆疊。 桌面上的並排排版在手機上變成垂直堆疊:

flex flex-col sm:flex-row items-center justify-between

手機上文字置中,桌面上靠左:

text-center sm:text-left

響應式內距,不會在小螢幕上吃掉太多空間:

px-3 sm:px-4       // tight containers
px-6 sm:px-10      // page-level padding

每頁上線前的驗證清單:320px、375px、428px、768px、1024px+。檢查項目:沒有橫向捲動條、沒有文字被截斷、足夠的觸控目標(最少 44x44px)、數據顯示會換行而不是溢出。

696 行裡的兩種語言

Rocky 的團隊用廣東話溝通。OBHK 的設施是雙語環境 — 文件用英文,日常對話用廣東話。不是每個職員都能輕鬆閱讀英文。App 必須兩種語言都能用。

next-intl 處理路由 — 每一頁都在 /en/ 或 /tc/ 下。翻譯檔各 696 行(messages/en.json、messages/tc.json),涵蓋了從「肉醬意粉」到飲食標籤到營養警告訊息的所有內容。Server components 使用 setRequestLocale(),client components 使用 useTranslations()。

雙語要求影響了一些在純英文下會簡單得多的決策。食材名稱同時帶有 name_en 和 name_zh。介面根據語言環境顯示對應的名稱,找不到時回退到英文。警告訊息把參加者名字嵌入翻譯字串中:「Pork Burger in D1 is not halal — affects Ahmad」變成「豬肉漢堡扒 (D1) 不是清真 — 影響 Ahmad。」

國際化從第一個 commit 就加入了(46e1014)。這是刻意的。事後才加翻譯意味著要翻遍每個 component 找硬編碼的字串。從一開始就做雙語,意味著翻譯 key 就是寫文字的自然方式。

淺色與深色

App 內建了主題切換器 — 淺色和深色。淺色主題在實際使用中證明可讀性更好,尤其是像工作台這種數據密集的畫面,你需要掃視食材數量和成本欄。深色主題在低光環境下仍然好用,但倉庫用的是日光燈,在明亮環境光下深底淺字可能更難閱讀。

切換之所以簡單到不值一提:app 裡每個顏色都引用 CSS 變數。沒有任何硬編碼的 Tailwind 灰色或白色。

--bg-deep          // deepest background
--bg-surface       // card/panel background
--bg-elevated      // elevated elements
--text-primary     // main text
--text-secondary   // supporting text
--accent           // highlight color

深色一套變數,淺色一套變數。主題切換就是在兩套之間翻轉。每個 component 自動跟隨變化,因為沒有任何一個直接引用顏色 — 全部透過變數。這是那種日後會產生回報的決策:一開始的「只用深色」美學限制,在有人要求淺色模式的那一刻就變成了可切換的系統。

為不用軟件思維思考的人而建

最難的設計限制不是螢幕寬度或語言。而是為那些有深厚領域專業知識、但對學習軟件慣例毫無興趣的使用者而建。

一個補給主任知道第三天需要耐放的蔬菜,因為葉菜撐不到那時候。他們知道咖喱粉不會線性 scaling。他們知道哪個供應商有清真腸。他們不知道什麼是「情境」,也不在乎什麼叫「份量因子」,更不知道下拉選單裡 per_person 和 per_group 的分別。

說明系統(8e7b4cb)用的是頁面級抽屜 — 一個小按鈕展開後解釋這個頁面做什麼、為什麼數字看起來是這樣。內嵌概念卡在術語出現的地方就地解釋。漸進式揭示(fae6263)把技術細節藏在可展開的區域後面,讓預設視圖保持乾淨,複雜度隨時可用但不是強制的。

工作台不會向使用者顯示「scaling = per_person」。它顯示的是當你改變團隊人數時數量會跟著改變。實作細節是不可見的。行為是一目了然的。

無障礙審查(4b86199)在 16 個檔案中發現了問題 — 缺少 ARIA 標籤、螢幕閱讀器無法使用的焦點狀態、沒有正確 role 的 toast 通知、會困住鍵盤使用者的 tab 互動。這些在正常使用中都看不到。但對於像 Outward Bound 這樣的機構應該示範的包容性無障礙而言,每一個都重要。

Field Ready,但未經實戰

軟件已經為倉庫做好準備。它處理了試算表做不到的情境 — 可變團隊人數、交叉飲食限制、成本匯總、營養分析。它在 320px 的手機上能用。它支援廣東話。

但它尚未經歷過的是:一個星期一的早上,補給主任要為一個 22 人的團隊打包,有三種飲食限制和一個預算。那個考驗即將到來。開啟這個項目的那張試算表將會是基準 — 不是因為它有錯,而是因為它代表了多年的機構知識,軟件需要先追平才能超越。

霧還沒散。但山脊線是走得通的。1

Footnotes

  1. 項目名稱的由來:ridgeline(山脊線)是你沿著山脈頂部走的那條線 — 一條最暴露的路徑,每個決定都很重要,因為你已經走上去了,無法回頭。五篇文章之後,這個名字仍然感覺恰如其分。 ↩