AI 工具 21 min read

mobile-native skill 實測:19 項審查抓出 14 個手機坑

mobile-native 是 Emil Kowalski 以 MIT 開源的 AI agent skill,專抓網頁在手機上露餡的 11 種症狀,例如 hover 黏住、100vh 與輸入框放大。我點名它審自己的部落格,19 列抓出 14 個問題、修完再審又找到一個新坑,但模擬器只能證明 CSS 上線,iOS 實機仍要拿手機驗。

mobile-native skill 實測:19 項審查抓出 14 個手機坑
本文目錄 · 11
懶人包
  • mobile-native 是 Emil Kowalski 收在 emilkowalski/skills(MIT)裡的 AI agent skill,專門處理「網頁在手機上像網站、不像 App」的 11 種症狀,2026-09-15 加入 repo。
  • repo 在 2026-10-21 是 48,160 stars,但這是 15 支 skill 共用的數字,不是 mobile-native 一支的。
  • 我用 Claude Code(Sonnet)點名 /mobile-native 審自己部落格的修前版本:19 列裡 14 列有問題,包含 12px 搜尋框會讓 iOS 自動放大、選單用 100vh、沒有 viewport-fit=cover。一次約 108 秒、0.43 美元。
  • 修完再審一次剩 10 項,其中 1 項是修法本身帶出來的新坑:底部導覽加了安全區,body 的留白卻沒跟著加。
  • Playwright 驗證線上版:觸控模擬下搜尋框 16px、滑鼠視窗同樣 390px 寬仍是 12px,選單高度剛好等於可視高度。iOS 實機行為我沒有驗證,模擬器證明不了安全區和網址列。
  1. mobile-native skill 是什麼?
  2. 它什麼時候會被觸發?
  3. 11 種症狀各在講什麼?
  4. 怎麼安裝和點名?
  5. 修前審查:它在我的部落格找到什麼?
  6. 怎麼修:commit 07d7267 的三段 diff
  7. 線上驗證:手機寬度量到什麼?
  8. 修後再審:還剩哪些問題?
  9. 有哪些限制和坑?
  10. 適合誰、不適合誰?
  11. 延伸資源

📱 mobile-native skill 是什麼?

mobile-native 是一份 311 行的 SKILL.md,給 Claude Code 這類 AI coding agent 讀。它自稱是「修東西的 skill」,只做一件事:把網頁在手機上露餡的地方一個一個拿掉。

它管的是平台層,也就是 viewport、觸控、捲動、安全區和瀏覽器外框。動畫怎麼設計交給同 repo 的 animate,動畫審查交給 review-animations,React Native 交給 animate-expo,這些它都不碰。

整份文件的核心論點有兩個,我覺得很準:

  • 手機上「感覺卡卡的」多半不是動畫問題,而是點擊延遲、點下去閃一塊灰色、或 hover 狀態黏住不放。
  • 桌機 Chrome 的裝置模擬不是手機。文件裡列的問題幾乎都不會在模擬器重現,只在模擬器測就會全部上線。
第二點後面會反咬我一口,我在「限制」那節會講。

🎯 它什麼時候會被觸發?

frontmatter 只有 name 和 description 兩個欄位,沒有 disable-model-invocation,所以模型會依描述自己決定要不要載入。描述寫的觸發情境有:

  • 網頁 App 在為手機開發、或在手機上被審查時
  • 「在 Chrome 正常、在我手機上怪怪的」
  • 做 PWA、bottom sheet、輪播、全螢幕版面或任何觸控互動時
會自動觸發不代表每次都會。同一位作者的 apple-design 我測過沒點名 3 次只觸發 1 次,mobile-native 我這次沒做觸發率實驗,所以實測一律用點名的方式跑。

另外文件裡有一段「初次回應」規則:沒帶具體問題就叫它時,它只會回一句固定的開場白,什麼都不做。所以點名時記得把要它做的事寫在同一句。

📋 11 種症狀各在講什麼?

文件的主體是一張症狀對照表,每一列再展開成一節講原因和程式碼。我依性質分成五組轉述:

