From 06dfa399e6775c764d1e538239a6cdf08da55ca3 Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Tue, 4 Feb 2025 12:38:31 +0100 Subject: [PATCH] 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 --- CHANGELOG.md | 3 +- packages/tailwindcss/src/index.test.ts | 39 ++++++++++++++++++++++++++ packages/tailwindcss/src/index.ts | 2 +- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2686352696a..7ff6cdbb6bc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/packages/tailwindcss/src/index.test.ts b/packages/tailwindcss/src/index.test.ts index 67a0a3d64dab..497ccc034173 100644 --- a/packages/tailwindcss/src/index.test.ts +++ b/packages/tailwindcss/src/index.test.ts @@ -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( diff --git a/packages/tailwindcss/src/index.ts b/packages/tailwindcss/src/index.ts index 2baed9acf6a5..37c19a3f3edf 100644 --- a/packages/tailwindcss/src/index.ts +++ b/packages/tailwindcss/src/index.ts @@ -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) {