Skip to content

Commit 4e6c26c

Browse files
authored
fix(preset-wind4): box-shadow & text-shadow & drop-shadow aligned with tw4 (#4888)
1 parent 21c1e21 commit 4e6c26c

7 files changed

Lines changed: 246 additions & 43 deletions

File tree

packages-presets/preset-wind4/src/rules/filters.ts

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import type { CSSValueInput, CSSValues, Rule, RuleContext } from '@unocss/core'
22
import type { Theme } from '../theme'
3-
import { colorableShadows, colorResolver, defineProperty, globalKeywords, h } from '../utils'
3+
import {
4+
colorableShadows,
5+
colorResolver,
6+
defineProperty,
7+
getStringComponents,
8+
globalKeywords,
9+
h,
10+
hasParseableColor,
11+
} from '../utils'
412

513
const filterBaseKeys = [
614
'blur',
@@ -67,19 +75,34 @@ function toFilter(varName: string, resolver: (str: string, theme: Theme) => stri
6775
}
6876
}
6977

70-
function dropShadowResolver([, s]: string[], { theme }: RuleContext<Theme>) {
71-
let v = theme.dropShadow?.[s || 'DEFAULT']
72-
if (v != null) {
73-
const shadows = colorableShadows(v, '--un-drop-shadow-color')
78+
function dropShadowResolver(match: string[], ctx: RuleContext<Theme>) {
79+
const [, s] = match
80+
const { theme } = ctx
81+
let res: string[] = []
82+
if (s) {
83+
res = getStringComponents(s, '/', 2) ?? []
84+
if (s.startsWith('/'))
85+
res = ['', s.slice(1)]
86+
}
87+
let v = theme.dropShadow?.[res[0] || 'DEFAULT']
88+
const c = s ? h.bracket.cssvar(s) : undefined
89+
90+
if ((v != null || c != null) && !hasParseableColor(c, theme)) {
91+
const alpha = res[1] ? h.bracket.percent.cssvar(res[1]) : undefined
7492
return [
7593
{
76-
'--un-drop-shadow': `drop-shadow(${shadows.join(') drop-shadow(')})`,
94+
'--un-drop-shadow-opacity': alpha,
95+
'--un-drop-shadow': `drop-shadow(${colorableShadows((v || c)!, '--un-drop-shadow-color', alpha).join(') drop-shadow(')})`,
7796
'filter': filterCSS,
7897
},
7998
...filterProperties,
8099
]
81100
}
82101

102+
if (hasParseableColor(s, theme)) {
103+
return colorResolver('--un-drop-shadow-color', 'drop-shadow')(match, ctx)
104+
}
105+
83106
v = h.bracket.cssvar(s) ?? (s === 'none' ? '' : undefined)
84107
if (v != null) {
85108
return [
@@ -98,7 +121,7 @@ export const filters: Rule<Theme>[] = [
98121
[/^(?:(backdrop-)|filter-)?brightness-(.+)$/, toFilter('brightness', s => h.bracket.cssvar.percent(s)), { autocomplete: ['(backdrop|filter)-brightness-<percent>', 'brightness-<percent>'] }],
99122
[/^(?:(backdrop-)|filter-)?contrast-(.+)$/, toFilter('contrast', s => h.bracket.cssvar.percent(s)), { autocomplete: ['(backdrop|filter)-contrast-<percent>', 'contrast-<percent>'] }],
100123
// drop-shadow only on filter
101-
[/^(?:filter-)?drop-shadow(?:-(.+))?$/, dropShadowResolver, {
124+
[/^(?:filter-)?drop-shadow(?:-?(.+))?$/, dropShadowResolver, {
102125
autocomplete: [
103126
'filter-drop',
104127
'filter-drop-shadow',
@@ -107,16 +130,18 @@ export const filters: Rule<Theme>[] = [
107130
'drop-shadow-color',
108131
'filter-drop-shadow-$dropShadow',
109132
'drop-shadow-$dropShadow',
133+
'filter-drop-shadow-$colors',
134+
'drop-shadow-$colors',
110135
'filter-drop-shadow-color-$colors',
111136
'drop-shadow-color-$colors',
112137
'filter-drop-shadow-color-(op|opacity)',
113138
'drop-shadow-color-(op|opacity)',
114139
'filter-drop-shadow-color-(op|opacity)-<percent>',
115-
'drop-shadow-color-(op|opacity)-<percent>',
140+
'drop-shadow(-color)?-(op|opacity)-<percent>',
116141
],
117142
}],
118143
[/^(?:filter-)?drop-shadow-color-(.+)$/, colorResolver('--un-drop-shadow-color', 'drop-shadow')],
119-
[/^(?:filter-)?drop-shadow-color-op(?:acity)?-?(.+)$/, ([, opacity]) => ({ '--un-drop-shadow-opacity': h.bracket.percent(opacity) })],
144+
[/^(?:filter-)?drop-shadow(?:-color)?-op(?:acity)?-?(.+)$/, ([, opacity]) => ({ '--un-drop-shadow-opacity': h.bracket.percent(opacity) })],
120145
[/^(?:(backdrop-)|filter-)?grayscale(?:-(.+))?$/, toFilter('grayscale', percentWithDefault), { autocomplete: ['(backdrop|filter)-grayscale', '(backdrop|filter)-grayscale-<percent>', 'grayscale-<percent>'] }],
121146
[/^(?:(backdrop-)|filter-)?hue-rotate-(.+)$/, toFilter('hue-rotate', s => h.bracket.cssvar.degree(s))],
122147
[/^(?:(backdrop-)|filter-)?invert(?:-(.+))?$/, toFilter('invert', percentWithDefault), { autocomplete: ['(backdrop|filter)-invert', '(backdrop|filter)-invert-<percent>', 'invert-<percent>'] }],

packages-presets/preset-wind4/src/rules/shadow.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
import type { CSSObject, CSSValueInput, Rule, RuleContext } from '@unocss/core'
22
import type { Theme } from '../theme'
3-
import { colorableShadows, colorResolver, defineProperty, h, hasParseableColor, hyphenate } from '../utils'
3+
import {
4+
colorableShadows,
5+
colorResolver,
6+
defineProperty,
7+
getStringComponents,
8+
h,
9+
hasParseableColor,
10+
hyphenate,
11+
} from '../utils'
412

513
export const shadowProperties = {
614
shadow: defineProperty('--un-shadow', { initialValue: '0 0 #0000' }),
@@ -19,7 +27,7 @@ export const shadowProperties = {
1927

2028
export const boxShadows: Rule<Theme>[] = [
2129
// shadow
22-
[/^shadow(?:-(.+))?$/, handleShadow('shadow'), { autocomplete: ['shadow-$colors', 'shadow-$shadow'] }],
30+
[/^shadow(?:-?(.+))?$/, handleShadow('shadow'), { autocomplete: ['shadow-$colors', 'shadow-$shadow'] }],
2331
[/^shadow-op(?:acity)?-?(.+)$/, ([, opacity]) => ({ '--un-shadow-opacity': h.bracket.percent.cssvar(opacity) }), { autocomplete: 'shadow-(op|opacity)-<percent>' }],
2432

2533
// inset shadow
@@ -31,14 +39,22 @@ function handleShadow(themeKey: 'shadow' | 'insetShadow') {
3139
return (match: RegExpMatchArray, ctx: RuleContext<Theme>): CSSObject | (CSSValueInput | string)[] | undefined => {
3240
const [, d] = match
3341
const { theme } = ctx
34-
const v = theme[themeKey]?.[d || 'DEFAULT']
42+
let res: string[] = []
43+
if (d) {
44+
res = getStringComponents(d, '/', 2) ?? []
45+
if (d.startsWith('/'))
46+
res = ['', d.slice(1)]
47+
}
48+
const v = theme[themeKey]?.[res[0] || 'DEFAULT']
3549
const c = d ? h.bracket.cssvar(d) : undefined
3650
const shadowVar = hyphenate(themeKey)
3751

3852
if ((v != null || c != null) && !hasParseableColor(c, theme)) {
53+
const alpha = res[1] ? h.bracket.percent.cssvar(res[1]) : undefined
3954
return [
4055
{
41-
[`--un-${shadowVar}`]: colorableShadows((v || c)!, `--un-${shadowVar}-color`).join(','),
56+
[`--un-${shadowVar}-opacity`]: alpha,
57+
[`--un-${shadowVar}`]: colorableShadows((v || c)!, `--un-${shadowVar}-color`, alpha).join(','),
4258
'box-shadow': 'var(--un-inset-shadow), var(--un-inset-ring-shadow), var(--un-ring-offset-shadow), var(--un-ring-shadow), var(--un-shadow)',
4359
},
4460
...Object.values(shadowProperties),

packages-presets/preset-wind4/src/rules/typography.ts

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
import type { CSSObject, CSSValueInput, Rule, RuleContext } from '@unocss/core'
22
import type { Theme } from '../theme'
3-
import { colorableShadows, colorResolver, defineProperty, getStringComponent, globalKeywords, h, isCSSMathFn, numberResolver } from '../utils'
3+
import {
4+
colorableShadows,
5+
colorResolver,
6+
defineProperty,
7+
getStringComponent,
8+
getStringComponents,
9+
globalKeywords,
10+
h,
11+
hasParseableColor,
12+
isCSSMathFn,
13+
numberResolver,
14+
} from '../utils'
415
import { bracketTypeRe } from '../utils/handlers/regex'
516
import { generateThemeVariable, themeTracking } from '../utils/track'
617

@@ -244,22 +255,30 @@ export const textStrokes: Rule<Theme>[] = [
244255
[/^text-stroke-op(?:acity)?-?(.+)$/, ([, opacity]) => ({ '--un-text-stroke-opacity': h.bracket.percent.cssvar(opacity) }), { autocomplete: 'text-stroke-(op|opacity)-<percent>' }],
245256
]
246257

247-
const opRE = /op(?:acity)?-/
248-
export const textShadows: Rule<Theme>[] = [
249-
[/^text-shadow(?:-(.+))?$/, (match, ctx) => {
250-
const [_, s = 'DEFAULT'] = match
251-
const v = ctx.theme.textShadow?.[s]
252-
if (v != null) {
253-
return {
254-
'--un-text-shadow': colorableShadows(v, '--un-text-shadow-color').join(','),
255-
'text-shadow': 'var(--un-text-shadow)',
256-
}
258+
function handleTextShadow(match: RegExpMatchArray, ctx: RuleContext<Theme>): CSSObject | (CSSValueInput | string)[] | undefined {
259+
const [, s] = match
260+
const { theme } = ctx
261+
let res: string[] = []
262+
if (s) {
263+
res = getStringComponents(s, '/', 2) ?? []
264+
}
265+
const v = theme.textShadow?.[res[0]]
266+
const c = s ? h.bracket.cssvar(s) : undefined
267+
268+
if ((v != null || c != null) && !hasParseableColor(c, theme)) {
269+
const alpha = res[1] ? h.bracket.percent.cssvar(res[1]) : undefined
270+
return {
271+
'--un-text-shadow-opacity': alpha,
272+
'--un-text-shadow': colorableShadows((v || c)!, '--un-text-shadow-color', alpha).join(','),
273+
'text-shadow': 'var(--un-text-shadow)',
257274
}
258-
if (opRE.test(s))
259-
return { '--un-text-shadow-opacity': h.bracket.percent.cssvar(s.replace(opRE, '')) }
275+
}
260276

261-
return colorResolver('--un-text-shadow-color', 'text-shadow')(match, ctx) ?? { 'text-shadow': h.bracket.cssvar.global(s) }
262-
}, {
277+
return colorResolver('--un-text-shadow-color', 'text-shadow')(match, ctx) ?? { 'text-shadow': h.bracket.cssvar.global(s) }
278+
}
279+
280+
export const textShadows: Rule<Theme>[] = [
281+
[/^text-shadow-(.+)$/, handleTextShadow, {
263282
autocomplete: [
264283
'text-shadow-$textShadow',
265284
'text-shadow(-color)?-$colors',
@@ -269,7 +288,7 @@ export const textShadows: Rule<Theme>[] = [
269288

270289
// colors
271290
[/^text-shadow-color-(.+)$/, colorResolver('--un-text-shadow-color', 'text-shadow'), { autocomplete: 'text-shadow-color-$colors' }],
272-
[/^text-shadow-color-op(?:acity)?-?(.+)$/, ([, opacity]) => ({ '--un-text-shadow-opacity': h.bracket.percent.cssvar(opacity) }), { autocomplete: 'text-shadow-color-(op|opacity)-<percent>' }],
291+
[/^text-shadow(?:-color)?-op(?:acity)?-?(.+)$/, ([, opacity]) => ({ '--un-text-shadow-opacity': h.bracket.percent.cssvar(opacity) }), { autocomplete: 'text-shadow(-color)?-(op|opacity)-<percent>' }],
273292
]
274293

275294
const fontVariantNumericProperties = [

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -270,11 +270,14 @@ export function colorCSSGenerator(
270270
if (keys) {
271271
themeTracking(`colors`, keys)
272272
if (!modifier) {
273+
const colorValue = ['shadow', 'inset-shadow', 'text-shadow', 'drop-shadow'].includes(varName)
274+
? `${alpha ? `color-mix(in oklab, ${value} ${alpha}, transparent)` : `${value}`} var(${alphaKey})`
275+
: `${value} ${alpha ?? `var(${alphaKey})`}`
273276
result.push({
274277
[symbols.parent]: '@supports (color: color-mix(in lab, red, red))',
275278
[symbols.noMerge]: true,
276279
[symbols.shortcutsNoMerge]: true,
277-
[property]: `color-mix(in oklab, ${value} ${alpha ?? `var(${alphaKey})`}, transparent)${rawColorComment}`,
280+
[property]: `color-mix(in oklab, ${colorValue}, transparent)${rawColorComment}`,
278281
})
279282
}
280283
}
@@ -299,7 +302,7 @@ export function colorResolver(property: string, varName: string) {
299302

300303
// #endregion
301304

302-
export function colorableShadows(shadows: string | string[], colorVar: string) {
305+
export function colorableShadows(shadows: string | string[], colorVar: string, alpha?: string) {
303306
const colored = []
304307
shadows = toArray(shadows)
305308
for (let i = 0; i < shadows.length; i++) {
@@ -320,19 +323,20 @@ export function colorableShadows(shadows: string | string[], colorVar: string) {
320323
if (parseCssColor(components.at(0))) {
321324
const color = parseCssColor(components.shift())
322325
if (color)
323-
colorVarValue = `, ${colorToString(color)}`
326+
colorVarValue = colorToString(color)
324327
}
325328
else if (parseCssColor(lastComp)) {
326329
const color = parseCssColor(components.pop())
327330
if (color)
328-
colorVarValue = `, ${colorToString(color)}`
331+
colorVarValue = colorToString(color)
329332
}
330333
else if (lastComp && cssVarFnRE.test(lastComp)) {
331-
const color = components.pop()!
332-
colorVarValue = `, ${color}`
334+
const color = components.pop()
335+
if (color)
336+
colorVarValue = color
333337
}
334338

335-
colored.push(`${isInset ? 'inset ' : ''}${components.join(' ')} var(${colorVar}${colorVarValue})`)
339+
colored.push(`${isInset ? 'inset ' : ''}${components.join(' ')} var(${colorVar}, ${alpha ? `oklab(from ${colorVarValue} l a b / ${alpha})` : colorVarValue})`)
336340
}
337341

338342
return colored

0 commit comments

Comments
 (0)