Skip to content

Commit ce4332c

Browse files
authored
feat(preset-wind4): enhance color systax & support color-interpolation-method parsed (#4729)
1 parent 7d61a0b commit ce4332c

File tree

7 files changed

+372
-162
lines changed

7 files changed

+372
-162
lines changed

packages-presets/preset-wind4/src/utils/utilities.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { CSSEntries, CSSObject, CSSObjectInput, CSSValueInput, DynamicMatcher, RuleContext, StaticRule, VariantContext } from '@unocss/core'
22
import type { Theme } from '../theme'
33
import { symbols, toArray } from '@unocss/core'
4-
import { colorToString, getStringComponent, getStringComponents, parseCssColor } from '@unocss/rule-utils'
4+
import { colorToString, getStringComponent, getStringComponents, isInterpolatedMethod, parseCssColor } from '@unocss/rule-utils'
55
import { SpecialColorKey } from './constant'
66
import { h } from './handlers'
77
import { bracketTypeRe, numberWithUnitRE } from './handlers/regex'
@@ -127,17 +127,37 @@ export function splitShorthand(body: string, type: string) {
127127
* 'red-100' // From theme, plus scale
128128
* 'red-100/20' // From theme, plus scale/opacity
129129
* '[rgb(100 2 3)]/[var(--op)]' // Bracket with rgb color and bracket with opacity
130+
* '[rgb(100 2 3)]/[var(--op)]/[in_oklab]' // Bracket with rgb color, bracket with opacity and bracket with interpolation method
130131
*
131132
* @param body - Color string to be parsed.
132133
* @param theme - {@link Theme} object.
133134
* @return object if string is parseable.
134135
*/
135136
export function parseColor(body: string, theme: Theme) {
136-
const split = splitShorthand(body, 'color')
137+
let split
138+
const [front, ...rest] = getStringComponents(body, ['/', ':'], 3) ?? []
139+
140+
if (front != null) {
141+
const match = (front.match(bracketTypeRe) ?? [])[1]
142+
143+
if (match == null || match === 'color') {
144+
split = [front, ...rest]
145+
}
146+
}
147+
137148
if (!split)
138149
return
139150

140-
const [main, opacity] = split
151+
let opacity: string | undefined
152+
let [main, opacityOrModifier, modifier] = split as [string, string | undefined, string | undefined]
153+
154+
if (isInterpolatedMethod(opacityOrModifier) || isInterpolatedMethod(h.bracket(opacityOrModifier ?? ''))) {
155+
modifier = opacityOrModifier
156+
}
157+
else {
158+
opacity = opacityOrModifier
159+
}
160+
141161
const colors = main
142162
.replace(/([a-z])(\d)/g, '$1-$2')
143163
.split(/-/g)
@@ -167,6 +187,7 @@ export function parseColor(body: string, theme: Theme) {
167187

168188
return {
169189
opacity,
190+
modifier: (modifier && h.bracket.cssvar(modifier)) || modifier,
170191
name,
171192
no,
172193
color: color ?? SpecialColorKey[name as keyof typeof SpecialColorKey],
@@ -247,7 +268,7 @@ export function colorCSSGenerator(
247268
if (!data)
248269
return
249270

250-
const { color, keys, alpha } = data
271+
const { color, keys, alpha, modifier } = data
251272
const rawColorComment = ctx?.generator.config.envMode === 'dev' && color ? ` /* ${color} */` : ''
252273
const css: CSSObject = {}
253274

@@ -260,16 +281,24 @@ export function colorCSSGenerator(
260281
else {
261282
const alphaKey = `--un-${varName}-opacity`
262283
const value = keys ? generateThemeVariable('colors', keys) : color
263-
264-
if (!alpha) {
265-
css[alphaKey] = alpha
284+
let method = modifier ?? (keys ? 'in srgb' : 'in oklab')
285+
if (!method.startsWith('in ') && !method.startsWith('var(')) {
286+
method = `in ${method}`
266287
}
267-
css[property] = `color-mix(in oklch, ${value} ${alpha ?? `var(${alphaKey})`}, transparent)${rawColorComment}`
268288

289+
css[property] = `color-mix(${method}, ${value} ${alpha ?? `var(${alphaKey})`}, transparent)${rawColorComment}`
269290
result.push(defineProperty(alphaKey, { syntax: '<percentage>', initialValue: '100%' }))
270291

271292
if (keys) {
272293
themeTracking(`colors`, keys)
294+
if (!modifier) {
295+
result.push({
296+
[symbols.parent]: '@supports (color: color-mix(in lab, red, red))',
297+
[symbols.noMerge]: true,
298+
[symbols.shortcutsNoMerge]: true,
299+
[property]: `color-mix(in oklab, ${value} ${alpha ?? `var(${alphaKey})`}, transparent)${rawColorComment}`,
300+
})
301+
}
273302
}
274303
if (ctx?.theme) {
275304
detectThemeValue(color, ctx.theme)

packages-presets/rule-utils/src/colors.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,22 @@ export interface ParsedColorValue {
3636
alpha: string | number | undefined
3737
}
3838

39-
/* eslint-disable no-case-declarations */
4039
export const cssColorFunctions = ['hsl', 'hsla', 'hwb', 'lab', 'lch', 'oklab', 'oklch', 'rgb', 'rgba']
40+
export const rectangularColorSpace = ['srgb', 'srgb-linear', 'display-p3', 'a98-rgb', 'prophoto-rgb', 'rec2020', 'lab', 'oklab', 'xyz', 'xyz-d50', 'xyz-d65']
41+
export const polarColorSpace = ['hsl', 'hwb', 'lch', 'oklch']
42+
export const hueInterpolationMethods = ['shorter', 'longer', 'increasing', 'decreasing'] // hue interpolation methods for polar color spaces
4143
export const alphaPlaceholders = ['%alpha', '<alpha-value>']
4244
export const alphaPlaceholdersRE = new RegExp(alphaPlaceholders.map(v => escapeRegExp(v)).join('|'), 'g')
4345

46+
export function isInterpolatedMethod(type?: string): boolean {
47+
if (!type)
48+
return false
49+
50+
return rectangularColorSpace.some(space => type.includes(space))
51+
|| polarColorSpace.some(space => type.includes(space))
52+
|| hueInterpolationMethods.some(method => type.includes(method))
53+
}
54+
4455
export function hex2rgba(hex = ''): RGBAColorValue | undefined {
4556
const color = parseHexColor(hex)
4657
if (color != null) {
@@ -129,7 +140,7 @@ function parseHexColor(str: string): CSSColorValue | undefined {
129140

130141
switch (body.length) {
131142
case 3:
132-
case 4:
143+
case 4: {
133144
const digits = Array.from(body, s => Number.parseInt(s, 16)).map(n => (n << 4) | n)
134145
return {
135146
type: 'rgb',
@@ -138,9 +149,10 @@ function parseHexColor(str: string): CSSColorValue | undefined {
138149
? undefined
139150
: Math.round(digits[3] / 255 * 100) / 100,
140151
}
152+
}
141153

142154
case 6:
143-
case 8:
155+
case 8: {
144156
const value = Number.parseInt(body, 16)
145157
return {
146158
type: 'rgb',
@@ -151,6 +163,7 @@ function parseHexColor(str: string): CSSColorValue | undefined {
151163
? undefined
152164
: Math.round((value & 0xFF) / 255 * 100) / 100,
153165
}
166+
}
154167
}
155168
}
156169

packages-presets/rule-utils/src/utilities.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ export function getStringComponent(str: string, open: string, close: string, sep
7878
]
7979
}
8080

81-
export function getStringComponents(str: string, separators: string | string[], limit?: number) {
81+
export function getStringComponents(str: string, separators: string | string[], limit?: number, open: string = '(', close: string = ')') {
8282
limit = limit ?? 10
8383
const components = []
8484
let i = 0
8585
while (str !== '') {
8686
if (++i > limit)
8787
return
88-
const componentPair = getStringComponent(str, '(', ')', separators)
88+
const componentPair = getStringComponent(str, open, close, separators)
8989
if (!componentPair)
9090
return
9191
const [component, rest] = componentPair

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy