tailwindcss/packages
한국 04588b1e8f
Fix theme() in JS plugins returning unresolved object instead of DEFAULT value (#20299)
## Summary

When a CSS theme key defined via `@theme` (or a JS config's `theme`
object) shares a dash-separated prefix with a sibling key — e.g.
`--color-foo` and `--color-foo-bar` — calling `theme('colors.foo')` from
inside a JS plugin (`addUtilities`, `addComponents`, etc.) does not
resolve to the `foo` value. Instead it returns an internal
disambiguation object shaped like `{ DEFAULT: 'red', bar: 'blue',
__CSS_VALUES__: {...} }`, because there's no way to tell from CSS custom
property names alone whether `foo-bar` is a sibling key or a nested
sub-key of `foo`.

This same ambiguity was already fixed for the CSS-embedded `theme()`
function in #19097 (which unwraps to the `DEFAULT` key when present),
and the changelog entry for that PR states it fixes this "in JS configs
**and plugins**" — but the fix only touched `apply-compat-hooks.ts`'s
`resolveThemeValue`, not `createThemeFn`'s `theme` function that's
exposed directly to plugins in `plugin-functions.ts`. This PR closes
that gap by applying the same DEFAULT-unwrapping there.

Without this fix, passing the raw object into `addUtilities` (a very
natural thing to do, since a plugin author expects a string) produces
broken CSS — the reserved `DEFAULT` key gets mangled into a garbage
property name (`-d-e-f-a-u-l-t`) by the kebab-case conversion, and the
internal `__CSS_VALUES__` bookkeeping leaks into the generated
stylesheet.

### Minimal reproduction

```js
// tailwind.config.js (registered via @config, or any @plugin-registered plugin)
const plugin = require('tailwindcss/plugin')

module.exports = {
  plugins: [
    plugin(function ({ addUtilities, theme }) {
      addUtilities({
        '.example-foo': { color: theme('colors.foo') },
      })
    }),
  ],
}
```
```css
@import "tailwindcss";
@config "./tailwind.config.js";
@theme {
  --color-foo: red;
  --color-foo-bar: blue;
}
```

**Before:**
```css
.example-foo color {
  -d-e-f-a-u-l-t: red;
  bar: blue;
}
.example-foo color __CSS_VALUES__ {
  -d-e-f-a-u-l-t: 0;
  bar: 0;
}
```

**After:**
```css
.example-foo {
  color: red;
}
```

## Test plan

- Added a regression test in
`packages/tailwindcss/src/compat/plugin-api.test.ts` ("theme() resolves
the DEFAULT value when a bare CSS theme key shares a prefix with a
sibling key")
- Verified via `pnpm --filter tailwindcss exec vitest run` that this
test fails with the exact broken output shown above when the fix is
reverted, and passes once it's applied
- Ran the full `tailwindcss` package test suite (`pnpm --filter
tailwindcss exec vitest run`) — 4684 tests passing, no regressions
- Verified formatting on the changed files with `npx prettier --check`

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2026-07-02 13:20:14 +00:00
..
@tailwindcss-browser 4.3.2 (#20281) 2026-06-29 10:10:00 -04:00
@tailwindcss-cli Add --poll option to @tailwindcss/cli (#20297) 2026-07-01 21:25:26 +02:00
@tailwindcss-node 4.3.2 (#20281) 2026-06-29 10:10:00 -04:00
@tailwindcss-postcss fix: exclude iframes from focus-visible auto outline in Preflight (#20292) 2026-07-02 10:39:28 +00:00
@tailwindcss-standalone Bump dependencies (#20300) 2026-07-02 11:45:49 +02:00
@tailwindcss-upgrade 4.3.2 (#20281) 2026-06-29 10:10:00 -04:00
@tailwindcss-vite 4.3.2 (#20281) 2026-06-29 10:10:00 -04:00
@tailwindcss-webpack 4.3.2 (#20281) 2026-06-29 10:10:00 -04:00
internal-example-plugin Prefix internal modules with internal-* (#14074) 2024-07-29 10:58:07 -04:00
tailwindcss Fix theme() in JS plugins returning unresolved object instead of DEFAULT value (#20299) 2026-07-02 13:20:14 +00:00
tsconfig.base.json Improve debug logs (#15303) 2024-12-11 15:27:20 +01:00