Make @reference emit variable fallbacks instead of CSS variable declarations (#16774)
Fixes #16725 When using `@reference "tailwindcss";` inside a separate CSS root (e.g. Svelte `<style>` components, CSS modules, etc.), we have no guarantee that the CSS variables will be defined in the main stylesheet (or if there even is one). To work around potential issues with this we decided in #16676 that we would emit all used CSS variables from the `@theme` inside the `@reference` block. However, this is not only a bit surprising but also unexpected in CSS modules and Next.js that **requires CSS module files to only create scope-able declarations**. To fix this issue, we decided to not emit CSS variables but instead ensure all `var(…)` calls we create for theme values in reference mode will simply have their fallback value added. This ensures styles work as-expected even if the root Tailwind file does not pick up the variable as being used or _if you don't add a root at all_. Furthermore we do not duplicate any variable declarations across your stylesheets and you still have the ability to change variables at runtime. ## Test plan - Updated snapshots everywhere (see diff) - New Next.js CSS modules integration test
This commit is contained in:
parent
59e003e6d1
commit
b38948337d
22 changed files with 201 additions and 205 deletions
|
|
@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
### Fixed
|
||||
|
||||
- Vite: Don't crash when importing a virtual module in JavaScript that ends in `.css` ([#16780](https://github.com/tailwindlabs/tailwindcss/pull/16780))
|
||||
- Ensure `@reference "…"` does not emit CSS variables ([#16774](https://github.com/tailwindlabs/tailwindcss/pull/16774))
|
||||
- Fix an issue where `@reference "…"` would sometimes omit keyframe animations ([#16774](https://github.com/tailwindlabs/tailwindcss/pull/16774))
|
||||
|
||||
## [4.0.8] - 2025-02-21
|
||||
|
||||
|
|
|
|||
|
|
@ -643,7 +643,7 @@ test(
|
|||
}
|
||||
`,
|
||||
'index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
|
||||
/* (1) */
|
||||
/* - Only './src' should be auto-scanned, not the current working directory */
|
||||
|
|
@ -774,7 +774,7 @@ test(
|
|||
}
|
||||
`,
|
||||
'project-a/src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
|
||||
/* Run auto-content detection in ../../project-b */
|
||||
@import 'tailwindcss/utilities' source('../../project-b');
|
||||
|
|
@ -1132,7 +1132,7 @@ test(
|
|||
}
|
||||
`,
|
||||
'index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
|
||||
/* (1) */
|
||||
/* - Only './src' should be auto-scanned, not the current working directory */
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ test(
|
|||
<div class="aspect-w-16"></div>
|
||||
`,
|
||||
'src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
@plugin '@tailwindcss/forms';
|
||||
|
|
|
|||
|
|
@ -659,7 +659,7 @@ test(
|
|||
}
|
||||
`,
|
||||
'index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
|
||||
/* (1) */
|
||||
/* - Only './src' should be auto-scanned, not the current working directory */
|
||||
|
|
@ -799,7 +799,7 @@ test(
|
|||
}
|
||||
`,
|
||||
'project-a/src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
|
||||
/* Run auto-content detection in ../../project-b */
|
||||
@import 'tailwindcss/utilities' source('../../project-b');
|
||||
|
|
@ -1163,7 +1163,7 @@ test(
|
|||
}
|
||||
`,
|
||||
'index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
|
||||
/* (1) */
|
||||
/* - Only './src' should be auto-scanned, not the current working directory */
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ test(
|
|||
<div class="one:underline two:underline"></div>
|
||||
`,
|
||||
'src/shared.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
`,
|
||||
'src/root1.css': css`
|
||||
|
|
|
|||
|
|
@ -19,21 +19,13 @@ test(
|
|||
}
|
||||
`,
|
||||
'postcss.config.mjs': js`
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
`,
|
||||
'next.config.mjs': js`
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
|
||||
export default nextConfig
|
||||
`,
|
||||
'next.config.mjs': js`export default {}`,
|
||||
'app/layout.js': js`
|
||||
import './globals.css'
|
||||
|
||||
|
|
@ -46,12 +38,21 @@ test(
|
|||
}
|
||||
`,
|
||||
'app/page.js': js`
|
||||
import styles from './page.module.css'
|
||||
export default function Page() {
|
||||
return <h1 className="text-3xl font-bold underline">Hello, Next.js!</h1>
|
||||
return (
|
||||
<h1 className={styles.heading + ' text-3xl font-bold underline'}>Hello, Next.js!</h1>
|
||||
)
|
||||
}
|
||||
`,
|
||||
'app/page.module.css': css`
|
||||
@reference './globals.css';
|
||||
.heading {
|
||||
@apply text-red-500 animate-ping;
|
||||
}
|
||||
`,
|
||||
'app/globals.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
`,
|
||||
},
|
||||
|
|
@ -60,14 +61,26 @@ test(
|
|||
await exec('pnpm next build')
|
||||
|
||||
let files = await fs.glob('.next/static/css/**/*.css')
|
||||
expect(files).toHaveLength(1)
|
||||
let [filename] = files[0]
|
||||
expect(files).toHaveLength(2)
|
||||
|
||||
await fs.expectFileToContain(filename, [
|
||||
let globalCss: string | null = null
|
||||
let moduleCss: string | null = null
|
||||
for (let [filename, content] of files) {
|
||||
if (content.includes('@keyframes page_ping')) moduleCss = filename
|
||||
else globalCss = filename
|
||||
}
|
||||
|
||||
await fs.expectFileToContain(globalCss!, [
|
||||
candidate`underline`,
|
||||
candidate`font-bold`,
|
||||
candidate`text-3xl`,
|
||||
])
|
||||
|
||||
await fs.expectFileToContain(moduleCss!, [
|
||||
'color:var(--color-red-500,oklch(.637 .237 25.331)',
|
||||
'animation:var(--animate-ping,ping 1s cubic-bezier(0,0,.2,1) infinite)',
|
||||
/@keyframes page_ping.*{75%,to{transform:scale\(2\);opacity:0}/,
|
||||
])
|
||||
},
|
||||
)
|
||||
|
||||
|
|
@ -90,21 +103,13 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
|
|||
}
|
||||
`,
|
||||
'postcss.config.mjs': js`
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
`,
|
||||
'next.config.mjs': js`
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
|
||||
export default nextConfig
|
||||
`,
|
||||
'next.config.mjs': js`export default {}`,
|
||||
'app/layout.js': js`
|
||||
import './globals.css'
|
||||
|
||||
|
|
@ -117,12 +122,19 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
|
|||
}
|
||||
`,
|
||||
'app/page.js': js`
|
||||
import styles from './page.module.css'
|
||||
export default function Page() {
|
||||
return <h1 className="underline">Hello, Next.js!</h1>
|
||||
return <h1 className={styles.heading + ' underline'}>Hello, Next.js!</h1>
|
||||
}
|
||||
`,
|
||||
'app/page.module.css': css`
|
||||
@reference './globals.css';
|
||||
.heading {
|
||||
@apply text-red-500 animate-ping content-['module'];
|
||||
}
|
||||
`,
|
||||
'app/globals.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
`,
|
||||
},
|
||||
|
|
@ -142,13 +154,16 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
|
|||
await retryAssertion(async () => {
|
||||
let css = await fetchStyles(url)
|
||||
expect(css).toContain(candidate`underline`)
|
||||
expect(css).toContain('content: var(--tw-content)')
|
||||
expect(css).toContain('@keyframes')
|
||||
})
|
||||
|
||||
await fs.write(
|
||||
'app/page.js',
|
||||
js`
|
||||
import styles from './page.module.css'
|
||||
export default function Page() {
|
||||
return <h1 className="underline text-red-500">Hello, Next.js!</h1>
|
||||
return <h1 className={styles.heading + ' underline bg-red-500'}>Hello, Next.js!</h1>
|
||||
}
|
||||
`,
|
||||
)
|
||||
|
|
@ -157,7 +172,9 @@ describe.each(['turbo', 'webpack'])('%s', (bundler) => {
|
|||
await retryAssertion(async () => {
|
||||
let css = await fetchStyles(url)
|
||||
expect(css).toContain(candidate`underline`)
|
||||
expect(css).toContain(candidate`text-red-500`)
|
||||
expect(css).toContain(candidate`bg-red-500`)
|
||||
expect(css).toContain('content: var(--tw-content)')
|
||||
expect(css).toContain('@keyframes')
|
||||
})
|
||||
},
|
||||
)
|
||||
|
|
@ -181,21 +198,13 @@ test(
|
|||
}
|
||||
`,
|
||||
'postcss.config.mjs': js`
|
||||
/** @type {import('postcss-load-config').Config} */
|
||||
const config = {
|
||||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
`,
|
||||
'next.config.mjs': js`
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {}
|
||||
|
||||
export default nextConfig
|
||||
`,
|
||||
'next.config.mjs': js`export default {}`,
|
||||
'app/a/[slug]/page.js': js`
|
||||
export default function Page() {
|
||||
return <h1 className="content-['[slug]']">Hello, Next.js!</h1>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ test(
|
|||
<div className="ui-open:flex"></div>
|
||||
`,
|
||||
'src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
@plugin '@headlessui/tailwindcss';
|
||||
`,
|
||||
|
|
@ -65,7 +65,7 @@ test(
|
|||
<div className="ui-open:flex"></div>
|
||||
`,
|
||||
'src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
@plugin '@headlessui/tailwindcss';
|
||||
`,
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
|
|||
}
|
||||
`,
|
||||
'project-a/src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
@config '../tailwind.config.js';
|
||||
@source '../../project-b/src/**/*.html';
|
||||
|
|
@ -147,7 +147,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
|
|||
}
|
||||
`,
|
||||
'project-a/src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
@config '../tailwind.config.js';
|
||||
@source '../../project-b/src/**/*.html';
|
||||
|
|
@ -291,7 +291,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
|
|||
}
|
||||
`,
|
||||
'project-a/src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
@import './custom-theme.css';
|
||||
@config '../tailwind.config.js';
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ test(
|
|||
</body>
|
||||
`,
|
||||
'src/shared.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
`,
|
||||
'src/root1.css': css`
|
||||
|
|
@ -119,7 +119,7 @@ test(
|
|||
</body>
|
||||
`,
|
||||
'src/shared.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
`,
|
||||
'src/root1.css': css`
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ function createSetup(transformer: 'postcss' | 'lightningcss') {
|
|||
</body>
|
||||
`,
|
||||
'src/index.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
.foo {
|
||||
|
|
@ -111,7 +111,7 @@ describe.each(['postcss', 'lightningcss'] as const)('%s', (transformer) => {
|
|||
await fs.write(
|
||||
'src/index.css',
|
||||
css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
.foo {
|
||||
|
|
@ -155,7 +155,7 @@ describe.each(['postcss', 'lightningcss'] as const)('%s', (transformer) => {
|
|||
await fs.write(
|
||||
'src/index.css',
|
||||
css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
|
||||
.foo {
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
|
|||
@plugin '#js-alias';
|
||||
`,
|
||||
'src/alias.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
`,
|
||||
'src/plugin.js': js`
|
||||
|
|
@ -117,7 +117,7 @@ describe.each(['postcss', 'lightningcss'])('%s', (transformer) => {
|
|||
@plugin '#js-alias';
|
||||
`,
|
||||
'src/alias.css': css`
|
||||
@import 'tailwindcss/theme' theme(reference);
|
||||
@reference 'tailwindcss/theme';
|
||||
@import 'tailwindcss/utilities';
|
||||
`,
|
||||
'src/plugin.js': js`
|
||||
|
|
|
|||
|
|
@ -100,8 +100,8 @@ test(
|
|||
|
||||
await fs.expectFileToContain(files[0][0], [
|
||||
candidate`underline`,
|
||||
'.global{color:var(--color-green-500);animation:2s ease-in-out infinite globalKeyframes}',
|
||||
/\.local.svelte-.*\{color:var\(--color-red-500\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/,
|
||||
'.global{color:var(--color-green-500,oklch(.723 .219 149.579));animation:2s ease-in-out infinite globalKeyframes}',
|
||||
/\.local.svelte-.*\{color:var\(--color-red-500\,oklch\(\.637 \.237 25\.331\)\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/,
|
||||
/@keyframes globalKeyframes\{/,
|
||||
/@keyframes svelte-.*-localKeyframes\{/,
|
||||
])
|
||||
|
|
@ -213,10 +213,10 @@ test(
|
|||
let [, css] = files[0]
|
||||
expect(css).toContain(candidate`underline`)
|
||||
expect(css).toContain(
|
||||
'.global{color:var(--color-green-500);animation:2s ease-in-out infinite globalKeyframes}',
|
||||
'.global{color:var(--color-green-500,oklch(.723 .219 149.579));animation:2s ease-in-out infinite globalKeyframes}',
|
||||
)
|
||||
expect(css).toMatch(
|
||||
/\.local.svelte-.*\{color:var\(--color-red-500\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/,
|
||||
/\.local.svelte-.*\{color:var\(--color-red-500,oklch\(\.637 \.237 25\.331\)\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/,
|
||||
)
|
||||
expect(css).toMatch(/@keyframes globalKeyframes\{/)
|
||||
expect(css).toMatch(/@keyframes svelte-.*-localKeyframes\{/)
|
||||
|
|
@ -238,14 +238,16 @@ test(
|
|||
let [, css] = files[0]
|
||||
expect(css).toContain(candidate`font-bold`)
|
||||
expect(css).toContain(
|
||||
'.global{color:var(--color-green-500);animation:2s ease-in-out infinite globalKeyframes}',
|
||||
'.global{color:var(--color-green-500,oklch(.723 .219 149.579));animation:2s ease-in-out infinite globalKeyframes}',
|
||||
)
|
||||
expect(css).toMatch(
|
||||
/\.local.svelte-.*\{color:var\(--color-red-500\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/,
|
||||
/\.local.svelte-.*\{color:var\(--color-red-500,oklch\(\.637 \.237 25\.331\)\);animation:2s ease-in-out infinite svelte-.*-localKeyframes\}/,
|
||||
)
|
||||
expect(css).toMatch(/@keyframes globalKeyframes\{/)
|
||||
expect(css).toMatch(/@keyframes svelte-.*-localKeyframes\{/)
|
||||
expect(css).toMatch(/\.bar.svelte-.*\{color:var\(--color-pink-500\)\}/)
|
||||
expect(css).toMatch(
|
||||
/\.bar.svelte-.*\{color:var\(--color-pink-500,oklch\(\.656 \.241 354\.308\)\)\}/,
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ test('@apply can be used without emitting the theme in the CSS file', async () =
|
|||
|
||||
let result = await processor.process(
|
||||
css`
|
||||
@import 'tailwindcss/theme.css' theme(reference);
|
||||
@reference 'tailwindcss/theme.css';
|
||||
.foo {
|
||||
@apply text-red-500;
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ test('@apply can be used without emitting the theme in the CSS file', async () =
|
|||
|
||||
expect(result.css.trim()).toMatchInlineSnapshot(`
|
||||
".foo {
|
||||
color: var(--color-red-500);
|
||||
color: var(--color-red-500, oklch(.637 .237 25.331));
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -256,11 +256,7 @@ export function walkDepth(
|
|||
|
||||
// Optimize the AST for printing where all the special nodes that require custom
|
||||
// handling are handled such that the printing is a 1-to-1 transformation.
|
||||
export function optimizeAst(
|
||||
ast: AstNode[],
|
||||
designSystem: DesignSystem,
|
||||
firstThemeRule: StyleRule | null,
|
||||
) {
|
||||
export function optimizeAst(ast: AstNode[], designSystem: DesignSystem) {
|
||||
let atRoots: AstNode[] = []
|
||||
let seenAtProperties = new Set<string>()
|
||||
let cssThemeVariables = new DefaultMap<
|
||||
|
|
@ -396,27 +392,8 @@ export function optimizeAst(
|
|||
|
||||
// Context
|
||||
else if (node.kind === 'context') {
|
||||
// Remove reference imports from printing
|
||||
if (node.context.reference) {
|
||||
if (firstThemeRule) {
|
||||
let path = findNode(node.nodes, (node) => node === firstThemeRule)
|
||||
if (path) {
|
||||
let newPathToFirstThemeRule = path
|
||||
.filter((node) => node.kind === 'at-rule')
|
||||
.map((node) => ({ ...node, nodes: [] }))
|
||||
.reverse() as AtRule[]
|
||||
|
||||
let child = firstThemeRule as AstNode
|
||||
for (let node of newPathToFirstThemeRule) {
|
||||
node.nodes = [child]
|
||||
child = node
|
||||
}
|
||||
|
||||
let newParent: AstNode[] = []
|
||||
transform(child, newParent, { ...context, ...node.context, reference: false }, depth)
|
||||
parent.push(...newParent)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
} else {
|
||||
for (let child of node.nodes) {
|
||||
|
|
|
|||
|
|
@ -421,7 +421,7 @@ test('supports theme(reference) imports', async () => {
|
|||
),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
".text-red-500 {
|
||||
color: var(--color-red-500);
|
||||
color: var(--color-red-500, red);
|
||||
}
|
||||
"
|
||||
`)
|
||||
|
|
@ -580,12 +580,8 @@ test('resolves @reference as `@import "…" reference`', async () => {
|
|||
{ loadStylesheet, candidates: ['text-red-500'] },
|
||||
),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
":root, :host {
|
||||
--color-red-500: red;
|
||||
}
|
||||
|
||||
.text-red-500 {
|
||||
color: var(--color-red-500);
|
||||
".text-red-500 {
|
||||
color: var(--color-red-500, red);
|
||||
}
|
||||
"
|
||||
`)
|
||||
|
|
|
|||
|
|
@ -1389,7 +1389,7 @@ test('blocklisted candidates are not generated', async () => {
|
|||
}
|
||||
.md\\:bg-white {
|
||||
@media (width >= 48rem) {
|
||||
background-color: var(--color-white);
|
||||
background-color: var(--color-white, #fff);
|
||||
}
|
||||
}
|
||||
"
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ export function buildDesignSystem(theme: Theme): DesignSystem {
|
|||
},
|
||||
})
|
||||
|
||||
astNodes = optimizeAst(astNodes, designSystem, null)
|
||||
astNodes = optimizeAst(astNodes, designSystem)
|
||||
|
||||
if (astNodes.length === 0 || wasInvalid) {
|
||||
result.push(null)
|
||||
|
|
|
|||
|
|
@ -325,12 +325,6 @@ describe('@apply', () => {
|
|||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@property --tw-translate-x {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
|
|
@ -347,6 +341,12 @@ describe('@apply', () => {
|
|||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -1822,7 +1822,7 @@ describe('Parsing theme values from CSS', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
test('theme values added as reference are not included in the output as variables', async () => {
|
||||
test('theme values added as reference are not included in the output as variables but emit fallback values', async () => {
|
||||
expect(
|
||||
await compileCss(
|
||||
css`
|
||||
|
|
@ -1842,7 +1842,7 @@ describe('Parsing theme values from CSS', () => {
|
|||
}
|
||||
|
||||
.bg-potato {
|
||||
background-color: var(--color-potato);
|
||||
background-color: var(--color-potato, #ac855b);
|
||||
}
|
||||
|
||||
.bg-tomato {
|
||||
|
|
@ -1874,7 +1874,17 @@ describe('Parsing theme values from CSS', () => {
|
|||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
".animate-foo {
|
||||
animation: var(--animate-foo);
|
||||
animation: var(--animate-foo, foo 1s infinite);
|
||||
}
|
||||
|
||||
@keyframes foo {
|
||||
0%, 100% {
|
||||
color: red;
|
||||
}
|
||||
|
||||
50% {
|
||||
color: #00f;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -1911,11 +1921,21 @@ describe('Parsing theme values from CSS', () => {
|
|||
}
|
||||
|
||||
.animate-foo {
|
||||
animation: var(--animate-foo);
|
||||
animation: var(--animate-foo, foo 1s infinite);
|
||||
}
|
||||
|
||||
.bg-pink {
|
||||
background-color: var(--color-pink);
|
||||
}
|
||||
|
||||
@keyframes foo {
|
||||
0%, 100% {
|
||||
color: red;
|
||||
}
|
||||
|
||||
50% {
|
||||
color: #00f;
|
||||
}
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -1936,7 +1956,7 @@ describe('Parsing theme values from CSS', () => {
|
|||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
".bg-potato {
|
||||
background-color: var(--color-potato);
|
||||
background-color: var(--color-potato, #c794aa);
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -1991,11 +2011,11 @@ describe('Parsing theme values from CSS', () => {
|
|||
}
|
||||
|
||||
.bg-avocado {
|
||||
background-color: var(--color-avocado);
|
||||
background-color: var(--color-avocado, #c0cc6d);
|
||||
}
|
||||
|
||||
.bg-potato {
|
||||
background-color: var(--color-potato);
|
||||
background-color: var(--color-potato, #ac855b);
|
||||
}
|
||||
|
||||
.bg-tomato {
|
||||
|
|
@ -2323,7 +2343,7 @@ describe('Parsing theme values from CSS', () => {
|
|||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
".bg-potato {
|
||||
background-color: var(--color-potato);
|
||||
background-color: var(--color-potato, #efb46b);
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -3696,7 +3716,7 @@ it("should error when `layer(…)` is used, but it's not the first param", async
|
|||
)
|
||||
})
|
||||
|
||||
describe('`@reference "…" reference`', () => {
|
||||
describe('`@reference "…" imports`', () => {
|
||||
test('recursively removes styles', async () => {
|
||||
let loadStylesheet = async (id: string, base: string) => {
|
||||
if (id === './foo/baz.css') {
|
||||
|
|
@ -3849,14 +3869,8 @@ describe('`@reference "…" reference`', () => {
|
|||
},
|
||||
),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
"@layer theme {
|
||||
:root, :host {
|
||||
--animate-spin: spin 1s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.bar {
|
||||
animation: var(--animate-spin);
|
||||
".bar {
|
||||
animation: var(--animate-spin, spin 1s linear infinite);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
|
|
@ -3867,7 +3881,7 @@ describe('`@reference "…" reference`', () => {
|
|||
`)
|
||||
})
|
||||
|
||||
test('emits theme variables and keyframes defined inside @reference-ed files', async () => {
|
||||
test('emits CSS variable fallback and keyframes defined inside @reference-ed files', async () => {
|
||||
let loadStylesheet = async (id: string, base: string) => {
|
||||
switch (id) {
|
||||
case './one.css': {
|
||||
|
|
@ -3924,18 +3938,9 @@ describe('`@reference "…" reference`', () => {
|
|||
{ loadStylesheet },
|
||||
),
|
||||
).resolves.toMatchInlineSnapshot(`
|
||||
"@layer two {
|
||||
@layer three {
|
||||
:root, :host {
|
||||
--color-red: red;
|
||||
--animate-wiggle: wiggle 1s ease-in-out infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bar {
|
||||
animation: var(--animate-wiggle);
|
||||
color: var(--color-red);
|
||||
".bar {
|
||||
animation: var(--animate-wiggle, wiggle 1s ease-in-out infinite);
|
||||
color: var(--color-red, red);
|
||||
}
|
||||
|
||||
@keyframes wiggle {
|
||||
|
|
|
|||
|
|
@ -448,6 +448,10 @@ async function parseCss(
|
|||
if (node.name === '@theme') {
|
||||
let [themeOptions, themePrefix] = parseThemeOptions(node.params)
|
||||
|
||||
if (context.reference) {
|
||||
themeOptions |= ThemeOptions.REFERENCE
|
||||
}
|
||||
|
||||
if (themePrefix) {
|
||||
if (!IS_VALID_PREFIX.test(themePrefix)) {
|
||||
throw new Error(
|
||||
|
|
@ -463,11 +467,6 @@ async function parseCss(
|
|||
// Collect `@keyframes` rules to re-insert with theme variables later,
|
||||
// since the `@theme` rule itself will be removed.
|
||||
if (child.kind === 'at-rule' && child.name === '@keyframes') {
|
||||
// Do not track/emit `@keyframes`, if they are part of a `@theme reference`.
|
||||
if (themeOptions & ThemeOptions.REFERENCE) {
|
||||
return WalkAction.Skip
|
||||
}
|
||||
|
||||
theme.addKeyframes(child)
|
||||
return WalkAction.Skip
|
||||
}
|
||||
|
|
@ -540,8 +539,9 @@ async function parseCss(
|
|||
let keyframesRules = designSystem.theme.getKeyframes()
|
||||
for (let keyframes of keyframesRules) {
|
||||
// Wrap `@keyframes` in `AtRoot` so they are hoisted out of `:root` when
|
||||
// printing.
|
||||
nodes.push(atRoot([keyframes]))
|
||||
// printing. We push it to the top-level of the AST so that an eventual
|
||||
// `@reference` does not cut it out when printing the document.
|
||||
ast.push(context({ theme: true }, [atRoot([keyframes])]))
|
||||
}
|
||||
|
||||
firstThemeRule.nodes = [context({ theme: true }, nodes)]
|
||||
|
|
@ -603,7 +603,6 @@ async function parseCss(
|
|||
root,
|
||||
utilitiesNode,
|
||||
features,
|
||||
firstThemeRule,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -616,10 +615,7 @@ export async function compileAst(
|
|||
features: Features
|
||||
build(candidates: string[]): AstNode[]
|
||||
}> {
|
||||
let { designSystem, ast, globs, root, utilitiesNode, features, firstThemeRule } = await parseCss(
|
||||
input,
|
||||
opts,
|
||||
)
|
||||
let { designSystem, ast, globs, root, utilitiesNode, features } = await parseCss(input, opts)
|
||||
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
ast.unshift(comment(`! tailwindcss v${version} | MIT License | https://tailwindcss.com `))
|
||||
|
|
@ -647,7 +643,7 @@ export async function compileAst(
|
|||
}
|
||||
|
||||
if (!utilitiesNode) {
|
||||
compiled ??= optimizeAst(ast, designSystem, firstThemeRule)
|
||||
compiled ??= optimizeAst(ast, designSystem)
|
||||
return compiled
|
||||
}
|
||||
|
||||
|
|
@ -669,7 +665,7 @@ export async function compileAst(
|
|||
// If no new candidates were added, we can return the original CSS. This
|
||||
// currently assumes that we only add new candidates and never remove any.
|
||||
if (!didChange) {
|
||||
compiled ??= optimizeAst(ast, designSystem, firstThemeRule)
|
||||
compiled ??= optimizeAst(ast, designSystem)
|
||||
return compiled
|
||||
}
|
||||
|
||||
|
|
@ -681,7 +677,7 @@ export async function compileAst(
|
|||
// CSS. This currently assumes that we only add new ast nodes and never
|
||||
// remove any.
|
||||
if (previousAstNodeCount === newNodes.length) {
|
||||
compiled ??= optimizeAst(ast, designSystem, firstThemeRule)
|
||||
compiled ??= optimizeAst(ast, designSystem)
|
||||
return compiled
|
||||
}
|
||||
|
||||
|
|
@ -689,7 +685,7 @@ export async function compileAst(
|
|||
|
||||
utilitiesNode.nodes = newNodes
|
||||
|
||||
compiled = optimizeAst(ast, designSystem, firstThemeRule)
|
||||
compiled = optimizeAst(ast, designSystem)
|
||||
return compiled
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ test('a prefix can be configured via @import theme(…)', async () => {
|
|||
]),
|
||||
).toMatchInlineSnapshot(`
|
||||
".tw\\:bg-potato {
|
||||
background-color: var(--tw-color-potato);
|
||||
background-color: var(--tw-color-potato, #7a4724);
|
||||
}
|
||||
.tw\\:custom {
|
||||
color: red;
|
||||
|
|
|
|||
|
|
@ -177,11 +177,20 @@ export class Theme {
|
|||
}
|
||||
|
||||
#var(themeKey: string) {
|
||||
if (!this.values.has(themeKey)) {
|
||||
let value = this.values.get(themeKey)
|
||||
if (!value) {
|
||||
return null
|
||||
}
|
||||
|
||||
return `var(${escape(this.#prefixKey(themeKey))})`
|
||||
// Since @theme blocks in reference mode do not emit the CSS variables, we can not assume that
|
||||
// the values will eventually be set up in the browser (e.g. when using `@apply` inside roots
|
||||
// that use `@reference`). Ensure we set up a fallback in these cases.
|
||||
let fallback = null
|
||||
if (value.options & ThemeOptions.REFERENCE) {
|
||||
fallback = value.value
|
||||
}
|
||||
|
||||
return `var(${escape(this.#prefixKey(themeKey))}${fallback ? `, ${fallback}` : ''})`
|
||||
}
|
||||
|
||||
markUsedVariable(themeKey: string) {
|
||||
|
|
|
|||
|
|
@ -10649,15 +10649,15 @@ test('bg', async () => {
|
|||
),
|
||||
).toMatchInlineSnapshot(`
|
||||
".bg-current\\/custom {
|
||||
background-color: color-mix(in oklab, currentColor var(--opacity-custom), transparent);
|
||||
background-color: color-mix(in oklab, currentColor var(--opacity-custom, var(--custom-opacity)), transparent);
|
||||
}
|
||||
|
||||
.bg-current\\/half {
|
||||
background-color: color-mix(in oklab, currentColor var(--opacity-half), transparent);
|
||||
background-color: color-mix(in oklab, currentColor var(--opacity-half, .5), transparent);
|
||||
}
|
||||
|
||||
.\\[color\\:red\\]\\/half {
|
||||
color: color-mix(in oklab, red var(--opacity-half), transparent);
|
||||
color: color-mix(in oklab, red var(--opacity-half, .5), transparent);
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -16734,8 +16734,8 @@ describe('custom utilities', () => {
|
|||
expect(optimizeCss(compiled).trim()).toMatchInlineSnapshot(`
|
||||
"@layer utilities {
|
||||
.text-sm {
|
||||
font-size: var(--text-sm);
|
||||
line-height: var(--tw-leading, var(--text-sm--line-height));
|
||||
font-size: var(--text-sm, .875rem);
|
||||
line-height: var(--tw-leading, var(--text-sm--line-height, 1.25rem));
|
||||
font-size: var(--text-sm, .8755rem);
|
||||
line-height: var(--text-sm--line-height, 1.255rem);
|
||||
text-rendering: optimizeLegibility;
|
||||
|
|
@ -16771,7 +16771,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.rounded-xl {
|
||||
border-radius: var(--radius-xl);
|
||||
border-radius: var(--radius-xl, 16px);
|
||||
}
|
||||
}"
|
||||
`)
|
||||
|
|
@ -17013,19 +17013,19 @@ describe('custom utilities', () => {
|
|||
expect(await compileCss(input, ['tab-1', 'tab-2', 'tab-4', 'tab-github']))
|
||||
.toMatchInlineSnapshot(`
|
||||
".tab-1 {
|
||||
tab-size: var(--tab-size-1);
|
||||
tab-size: var(--tab-size-1, 1);
|
||||
}
|
||||
|
||||
.tab-2 {
|
||||
tab-size: var(--tab-size-2);
|
||||
tab-size: var(--tab-size-2, 2);
|
||||
}
|
||||
|
||||
.tab-4 {
|
||||
tab-size: var(--tab-size-4);
|
||||
tab-size: var(--tab-size-4, 4);
|
||||
}
|
||||
|
||||
.tab-github {
|
||||
tab-size: var(--tab-size-github);
|
||||
tab-size: var(--tab-size-github, 8);
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['tab-3', 'tab-gitlab'])).toEqual('')
|
||||
|
|
@ -17054,19 +17054,19 @@ describe('custom utilities', () => {
|
|||
expect(await compileCss(input, ['tab-1', 'tab-2', 'tab-4', 'tab-github']))
|
||||
.toMatchInlineSnapshot(`
|
||||
".tab-1 {
|
||||
tab-size: var(--tab-size-1);
|
||||
tab-size: var(--tab-size-1, 1);
|
||||
}
|
||||
|
||||
.tab-2 {
|
||||
tab-size: var(--tab-size-2);
|
||||
tab-size: var(--tab-size-2, 2);
|
||||
}
|
||||
|
||||
.tab-4 {
|
||||
tab-size: var(--tab-size-4);
|
||||
tab-size: var(--tab-size-4, 4);
|
||||
}
|
||||
|
||||
.tab-github {
|
||||
tab-size: var(--tab-size-github);
|
||||
tab-size: var(--tab-size-github, 8);
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['tab-3', 'tab-gitlab'])).toEqual('')
|
||||
|
|
@ -17091,19 +17091,19 @@ describe('custom utilities', () => {
|
|||
expect(await compileCss(input, ['tab-1', 'tab-2', 'tab-4', 'tab-github']))
|
||||
.toMatchInlineSnapshot(`
|
||||
".tab-1 {
|
||||
tab-size: var(--tab-size-1);
|
||||
tab-size: var(--tab-size-1, 1);
|
||||
}
|
||||
|
||||
.tab-2 {
|
||||
tab-size: var(--tab-size-2);
|
||||
tab-size: var(--tab-size-2, 2);
|
||||
}
|
||||
|
||||
.tab-4 {
|
||||
tab-size: var(--tab-size-4);
|
||||
tab-size: var(--tab-size-4, 4);
|
||||
}
|
||||
|
||||
.tab-github {
|
||||
tab-size: var(--tab-size-github);
|
||||
tab-size: var(--tab-size-github, 8);
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['tab-3', 'tab-gitlab'])).toEqual('')
|
||||
|
|
@ -17377,7 +17377,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.tab-github {
|
||||
tab-size: var(--tab-size-github);
|
||||
tab-size: var(--tab-size-github, 8);
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['tab-[#0088cc]', 'tab-[1px]'])).toEqual('')
|
||||
|
|
@ -17409,7 +17409,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.example-full {
|
||||
--value: var(--example-full);
|
||||
--value: var(--example-full, 100%);
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['example-half', 'example-[#0088cc]'])).toEqual('')
|
||||
|
|
@ -17453,7 +17453,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.example-full {
|
||||
--value: var(--example-full);
|
||||
--value: var(--example-full, 100%);
|
||||
}
|
||||
|
||||
.tab-76 {
|
||||
|
|
@ -17465,7 +17465,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.tab-github {
|
||||
tab-size: var(--tab-size-github);
|
||||
tab-size: var(--tab-size-github, 8);
|
||||
}"
|
||||
`)
|
||||
expect(
|
||||
|
|
@ -17509,7 +17509,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.-example-full {
|
||||
--value: calc(var(--example-full) * -1);
|
||||
--value: calc(var(--example-full, 100%) * -1);
|
||||
}
|
||||
|
||||
.example-\\[10px\\] {
|
||||
|
|
@ -17521,7 +17521,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.example-full {
|
||||
--value: var(--example-full);
|
||||
--value: var(--example-full, 100%);
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['example-10'])).toEqual('')
|
||||
|
|
@ -17618,9 +17618,9 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.example-sm\\/7 {
|
||||
--value: var(--value-sm);
|
||||
--modifier: var(--modifier-7);
|
||||
--modifier-with-calc: calc(var(--modifier-7) * 2);
|
||||
--value: var(--value-sm, 14px);
|
||||
--modifier: var(--modifier-7, 28px);
|
||||
--modifier-with-calc: calc(var(--modifier-7, 28px) * 2);
|
||||
}
|
||||
|
||||
.example-\\[12px\\] {
|
||||
|
|
@ -17628,7 +17628,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.example-sm {
|
||||
--value: var(--value-sm);
|
||||
--value: var(--value-sm, 14px);
|
||||
}"
|
||||
`)
|
||||
expect(
|
||||
|
|
@ -17660,7 +17660,7 @@ describe('custom utilities', () => {
|
|||
}
|
||||
|
||||
.example-video {
|
||||
--value: var(--example-video);
|
||||
--value: var(--example-video, 16 / 9);
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['example-foo'])).toEqual('')
|
||||
|
|
@ -17684,14 +17684,14 @@ describe('custom utilities', () => {
|
|||
|
||||
expect(await compileCss(input, ['example-xs', 'example-xs/6'])).toMatchInlineSnapshot(`
|
||||
".example-xs\\/6 {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
line-height: 6;
|
||||
}
|
||||
|
||||
.example-xs {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['example-foo', 'example-xs/foo'])).toEqual('')
|
||||
|
|
@ -17715,14 +17715,14 @@ describe('custom utilities', () => {
|
|||
|
||||
expect(await compileCss(input, ['example-xs', 'example-xs/6'])).toMatchInlineSnapshot(`
|
||||
".example-xs\\/6 {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
line-height: 6;
|
||||
}
|
||||
|
||||
.example-xs {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['example-foo', 'example-xs/foo'])).toEqual('')
|
||||
|
|
@ -17746,14 +17746,14 @@ describe('custom utilities', () => {
|
|||
|
||||
expect(await compileCss(input, ['example-xs', 'example-xs/6'])).toMatchInlineSnapshot(`
|
||||
".example-xs\\/6 {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
line-height: 6;
|
||||
}
|
||||
|
||||
.example-xs {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['example-foo', 'example-xs/foo'])).toEqual('')
|
||||
|
|
@ -17777,14 +17777,14 @@ describe('custom utilities', () => {
|
|||
|
||||
expect(await compileCss(input, ['example-xs', 'example-xs/6'])).toMatchInlineSnapshot(`
|
||||
".example-xs\\/6 {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
line-height: 6;
|
||||
}
|
||||
|
||||
.example-xs {
|
||||
font-size: var(--text-xs);
|
||||
line-height: var(--text-xs--line-height);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
}"
|
||||
`)
|
||||
expect(await compileCss(input, ['example-foo', 'example-xs/foo'])).toEqual('')
|
||||
|
|
@ -17880,7 +17880,7 @@ describe('custom utilities', () => {
|
|||
|
||||
expect(await compileCss(input, ['tab-github'])).toMatchInlineSnapshot(`
|
||||
".tab-github {
|
||||
tab-size: var(--tab-size-github);
|
||||
tab-size: var(--tab-size-github, 8);
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
@ -17945,7 +17945,7 @@ describe('custom utilities', () => {
|
|||
|
||||
expect(await compileCss(input, ['example-xs'])).toMatchInlineSnapshot(`
|
||||
".example-xs {
|
||||
font-size: var(--text-xs);
|
||||
font-size: var(--text-xs, .75rem);
|
||||
line-height: 1.33333;
|
||||
}"
|
||||
`)
|
||||
|
|
@ -17972,7 +17972,7 @@ describe('custom utilities', () => {
|
|||
expect(await compileCss(input, ['example-xs'])).toMatchInlineSnapshot(`
|
||||
".example-xs {
|
||||
font-size: .75rem;
|
||||
line-height: var(--text-xs--line-height);
|
||||
line-height: var(--text-xs--line-height, calc(1 / .75));
|
||||
}"
|
||||
`)
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue