動態樣式
pika() 的引數必須是靜態的,但你的 UI 不是。本頁介紹涵蓋執行階段驅動樣式的各種模式。
為什麼會有這個限制
在建置時期,外掛會從你的原始碼擷取每一次 pika() 呼叫,並把它的引數當成一段自足的運算式來求值,過程中不會執行你模組裡的其他程式碼(packages/integration/src/ctx.ts)。任何引用變數、呼叫函式,或根據執行階段狀態分支的內容,都無法用這種方式求值:
// ❌ 這些在建置時期會失敗:呼叫看不到 `color` 或 `isDark`
pika({ color })
pika({ color: isDark ? 'white' : 'black' })ESLint 規則 no-dynamic-args 會在建置之前先抓出這些問題。下面每個模式背後的關鍵觀念是:樣式的集合必須是靜態的,但你在執行階段要套用哪個樣式完全由你決定;pika() 只是回傳一個字串。
模式 1:Variant Map
每個 variant 寫一次 pika() 呼叫,然後在執行階段從中選擇。每一次呼叫都在建置時期編譯;選擇的動作只是單純的物件存取。
const buttonVariants = {
primary: pika({
backgroundColor: '#3b82f6',
color: 'white',
}),
danger: pika({
backgroundColor: '#ef4444',
color: 'white',
}),
}
// 在執行階段選擇 variant 只是單純的物件存取:
// 上面每一次 pika() 呼叫在建置時期就已經編譯完成。
export function buttonClass(kind: keyof typeof buttonVariants) {
return buttonVariants[kind]
}@layer utilities {
.pk-a {
background-color: #3b82f6;
}
.pk-b {
color: white;
}
.pk-c {
background-color: #ef4444;
}
}注意在輸出裡,共用的宣告(color: white)會在各個 variant 之間去除重複,合併成同一個原子 class。
布林狀態的運作方式相同:
const tabClass = pika({ padding: '0.5rem 1rem' })
const activeTabClass = pika({ fontWeight: '700', color: '#3b82f6' })
// 在執行階段組合建置時期產生的字串
const className = isActive ? `${tabClass} ${activeTabClass}` : tabClass警告
當兩個 variant 在同一個元素上設定了重疊的屬性時,哪一個會勝出是由樣式表決定,而不是由 class 在標記(markup)中出現的順序決定。請讓 variant map 在每個屬性上互斥,或像上面那樣把基礎樣式與 variant 樣式拆開。見 PikaCSS 如何產生 CSS。
模式 2:用 CSS 變數處理真正的執行階段值
當數值本身只存在於執行階段時(滑桿位置、計算出來的顏色、使用者內容),可在樣式定義裡引用一個 CSS 自訂屬性,並透過行內 style 屬性來設定該屬性:
const progressBarClass = pika({
width: 'var(--progress, 0%)',
height: '0.5rem',
backgroundColor: '#3b82f6',
transition: 'width 0.2s ease',
})
export { progressBarClass }@layer utilities {
.pk-a {
width: var(--progress, 0%);
}
.pk-b {
height: 0.5rem;
}
.pk-c {
background-color: #3b82f6;
}
.pk-d {
transition: width 0.2s ease;
}
}接著在執行階段餵入這個變數;原子 class 永遠不會改變:
<div :class="progressBarClass" :style="{ '--progress': `${percent}%` }" /><div className={progressBarClass} style={{ '--progress': `${percent}%` }} />模式 3:把 Shortcut 當成配方
對於帶有具名 variant 的可重複使用多屬性樣式,可在引擎設定裡定義 shortcut。shortcut 可以互相組合(一個 variant 可以包含基礎 shortcut),而使用時仍然維持為靜態字串:
// pika.config.ts
import { defineEngineConfig } from '@pikacss/core'
export default defineEngineConfig({
shortcuts: {
definitions: [
['btn', {
display: 'inline-flex',
alignItems: 'center',
padding: '0.5rem 1rem',
borderRadius: '0.5rem',
border: 'none',
cursor: 'pointer',
}],
['btn-primary', ['btn', { backgroundColor: '#3b82f6', color: 'white' }]],
['btn-danger', ['btn', { backgroundColor: '#ef4444', color: 'white' }]],
],
},
})const primaryButtonClass = pika('btn-primary')
const dangerButtonClass = pika('btn-danger')
export { dangerButtonClass, primaryButtonClass }@layer utilities {
.pk-a {
display: inline-flex;
}
.pk-b {
align-items: center;
}
.pk-c {
padding: 0.5rem 1rem;
}
.pk-d {
border-radius: 0.5rem;
}
.pk-e {
border: none;
}
.pk-f {
cursor: pointer;
}
.pk-g {
background-color: #3b82f6;
}
.pk-h {
color: white;
}
.pk-i {
background-color: #ef4444;
}
}與模式 1 結合,即可在執行階段選擇一個 shortcut:
const buttonVariants = {
primary: pika('btn-primary'),
danger: pika('btn-danger'),
}動態的 shortcut 規則(以正規表達式為基礎,例如 @pikacss/plugin-icons 提供的圖示 shortcut)仍然要求比對到的字串本身要靜態地出現在某個 pika() 呼叫裡。
選擇一個模式
| 情境 | 模式 |
|---|---|
| 少數幾個已知的 variant(尺寸、用途、作用中狀態) | Variant map |
| 在執行階段計算出來的值(位置、百分比、使用者顏色) | CSS 變數 + 行內樣式 |
| 跨元件共用的可重複使用配方 | Shortcut |
下一步
- PikaCSS 如何產生 CSS:引擎如何處理你的呼叫。
- Shortcuts:完整的 shortcut 設定參考。
- ESLint 設定:及早抓出動態引數。