分組症狀它給的修法重點
觸控回饋hover 點完黏住所有 :hover 包進 @media (hover: hover) and (pointer: fine)
觸控回饋點擊時閃灰框或藍框關掉 -webkit-tap-highlight-color
觸控回饋點下去反應慢在按下那一刻就給回饋(:active),加 touch-action: manipulation
觸控回饋長按會選到按鈕文字只在控制元件上加 user-select: none,不要加在 body
尺寸與版面高度不對App 外殼用 100dvh,首屏用 100svh
尺寸與版面點輸入框整頁放大輸入框字級至少 16px
尺寸與版面內容停在瀏海外面viewport-fit=cover 搭配 env(safe-area-inset-*)
捲動與手勢下拉重新整理搶走捲動overscroll-behavior
捲動與手勢輪播被拖成垂直捲動手勢區加 touch-action: pan-y
瀏覽器外框狀態列顏色對不上每個色彩配置各一條 theme-color
測試在 Chrome 對、在手機錯上實機測
表格之外還有三塊值得看:
  • 5 條硬規則:每個修法都要附理由;用媒體查詢判斷能力,不准用 user agent 或螢幕寬度猜是不是觸控;觸控和滑鼠會同時存在(iPad 接觸控板、觸控筆電);絕對不准禁止縮放;沒上實機不算修好。
  • Baseline:新專案第一個元件之前就該放的底線設定,包括 interactive-widget=resizes-content(讓 Android 鍵盤彈出時縮小版面)和 -webkit-text-size-adjust: 100%(橫向時不自動放大字)。
  • Never Ship 表:12 條上線前自檢,例如 100dvh 不要用在行銷首屏(捲動時會跳版)、env() 沒配 viewport-fit=cover 數值永遠是 0、從裝置模擬判定「修好了」也在禁止之列。
輸出格式它也規定好了:修完只寫三段,「哪裡錯」「改了什麼」「哪些要拿手機確認」,不要寫成長篇報告。

⚙️ 怎麼安裝和點名?

只裝這一支:

npx skills@latest add emilkowalski/skills --skill mobile-native

我在 2026-10-21 用 skills CLI 1.7.4 跑過,加 --agent claude-code --copy -y 會跳過互動選單,直接把 SKILL.md 複製到專案的 .claude/skills/mobile-native/,並產生一份 skills-lock.json 記下來源與雜湊值。安裝過程會顯示三家安全掃描的結果,這支是 Safe/0 alerts/Low Risk。

點名就是在需求前面打 /mobile-native。我的審查指令是這樣開頭的:

/mobile-native 只審查、不要修改任何檔案。審查目前資料夾(Astro + Tailwind v4 部落格的 src/)在手機瀏覽器上的問題。

注意文件的預設行為是「直接修」,它的 Output 段第一句就是套用修法。只想要報告的話,要像上面那樣明講不准改檔。

🔍 修前審查:它在我的部落格找到什麼?

我把部落格修正前(commit 07d7267 的上一版)的 src/ 複製到一個乾淨資料夾,裝好 skill,用 claude -p 加 Sonnet 跑上面那段指令,並要求每一列標明「看程式碼就能確定」還是「需要實機」。

結果:約 108 秒、0.43 美元、20 輪,用了 Glob 1 次、Grep 10 次、Read 8 次。產出 19 列,其中 14 列有問題、3 列確認沒問題、2 列是實機測試與 Baseline 的補充。

有一點要照實說:這次輸出的紀錄裡看不到 skill 全文被展開的痕跡,我判斷它有生效的依據是輸出逐字使用了 skill 症狀表的英文名稱,以及它提到只有 Baseline 段才寫的 interactive-widget 和 -webkit-text-size-adjust。

本文要修的三項就是從這份清單來的:

症狀它指出的位置證據
點輸入框整頁放大BlogFilters.tsx:176 與會員登入、留言板等 5 個元件搜尋框是 text-xs(12px),其他輸入框是 text-sm(14px),都低於 16px
高度不對NavbarNeub.astro:319手機選單面板寫死 height: calc(100vh - 60px)
內容停在瀏海外面RootLayout.astro:112、NavbarNeub.astro:357viewport 沒有 viewport-fit=cover,全站沒有任何 env(safe-area-inset-*),底部導覽沒有底部留白
其他有問題的 11 列包括:自訂 CSS 的 :hover 都沒包媒體查詢、沒有關掉點擊高亮、沒有 touch-action、選單面板缺 overscroll-behavior、控制元件長按會選到字、沒有 theme-color。

它也有判斷「不用改」的地方,而且判斷對了。Tailwind v4 的 hover: 類別本身就會編譯成包在 @media (hover: hover) 裡的規則,它把這列標成未發現;viewport 沒有 user-scalable=no,它也明講保持現狀。

