Skip to content

Commit ac83f35

Browse files
Handle --theme(…, initial) when encountered by an --theme(…, fallback) call
1 parent 2c53425 commit ac83f35

File tree

6 files changed

+145
-45
lines changed

6 files changed

+145
-45
lines changed

packages/@tailwindcss-postcss/src/__snapshots__/index.test.ts.snap

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,8 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
99
--text-2xl: 1.5rem;
1010
--text-2xl--line-height: calc(2 / 1.5);
1111
--font-weight-bold: 700;
12-
--default-font-family: var(--font-sans);
13-
--default-font-feature-settings: var(--font-sans--font-feature-settings, normal);
14-
--default-font-variation-settings: var(--font-sans--font-variation-settings, normal);
15-
--default-mono-font-family: var(--font-mono);
16-
--default-mono-font-feature-settings: var(--font-mono--font-feature-settings, normal);
17-
--default-mono-font-variation-settings: var(--font-mono--font-variation-settings, normal);
12+
--default-font-family: var(--font-sans, initial);
13+
--default-mono-font-family: var(--font-mono, initial);
1814
}
1915
}
2016
@@ -37,9 +33,9 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
3733
-webkit-text-size-adjust: 100%;
3834
tab-size: 4;
3935
line-height: 1.5;
40-
font-family: var(--default-font-family);
41-
font-feature-settings: var(--default-font-feature-settings);
42-
font-variation-settings: var(--default-font-variation-settings);
36+
font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
37+
font-feature-settings: var(--default-font-feature-settings, normal);
38+
font-variation-settings: var(--default-font-variation-settings, normal);
4339
-webkit-tap-highlight-color: transparent;
4440
}
4541
@@ -76,9 +72,9 @@ exports[`\`@import 'tailwindcss'\` is replaced with the generated CSS 1`] = `
7672
}
7773
7874
code, kbd, samp, pre {
79-
font-family: var(--default-mono-font-family);
80-
font-feature-settings: var(--default-mono-font-feature-settings);
81-
font-variation-settings: var(--default-mono-font-variation-settings);
75+
font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
76+
font-feature-settings: var(--default-mono-font-feature-settings, normal);
77+
font-variation-settings: var(--default-mono-font-variation-settings, normal);
8278
font-size: 1em;
8379
}
8480

packages/tailwindcss/src/__snapshots__/index.test.ts.snap

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,8 @@ exports[`compiling CSS > prefix all CSS variables inside preflight 1`] = `
9898
:root, :host {
9999
--tw-font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
100100
--tw-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
101-
--tw-default-font-family: var(--tw-font-sans);
102-
--tw-default-font-feature-settings: var(--font-sans--font-feature-settings, normal);
103-
--tw-default-font-variation-settings: var(--font-sans--font-variation-settings, normal);
104-
--tw-default-mono-font-family: var(--tw-font-mono);
105-
--tw-default-mono-font-feature-settings: var(--font-mono--font-feature-settings, normal);
106-
--tw-default-mono-font-variation-settings: var(--font-mono--font-variation-settings, normal);
101+
--tw-default-font-family: var(--tw-font-sans, initial);
102+
--tw-default-mono-font-family: var(--tw-font-mono, initial);
107103
}
108104
}
109105
@@ -126,9 +122,9 @@ exports[`compiling CSS > prefix all CSS variables inside preflight 1`] = `
126122
-webkit-text-size-adjust: 100%;
127123
tab-size: 4;
128124
line-height: 1.5;
129-
font-family: var(--tw-default-font-family);
130-
font-feature-settings: var(--tw-default-font-feature-settings);
131-
font-variation-settings: var(--tw-default-font-variation-settings);
125+
font-family: var(--tw-default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
126+
font-feature-settings: var(--tw-default-font-feature-settings, normal);
127+
font-variation-settings: var(--tw-default-font-variation-settings, normal);
132128
-webkit-tap-highlight-color: transparent;
133129
}
134130
@@ -165,9 +161,9 @@ exports[`compiling CSS > prefix all CSS variables inside preflight 1`] = `
165161
}
166162
167163
code, kbd, samp, pre {
168-
font-family: var(--tw-default-mono-font-family);
169-
font-feature-settings: var(--tw-default-mono-font-feature-settings);
170-
font-variation-settings: var(--tw-default-mono-font-variation-settings);
164+
font-family: var(--tw-default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
165+
font-feature-settings: var(--tw-default-mono-font-feature-settings, normal);
166+
font-variation-settings: var(--tw-default-mono-font-variation-settings, normal);
171167
font-size: 1em;
172168
}
173169

packages/tailwindcss/src/ast.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,13 @@ export function optimizeAst(ast: AstNode[], designSystem: DesignSystem) {
282282

283283
// Track variables defined in `@theme`
284284
if (context.theme && node.property[0] === '-' && node.property[1] === '-') {
285+
// Variables that resolve to `initial` should never be emitted. This can happen because of
286+
// the `--theme(…)` being used and evaluated lazily
287+
if (node.value === 'initial') {
288+
node.value = undefined
289+
return
290+
}
291+
285292
if (!context.keyframes) {
286293
cssThemeVariables.get(parent).add(node)
287294
}

packages/tailwindcss/src/css-functions.test.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,16 +218,70 @@ describe('--theme(…)', () => {
218218
`)
219219
})
220220

221-
test('--theme(--unknown, fallback)', async () => {
221+
test('--theme(…) contains the fallback when the default namespace is unset and a prior `--theme(…)` function contains `initial`', async () => {
222222
expect(
223223
await compileCss(css`
224+
@theme prefix(tw) {
225+
--default-font-family: --theme(--font-family, initial);
226+
}
227+
.red {
228+
font-family: --theme(--default-font-family, Potato Sans, sans-serif);
229+
}
230+
`),
231+
).toMatchInlineSnapshot(`
232+
".red {
233+
font-family: var(--tw-default-font-family, Potato Sans, sans-serif);
234+
}"
235+
`)
236+
})
237+
238+
test('--theme(…) contains the fallback when the default namespace is unset and a prior `--theme(…)` function contains `initial` in @reference', async () => {
239+
expect(
240+
await compileCss(css`
241+
@theme reference prefix(tw) {
242+
--default-font-family: --theme(--font-family, initial);
243+
}
244+
.red {
245+
font-family: --theme(--default-font-family, Potato Sans, sans-serif);
246+
}
247+
`),
248+
).toMatchInlineSnapshot(`
249+
".red {
250+
font-family: var(--tw-default-font-family, Potato Sans, sans-serif);
251+
}"
252+
`)
253+
})
254+
255+
test('--theme(…) returns the fallback when the default namespace is unset and a prior `--theme(…)` function returns `initial` as inline value', async () => {
256+
expect(
257+
await compileCss(css`
258+
@theme prefix(tw) {
259+
--default-font-family: --theme(--font-family inline, initial);
260+
}
261+
.red {
262+
font-family: --theme(--default-font-family inline, Potato Sans, sans-serif);
263+
}
264+
`),
265+
).toMatchInlineSnapshot(`
266+
".red {
267+
font-family: Potato Sans, sans-serif;
268+
}"
269+
`)
270+
})
271+
272+
test('--theme(…) returns the fallback when the default namespace is unset in @reference mode', async () => {
273+
expect(
274+
await compileCss(css`
275+
@theme reference prefix(tw) {
276+
--default-font-family: --theme(--font-family, initial);
277+
}
224278
.red {
225-
color: --theme(--unknown, red);
279+
font-family: --theme(--default-font-family, Potato Sans, sans-serif);
226280
}
227281
`),
228282
).toMatchInlineSnapshot(`
229283
".red {
230-
color: var(--unknown, red);
284+
font-family: var(--tw-default-font-family, Potato Sans, sans-serif);
231285
}"
232286
`)
233287
})

packages/tailwindcss/src/css-functions.ts

Lines changed: 59 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ const CSS_FUNCTIONS: Record<
1515
theme: legacyTheme,
1616
}
1717

18-
function alpha(_designSystem: DesignSystem, _source: AstNode, value: string, ...rest: string[]) {
18+
function alpha(
19+
_designSystem: DesignSystem,
20+
_source: AstNode,
21+
value: string,
22+
...rest: string[]
23+
): string {
1924
let [color, alpha] = segment(value, '/').map((v) => v.trim())
2025

2126
if (!color || !alpha) {
@@ -33,7 +38,12 @@ function alpha(_designSystem: DesignSystem, _source: AstNode, value: string, ...
3338
return withAlpha(color, alpha)
3439
}
3540

36-
function spacing(designSystem: DesignSystem, _source: AstNode, value: string, ...rest: string[]) {
41+
function spacing(
42+
designSystem: DesignSystem,
43+
_source: AstNode,
44+
value: string,
45+
...rest: string[]
46+
): string {
3747
if (!value) {
3848
throw new Error(`The --spacing(…) function requires an argument, but received none.`)
3949
}
@@ -54,7 +64,12 @@ function spacing(designSystem: DesignSystem, _source: AstNode, value: string, ..
5464
return `calc(${multiplier} * ${value})`
5565
}
5666

57-
function theme(designSystem: DesignSystem, source: AstNode, path: string, ...fallback: string[]) {
67+
function theme(
68+
designSystem: DesignSystem,
69+
source: AstNode,
70+
path: string,
71+
...fallback: string[]
72+
): string {
5873
if (!path.startsWith('--')) {
5974
throw new Error(`The --theme(…) function can only be used with CSS variables from your theme.`)
6075
}
@@ -75,20 +90,33 @@ function theme(designSystem: DesignSystem, source: AstNode, path: string, ...fal
7590

7691
let resolvedValue = designSystem.resolveThemeValue(path, inline)
7792

78-
if (!resolvedValue && fallback.length > 0) {
79-
if (inline) {
80-
return fallback.join(', ')
81-
} else {
82-
return `var(${path}, ${fallback.join(', ')})`
83-
}
84-
}
85-
8693
if (!resolvedValue) {
94+
if (fallback.length > 0) return fallback.join(', ')
8795
throw new Error(
8896
`Could not resolve value for theme function: \`theme(${path})\`. Consider checking if the variable name is correct or provide a fallback value to silence this error.`,
8997
)
9098
}
9199

