Skip to content

Selectors

core:selectors 插件在渲染前解析選擇器別名。它支援靜態對應和基於 RegExp 的動態模式,並具備遞迴解析功能。

運作原理

  1. 選擇器定義在 rawConfigConfigured 期間從 config.selectors.selectors 收集。
  2. configureEngine 期間,每個定義會被解析並註冊:
    • 靜態規則儲存供精確比對查找。
    • 動態規則儲存供基於 RegExp 的比對。
    • 為已知選擇器新增自動補齊條目。
  3. transformSelectors 期間,每個選擇器字串透過 SelectorResolver 遞迴解析。
  4. 若選擇器不符合任何規則,則原始字串保持不變並回傳。
  5. 動態解析結果會透過 onResolved 自動回饋至自動補齊。

設定

ts
interface SelectorsConfig {
  /** Array of selector definitions. */
  selectors: Selector[]
}

選擇器定義格式

有四種定義選擇器的方式。

字串形式

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

ts
import { defineSelector } from '@pikacss/core'

// String form — autocomplete suggestion only, no resolution rule
const selector = defineSelector('hover')

元組形式——靜態

雙元素元組將選擇器名稱對應至一個或多個替換字串。使用 $ 作為元素預設選擇器的佔位符(見下方的 $ 佔位符)。

ts
type TupleFormStatic = [selector: string, value: string | string[]]
ts
import { defineSelector } from '@pikacss/core'

// Static tuple: [name, replacement]
// Use $ as a placeholder for the element's default selector
const hover = defineSelector(['hover', '$:hover'])
const focus = defineSelector(['focus', '$:focus'])
const firstChild = defineSelector(['first-child', '$:first-child'])

// Ancestor / wrapper selectors
const dark = defineSelector(['dark', '[data-theme="dark"] $'])

// At-rules — do NOT include $ inside at-rules
const md = defineSelector(['md', '@media (min-width: 768px)'])
const lg = defineSelector(['lg', '@media (min-width: 1024px)'])

// Multiple values (array form)
const hoverOrFocus = defineSelector(['hover-or-focus', ['$:hover', '$:focus']])

元組形式——動態

具有 RegExp 模式和解析函式的元組。函式接收 RegExpMatchArray 並回傳一個或多個替換字串。可選的第三個元素提供自動補齊提示。

ts
type TupleFormDynamic = [selector: RegExp, value: (matched: RegExpMatchArray) => string | string[], autocomplete?: string | string[]]
ts
import { defineSelector } from '@pikacss/core'

// Dynamic tuple: [pattern, resolver, autocomplete?]
// The resolver function receives the RegExp match array
const screen = defineSelector([
	/^screen-(\d+)$/,
	m => `@media (min-width: ${m[1]}px)`,
	['screen-640', 'screen-768', 'screen-1024'], // autocomplete hints
])

物件形式

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

ts
import { defineSelector } from '@pikacss/core'

// Object form — static
const hover = defineSelector({
	selector: 'hover',
	value: '$:hover',
})

// Object form — dynamic (with autocomplete)
const breakpoint = defineSelector({
	selector: /^bp-(\d+)$/,
	value: (m: RegExpMatchArray) => `@media (min-width: ${m[1]}px)`,
	autocomplete: ['bp-640', 'bp-768', 'bp-1024'],
})

完整範例

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

export default defineEngineConfig({
	selectors: {
		selectors: [
			// String form — autocomplete only
			'my-selector',

			// Static selectors
			['hover', '$:hover'],
			['focus', '$:focus'],
			['first-child', '$:first-child'],
			['dark', '[data-theme="dark"] $'],
			['md', '@media (min-width: 768px)'],
			['lg', '@media (min-width: 1024px)'],

			// Dynamic selectors
			[
				/^screen-(\d+)$/,
				m => `@media (min-width: ${m[1]}px)`,
				['screen-640', 'screen-768', 'screen-1024'],
			],

			// Object form
			{
				selector: 'active',
				value: '$:active',
			},
		],
	},
})

pika() 搭配使用

在樣式定義中使用選擇器名稱作為鍵。任何不是 CSS 屬性的鍵都會被視為選擇器:

