diff --git a/CHANGELOG.md b/CHANGELOG.md index 1089c156e854..9111c69afd6d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Ensure there is always CLI feedback on save even when no new classes were found ([#14351](https://github.com/tailwindlabs/tailwindcss/pull/14351)) +- Properly resolve `theme('someKey.DEFAULT')` when all `--some-key-*` keys have a suffix ([#14354](https://github.com/tailwindlabs/tailwindcss/pull/14354)) ## [4.0.0-alpha.23] - 2024-09-05 diff --git a/packages/tailwindcss/src/compat/plugin-functions.ts b/packages/tailwindcss/src/compat/plugin-functions.ts index ef0a6d0c8b59..810953aab29d 100644 --- a/packages/tailwindcss/src/compat/plugin-functions.ts +++ b/packages/tailwindcss/src/compat/plugin-functions.ts @@ -115,7 +115,7 @@ function readFromCss(theme: Theme, path: string[]) { } // We have to turn the map into object-like structure for v3 compatibility - let obj = {} + let obj: Record = {} let useNestedObjects = false // paths.some((path) => nestedKeys.has(path)) for (let [key, value] of map) { @@ -134,20 +134,19 @@ function readFromCss(theme: Theme, path: string[]) { set(obj, path, value) } - if ('DEFAULT' in obj) { - // The request looked like `theme('animation.DEFAULT')` and was turned into - // a lookup for `--animation-*` and we should extract the value for the - // `DEFAULT` key from the list of possible values - if (path[path.length - 1] === 'DEFAULT') { - return obj.DEFAULT - } + // If the request looked like `theme('animation.DEFAULT')` it would have been + // turned into a lookup for `--animation-*` so we should extract the value for + // the `DEFAULT` key from the list of possible values. If there is no + // `DEFAULT` in the list, there is no match so return `null`. + if (path[path.length - 1] === 'DEFAULT') { + return obj?.DEFAULT ?? null + } - // The request looked like `theme('animation.spin')` and was turned into a - // lookup for `--animation-spin-*` which had only one entry which means it - // should be returned directly - if (Object.keys(obj).length === 1) { - return obj.DEFAULT - } + // The request looked like `theme('animation.spin')` and was turned into a + // lookup for `--animation-spin-*` which had only one entry which means it + // should be returned directly. + if ('DEFAULT' in obj && Object.keys(obj).length === 1) { + return obj.DEFAULT } return obj diff --git a/packages/tailwindcss/src/plugin-api.test.ts b/packages/tailwindcss/src/plugin-api.test.ts index 087dea0bc433..e7ed5e8a681e 100644 --- a/packages/tailwindcss/src/plugin-api.test.ts +++ b/packages/tailwindcss/src/plugin-api.test.ts @@ -804,6 +804,35 @@ describe('theme', async () => { expect(fn).toHaveBeenCalledWith('blue') }) + test("`theme('*.DEFAULT')` resolves to `undefined` when all theme keys in that namespace have a suffix", async ({ + expect, + }) => { + let input = css` + @tailwind utilities; + @plugin "my-plugin"; + @theme { + --transition-timing-function-in: ease-in; + --transition-timing-function-out: ease-out; + } + ` + + let fn = vi.fn() + + await compile(input, { + loadPlugin: async () => { + return plugin(({ theme }) => { + fn(theme('transitionTimingFunction.DEFAULT')) + fn(theme('transitionTimingFunction.in')) + fn(theme('transitionTimingFunction.out')) + }) + }, + }) + + expect(fn).toHaveBeenNthCalledWith(1, undefined) + expect(fn).toHaveBeenNthCalledWith(2, 'ease-in') + expect(fn).toHaveBeenNthCalledWith(3, 'ease-out') + }) + test('nested theme key lookups work even for flattened keys', async ({ expect }) => { let input = css` @tailwind utilities;