🛠️ 怎麼修:commit 07d7267 的三段 diff

部落格的 commit 07d7267(2026-10-21)改了 4 個檔案。和這三個症狀有關的片段如下。

一、輸入框 16px(iOS 點擊放大),global.css:

+@media (pointer: coarse) {
+  input:not([type="checkbox"], [type="radio"], [type="range"]), textarea, select {
+    font-size: max(16px, 1em);
+  }
+}

用 (pointer: coarse) 而不是 max-width,是照 skill 的硬規則「依能力判斷,不依寬度判斷」:桌機上把視窗縮窄,搜尋框維持原本 12px 的設計;只有主要指標是手指的裝置才拉到 16px。max(16px, 1em) 保留了原本就比 16px 大的輸入框。checkbox、radio、range 沒有文字可放大,所以排除。

二、選單 100vh 改成 dvh,NavbarNeub.astro:

-<div class="mobile-menu-panel fixed ... overflow-y-auto" style="top: 60px; height: calc(100vh - 60px);">
+<div class="mobile-menu-panel fixed ... overflow-y-auto" style="top: 60px;">
+  .mobile-menu-panel {
+    height: calc(100vh - 60px);
+    height: calc(100dvh - 60px);

高度從行內 style 搬到 <style> 區塊,寫兩行:舊瀏覽器看不懂 dvh 會用第一行的 100vh,新瀏覽器用第二行。手機網址列展開時,100vh 是網址列收起來的高度,面板底部會超出可視範圍;dvh 會跟著網址列伸縮。選單是貼著視窗的外殼元件,所以用 dvh 而不是 svh,這和 skill 的分法一致。

三、viewport-fit=cover 加 safe-area-inset-bottom,RootLayout.astro 和 NavbarNeub.astro:

-<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
+<meta name="theme-color" content="#FFFFFF" />
-  class="mobile-bottom-nav md:hidden fixed bottom-0 ... grid grid-cols-5"
+  class="mobile-bottom-nav md:hidden fixed bottom-0 ... grid grid-cols-5 pb-[env(safe-area-inset-bottom)]"

兩步缺一不可:viewport-fit=cover 讓頁面畫到瀏海和 Home 指示條底下,env(safe-area-inset-bottom) 再把底部導覽的內容推回安全範圍。只加後者、沒加 meta,env() 的值永遠是 0。

同一個 commit 也把 .lift:hover 包進 (hover: hover) and (pointer: fine),並補了 80ms 的 .lift:active 按下回饋,對應「hover 黏住」和「點下去反應慢」兩列。

🧪 線上驗證:手機寬度量到什麼?

修完上線後,我用 Playwright 對 https://yanchen.app/blog/ 開兩種瀏覽環境,讀瀏覽器實際算出來的值:

項目觸控模擬(iPhone 13 裝置設定)滑鼠視窗(390×844)
pointer: coarse是否
搜尋框 computed font-size16px12px
搜尋框 class仍是 text-xs仍是 text-xs
viewport metawidth=device-width, initial-scale=1.0, viewport-fit=cover同左
theme-color#FFFFFF同左
選單面板高度604px(top 60、bottom 664,等於可視高度 664)未量(桌機不顯示)
iPhone 13 的裝置設定在 Playwright 裡是螢幕 390×844、可視區 390×664,並開啟觸控。兩邊寬度都是 390px,搜尋框一個 16px、一個 12px,代表修法是依指標能力分流,不是依寬度。 同樣 390px 寬,觸控裝置的搜尋框算出 16px,滑鼠視窗仍是 12px 觸控模擬下的部落格列表頁,搜尋框與分類篩選

打開漢堡選單後,面板從 60px 一路延伸到可視區底部:

觸控模擬下打開的手機選單,7 個項目都在可視範圍內,底部導覽列疊在面板最下方

這些數字能證明的是:CSS 和 meta 已經上線、規則有在觸控環境生效。不能證明的部分我留到限制那節講。

🔁 修後再審:還剩哪些問題?

我對修後版本跑了同一段指令:約 113 秒、0.48 美元、22 輪。結果是 10 項問題、2 項未發現、1 列實機提醒。

三個修法都被它認可:輸入框那列變成「未發現」,並點名 pointer: coarse 規則已涵蓋 12px 的搜尋框;選單那兩行 100vh/100dvh 被判定是正確寫法。

剩下的 10 項大多是修前就有、這次沒處理的:

  • 其他 :hover(文章內連結、程式碼複製鈕、主題切換、卡片封面)沒包媒體查詢
  • 沒有關掉點擊高亮、沒有 touch-action: manipulation
  • 底部導覽只有 hover:,觸控時按下去沒有任何回饋
  • 選單面板和留言列表缺 overscroll-behavior: contain
  • 控制元件沒有 user-select: none
  • 只有一條 theme-color,切到 anti 主題時導覽列變米色、狀態列卻還是白色
  • 一個主題和 404 頁還在用 100vh/min-h-screen
真正有意思的是這一條,修前審查沒有、修後才出現:
NavbarNeub.astro:400 的 body { padding-bottom: 64px } 沒有加安全區。底部導覽現在是 64px 加上安全區高度,頁面底部卻只留 64px,頁尾最後一段會被導覽列蓋住。

我回頭 grep 確認它說的是對的。這是修法帶出來的副作用:導覽列變高了,替它讓位的留白沒跟著變。建議修法是 padding-bottom: calc(64px + env(safe-area-inset-bottom, 0px))。這條目前還沒修。

另外它也提醒橫向時左右安全區沒處理,兩條導覽列都只加了 bottom。

⚠️ 有哪些限制和坑?

一、模擬器量不到它最在意的那幾項。 Playwright 用的 Chromium 沒有網址列也沒有瀏海,env(safe-area-inset-bottom) 量到 0px,100vh 和 100dvh 算出來一樣,所以表格裡「604px 等於可視高度」不能證明 dvh 的效果。iOS 點輸入框會不會放大、Home 指示條有沒有壓到導覽列、網址列收合時選單會不會被切,我都沒有在實機驗證。這正是 skill 自己 Never Ship 表裡寫的「不要從裝置模擬判定修好了」。

二、選單底部和底部導覽重疊。 截圖裡選單面板延伸到可視區最底,底部導覽列疊在它上面(兩者在同一層)。目前 7 個項目剛好放得下,面板最後約 56px 是空白;如果之後選單再多一項,最後一項捲到底時會落在導覽列後面。

三、pointer: coarse 不是「是不是手機」。 它問的是主要指標裝置。觸控筆電如果主要指標是觸控板,就會拿到 12px;平板接滑鼠也一樣。這是 skill 刻意的設計,但要知道修法涵蓋的範圍是「主要用手指點的裝置」。

四、不要照表全收。 症狀表寫「下拉重整」的修法是在 html, body 加 overscroll-behavior: none,但部落格是文件型網站,使用者會想下拉重新整理。審查結果自己也說了根節點不必設 none,只在選單面板這種內部捲動區加 contain。文件裡其實也有寫「捲動型文件可以拿掉這行」,只是在 Baseline 段,不在症狀表。

五、theme-color 只放一條是 skill 明列的禁止事項。 07d7267 加的是單一 #FFFFFF,部落格沒有深色模式所以影響小,但 anti 主題切換時要用 JavaScript 同步更新,這也還沒做。

👥 適合誰、不適合誰?

適合:

  • 網站有大量手機流量、但開發和測試都在桌機上做的人
  • 做 PWA、底部導覽、bottom sheet、全螢幕版面的前端
  • 想在上線前快速盤點「哪些看程式碼就能確定、哪些一定要實機」的人。它的確定度分類對排測試優先序很有用
不適合:
  • 寫原生 iOS/Android App。這支只處理瀏覽器,原生 Swift 看同 repo 的 write-swift,React Native 看 animate-expo
  • 想調整動畫手感。那是 animate 和 apple-design 的範圍
  • 沒有任何手機可以測的情況。它的一半價值在告訴你「這項要上實機」,沒有實機就只拿到一半

📚 延伸資源

下一步:如果你的網站有固定在底部的元件,先 grep 一次 100vh 和 safe-area。兩個都有、而且 viewport-fit=cover 也在,再拿手機實際點一次輸入框和選單,這兩項模擬器騙不了你也幫不了你。
author
陳
陳彥彤

AI 工程師 · AI 顧問。Java 後端 8 年、AI 工程師 2 年。AI 內訓 · AI 導入顧問 · 前後端與雲端培訓。

support

覺得文章有用可以到 GitHub 給個 star,或是透過信箱聊聊 AI 內訓、AI 導入顧問或前後端 / 雲端培訓。