diff --git a/.changeset/spotty-doodles-love.md b/.changeset/spotty-doodles-love.md new file mode 100644 index 000000000..cc2093ba6 --- /dev/null +++ b/.changeset/spotty-doodles-love.md @@ -0,0 +1,5 @@ +--- +"shadcn": patch +--- + +Prevent duplicate keyframes when adding components diff --git a/packages/shadcn/src/utils/updaters/update-css.ts b/packages/shadcn/src/utils/updaters/update-css.ts index 4ffb250b3..13b2c7435 100644 --- a/packages/shadcn/src/utils/updaters/update-css.ts +++ b/packages/shadcn/src/utils/updaters/update-css.ts @@ -262,13 +262,32 @@ function updateCssPlugin(css: z.infer) { ) } - 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)) { diff --git a/packages/shadcn/test/utils/updaters/update-css.test.ts b/packages/shadcn/test/utils/updaters/update-css.test.ts index b5dd5f25d..c1d3a6fb5 100644 --- a/packages/shadcn/test/utils/updaters/update-css.test.ts +++ b/packages/shadcn/test/utils/updaters/update-css.test.ts @@ -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; + } + } + }" + `) + }) })