樣式與 Token

framework 透過 Nuxt module 注入一套 CSS 基礎層:reset、響應式工具類、設計 Token(CSS 變數),以及可選的暗色主題覆蓋。所有 Token 是單一事實源——原生 CSS 的 var(--primary) 與 Tailwind 的 bg-primary 共用同一套底層變數,業務覆蓋變數即同時影響兩邊。

本頁講「有哪些 Token、怎麼用、Tailwind 怎麼對應」。CSS class 命名 / 單位約束 / 硬編碼零容忍等編碼規範見 開發規範 · CSS;主題切換(亮 / 暗 / 跟隨系統)見 主題系統。

CSS 基礎層與注入

定位:基線樣式非零樣式(framework#210)——swift-web 元件庫不是零樣式 headless,每個元件預設自帶「基本能用」的簡潔基線(版面不塌陷 / 不黏連、彈層定位正確、狀態回饋可辨);「醜但能用」合格。視覺細節由專案 Token / CSS 覆蓋。

framework 隨 npm 套件發布 9 個樣式檔案,由 module 按 swiftConfig 注入:

檔案作用注入條件
tokens.css:root 設計 Token(單一事實源)+ 基礎元素樣式永遠注入(無開關)
reset.css精簡 resetstyles.reset !== false(預設開)
responsive.css斷點 + .sw-* 工具類 + 觸控優化styles.responsive !== false(預設開)
theme.css暗色 Token 覆蓋(@media + [data-theme])theme.mode 為 system / toggle(固定 dark / light 改注入對應 color-scheme 宣告)
color-scheme-dark.css宣告 color-scheme: dark,讓瀏覽器原生 UI(捲軸 / 表單控件等)跟隨固定深色站(深色調色盤由 applier 的 overrides.css 提供)theme.mode 為 dark
color-scheme-light.css宣告 color-scheme: light,鎖定瀏覽器原生 UI 為淺色(即使系統處於深色模式也不跟隨)theme.mode 為 light
modal.cssSwModal 預設皮膚(--sw-modal-* 映射語義 Token)styles.modal !== false(預設開)
toast.cssSwToast 預設皮膚(--sw-toast-* 映射語義 Token)styles.toast !== false(預設開)
tailwind-preset.css@theme inline 把 Token 映射到 Tailwind 命名空間啟用 Tailwind 時業務側 @import

注入順序:framework 先注入,業務的 overrides.css / custom.css 在 nuxt.config.ts 的 css[] 中後追加,因此能覆蓋。開關詳見 module options · styles。

Token 清單

所有 Token 定義在 tokens.css 的 :root。業務直接 var(--x) 引用,不寫 fallback——:root 預設值已兜底。暗色值在 theme.mode 為 system / toggle 時由 theme.css 覆蓋(手動鎖定 [data-theme='dark'] 或系統 prefers-color-scheme: dark);固定 dark 模式則由 applier 直接在 :root 定義深色調色盤。

顏色

語意命名 + foreground 配對(Shadcn v4 約定):--x 是背景 / 主體色,--x-foreground 是其上的文字色。

變數用途LightDark
--background頁面底色#ffffff#0f172a
--foreground頁面主文字#1a1a1a#f1f5f9
--foreground-soft內文弱化檔(介於 --foreground 與 --muted-foreground 之間的第三級文字層)#4b5563#cbd5e1
--card卡片底色#ffffff#1e293b
--card-foreground卡片內文字#1a1a1a#f1f5f9
--popover浮層底色#ffffff#1e293b
--popover-foreground浮層文字#1a1a1a#f1f5f9
--primary主色#3b82f6#60a5fa
--primary-foreground主色上的文字#ffffff#0f172a
--gradient-brand品牌漸層佔位(CTA 類以 background-image: var(--gradient-brand) 消費;預設為兩端同為主色的退化漸層 = 視覺純色,品牌主色是漸層的站點覆蓋為真漸層即全站生效)linear-gradient(135deg, var(--primary), var(--primary))跟隨 --primary
--secondary次色背景#f3f4f6#334155
--secondary-foreground次色文字#1a1a1a#f1f5f9
--accent強調(hover / active 底色)#f3f4f6#334155
--accent-foreground強調文字#1a1a1a#f1f5f9
--muted弱化背景#f8f9fa#1e293b
--muted-foreground弱化文字#6b7280#94a3b8
--destructive危險 / 錯誤#ef4444#f87171
--destructive-foreground危險文字#ffffff#0f172a
--success成功#22c55e#4ade80
--success-foreground成功文字#ffffff#0f172a
--warning警告#f59e0b#fbbf24
--warning-foreground警告文字#ffffff#0f172a
--info資訊#3b82f6#60a5fa
--info-foreground資訊文字#ffffff#0f172a
--border通用邊框#e5e7eb#334155
--input表單邊框#e5e7eb#334155
--ringfocus ring#3b82f6#60a5fa

排版

變數預設值
--font-sanssystem-ui, -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Segoe UI', Roboto, Arial, sans-serif
--font-mono'SF Mono', 'Cascadia Code', Consolas, monospace

圓角與陰影

變數預設值說明
--radius0.5rem圓角基準,業務最常改的就這個
--shadow-sm0 1px 3px rgb(0 0 0 / 8%)小陰影
--shadow-md0 4px 12px rgb(0 0 0 / 12%)中陰影
--shadow-lg0 10px 30px rgb(0 0 0 / 16%)大陰影
--scrimlinear-gradient(transparent, rgb(0 0 0 / 68%))封面底部遮罩(資訊層保文字可讀,媒體站標配;恆深色不隨主題變。background: var(--scrim) 一行消費、免 no-hardcode disable。Hero 大圖複合藝術遮罩不在此列——品牌面業務自定)
--scrim-toplinear-gradient(rgb(0 0 0 / 45%), transparent)封面頂部遮罩(播放器頂欄 / 頁頭壓字場景)

表單控件

變數預設值說明
--control-height2rem表單控件統一高度(SwInput / SwSelect / SwSegmented 預設等高;覆蓋即全站控件同步縮放)
--control-font-size0.875rem表單控件統一字號(SwInput 在觸摸設備預設抬回 1rem,防 iOS 對 <16px 輸入框聚焦自動放大)

佈局

變數預設值說明
--nav-height60px(行動端 56px)頂欄高度
--container-padding24px(行動端 16px;≥1280 48px、≥1600 96px)容器左右內邊距 —— 寬螢幕留白由它提供,不是 --content-max-width
--content-max-width1920px內容最大寬度(內容站基線,fw#283。1920 螢幕上等於滿鋪、2560 螢幕上置中留白;窄容器站在 overrides 寫回 1200px)

不收錄 spacing(--space-4 等)、fontSize / fontWeight / lineHeight、色階(--gray-50~--gray-900):Tailwind 啟用時用 p-4 / text-lg,原生場景直接寫數值更自由,framework 不做萬能 Token 倉庫。

此外有少量元件內部 Token(如 SwImage 的 --sw-image-placeholder-bg),僅供對應元件客製,不屬於通用調色盤。

元件皮膚變數(modal / toast)

SwModal / SwToast 的預設皮膚由 framework 注入(modal.css / toast.css),皮膚變數預設值全部映射上方語義 Token——改語義 Token 全站聯動(含彈窗 / toast),改下列變數只動對應元件。在 overrides.css 平級 :root 覆蓋即可(彈窗 / toast 都 Teleport 到 body,scoped 樣式覆蓋不到)。整套皮膚可分別用 framework.styles.modal / styles.toast 設 false 關閉。

--sw-modal-*(SwModal / SwModalHost)

變數預設值說明
--sw-modal-overlay-bgrgb(0 0 0 / 45%)遮罩底色(固定值,跨主題一致)
--sw-modal-bgvar(--background)彈窗底色
--sw-modal-bordervar(--border)彈窗描邊
--sw-modal-shadowvar(--shadow-lg)彈窗陰影
--sw-modal-radius0.75rem彈窗圓角
--sw-modal-width28rem(內聯兜底)彈窗寬度(實際 min(90vw, …))
--sw-modal-title-colorvar(--foreground)標題色
--sw-modal-desc-colorvar(--muted-foreground)描述色
--sw-modal-text-colorvar(--foreground)正文色
--sw-modal-close-colorvar(--muted-foreground)關閉 ✕ 色
--sw-modal-close-bgtransparent✕ 底色(設實色 + radius: 50% 即圓形帶底)
--sw-modal-close-radius6px✕ 圓角(設 50% 變圓形)
--sw-modal-close-size1.75rem✕ 見方尺寸
--sw-modal-close-hover-bgvar(--muted)✕ hover 底色
--sw-modal-close-hover-colorvar(--foreground)✕ hover 色
--sw-modal-footer-bordervar(--border)底部操作欄分隔線
--sw-modal-btn-cancel-bgvar(--background)取消鍵底色
--sw-modal-btn-cancel-bordervar(--border)取消鍵描邊
--sw-modal-btn-cancel-colorvar(--foreground)取消鍵文字
--sw-modal-btn-cancel-hover-bgvar(--muted)取消鍵 hover 底色
--sw-modal-btn-confirm-bgvar(--primary)確認鍵底色(hover 走 filter: brightness)
--sw-modal-btn-confirm-colorvar(--primary-foreground)確認鍵文字

--sw-toast-*(SwToast / useToast)

變數預設值說明
--sw-toast-bgcolor-mix(in srgb, var(--background) 92%, transparent)toast 底色(半透明毛玻璃)
--sw-toast-colorvar(--foreground)文字色
--sw-toast-bordervar(--border)描邊
--sw-toast-shadowvar(--shadow-md)陰影
--sw-toast-close-colorvar(--muted-foreground)關閉 ✕ 色
--sw-toast-close-hover-bgvar(--muted)✕ hover 底色
--sw-toast-close-hover-colorvar(--foreground)✕ hover 色
--sw-toast-icon-successvar(--success)✓ 圖示色
--sw-toast-icon-errorvar(--destructive)✕ 圖示色
--sw-toast-icon-warningvar(--warning)! 圖示色
--sw-toast-icon-infovar(--info)i 圖示色

原生 CSS 與 Tailwind 同源

tailwind-preset.css 用 Tailwind v4 的 @theme inline 把 :root 的每個 Token remap 到 Tailwind 命名空間——不定義新值,只做映射。於是一套 Token 同時驅動兩種寫法:

/* 原生 CSS */
.cta {
  background: var(--primary);
  color: var(--primary-foreground);
  border-radius: var(--radius);
}
<!-- Tailwind 工具類(等價) -->
<button class="bg-primary text-primary-foreground rounded-lg">下單</button>

啟用 Tailwind(sw config set features.tailwind true,見 CLI)後,業務側 tailwind.css:

@import 'tailwindcss';
@import '@swift-web/framework/styles/tailwind-preset.css';

暗色切換時兩種寫法都自動跟隨,無需 dark: 前綴——因為底層 var() 已被 theme.css 覆蓋。

映射表

framework Token(原生 var())Tailwind 工具類
--background / --foregroundbg-background / text-foreground
--card / --card-foregroundbg-card / text-card-foreground
--popover / --popover-foregroundbg-popover / text-popover-foreground
--primary / --primary-foregroundbg-primary / text-primary-foreground / border-primary
--secondary / --accent / --muted(含各自 foreground)bg-secondary / bg-accent / bg-muted …(同模式)
--destructive / --success / --warning / --infobg-destructive / text-success …(同模式)
--border / --input / --ringborder-border / border-input / ring-ring
--radius(派生 sm/md/lg/xl)rounded-sm / rounded-md / rounded-lg / rounded-xl
--font-sans / --font-monofont-sans / font-mono
--shadow-sm / --shadow-md / --shadow-lgshadow-sm / shadow-md / shadow-lg
斷點 768px / 1024pxsm: / md: 前綴

圓角注意:原生 CSS 只有單一 var(--radius);rounded-sm/md/lg/xl 由 preset 基於 --radius 用 calc() 派生(覆蓋 --radius 即整體縮放)。

別在手寫 CSS 裡引 var(--color-primary):--color-* 是 preset 給 Tailwind 用的橋接變數(= var(--primary)),手寫 CSS 直接引它屬用錯層——一律用語意 Token var(--primary)。

改預設值

業務只在 app/assets/styles/overrides.css 的 :root 覆蓋,原生與 Tailwind 兩邊自動跟隨:

/* app/assets/styles/overrides.css */
:root {
  --primary: #6366f1;
  --radius: 0.75rem;
}

/* 暗色單獨覆蓋(theme.mode 為 system / toggle 時) */
:root[data-theme='dark'] {
  --primary: #818cf8;
}

業務自訂 Token 用 --brand-* / --page-* 等前綴避開 framework 命名空間,集中定義到專案級 :root(同樣不寫 fallback)。詳見 主題系統 · 自訂主題變數。

響應式工具類

responsive.css 提供斷點約定 + 一組 .sw- 前綴工具類(前綴避免與 Tailwind .container 等衝突)。

斷點(Mobile-first):

斷點範圍
Mobile< 768px
Tablet768px – 1023px
Desktop≥ 1024px

最小支援寬度 360px(Android 主流窄螢幕,framework#446):framework 元件的手機 e2e 統一以 360 執行。低於 360(iPhone SE / mini 開啟「放大顯示」後的 320、摺疊螢幕外螢幕)不保證、不設門禁。

工具類:

類名作用
.sw-container響應式容器:max-width: var(--content-max-width) + 置中 + 左右 var(--container-padding)
.sw-show-mobile僅 < 768px 顯示(其餘隱藏)
.sw-hide-mobile在 < 768px 隱藏
.sw-hide-tablet在 768px – 1023px 隱藏
.sw-hide-desktop在 ≥ 1024px 隱藏

行動端(< 768px)下 --nav-height 自動降到 56px、--container-padding 降到 16px;寬螢幕反向遞增(≥1280 → 48px,≥1600 → 96px,fw#284)。

🔴 兩個版面變數分工不同,這是最容易用錯的地方:--content-max-width 定「最寬到哪封頂」(只對 2K/4K 螢幕起作用——預設 1920 在 1920 螢幕上夠不著、容器等於滿鋪);--container-padding 定「任何寬度下邊上留多少」,你在寬螢幕看到的留白全部來自它。想收窄內容區必須兩個一起調:只改 --content-max-width 會在寬螢幕被 96px 的 padding 二次擠窄(如設 1280 後實際只剩 1088)。

responsive.css 有意不含「給 a / button / [role=button] 加全域 min-height」這類規則(framework#265 移除):規範要求的是熱區而非視覺盒(WCAG 對 target 的定義是「可接受指標操作的區域」,Material Design 3 明確說熱區「extending beyond the visual bounds of an element」),合規基線是 WCAG 2.5.8 AA 的 24×24(44×44 出自 AAA 的 2.5.5),且 inline 文字連結兩條 SC 都明文豁免。視覺尺寸由元件自身設計決定;要放大觸控目標用下面的 sw-hit-area 擴熱區。

緊湊控件熱區擴展(opt-in):sw-hit-area —— 視覺刻意小於 44px 的按鈕 / 連結(圓形關閉 ✕、chips 內移除鈕、圖示鈕等)加 sw-hit-area class:由偽元素把點擊熱區擴到 44×44,視覺完全不變(業界推薦寫法:position: relative + 偽元素 + pointer: coarse 門控)。framework 內建的緊湊圖示鈕已在元件內部掛好(SwThemeSwitch / SwPagination / SwAdPause 關閉鈕 / SwEditorToolbar 工具鈕 / 彈窗關閉鈕 / SwSelect chips ✕ / 輪播圓點等),applier 用 Sw* 元件無需任何觸控端補丁;自建的緊湊控件按需自己加。注意:① 宿主元素不得再佔用 ::before;② 密集網格不要加 —— 如 emoji 選擇器(8 列 / gap 2px)擴到 44×44 會覆蓋鄰格造成誤選。

<!-- 2rem 圓形圖示鈕:視覺保持正圓,觸控熱區 44×44 -->
<SwButton class="close-btn sw-hit-area" aria-label="關閉">…</SwButton>

FAQ

為什麼用 --primary 而不是 --color-primary? 遵循 Shadcn v4 約定,原生寫 var(--primary) 比 var(--color-primary) 短、語意同樣清楚。Tailwind 的 @theme inline 會把它 remap 成 Tailwind 命名空間的 --color-primary,對業務透明。

固定淺色(theme.mode: 'light')時這些 Token 還能用嗎? 能。tokens.css 永遠注入,只是沒有暗色覆蓋——業務是純亮色主題。

業務想加自己的 Token 怎麼辦? 在 overrides.css / custom.css 的 :root 加 --brand-* 等自訂前綴變數即可。Tailwind 場景若要產生工具類,再在業務 tailwind.css 裡 @theme { --color-brand: ...; }。

為什麼不收錄 spacing / fontSize Token? Tailwind 啟用時自帶(p-4 / text-lg),原生場景業務寫數值更自由。framework 不做萬能 Token 倉庫,避免給下游造成負擔。