Skip to content

Shortcuts

core:shortcuts 插件支援可重複使用的樣式組合。定義具名樣式捷徑,並以字串樣式項目的形式傳入 pika(),或在樣式定義中透過 __shortcut 屬性來使用它們。

運作原理

  1. 捷徑定義在 rawConfigConfigured 期間從 config.shortcuts.shortcuts 收集。
  2. configureEngine 期間,每個定義會被解析並註冊:
    • 靜態規則儲存供精確比對查找。
    • 動態規則儲存供基於 RegExp 的比對。
    • 為已知捷徑新增自動補齊條目。
  3. 插件在兩個鉤子上運作:
    • transformStyleItems — 字串樣式項目會透過 ShortcutResolver 進行檢查。若符合,解析後的項目會取代原始字串。
    • transformStyleDefinitions — 當樣式定義包含 __shortcut 屬性時,捷徑會被解析,產生的樣式定義會插入在目前定義其餘屬性的前面
  4. 未匹配的捷徑字串保持不變(直接傳遞)。

設定

ts
interface ShortcutsConfig {
  /** Array of shortcut definitions. @default [] */
  shortcuts: Shortcut[]
}

捷徑定義格式

共有 5 種定義捷徑的形式:1 種字串形式、2 種元組形式和 2 種物件形式。

字串形式

純字串僅作為自動補齊建議登錄——不會建立解析規則。

ts
// String form: autocomplete-only, no resolution rule
const shortcut = 'flex-center'

元組形式——靜態

ts
type TupleFormStatic = [shortcut: string, value: Arrayable<ResolvedStyleItem>]

值可以是 StyleDefinition 物件、參照另一個捷徑的 string,或兩者的陣列:

ts
// Tuple form — static: [name, value]
// value can be a StyleDefinition, a string (another shortcut), or an array of both
const shortcuts = [
	// Single style definition
	['flex-center', {
		display: 'flex',
		alignItems: 'center',
		justifyContent: 'center',
	}],

	// Multiple style items (array of StyleDefinitions)
	['btn-base', [
		{ padding: '0.5rem 1rem', borderRadius: '0.25rem', cursor: 'pointer' },
		{ border: 'none', fontSize: '1rem' },
	]],

	// Reference another shortcut by name (string value)
	['centered', 'flex-center'],
]

元組形式——動態

ts
type TupleFormDynamic = [shortcut: RegExp, value: (matched: RegExpMatchArray) => Awaitable<Arrayable<ResolvedStyleItem>>, autocomplete?: Arrayable<string>]

解析函式接收來自模式比對的 RegExpMatchArray,並回傳一個或多個 ResolvedStyleItem。可選的自動補齊提示為動態模式提供 IDE 建議:

ts
// Tuple form — dynamic: [pattern, resolver, autocomplete?]
// The resolver receives RegExpMatchArray and returns ResolvedStyleItem(s)
const shortcuts = [
	// Dynamic margin shortcut
	[
		/^m-(\d+)$/,
		(m: RegExpMatchArray) => ({ margin: `${Number(m[1]) * 0.25}rem` }),
		['m-1', 'm-2', 'm-4', 'm-8'], // autocomplete hints
	],

	// Dynamic size shortcut returning a single definition
	[
		/^size-(\d+)$/,
		(m: RegExpMatchArray) => ({
			width: `${m[1]}px`,
			height: `${m[1]}px`,
		}),
		['size-16', 'size-24', 'size-32'],
	],

	// Dynamic shortcut returning multiple style items
	[
		/^card-(\w+)$/,
		(m: RegExpMatchArray) => [
			{ padding: '1rem', borderRadius: '0.5rem' },
			{ backgroundColor: m[1] },
		],
	],
]

物件形式

等同於元組形式,但使用具名屬性。靜態和動態兩種變體均支援:

ts
// Object form — equivalent to tuple forms but with named properties
const shortcuts = [
	// Object form — static
	{
		shortcut: 'flex-center',
		value: {
			display: 'flex',
			alignItems: 'center',
			justifyContent: 'center',
		},
	},

	// Object form — dynamic
	{
		shortcut: /^p-(\d+)$/,
		value: (m: RegExpMatchArray) => ({
			padding: `${Number(m[1]) * 0.25}rem`,
		}),
		autocomplete: ['p-1', 'p-2', 'p-4'],
	},
]

完整範例

ts
// pika.config.ts
import { defineEngineConfig } from '@pikacss/unplugin-pikacss'

