← 所有文章

表單準則:每個欄位都是一個問題

表單,是介面停止展示、開始發問的那一刻。每個欄位都是拋給一個本來想去做別的事的人的問題:多餘的欄位是一種強加,含混的欄位則是一次小小的背叛。表單的手藝可以濃縮成一套隨時放在腦子裡的準則——問得更少,排成一欄,標籤始終可見;依照人們自然作答的方式接受答案;等使用者說完一個念頭再驗證,而不是打字打到一半就動手;錯誤訊息要寫清楚該怎麼修;以及無論如何,絕不丟棄使用者已經輸入的內容。現實中絕大多數的表單摩擦,都是這幾條裡某一條被違反的結果。 {.answer-block}

TL;DR

  • 表單是一場對話,所以要像人一樣發問: 問題盡可能少,排成一欄依序推進,依主題分組——而每個欄位都得撐得住「發問的代價」這一問。
  • 版面早有定論: 單欄、標籤置於欄位上方、欄位寬度暗示答案的長度。多欄表單和浮動標籤這類花招,是拿真實的理解力去換想像中的精緻。
  • 依照人們給出答案的方式接受答案。 順手去掉多餘的空白,電話號碼什麼格式都收,把一個欄位保持完整而不是拆成三個輸入框——正規化是軟體的工作,不是使用者的工作。
  • 在欄位失去焦點時驗證,而不是每敲一個鍵就驗證;錯誤訊息要能教人修正: 哪裡出了錯、該怎麼改,就寫在出問題的那個欄位旁邊。
  • 使用者輸入的內容是神聖的。 送出失敗就清空表單,或者擺一個不肯說明理由的停用按鈕,會把一位樂意配合的參與者變成前參與者。

為什麼每個欄位都是一個問題

把表單當成一次訪談的逐字稿,它的品質立刻就變得可讀。一位稱職的訪談者會出於習慣問你的傳真號碼嗎?會在不說明理由的情況下要你交出出生日期嗎?會在你拼寫電子郵件拼到一半時打斷你,宣布它無效嗎?會因為你的郵遞區號裡多了一個空白,就把你說過的一切統統忘掉嗎?這些做法在真實上線的表單裡全都找得到對應版本,而使用者對它們的感受,和面對那位訪談者時一模一樣:無禮。

這個視角也直接推出了第一條、同時也是最重要的一條規則,它先於一切版面與樣式:每個欄位都必須以「發問的代價」為尺,證明自己存在的必要。 每多問一個問題,放棄率就上升一分;每收集一個答案,就多一份必須儲存、保護並為之負責的資料。表單設計中威力最大的動作是刪除——被你刪掉的那個欄位,勝過你可能施加在它身上的任何打磨。最經典的憑證來自 Expedia:把訂房表單裡「Company」這一個選填欄位刪掉,據報導每年價值約 1200 萬美元——在此之前有客戶在那裡填了往來銀行的名稱,結果地址驗證失敗。一個欄位,經過誠實的拷問,就贏過了任何一次重新設計。「選填」不構成理由;它只是較輕的強加,終究還是強加。只問完成這筆交易所必需的內容,把那少數真正選填的欄位標示為選填,其餘一律延後到關係夠穩固之後再說。(對立陣營的做法是給每個必填欄位加上星號;當幾乎一切都必填時,星號就成了壁紙。)

版面規則

表單版面是設計中少數幾個證據基本已成定論的角落,因此偏離它就等於主動選擇與使用者為敵:

單欄。 表單是一串依序拋出的問題,而單欄讓這個順序毫無歧義:作答、下移、完成。多欄版面在每一列都逼使用者做一次閱讀順序的判斷——橫著走還是直著走?——而人們的判斷並不一致,於是漏掉了自己根本沒看見的欄位。在最著名的那項眼動追蹤對照中,同樣的欄位排成一欄,比拆成兩欄大約快十五秒完成。例外是那些確實讀作同一個答案的複合項:同一列上的城市/州別/郵遞區號,拆成三段的日期。它們是一個問題穿了三個輸入框的外衣,而不是三個問題。

標籤置於欄位上方,始終可見。 標籤擺在欄位旁邊,會讓視線走出參差不齊的折線;標籤塞進欄位裡(把佔位文字當標籤用),則在使用者開始打字的那一刻消失,而那恰恰是最需要它的時刻——長表單填到一半,每個已填欄位都變成一個「這欄剛才問的是什麼」的盲盒。折衷方案浮動標籤雖然在取得焦點後依然保留,卻縮到了輔助文字的尺寸,還讓空欄位看起來像已填:同一筆交易的溫和版本,代價依舊由理解力支付。佔位文字是用來給格式提示的(「[email protected]」),永遠不該用來承載問題本身。

欄位寬度本身就是資訊。 一個和地址欄一樣寬的郵遞區號欄位,是在對答案的形狀說謊。依預期內容替輸入框訂尺寸——郵遞區號短、地址長——和用間距編碼分組是同一門手藝:讓幾何形狀默默傳達訊息。

依主題分組,並讓留白來完成分組。 聯絡方式、寄送、付款——相關問題成簇,簇與簇之間留有清晰的接縫,而接縫由留白構成,不是方框,也不是分隔線。讀起來像三個小主題的表單,在心理上比同樣的欄位堆成一整塊無差別石板要小得多。

輸入規則

輸入設計的主旨只有一句話:正規化是軟體的工作。 格式上的負擔無論多重,都由機器來扛,因為在意格式的本來就是機器。

  • 接受潦草的答案。 去掉頭尾空白——自動完成的電子郵件末尾那個空白,害掉的登入次數遠超它應得的份額。電話號碼帶連字號、句點、空白、括號,或者什麼都不帶,一律照收。卡號有沒有分隔都接受。只要解析得了,就去解析;因為你想要 5558675309 而拒絕 555 867 5309,無異於要使用者用手替你跑一遍字串格式化的程式碼。
  • 絕不拆分使用者心裡視為一體的東西。 電話號碼分三個輸入框、日期分三個下拉選單、驗證碼分成六個單字元格子外加手寫的焦點跳轉——每一種都把一個完整的心理答案拆成一道導覽謎題,而且通常會破壞貼上,也就是使用者手上最有效率的輸入方式。(版面規則裡的複合項例外依然成立:日期拆成三段手動輸入沒有問題——罪過在於下拉選單的繁文縟節和被搶走的焦點,而不是相鄰本身。至於驗證碼,經得起時間考驗的答案是一個輸入框加上 autocomplete="one-time-code"。)
  • 召喚正確的鍵盤。 在觸控裝置上,type="email"inputmode="numeric" 這類屬性,決定了使用者是在為此而生的鍵盤上敲地址,還是在符號面板裡層層翻找 @。代價不過一個屬性。
  • 讓瀏覽器幫忙。 正確的 autocomplete 值,能把十二個欄位的結帳流程變成老客戶的兩次點按。在地址和付款欄位上停用自動填入——通常只是某場從未發生過的資安審查留下的迷信——等於扔掉表單所能得到的最大一筆加速。
  • 手指落在哪裡,就在哪裡接住它。 欄位、欄位上的按鈕,以及任何可點按的元素,都要守住平台的最小觸控目標——iOS 上 44pt,Android 上 48dp。一個緊湊優雅、拇指卻按不準的欄位,只是一件穿著手機戲服的桌機表單。

鍵盤與自動填入這兩條規則加起來,也不過幾個屬性的成本:

<input type="tel" autocomplete="tel">              <!-- phone keypad, autofilled -->
<input inputmode="numeric" autocomplete="one-time-code">  <!-- digit pad, code autofills -->

驗證規則

驗證的時機,是表單最常顯露敵意的地方,而規則很簡單:等使用者說完一個念頭再回應。 每敲一個鍵就觸發,等於衝著一個才打了四個字元的人大喊「電子郵件無效!」——句子還沒說完就先挑毛病。相反的流派只在送出時驗證,而它有一位分量十足的辯護者:GOV.UK 的設計系統正是這麼做的,送出的同時在頁面頂端給出錯誤摘要,因為摘要可以朗讀給螢幕閱讀器,也給鍵盤使用者一個統一的修正起點。這是一個自洽的立場,專為無障礙是硬性限制的服務而調校。不過對大多數產品表單,我仍然站在失焦這一邊:使用者填完一個欄位、往下走,在兩個念頭的接縫處拿到回饋,而那個念頭還是熱的。(一處微調:已經被判定為無效的欄位,可以改成每敲一鍵就重新驗證,這樣修好的瞬間紅色狀態就消失,而不是整整落後一個欄位。)

錯誤文案要通過的是同一場對話測試。錯誤訊息不是判決,而是一份修正說明。「輸入無效」過不了這一關——到底哪裡無效?「這個電子郵件地址少了 @」就過得了。(而且這份說明必須與真實規則相符:卡號的合法長度是 12 到 19 位數,所以「必須是 16 位數」不是錯誤訊息,而是一個把所有 Amex 持卡人擋在門外的驗證瑕疵。)把訊息放在它所指的那個欄位旁邊,並以文字呈現——只靠顏色會把色盲使用者排除在外,而文字是一個不算繪任何內容的讀者唯一看得見的通道——再以程式把它與輸入框繫結(aria-describedby),讓輔助技術把錯誤連同欄位一起朗讀,而不是任它孤零零地滯留在畫面上。語氣保持陳述事實:表單的職責是讓使用者順利通過,而不是裁定誰有過錯。整條規則就濃縮在下面這一對範例裡:

