Skip to content

Commit 7b59aac

Browse files
authored
Properly resolve theme('someKey.DEFAULT') when only --some-key-* keys exist (#14354)
This PR fixes an issue where theme function calls like `theme('transitionTimingFunction.DEFAULT')` would incorrectly resolve to an object when the set of defined CSS theme values looked like this: ```css @theme { --transition-timing-function-in: ease-in; --transition-timing-function-out: ease-out; --transition-timing-function-in-out: ease-out; } ``` We were mistakenly retrieving the entire `--transition-timing-function-*` namespace in this case and returning an object, even though the user is explicitly asking for a single value by including `.DEFAULT` in their call. This ensures it resolves to null instead. Fixes an issue I ran into on this live stream earlier today: https://x.com/adamwathan/status/1831740214051799281 --------- Co-authored-by: Adam Wathan <[email protected]>
1 parent f028eae commit 7b59aac

File tree

3 files changed

+43
-14
lines changed

3 files changed

+43
-14
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111

1212
- Ensure there is always CLI feedback on save even when no new classes were found ([#14351](https://github.com/tailwindlabs/tailwindcss/pull/14351))
13+
- Properly resolve `theme('someKey.DEFAULT')` when all `--some-key-*` keys have a suffix ([#14354](https://github.com/tailwindlabs/tailwindcss/pull/14354))
1314

1415
## [4.0.0-alpha.23] - 2024-09-05
1516

packages/tailwindcss/src/compat/plugin-functions.ts

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function readFromCss(theme: Theme, path: string[]) {
115115
}
116116

117117
// We have to turn the map into object-like structure for v3 compatibility
118-
let obj = {}
118+
let obj: Record<string, unknown> = {}
119119
let useNestedObjects = false // paths.some((path) => nestedKeys.has(path))
120120

121121
for (let [key, value] of map) {
@@ -134,20 +134,19 @@ function readFromCss(theme: Theme, path: string[]) {
134134
set(obj, path, value)
135135
}
136136

137-
if ('DEFAULT' in obj) {
138-
// The request looked like `theme('animation.DEFAULT')` and was turned into
139-
// a lookup for `--animation-*` and we should extract the value for the
140-
// `DEFAULT` key from the list of possible values
141-
if (path[path.length - 1] === 'DEFAULT') {
142-
return obj.DEFAULT
143-
}
137+
// If the request looked like `theme('animation.DEFAULT')` it would have been
138+
// turned into a lookup for `--animation-*` so we should extract the value for
139+
// the `DEFAULT` key from the list of possible values. If there is no
140+
// `DEFAULT` in the list, there is no match so return `null`.
141+
if (path[path.length - 1] === 'DEFAULT') {
142+
return obj?.DEFAULT ?? null
143+
}
144144

145-
// The request looked like `theme('animation.spin')` and was turned into a
146-
// lookup for `--animation-spin-*` which had only one entry which means it
147-
// should be returned directly
148-
if (Object.keys(obj).length === 1) {
149-
return obj.DEFAULT
150-
}
145+
// The request looked like `theme('animation.spin')` and was turned into a
146+
// lookup for `--animation-spin-*` which had only one entry which means it
147+
// should be returned directly.
148+
if ('DEFAULT' in obj && Object.keys(obj).length === 1) {
149+
return obj.DEFAULT
151150
}
152151

153152
return obj

packages/tailwindcss/src/plugin-api.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,35 @@ describe('theme', async () => {
804804
expect(fn).toHaveBeenCalledWith('blue')
805805
})
806806

807+
test("`theme('*.DEFAULT')` resolves to `undefined` when all theme keys in that namespace have a suffix", async ({
808+
expect,
809+
}) => {
810+
let input = css`
811+
@tailwind utilities;
812+
@plugin "my-plugin";
813+
@theme {
814+
--transition-timing-function-in: ease-in;
815+
--transition-timing-function-out: ease-out;
816+
}
817+
`
818+
819+
let fn = vi.fn()
820+
821+
await compile(input, {
822+
loadPlugin: async () => {
823+
return plugin(({ theme }) => {
824+
fn(theme('transitionTimingFunction.DEFAULT'))
825+
fn(theme('transitionTimingFunction.in'))
826+
fn(theme('transitionTimingFunction.out'))
827+
})
828+
},
829+
})
830+
831+
expect(fn).toHaveBeenNthCalledWith(1, undefined)
832+
expect(fn).toHaveBeenNthCalledWith(2, 'ease-in')
833+
expect(fn).toHaveBeenNthCalledWith(3, 'ease-out')
834+
})
835+
807836
test('nested theme key lookups work even for flattened keys', async ({ expect }) => {
808837
let input = css`
809838
@tailwind utilities;

0 commit comments

Comments
 (0)