100+
if (fallback.length === 0) {
101+
return resolvedValue
102+
}
103+
104+
// Inject the fallback…
105+
//
106+
// - …as the value if the value returned is `initial`
107+
// - …expect any `initial` fallbacks on `var(…)`, `theme(…)`, or `--theme(…)`
108+
// - …as the fallback if a `var(…)` with no fallback is returned
109+
if (resolvedValue === 'initial') return fallback.join(', ')
110+
if (
111+
resolvedValue.startsWith('var(') ||
112+
resolvedValue.startsWith('theme(') ||
113+
resolvedValue.startsWith('--theme(')
114+
) {
115+
let valueAst = ValueParser.parse(resolvedValue)
116+
injectFallbackForInitialFallback(valueAst, fallback.join(', '))
117+
return ValueParser.toCss(valueAst)
118+
}
119+
92120
return resolvedValue
93121
}
94122

@@ -97,7 +125,7 @@ function legacyTheme(
97125
_source: AstNode,
98126
path: string,
99127
...fallback: string[]
100-
) {
128+
): string {
101129
path = eventuallyUnquote(path)
102130

103131
let resolvedValue = designSystem.resolveThemeValue(path)
@@ -188,3 +216,22 @@ function eventuallyUnquote(value: string) {
188216

189217
return unquoted
190218
}
219+
220+
function injectFallbackForInitialFallback(ast: ValueParser.ValueAstNode[], fallback: string): void {
221+
ValueParser.walk(ast, (node) => {
222+
if (node.kind !== 'function') return
223+
if (node.value !== 'var' && node.value !== 'theme' && node.value !== '--theme') return
224+
225+
if (node.nodes.length === 1) {
226+
node.nodes.push({
227+
kind: 'word',
228+
value: `, ${fallback}`,
229+
})
230+
} else {
231+
let lastNode = node.nodes[node.nodes.length - 1]
232+
if (lastNode.kind === 'word' && lastNode.value === 'initial') {
233+
lastNode.value = fallback
234+
}
235+
}
236+
})
237+
}

packages/tailwindcss/theme.css

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -434,12 +434,12 @@
434434

435435
--default-transition-duration: 150ms;
436436
--default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
437-
--default-font-family: --theme(--font-sans);
438-
--default-font-feature-settings: --theme(--font-sans--font-feature-settings, normal);
439-
--default-font-variation-settings: --theme(--font-sans--font-variation-settings, normal);
440-
--default-mono-font-family: --theme(--font-mono);
441-
--default-mono-font-feature-settings: --theme(--font-mono--font-feature-settings, normal);
442-
--default-mono-font-variation-settings: --theme(--font-mono--font-variation-settings, normal);
437+
--default-font-family: --theme(--font-sans, initial);
438+
--default-font-feature-settings: --theme(--font-sans--font-feature-settings, initial);
439+
--default-font-variation-settings: --theme(--font-sans--font-variation-settings, initial);
440+
--default-mono-font-family: --theme(--font-mono, initial);
441+
--default-mono-font-feature-settings: --theme(--font-mono--font-feature-settings, initial);
442+
--default-mono-font-variation-settings: --theme(--font-mono--font-variation-settings, initial);
443443
}
444444

445445
/* Deprecated */

0 commit comments

Comments
 (0)