<!-- before: a verdict, visually nearby, programmatically stranded -->
<label for="email">Email</label>
<input id="email" type="email">
<span class="error">Invalid input</span>

<!-- after: a repair instruction, announced with its field -->
<label for="email">Email</label>
<input id="email" type="email"
       aria-invalid="true" aria-describedby="email-err">
<span id="email-err">This email address is missing its @</span>

還有兩條結構性規則為這套準則收尾。絕不要把停用送出按鈕當成驗證策略——一個不作任何解釋的死按鈕就是一道謎題,而使用者的下一步動作是離開;讓他們送出,然後精確指出哪裡需要處理。(送出請求進行中為了防止重複扣款而暫時停用是另一回事:那是狀態,不是評判。)以及這套準則中最根本的一條:送出失敗必須原樣保留使用者敲下的每一個字元。 出錯就清空的表單,是把一個人幾分鐘的心血當著他的面燒掉。任何視覺上的精修都無法從這裡挽回。

化作檢查清單的準則

以上所有內容的可執行版本,任何表單上線之前都可以逐條對照:

規則 它擋掉的禍害
每個欄位要麼被論證,要麼被刪除 出於好奇心的提問買來的放棄率
單欄,複合項除外 閱讀順序含混造成的漏填
標籤置於上方,始終可見 填到一半的盲盒;把佔位文字當標籤
欄位寬度符合答案的形狀 幾何形狀對預期輸入說謊
依主題分組,接縫由留白構成 無差別的一整塊石板
接受任何可解析的格式 使用者替你手工執行格式化程式碼
一個答案,一個輸入框(複合項除外) 貼上被破壞;焦點跳轉謎題
正確的鍵盤與 autocomplete 觸控上翻找符號;本可兩次點按卻要十二次
遵守平台觸控目標(44pt/48dp) 拇指按不中的優雅欄位
失焦時驗證;錯誤訊息能教人修正、緊鄰欄位、以 aria 繫結 打字打到一半就被責備;錯誤與輔助技術失聯
絕不為了驗證而停用送出 死按鈕謎題
輸入在失敗之後依然存活 被清空的表單,以及再也不回來的使用者

在設計系統裡,這些規則會固化進表單元件本身——一個文字欄位出廠時就自帶上方的標籤插槽、下方的錯誤插槽和內建的驗證時機——於是準則預設成立,偏離反而需要額外的力氣。這和動態效果 token所依據的是同一套系統化論證:要麼把決定編碼一次,要麼在每個功能裡重新爭論一遍。

常見問題

表單應該用一欄還是兩欄?

一欄。單欄讓作答順序毫無歧義,完成速度也有可量測的提升;多欄版面會造成漏填,因為使用者對閱讀順序的判斷並不一致。例外是複合答案——城市/州別/郵遞區號——它本來就是一個問題,只是用相鄰的幾個輸入框來表達。

表單驗證應該在什麼時候觸發?

在失去焦點時——也就是使用者離開某個欄位時——而不是每敲一個鍵;對大多數產品表單來說,也不該累積到送出時一併拋出。按鍵驗證是在對尚未說完的答案挑毛病;只在送出時驗證則把所有失敗一次倒出來,不過在螢幕閱讀器朗讀是硬性限制的場景裡,GOV.UK 那種送出時的錯誤摘要才是正確選擇。已經被標示為無效的欄位,可以改成每敲一鍵複查一次,這樣修好之後錯誤訊息立刻消失。

可以拿佔位文字當欄位標籤用嗎?

不可以。佔位文字一旦被當成標籤,就會在使用者開始打字時消失,而那正是他需要回想問題內容的時刻;到複查時,每個已填欄位都成了沒有標籤的資料。請在欄位上方保留一個可見標籤,把佔位文字留給格式範例。

送出按鈕應該在表單驗證通過前保持停用嗎?

不應該。一個不作解釋的停用送出按鈕,是一條要使用者自己去診斷的死路。讓它保持可用,並在送出時把每個尚未解決的欄位連同旁邊具體而有指導性的錯誤訊息一併呈現出來——同時保留使用者已經輸入的一切。

相關文章

圖示是一套詞彙,不是裝飾

介面圖示像一門語言,人人都認得的詞只有十來個。預設就要配文字標籤,一個產品只用一套圖示家族,一個動作只對應一個隱喻。

9 分鐘閱讀

動態語法:動畫何時才配得上它佔用的影格

介面動畫是一門語法很小的語言:四檔時長、兩條緩動規則、一個直白的測試。說不出它為什麼要動,就刪掉。

7 分鐘閱讀

設計哲學:上田文人與減法設計

上田文人在二十年間只做了三款遊戲,把一切無助於那一種情感的東西全部拿掉。這是減法設計,來自那位砍掉椅子的畫家。

8 分鐘閱讀