安安~我是ChiYu~
昨天,三份附件終於可以各自上傳、失敗與重試。可是我把活動名稱留空、Email 填錯,再按下「發布」,整份表單只亮起幾個紅框。為了排除活動頁自己的問題,我又把前幾天完成的「新增成員」表單抓回來重測,原始需求同樣很簡短:
幫我做一個新增成員表單,必填欄位錯了要顯示紅色。
昨天結尾留下的,是依照這句 Prompt 還原的第一版問題現場。

圖 1:和昨天結尾是同一張還原畫面。姓名留空、Email 格式錯誤、登入帳號太短,三個欄位卻只剩紅色外框可以辨認。
AI 的確把姓名、Email、登入帳號、幾個 Checkbox 和送出按鈕做了出來。送出失敗後再把欄位邊框改成紅色,也完全符合我寫下的要求。
然後就沒有然後了。
三個紅框很認真地告訴我「你填錯了」,卻沒有任何一個願意說先修哪裡、錯在什麼地方。
問題還是出在需求。紅框只能指出「這附近有事」,沒辦法回答哪一條規則沒通過、格式怎麼修、整份表單有幾個問題,也不會主動把焦點帶回第一個錯誤。
在 Vibe Coding 裡只寫「錯誤顯示紅色」,AI 很容易把驗證當成視覺效果。這次我要補的是一條修正路徑,讓使用者從送出失敗,一路走回正確欄位。
我先打開網站的 表單結構與驗證完整比較,看看哪些資訊該在輸入前出現,哪些要等出錯才上場。

圖 2:欄位名稱、填寫規則、單欄錯誤與整份表單摘要,各自在不同時機接手。
比較圖把紅框前後缺少的資訊攤開了。Label、Helper Text 與 Required Indicator 要在輸入前說明規則;Error Message 和 Validation Summary 則負責出錯後的修正。
我把「新增成員」表單拆成四個時刻。使用者不必認識八種術語,他只需要在每個時刻拿到下一步。
| 使用者正在做什麼 | 需要的元件或結構 | 畫面該回答什麼 |
|---|---|---|
| 剛看到欄位 | Label、Required Indicator | 這一欄要填什麼?一定要填嗎? |
| 還沒開始輸入 | Helper Text | 格式、上限或用途是什麼? |
| 面對一組選項 | Fieldset、Legend | 這幾個選項共同在決定什麼? |
| 目前欄位不符合規則 | Inline Validation、Error Message | 哪裡不對?要怎麼改? |
| 送出後還有多個問題 | Validation Summary | 還有幾件事沒修?我要回到哪裡? |
LumenDesk 的管理員要填姓名、公司 Email、設定登入帳號,再選通知管道。接下來八種資訊都圍繞這份表單出場,不另外換案例。
Label|欄位標籤 是欄位一直看得到的名字。像「成員姓名」與「公司 Email」,內容填進去後,Label 仍要留在畫面上。

圖 3:Label 與輸入框維持可見關係,欄位有內容時仍知道自己正在編輯什麼。
Placeholder 會在輸入後消失,不能代替 Label。W3C 建議使用 <label>,以相同的 for 與 id 連結輸入欄;這樣輔助科技能辨識欄位,點擊文字也會把焦點帶進控制項。W3C WAI:Labeling Controls
Helper Text|輔助文字 補充「怎麼填」或「為什麼要填」。公司 Email 欄位下方可以先說:「請輸入工作用 Email,邀請信會寄到這裡。」

圖 4:Helper Text 在輸入前交代 Email 用途與規則,不必等送出失敗才公布答案。
Helper Text 可以用 aria-describedby 與欄位建立關聯,讓輔助科技一起讀到說明。它負責補規則,仍然不能兼差當欄位名稱。MDN:aria-describedby
Required Indicator|必填標記 告訴使用者哪些資料一定要有。星號可以使用,前提是表單先說明星號代表必填,而且控制項本身也有 required 規則。

圖 5:必填規則同時有可見標記與欄位語意,不能只靠紅色猜意思。
如果必填的是一組 Checkbox 或 Radio,標記應放在整組的 Legend 附近,不要讓每個選項各自長出一顆星號。W3C 的表單驗證說明也建議,以可見文字和 required 同時呈現規則。W3C WAI:Validating Input
Fieldset|欄位群組 把相關控制項包成一個語意群組。LumenDesk 的 Email、站內通知與每週摘要不是三個互不相干的 Checkbox,它們共同回答「你想收到哪些通知?」

