Skip to content

Commit

Permalink
Do not emit @keyframes in @theme reference (#16120)
Browse files Browse the repository at this point in the history
This PR fixes na issue where `@keyframes` were emitted if they wre in a
`@theme
reference` and anothe `@theme {}` (that is not a reference) was present.

E.g.:

```css
@reference "tailwindcss";

@theme {
  /* ... */
}
```

Produces:
```css
:root, :host {
}
@Keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
@Keyframes ping {
  75%, 100% {
    transform: scale(2);
    opacity: 0;
  }
}
@Keyframes pulse {
  50% {
    opacity: 0.5;
  }
}
@Keyframes bounce {
  0%, 100% {
    transform: translateY(-25%);
    animation-timing-function: cubic-bezier(0.8, 0, 1, 1);
  }
  50% {
    transform: none;
    animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
  }
}
```

With this PR, the produced CSS looks like this instead:
```css
:root, :host {
}
```

Note: the empty `:root, :host` will be solved in a subsequent PR.

### Test plan

Added some unit tests, and a dedicated integration test.
  • Loading branch information
RobinMalfait authored Jan 31, 2025
1 parent 88c8906 commit 3aa0e49
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Vite: Ensure hot-reloading works with SolidStart setups ([#16052](https://github.com/tailwindlabs/tailwindcss/pull/16052))
- Vite: Fix a crash when starting the development server in SolidStart setups ([#16052](https://github.com/tailwindlabs/tailwindcss/pull/16052))
- Prevent camelCasing CSS custom properties added by JavaScript plugins ([#16103](https://github.com/tailwindlabs/tailwindcss/pull/16103))
- Do not emit `@keyframes` in `@theme reference` ([#16120](https://github.com/tailwindlabs/tailwindcss/pull/16120))

## [4.0.1] - 2025-01-29

Expand Down
62 changes: 62 additions & 0 deletions integrations/cli/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1196,3 +1196,65 @@ test(
`)
},
)

test(
'@theme reference should never emit values',
{
fs: {
'package.json': json`
{
"dependencies": {
"tailwindcss": "workspace:^",
"@tailwindcss/cli": "workspace:^"
}
}
`,
'src/index.css': css`
@reference "tailwindcss";
.keep-me {
color: red;
}
`,
},
},
async ({ fs, spawn, expect }) => {
let process = await spawn(
`pnpm tailwindcss --input src/index.css --output dist/out.css --watch`,
)
await process.onStderr((m) => m.includes('Done in'))

expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
"
--- ./dist/out.css ---
.keep-me {
color: red;
}
"
`)

await fs.write(
'./src/index.css',
css`
@reference "tailwindcss";
/* Not a reference! */
@theme {
--color-pink: pink;
}
.keep-me {
color: red;
}
`,
)
expect(await fs.dumpFiles('./dist/*.css')).toMatchInlineSnapshot(`
"
--- ./dist/out.css ---
.keep-me {
color: red;
}
"
`)
},
)
69 changes: 69 additions & 0 deletions packages/tailwindcss/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,75 @@ describe('Parsing themes values from CSS', () => {
`)
})

test('`@keyframes` added in `@theme reference` should not be emitted', async () => {
return expect(
await compileCss(
css`
@theme reference {
--animate-foo: foo 1s infinite;
@keyframes foo {
0%,
100% {
color: red;
}
50% {
color: blue;
}
}
}
@tailwind utilities;
`,
['animate-foo'],
),
).toMatchInlineSnapshot(`
".animate-foo {
animation: var(--animate-foo);
}"
`)
})

test('`@keyframes` added in `@theme reference` should not be emitted, even if another `@theme` block exists', async () => {
return expect(
await compileCss(
css`
@theme reference {
--animate-foo: foo 1s infinite;
@keyframes foo {
0%,
100% {
color: red;
}
50% {
color: blue;
}
}
}
@theme {
--color-pink: pink;
}
@tailwind utilities;
`,
['bg-pink', 'animate-foo'],
),
).toMatchInlineSnapshot(`
":root, :host {
--color-pink: pink;
}
.animate-foo {
animation: var(--animate-foo);
}
.bg-pink {
background-color: var(--color-pink);
}"
`)
})

test('theme values added as reference that override existing theme value suppress the output of the original theme value as a variable', async () => {
expect(
await compileCss(
Expand Down
6 changes: 6 additions & 0 deletions packages/tailwindcss/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ 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) {
replaceWith([])
return WalkAction.Skip
}

theme.addKeyframes(child)
replaceWith([])
return WalkAction.Skip
Expand Down

0 comments on commit 3aa0e49

Please # to comment.