主題與版面
小程序要看起來像 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 不支援)。