圖 6:Fieldset 把通知選項建立成一組,讓視覺排列與表單語意一致。
只放一個粗體小標,再把三個 Checkbox 排在下面,視覺上可能差不多,輔助科技得到的群組脈絡卻不同。Fieldset 解決的是結構,不是外框要不要畫出來。
這裡的「每週摘要」指一種可獨立訂閱的通知內容,所以可以和 Email、站內通知並列。如果需求其實是「每天、每週或每月寄一次」,那是在選頻率,而且一次只能選一個答案;這時就該換成 Radio。Checkbox 不會因為排得整齊,就自動把兩種問題合併成同一題。
Legend|群組標題 是 Fieldset 的題目,例如「你想收到哪些通知?」它說明整組控制項共同在決定什麼。

圖 7:Legend 替通知群組命名;每個 Checkbox 仍保留自己的 Label。
Legend 不能取代單一 Checkbox 的 Label,獨立欄位也不用為了排版硬塞進 Fieldset。先有共同問題,再使用這組結構。
W3C 的表單教學以 <fieldset> 與 <legend> 組織相關控制項,也提醒 Legend 要盡量簡短,因為部分螢幕閱讀器可能會在每個選項前一起讀出它。這兩個元素在畫面簡單時很容易被省略,等選項變多後,大家又開始猜每一組到底在問什麼。W3C WAI:Grouping Controls
Inline Validation|即時驗證 適合在規則已經能判斷時提早回饋。登入帳號至少 4 個字元,就等使用者離開欄位,或輸入達到可判斷長度後再檢查。

圖 8:Inline Validation 等到規則可判斷才回饋,初始空欄位不會先亮錯誤。
使用者剛 Tab 進空欄位,畫面就跳出「至少 4 個字元」,技術上很即時,操作上比較像搶答。初始狀態先保持安靜;真的輸入不足、含有不允許字元,或離開欄位後,再把規則說清楚。
@,就直接說缺了什麼Error Message|錯誤訊息 留在問題欄位旁,保留使用者原本的輸入,再說明錯誤原因與修正方式。

圖 9:錯誤的 Email 留在欄位中,訊息直接提供含有 @ 的格式範例。
「格式錯誤」仍然太省話。這裡會寫成「請輸入含有 @ 的公司 Email,例如 name@company.com」。W3C 也建議以 aria-invalid 標示錯誤欄位,並把錯誤訊息與欄位建立關聯。W3C:ARIA21
Validation Summary|驗證摘要 在送出失敗後出場。它放在表單前方,列出錯誤數量、欄位名稱、原因與頁內連結。

圖 10:Validation Summary 列出兩個問題並提供頁內連結,欄位旁仍保留各自修法。
摘要負責定位,欄位旁的 Error Message 負責修正。兩邊都要留著,長表單才不會變成「看完頁首提示,再努力記住自己要去哪裡」的記憶力測驗。
焦點要先停在摘要,還是直接回到第一個錯誤,沒有一條能套用所有表單的答案。重新載入整頁時,頁面標題與主標題也能回報錯誤;動態更新的長表單,則可以先讓摘要被宣告,再讓使用者從連結前往欄位。LumenDesk 這次採用另一條可驗收的路:摘要出現並列出總數,焦點直接回到第一個錯誤,摘要連結仍可前往其他欄位。重點不是背其中一套,而是操作後真的聽得到錯誤,也回得去修正。
我沒有先填資料,直接按下「建立成員」。頁首出現「請修正 2 個欄位」,列出「成員姓名:此欄位必填」與「公司 Email:請輸入含有 @ 的格式」;兩個欄位旁仍保留各自的錯誤訊息,焦點則回到第一個姓名欄位。
接著我點摘要中的「公司 Email」,焦點回到 Email 欄位。修正姓名與 Email 後,舊錯誤和摘要一起收起,其他已填入的非敏感資料也沒有被清空。
我接受這個結果,原因不是紅框夠醒目。摘要先交代兩個問題,頁內連結負責定位,欄位旁訊息繼續提供修法,焦點也真的回到可以動手修改的位置。這是 LumenDesk 這次選定的焦點策略,不是所有表單的唯一標準;它通過的理由,是這條路能從失敗一路走回完成。
如果 Vibe Coding 只寫「送出時顯示驗證錯誤」,通常只能得到紅框。我會把摘要、欄位訊息、焦點移動與資料保留全部寫進 Prompt,否則每個畫面單獨看都像有做,串起來卻走不回去。
W3C 建議在表單前方列出錯誤,讓每一項引用對應欄位、說明修正方式並提供頁內連結;欄位附近則保留更具體的回饋。W3C WAI:User Notification
走完空白送出後,我再把八種資訊放回整張表單。這張圖看的不是元件誰比較高級,而是它們應該在什麼時候出現。

圖 11:Label 與規則在輸入前出現;欄位錯誤留在旁邊;多個問題則由頁首摘要協助定位。
使用者還沒輸入時,Label、Helper Text 與 Required Indicator 先把題目說清楚。Fieldset 與 Legend 處理選項群組;單一欄位可以判斷錯誤後,Inline Validation 和 Error Message 才接手。送出後若同時出現多個問題,Validation Summary 再從頁首幫忙定位。
Validation Summary 不能取代欄位旁的訊息。它只負責把人帶到現場,真正的修法還是要留在欄位旁邊。
原始 Prompt 只規定錯誤要變紅。改寫後,我把輸入前的說明、出錯時機、摘要內容與焦點移動一起交代:
為 LumenDesk 建立「新增成員」表單,包含成員姓名、公司 Email、登入帳號與通知管道。
每個欄位都要有可見 Label,不能以 Placeholder 取代。成員姓名與公司 Email 為必填;在 Label 中以可理解的「必填」文字或已說明的標記呈現,控制項本身使用 required。表單開頭說明必填標記的意思。
公司 Email 的 Label 下方提供 Helper Text:「請輸入工作用 Email,邀請信會寄到這裡。」使用 aria-describedby 關聯說明。Email 格式錯誤時,保留使用者輸入,設定 aria-invalid,並在欄位附近顯示「請輸入含有 @ 的公司 Email,例如 name@company.com」。不要只畫紅色外框。
通知管道的 Email、站內通知與每週摘要放在同一個 Fieldset;Legend 使用「你想收到哪些通知?」。若整組必填,把規則寫在 Legend 或群組附近,不要讓每個選項各自冒出必填星號。
登入帳號可使用 Inline Validation,但初始空欄位不顯示錯誤。使用者離開欄位,或輸入已達可判斷長度後,才回饋至少 4 個字元、可使用英文字母、數字與連字號的規則;成功和錯誤都要有文字。
送出失敗時,在表單前顯示 Validation Summary,列出錯誤數量、欄位名稱、具體原因與頁內連結,並以動態訊息宣告錯誤總數。這個 Demo 顯示摘要後,把鍵盤焦點移到第一個錯誤欄位;點擊摘要連結時,則把焦點帶到對應欄位。摘要不能取代欄位旁的錯誤訊息。所有欄位需保留使用者已輸入的非敏感資料。
這份 Prompt 沒有指定紅框要多粗、紅色要多紅。它要求 AI 說清楚規則何時出現、錯誤怎麼修,以及送出失敗後焦點往哪裡走。
交付前,我會再跑一次完整路線:先送出空白表單,再填錯 Email、留空另一個必填欄位,接著從摘要點回欄位修正。舊錯誤要在修正後消失,沒有問題時摘要也要收起。
現在,空白送出會先列出兩個問題;點擊「公司 Email」能回到欄位,旁邊保留具體修法,其他已填資料也不會消失。修正完成後,摘要和舊錯誤一起收起。
這條路徑也能套回活動表單,讓數值、日期與附件錯誤回到各自的位置。原本只會亮紅框的畫面,終於知道怎麼把人帶回完成。
接著 AI 提出要讓內容管理「更完整」:六格驗證碼、標籤膠囊、色盤和整排編輯工具一次全上。功能看起來很多,管理員是不是真的需要,又是另一回事。
我把這份「功能全開」的第一版還原到網站。六格 OTP、四顆標籤、一套完整色盤和十二顆編輯工具排得整整齊齊,採用理由則一格都沒填。

圖 12:六格驗證碼、標籤膠囊、完整色盤與大型 Editor 同時出現;畫面沒有先回答哪些工作真的需要這些控制。
明天,我不會急著全部收下。我會先把這些進階輸入元件拿掉,看看管理員的工作到底有沒有變難。
aria-invalid 指出欄位錯誤
aria-describedby
資料查閱:2026-08-07。