Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Skip comments in Ruby files when checking for class names ([#19243](https://github.com/tailwindlabs/tailwindcss/pull/19243))
- Skip over arbitrary property utilities with a top-level `!` in the value ([#19243](https://github.com/tailwindlabs/tailwindcss/pull/19243))
- Support environment API in `@tailwindcss/vite` ([#18970](https://github.com/tailwindlabs/tailwindcss/pull/18970))
- Preserve case of theme keys from JS configs and plugins ([#19337](https://github.com/tailwindlabs/tailwindcss/pull/19337))
- Upgrade: Handle `future` and `experimental` config keys ([#19344](https://github.com/tailwindlabs/tailwindcss/pull/19344))

### Added
Expand Down
12 changes: 6 additions & 6 deletions integrations/upgrade/js-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@ test(
"
--- src/index.html ---
<div
class="tracking-super-wide leading-super-loose"
class="tracking-superWide leading-superLoose"
></div>
<div class="text-super-red/super-opaque leading-super-loose"></div>
<div class="text-superRed/superOpaque leading-superLoose"></div>

--- src/input.css ---
@import 'tailwindcss';
Expand All @@ -187,12 +187,12 @@ test(
--color-red-500: #ef4444;
--color-red-600: #dc2626;

--color-super-red: #ff0000;
--color-superRed: #ff0000;
--color-steel: rgb(70 130 180);
--color-smoke: rgba(245, 245, 245, var(--smoke-alpha, 1));

--opacity-*: initial;
--opacity-super-opaque: 95%;
--opacity-superOpaque: 95%;

--text-*: initial;
--text-xs: 0.75rem;
Expand Down Expand Up @@ -265,9 +265,9 @@ test(
--animate-spin-clockwise: spin-clockwise 1s linear infinite;
--animate-spin-counterclockwise: spin-counterclockwise 1s linear infinite;

--tracking-super-wide: 0.25em;
--tracking-superWide: 0.25em;

--leading-super-loose: 3;
--leading-superLoose: 3;

@keyframes spin-clockwise {
0% {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ test('config values can be merged into the theme', () => {
])
expect(theme.resolve('2xl', ['--text'])).toEqual('2rem')
expect(theme.resolveWith('2xl', ['--text'], ['--line-height'])).toEqual(['2rem', {}])
expect(theme.resolve('super-wide', ['--tracking'])).toEqual('0.25em')
expect(theme.resolve('super-loose', ['--leading'])).toEqual('3')
expect(theme.resolve('superWide', ['--tracking'])).toEqual('0.25em')
expect(theme.resolve('superLoose', ['--leading'])).toEqual('3')
expect(theme.resolve('1/2', ['--width'])).toEqual('60%')
expect(theme.resolve('0.5', ['--width'])).toEqual('60%')
expect(theme.resolve('100%', ['--width'])).toEqual('100%')
Expand Down
26 changes: 21 additions & 5 deletions packages/tailwindcss/src/compat/apply-config-to-theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,27 @@ export function keyPathToCssProperty(path: string[]) {
.map((path, idx, all) => (path === '1' && idx !== all.length - 1 ? '' : path))

// Resolve the key path to a CSS variable segment
.map((part) =>
part
.replaceAll('.', '_')
.replace(/([a-z])([A-Z])/g, (_, a, b) => `${a}-${b.toLowerCase()}`),
)
.map((part, idx) => {
part = part.replaceAll('.', '_')

let shouldConvert =
// The first "namespace" part should be converted to kebab-case
// This converts things like backgroundColor to `background-color`
idx === 0 ||
// Any tuple nested key should be converted to kebab-case
// These are identified with a leading `-`
// e.g. `fontSize.xs.1.lineHeight` -> `font-size-xs--line-height`
part.startsWith('-') ||
// `lineHeight` is a bit of a special case in which it does not
// always begin with a leading `-` even when as a nested tuple key
part === 'lineHeight'

if (shouldConvert) {
part = part.replace(/([a-z])([A-Z])/g, (_, a, b) => `${a}-${b.toLowerCase()}`)
}

return part
})

// Remove the `DEFAULT` key at the end of a path
// We're reading from CSS anyway so it'll be a string
Expand Down
52 changes: 52 additions & 0 deletions packages/tailwindcss/src/compat/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1719,3 +1719,55 @@ test('The theme() function does not try indexing into strings', async () => {
"
`)
})

test('camel case keys are preserved', async () => {
let compiler = await compile(
css`
@tailwind utilities;
@theme {
--color-blue-green: slate;
}
@config "./plugin.js";
`,
{
loadModule: async () => {
return {
base: '/',
path: '',
module: {
theme: {
extend: {
backgroundColor: {
lightGreen: '#c0ffee',
},
},
},
},
}
},
},
)

expect(
compiler.build([
// From CSS
'bg-blue-green', // should be output
'bg-blueGreen', // should not

// From JS config
'bg-light-green', // should not be output
'bg-lightGreen', // should be
]),
).toMatchInlineSnapshot(`
".bg-blue-green {
background-color: var(--color-blue-green);
}
.bg-lightGreen {
background-color: #c0ffee;
}
:root, :host {
--color-blue-green: slate;
}
"
`)
})