Ensure we always emit @keyframes correctly (#16237)

This PR fixes an issue where we didn't always generate the `@keyframes`.
Right now we only generate `@keyframes` _if_ they are being used as one
of the `--animate-*` utilities.

However, if your `--animate-*` definition is pretty long such that it is
defined across multiple lines, then we didn't always generate the
`@keyframes` for it.

This is because the animation name would look like
`'my-animation-name\n'` instead of `'my-animation-name'`.

Fixes: #16227
This commit is contained in:
Robin Malfait 2025-02-04 12:38:31 +01:00 committed by GitHub
parent ac202ffd50
commit 06dfa399e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 41 additions and 3 deletions

View File

@ -7,11 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- Nothing yet!
### Fixed
- Ensure that the `containers` JS theme key is added to the `--container-*` namespace. ([#16169](https://github.com/tailwindlabs/tailwindcss/pull/16169))
- Fix missing `@keyframes` definition ([#16237](https://github.com/tailwindlabs/tailwindcss/pull/16237))
## [4.0.3] - 2025-02-01

View File

@ -1170,6 +1170,45 @@ describe('Parsing themes values from CSS', () => {
`)
})
test('`@keyframes` in `@theme` are generated when name contains a new line', async () => {
expect(
await compileCss(
css`
@theme {
--animate-very-long-animation-name: very-long-animation-name
var(
--very-long-animation-name-configuration,
2.5s ease-in-out 0s infinite normal none running
);
@keyframes very-long-animation-name {
to {
opacity: 1;
}
}
}
@tailwind utilities;
`,
['animate-very-long-animation-name'],
),
).toMatchInlineSnapshot(`
":root, :host {
--animate-very-long-animation-name: very-long-animation-name var(--very-long-animation-name-configuration, 2.5s ease-in-out 0s infinite normal none running);
}
.animate-very-long-animation-name {
animation: var(--animate-very-long-animation-name);
}
@keyframes very-long-animation-name {
to {
opacity: 1;
}
}"
`)
})
test('`@theme` values can be unset', async () => {
expect(
await compileCss(

View File

@ -545,7 +545,7 @@ async function parseCss(
let keyframesRules = theme.getKeyframes()
if (keyframesRules.length > 0) {
let animationParts = [...theme.namespace('--animate').values()].flatMap((animation) =>
animation.split(' '),
animation.split(/\s+/),
)
for (let keyframesRule of keyframesRules) {