Theme / v2.15.0

UI UX Pro Max

把模糊需求變成可實作、可驗收的介面

實戰範例

實戰範例 004:建立可恢復的無障礙退款表單

從欄位語意、即時驗證到提交狀態,逐步建立滑鼠、鍵盤和讀屏器都能完成的退款申請表單。

建立可恢復的無障礙退款表單

專案情境

使用者要申請退款,表單包含原因、金額、附件和備註。送出失敗時不能清空內容,也不能只顯示一句「發生錯誤」。

1. 從 HTML 語意開始

<label for="amount">退款金額</label>
<input
  id="amount"
  name="amount"
  inputmode="decimal"
  aria-describedby="amount-help amount-error"
  aria-invalid="true"
/>
<p id="amount-help">最高可申請 NT$10,000。</p>
<p id="amount-error" role="alert">請輸入不超過可退款金額的數字。</p>

label 建立欄位名稱,aria-describedby 串起說明與錯誤,aria-invalid 表示目前狀態。錯誤文字仍然要在畫面上可見。

2. 選擇驗證時機

使用者離開欄位時可以檢查格式;送出時檢查整份表單;輸入過程不要在每一個字元都顯示錯誤,否則使用者還沒完成就被打斷。驗證訊息要回答三件事:哪裡錯、為什麼錯、怎麼修正。

function validateAmount(value: string, maximum: number) {
  const amount = Number(value);
  if (!value) return '請輸入退款金額。';
  if (!Number.isFinite(amount) || amount <= 0) return '請輸入大於 0 的數字。';
  if (amount > maximum) return `退款金額不能超過 NT$${maximum.toLocaleString()}。`;
  return null;
}

3. 處理送出狀態

async function submit(form) {
  setState({ status: 'submitting' });
  try {
    await createRefund(form);
    setState({ status: 'success', message: '退款申請已送出。' });
  } catch {
    setState({
      status: 'error',
      message: '送出失敗,資料已保留,請稍後重試。',
    });
  }
}

送出中停用按鈕並提供進度;失敗後恢復按鈕、保留輸入並把焦點移到錯誤摘要或第一個錯誤欄位。

4. 用鍵盤驗證

依序使用 Tab 進入所有欄位、Space 選擇選項、Enter 送出、Shift+Tab 返回。確認焦點順序符合閱讀順序,錯誤訊息出現時沒有把焦點送到不相關的位置。

驗收

  • 每個欄位有 visible label、說明和必要時的錯誤訊息。
  • 錯誤不只用紅色表達,讀屏器也能讀到。
  • 送出失敗時資料和焦點都可恢復。
  • 手機軟鍵盤不會遮住目前欄位或送出按鈕。

延伸練習

加入附件上傳的檔案格式、大小錯誤和上傳中狀態,並設計取消上傳與重新選檔的操作。