export default defineEngineConfig({
	shortcuts: {
		shortcuts: [
			// String form: autocomplete-only
			'my-custom-shortcut',

			// Tuple form — static
			['flex-center', {
				display: 'flex',
				alignItems: 'center',
				justifyContent: 'center',
			}],

			// Tuple form — static with multiple style items
			['btn', [
				{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center' },
				{ padding: '0.5rem 1rem', borderRadius: '0.25rem' },
				{ border: 'none', cursor: 'pointer', fontSize: '1rem' },
			]],

			// Tuple form — dynamic
			[
				/^m-(\d+)$/,
				m => ({ margin: `${Number(m[1]) * 0.25}rem` }),
				['m-1', 'm-2', 'm-4', 'm-8'],
			],

			// Object form — static
			{
				shortcut: 'sr-only',
				value: {
					position: 'absolute',
					width: '1px',
					height: '1px',
					padding: '0',
					margin: '-1px',
					overflow: 'hidden',
					clip: 'rect(0, 0, 0, 0)',
					whiteSpace: 'nowrap',
					borderWidth: '0',
				},
			},

			// Object form — dynamic
			{
				shortcut: /^size-(\d+)$/,
				value: m => ({ width: `${m[1]}px`, height: `${m[1]}px` }),
				autocomplete: ['size-16', 'size-24', 'size-32'],
			},
		],
	},
})

pika() 搭配使用

捷徑可以兩種方式使用:

作為字串引數

將捷徑名稱作為字串引數傳入 pika()。它們會與其他樣式項目一起被解析:

ts
// Use a shortcut as a string argument to pika()
// Shortcuts resolve to their underlying style items at build time

// Single shortcut
const centered = pika('flex-center')

// Shortcut mixed with inline style definitions
const card = pika(
	'flex-center',
	{ gap: '1rem', padding: '2rem' },
)

// Dynamic shortcut as string argument
const spaced = pika('m-4', { color: 'blue' })

產生的 CSS 輸出:

css
/* pika('flex-center', { gap: '1rem', padding: '2rem' }) */
.a { display: flex; }
.b { align-items: center; }
.c { justify-content: center; }
.d { gap: 1rem; }
.e { padding: 2rem; }

__shortcut 屬性

在樣式定義中使用 __shortcut 套用一個或多個捷徑。解析後的樣式會在定義中的其他屬性之前合併:

ts
// Use __shortcut property in a style definition
// Shortcut styles are inserted BEFORE other properties in the definition

// Single shortcut via __shortcut
const centered = pika({
	__shortcut: 'flex-center',
	gap: '1rem',
})

// Multiple shortcuts via __shortcut array
const button = pika({
	__shortcut: ['flex-center', 'btn'],
	backgroundColor: '#0ea5e9',
	color: 'white',
})

// Dynamic shortcut via __shortcut
const spacing = pika({
	__shortcut: 'm-4',
})

產生的 CSS 輸出:

css
/* pika({ __shortcut: 'flex-center', gap: '1rem' }) */
.a { display: flex; }
.b { align-items: center; }
.c { justify-content: center; }
.d { gap: 1rem; }

/* pika({ __shortcut: 'm-4' }) */
.e { margin: 1rem; }

屬性順序

使用 __shortcut 時,捷徑樣式會插入在定義其餘屬性的前面。這意味著與 __shortcut 一起定義的屬性可以覆寫捷徑的值。

defineShortcut() 輔助函式

使用 defineShortcut() 作為個別捷徑定義的型別安全恆等輔助函式。它提供完整的 TypeScript 自動補齊:

ts
import { defineEngineConfig, defineShortcut } from '@pikacss/unplugin-pikacss'

// defineShortcut() is a type-safe identity helper
// It provides full TypeScript autocomplete for shortcut definitions
const flexCenter = defineShortcut(['flex-center', {
	display: 'flex',
	alignItems: 'center',
	justifyContent: 'center',
}])

const dynamicMargin = defineShortcut({
	shortcut: /^m-(\d+)$/,
	value: m => ({ margin: `${Number(m[1]) * 0.25}rem` }),
	autocomplete: ['m-1', 'm-2', 'm-4'],
})

export default defineEngineConfig({
	shortcuts: {
		shortcuts: [flexCenter, dynamicMargin],
	},
})

遞迴解析

捷徑可以透過名稱參照其他捷徑。解析是遞迴的——捷徑的值可以是指向另一個已登錄捷徑的字串:

ts
// Shortcuts can reference other shortcuts — resolution is recursive
const shortcuts = [
	['flex-center', {
		display: 'flex',
		alignItems: 'center',
		justifyContent: 'center',
	}],

	// 'btn' resolves to 'flex-center' (another shortcut) + inline styles
	['btn', [
		'flex-center', // references the shortcut above
		{ padding: '0.5rem 1rem', borderRadius: '0.25rem' },
	]],
]

當呼叫 pika('btn') 時,引擎會先解析 'flex-center',再將結果與其餘的行內樣式合併。

自動補齊

插件會登錄:

  • __shortcut 作為樣式定義的額外屬性,接受 string | string[] 值。
  • 所有靜態捷徑名稱作為自動補齊建議。
  • 動態解析結果會透過 onResolved 自動回饋至自動補齊。

Engine API

插件可以用程式方式管理捷徑:

  • engine.shortcuts.resolverShortcutResolver 實例
  • engine.shortcuts.add(...list) — 在執行階段新增捷徑定義

行為說明

  • 動態解析結果在首次解析後會被快取。
  • 靜態和動態規則都儲存在繼承自 AbstractResolverShortcutResolver 中。
  • 無效的捷徑設定形式會被靜默略過。
  • 捷徑可以參照其他捷徑——解析是遞迴的。
  • 解析錯誤會被捕捉並記錄為警告;原始字串將被回傳。

原始碼參考

  • packages/core/src/internal/plugins/shortcuts.ts

Next