Prevent duplicate keyframes when adding components (#8993)

* fix: prevent duplicate keyframes when adding components

- Check for existing keyframes in @theme inline before adding
- Replace existing keyframes instead of creating duplicates
- Add test to verify keyframe replacement behavior

* chore: changeset

---------

Co-authored-by: shadcn <m@shadcn.com>
This commit is contained in:
Pasquale Vitiello
2025-12-09 10:01:34 +01:00
committed by GitHub
parent bdedce2750
commit 142cd8ef13
3 changed files with 62 additions and 6 deletions

View File

@@ -0,0 +1,5 @@
---
"shadcn": patch
---
Prevent duplicate keyframes when adding components

View File

@@ -262,13 +262,32 @@ function updateCssPlugin(css: z.infer<typeof registryItemCssSchema>) {
)
}
const keyframesRule = postcss.atRule({
name: "keyframes",
params,
raws: { semicolon: true, between: " ", before: "\n " },
})
// Check if a keyframe with the same name already exists
const existingKeyframesRule = themeInline.nodes?.find(
(node): node is AtRule =>
node.type === "atrule" &&
node.name === "keyframes" &&
node.params === params
)
themeInline.append(keyframesRule)
let keyframesRule: AtRule
if (existingKeyframesRule) {
// Replace existing keyframe
keyframesRule = postcss.atRule({
name: "keyframes",
params,
raws: { semicolon: true, between: " ", before: "\n " },
})
existingKeyframesRule.replaceWith(keyframesRule)
} else {
// Create new keyframe
keyframesRule = postcss.atRule({
name: "keyframes",
params,
raws: { semicolon: true, between: " ", before: "\n " },
})
themeInline.append(keyframesRule)
}
if (typeof properties === "object") {
for (const [step, stepProps] of Object.entries(properties)) {

View File

@@ -852,4 +852,36 @@ describe("transformCss", () => {
}"
`)
})
test("should replace existing keyframes instead of duplicating", async () => {
const input = `@import "tailwindcss";
@theme inline {
@keyframes skeleton {
to {
background-position: "-100% 0";
}
}
}`
const result = await transformCss(input, {
"@keyframes skeleton": {
"to": {
"background-position": "-200% 0",
},
},
})
expect(result).toMatchInlineSnapshot(`
"@import "tailwindcss";
@theme inline {
@keyframes skeleton {
to {
background-position: -200% 0;
}
}
}"
`)
})
})