ts
// Use selector names as keys in style definitions
const className = pika({
	color: 'black',
	hover: {
		color: 'blue',
	},
	dark: {
		color: 'white',
	},
	md: {
		fontSize: '1.25rem',
	},
})

產生的 CSS 輸出:

css
.a { color: black; }
.b:hover { color: blue; }
[data-theme="dark"] .c { color: white; }
@media (min-width: 768px) {
  .d { font-size: 1.25rem; }
}

$ 佔位符

在選擇器值中,$ 會被替換為元素的預設選擇器(預設為 .%,其中 % 是原子化樣式 ID 佔位符)。這使得偽類、祖先選擇器等得以實現。

ts
import { defineSelector } from '@pikacss/core'

// $ is replaced with the element's default selector (e.g., .%)
// Then % is replaced with the atomic style ID (e.g., a)

// Pseudo-class: append to the element selector
const hover = defineSelector(['hover', '$:hover'])
//   $ → .%  →  .%:hover  →  .a:hover

// Pseudo-element: append to the element selector
const before = defineSelector(['before', '$::before'])
//   $ → .%  →  .%::before  →  .a::before

// Ancestor selector: place $ after the parent
const dark = defineSelector(['dark', '[data-theme="dark"] $'])
//   $ → .%  →  [data-theme="dark"] .%  →  [data-theme="dark"] .a

// At-rules: no $ needed — the engine auto-appends the element selector
const md = defineSelector(['md', '@media (min-width: 768px)'])
//   no $, no %  →  defaultSelector (.%) is appended  →  two-level nesting:
//   @media (min-width: 768px) { .a { ... } }

佔位符行為摘要

定義$ → defaultSelector最終 CSS
['hover', '$:hover'].%:hover.a:hover { ... }
['before', '$::before'].%::before.a::before { ... }
['dark', '[data-theme="dark"] $'][data-theme="dark"] .%[data-theme="dark"] .a { ... }
['md', '@media (min-width: 768px)'](無 $@media (min-width: 768px) { .a { ... } }

At-Rules

對於 CSS at-rules,如 @media@container請勿在值中包含 $。當解析後的選擇器不包含 % 佔位符時,引擎會自動將預設選擇器(.%)作為巢狀層級附加,產生正確的兩層結構:

css
@media (min-width: 768px) {
  .a { ... }
}

WARNING

請勿將 $ 嵌入 at-rule 字串中(例如 @media (...) { $ })。這會產生 @media (...) { .a } 作為單一區塊選擇器,導致無效的 CSS。

遞迴解析

選擇器解析是遞迴的。選擇器值可以參照另一個選擇器名稱,並透過鏈式關係進行解析:

ts
import { defineEngineConfig } from '@pikacss/core'

export default defineEngineConfig({
	selectors: {
		selectors: [
			// Base selector
			['hover', '$:hover'],
			// Alias that resolves to another selector
			['alias-hover', 'hover'],
			// Chained: group-hover resolves through its own rule
			['group-hover', '.group:hover $'],
		],
	},
})
// Using 'alias-hover' in a style definition:
// → resolves 'alias-hover' to 'hover'
// → resolves 'hover' to '$:hover'
// → final output: .a:hover { ... }

defineSelector 輔助函式

使用 defineSelector() 輔助函式,可獲得具備完整自動補齊的型別安全選擇器定義:

ts
import { defineSelector } from '@pikacss/core'

// defineSelector() provides type safety and autocomplete
// for all selector definition formats
const hover = defineSelector(['hover', '$:hover'])
const dark = defineSelector({
	selector: 'dark',
	value: '[data-theme="dark"] $',
})

Engine API

插件可以用程式方式管理選擇器:

  • engine.selectors.resolverSelectorResolver 實例
  • engine.selectors.add(...list) — 在執行階段新增選擇器定義

行為說明

  • 無效的選擇器設定形式會被靜默略過。
  • 動態解析結果在首次解析後會被快取。
  • 靜態和動態規則都儲存在繼承自 AbstractResolverSelectorResolver 中。
  • $ 佔位符遵循引擎的 defaultSelector 選項(預設為 .%)。

原始碼參考

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

Next