Skip to content

主題與版面 ​

小程序要看起來像 App 的一部分。容器在每個 HTML 頁的 <head> 最前面注入一份 CSS(__im/theme.css),把 App 的設計 token 變成 CSS 變數,掛在 :root。

一律使用 CSS 變數 ​

css
.card {
  padding: var(--im-space-md);
  border-radius: var(--im-radius-card);
  background: var(--im-color-surface);
  color: var(--im-color-text);
  font-size: var(--im-font-body);
}
  • 顏色、間距、圓角、字級不准寫死數值,一律用 --im-* 變數。完整清單與目前的值見主題變數。
  • :root 已經設定 background-color: var(--im-color-background) 與 color: var(--im-color-text)。
  • 變數會隨 App 改版而調整,用變數的小程序不必跟著改。
類別變數例子
顏色--im-color-*--im-color-primary、--im-color-text-secondary、--im-color-surface、--im-color-danger;主色上的文字用 --im-color-on-accent
間距--im-space-*xxs 2、xs 4、sm 8、ms 12、md 16、ml 20、lg 24、xl 32、xxl 48(px)
圓角--im-radius-*xs、sm、md、card、sheet、pill
字級--im-font-*large-title、title1~title3、headline、body、callout、subhead、footnote、caption、caption2
安全區--im-safe-top、--im-safe-bottom見下方

深色模式 ​

  • 深淺色跟隨 App 的設定(App 可以設成淺色、深色或跟隨系統),變數的值會自動切換。只用變數的畫面不需要做任何事。
  • 使用者也可以換主題色、開增強對比:--im-color-primary 不一定是橘色。放在主色上的文字與圖示一律用 --im-color-on-accent(深色模式下某些主題色會改成深色字),不要寫死白色,也不要用 --im-color-on-primary(固定白色)。
  • 少數需要分開處理的地方(例如換一張深色版的插圖),用 <html> 上的 data-im-theme 屬性:
css
.hero { background-image: url('./hero-light.webp'); }
[data-im-theme='dark'] .hero { background-image: url('./hero-dark.webp'); }

不要用 prefers-color-scheme

prefers-color-scheme 反映的是系統設定。使用者在 App 裡選了深色、系統卻是淺色時,兩者會不一致。請用變數或 data-im-theme。

  • :root 也設定了 color-scheme,表單控制項與捲軸會跟著深淺色。
  • 使用者在小程序開著時切換深淺色,容器會即時更新變數;需要在 JS 裡反應時用 sdk.theme.onChange。

字級 ​

  • --im-font-* 已經乘上使用者的系統字級:使用者把字調大,變數就變大。請用這些變數,不要寫死 px,也不要用以 html 字級為基準的 rem 自己算。
  • 建議在 html 加上 text-size-adjust: 100%,避免 WebView 再自動放大一次。
  • 版面要能承受大字級:文字容器不要固定高度,單行文字用 text-overflow: ellipsis,多行內容讓它自然長高。
  • 字型用系統字型(system-ui, -apple-system, ...),不要打包字型檔。

安全區 ​

  • 導覽列由容器畫,你的頁面從導覽列下方開始,所以 --im-safe-top 目前是 0px。
  • --im-safe-bottom 是底部的 Home 指示條高度(iPhone 約 34px;沒有指示條或鍵盤打開時是 0px)。固定在底部的元素要加上它:
css
.bottom-bar {
  position: fixed;
  inset: auto 0 0;
  padding-bottom: calc(var(--im-space-sm) + var(--im-safe-bottom));
}
  • index.html 的 viewport 要有 viewport-fit=cover。

鍵盤 ​

輸入框聚焦時,容器會把小程序的可視區域縮到鍵盤上方。不要監聽 resize 自己計算鍵盤高度,也不要用 100vh 撐滿畫面後假設高度不變;用 flex 版面或 min-height: 100% 讓內容跟著可視區域。(100dvh 要 iOS 15.4 起才支援。)

不讓版面跳動 ​

  • 圖片、影片、非同步載入的區塊要先保留尺寸(width/height 或 aspect-ratio),載入後不推動其他元素。
  • 首屏骨架要和實際內容同尺寸:同樣的行高、同樣的列數,切換時位置不變。
  • 載入中、錯誤、空資料三種狀態切換時,上方元素的位置保持不變。
  • 先畫好再呼叫 sdk.ready(),見生命週期。

用原生元件 ​

Toast、對話框、選單、載入進度條一律呼叫 sdk.ui,不要自己畫:外觀、無障礙、深色模式都和 App 一致。導覽列與標題也由容器畫,用 sdk.ui.setTitle 改標題。

觸控 ​

  • 可點擊的元素至少 44×44 px。
  • 按下要有回饋(:active 改變底色,使用 --im-color-pressed);重要操作可以加上 sdk.system.haptics。
  • 按鈕加上 touch-action: manipulation,避免雙擊縮放延遲。

相容性 ​

App 最低支援 iOS 15,畫面層跑在 iOS 15 的 WKWebView(等同 Safari 15)。CLI 會把 JavaScript 語法轉譯到 Safari 15,但不會幫你轉換 CSS 新功能。

  • 用到的 CSS 功能都要在 Can I use 確認 Safari 15 支援。
  • 常見的地雷:color-mix()(Safari 16.2)、容器查詢 @container(Safari 16)、:has() 與 dvh/svh 單位(Safari 15.4,iOS 15.0–15.3 不支援)。