2023 年 5 月,我寫了三個線上心理測驗,塞在同一個 Django 專案裡跑在 Heroku 上。2026 年 7 月,我把它們全部重寫了一次——同一份題目、同一個作者,中間隔了三年。真正讓我動手的不是技術,是我終於看懂了一句三年前就有人跟我說過的話。
客戶轉述給我:「學員說手機上不好填。」
我當時的判斷很快:能填、填得完、會跑出結果、沒有任何錯誤訊息。所以它不是 bug,是體驗問題;不是必須,是有空再說。然後它就擱了三年。
這個判斷有個我當下完全沒意識到的前提:我是在桌機上驗收的。滑鼠點得到,我就看不到問題。
我設計的時候,腦袋裡的畫面是「一個人坐在電腦前慢慢填一份問卷」。實際發生的畫面完全是另一回事。而我從來沒問過。
重寫之前我先讀了一遍 2023 年的版本。那句「手機上不好填」不是抽象的抱怨——它在程式碼裡留下了三個很具體的痕跡。
| 測驗 | 題數 | 版面 |
|---|---|---|
| 樂觀測驗 | 48 | 單一表單,一頁到底 |
| 品味量表 | 24 | 單一 <table>,一頁到底 |
| 生涯抉擇 | 24 | 單一 <table>,一頁到底 |
最長的一份 48 題;三份加起來 240 個 radio,每一份都塞在同一個 <form> 裡。桌機上這叫「一目瞭然」,手機上這叫「捲三十秒還在第 11 題」。
每一題的四個選項,我都是這樣寫的:
<!-- 第 1 題 --> <input type="radio" name="question1" id="inlineRadio1" value="4"> <label for="inlineRadio1">1</label> <!-- 第 2 題……id 一模一樣 --> <input type="radio" name="question2" id="inlineRadio1" value="1"> <label for="inlineRadio1">1</label>
id="inlineRadio1" 在同一份文件裡出現了 24 次。HTML 的 id 必須唯一,重複時 for 只會綁到文件裡第一個符合的元素——也就是說,第 5 題那個寫著「1」的文字標籤,點下去選到的是第 1 題。
這裡有個很殘忍的細節:用滑鼠的人不會遇到。滑鼠會精準點在那個小圓圈上,圓圈本身是正常的。只有用手指、習慣點文字比較好按的人,才會踩到。而且它不會報錯、不會有紅字——你只是安靜地答錯了一題,然後拿到一份安靜地算錯的分數。
整個 2023 版本裡,只有一段 media query 是為小螢幕寫的。它是這樣寫的:
@media only screen and (max-width:990px){
input[type="radio"] {
transform: scale(0.5); /* 調整此值以縮小元件大小 */
}
}
Bootstrap 的 radio 預設大約 16px,縮成 0.5 倍之後大約 8px。而觸控目標的建議尺寸是 44px。
我看到這段的時候愣了一下,因為我完全記得當時在想什麼:手機上排版太擠了,那就把東西縮小。在桌機的思維裡,這是一個完全合理的反應——畫面不夠用就縮,縮完就排得下了。
問題是手機上「排得下」跟「點得到」是兩件事。我把版面問題解決了,代價是把使用者要碰的東西縮到剩下一半。
重寫的時候我沒有拿著「要用 Next.js」的清單,我拿的是那個我三年前沒有的畫面:課堂上、一群人、手機、隨時會被打斷。從那個畫面往回推,該做什麼其實很好決定。
// 每次作答變動就存回 localStorage,進頁時還原
localStorage.setItem(storageKey, JSON.stringify({ answers, index }));
存的是答案加上目前題號,所以回來的時候不只答案還在,連停在第幾題都一樣。讀寫都包在 try/catch 裡——無痕模式或擋了 storage 的瀏覽器會直接丟例外,那種情況下測驗要能照常做完,只是不會被記住而已。
2026 版為了「整頁不捲動」,做了一個叫 AutoFit 的元件:量一次可用空間、量一次內容的自然尺寸,算出倍率把內容縮到剛好塞得下。
// 只縮不放大(最大 1),取高/寬兩者較嚴格的比例 const s = Math.min(1, availH / needH, availW / needW); inner.style.transform = `scale(${s})`;
看到這裡你可能會覺得:等等,這不是跟 2023 年那個 scale(0.5) 一樣嗎?
形式上一模一樣,效果剛好相反,而差別只有一個:縮的是誰。
scale():左邊縮的是觸控目標,右邊縮的是承載版面的容器。2026 版的選項不是 radio 圓圈,是一個 width: 100% 的按鈕,內距上下各 11–15px、字級 16–18px——整條都能點,實際高度落在 44px 以上。就算 AutoFit 把整塊縮到 0.8 倍,它還是比 2023 年那個 8px 的圓圈大得多。
縮版面是為了讓內容不用捲;縮控制項是為了讓內容排得下。前者服務的是使用情境,後者服務的是我的排版焦慮。
| 症狀 | 2023 年我的處理 | 2026 年的處理 |
|---|---|---|
| 手機畫面塞不下 | 把控制項縮小一半 | 一次只出一題,整塊等比縮放 |
| 題目很多、很長 | 全部列出來,讓使用者自己捲 | 進度條 + 第 N / 總數 |
| 漏答 | required,送出時擋下來 | 答完才會前進,不會有漏答 |
| 中途離開 | 重來 | 存答案與題號,回來接著填 |
| 點不準 | (沒有意識到這是個問題) | 整條 44px 以上的按鈕 |
「不是必須」從來不是一則回饋的屬性,而是我判斷的結果——而我判斷時用的,是我自己的使用情境。回饋在現場是一個畫面(三十個人低頭戳手機、有人點了兩次還是沒選到),經過轉述到我手上只剩一句話,我就照那句話的份量排了優先級。三年後我學到的不是新框架,是先問一句「他們是在什麼情況下用的」。





