diff --git a/CHANGELOG.md b/CHANGELOG.md index 82bb2a70551c..7a95136a0e91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Refactor gradient implementation to work around [prettier/prettier#17058](https://github.com/prettier/prettier/issues/17058) ([#16072](https://github.com/tailwindlabs/tailwindcss/pull/16072)) - 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)) ## [4.0.1] - 2025-01-29 diff --git a/packages/tailwindcss/src/compat/plugin-api.test.ts b/packages/tailwindcss/src/compat/plugin-api.test.ts index 539c04a3c66c..4502118f458f 100644 --- a/packages/tailwindcss/src/compat/plugin-api.test.ts +++ b/packages/tailwindcss/src/compat/plugin-api.test.ts @@ -1534,6 +1534,38 @@ describe('addBase', () => { " `) }) + + test('does not modify CSS variables', async () => { + let input = css` + @plugin "my-plugin"; + ` + + let compiler = await compile(input, { + loadModule: async () => ({ + module: plugin(function ({ addBase }) { + addBase({ + ':root': { + '--PascalCase': '1', + '--camelCase': '1', + '--UPPERCASE': '1', + }, + }) + }), + base: '/root', + }), + }) + + expect(compiler.build([])).toMatchInlineSnapshot(` + "@layer base { + :root { + --PascalCase: 1; + --camelCase: 1; + --UPPERCASE: 1; + } + } + " + `) + }) }) describe('addVariant', () => { diff --git a/packages/tailwindcss/src/compat/plugin-api.ts b/packages/tailwindcss/src/compat/plugin-api.ts index 13673280769b..8db4d68973fb 100644 --- a/packages/tailwindcss/src/compat/plugin-api.ts +++ b/packages/tailwindcss/src/compat/plugin-api.ts @@ -499,15 +499,18 @@ export function objectToAst(rules: CssInJs | CssInJs[]): AstNode[] { for (let [name, value] of entries) { if (typeof value !== 'object') { - if (!name.startsWith('--') && value === '@slot') { - ast.push(rule(name, [atRule('@slot')])) - } else { + if (!name.startsWith('--')) { + if (value === '@slot') { + ast.push(rule(name, [atRule('@slot')])) + continue + } + // Convert camelCase to kebab-case: // https://github.com/postcss/postcss-js/blob/b3db658b932b42f6ac14ca0b1d50f50c4569805b/parser.js#L30-L35 name = name.replace(/([A-Z])/g, '-$1').toLowerCase() - - ast.push(decl(name, String(value))) } + + ast.push(decl(name, String(value))) } else if (Array.isArray(value)) { for (let item of value) { if (typeof item === 'string') {