From f945c3d29fe0235c37c46bc00df906a720707738 Mon Sep 17 00:00:00 2001 From: Jordan Pittman Date: Mon, 13 Oct 2025 11:44:33 -0400 Subject: [PATCH] =?UTF-8?q?Don=E2=80=99t=20index=20into=20strings=20with?= =?UTF-8?q?=20the=20`theme(=E2=80=A6)`=20function=20(#19111)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Indexing into a string is only ever going to produce a single character and is almost guaranteed to be a mistake. The legacy keypath notation is meant to traverse objects and arrays — not strings. Fixes #19104 --- CHANGELOG.md | 1 + .../tailwindcss/src/compat/config.test.ts | 32 +++++++++++++++++++ .../src/compat/plugin-functions.ts | 5 +++ 3 files changed, 38 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 375d5079e..56d453605 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix resolving theme keys when starting with the name of another theme key in JS configs and plugins ([#19097](https://github.com/tailwindlabs/tailwindcss/pull/19097)) - Allow named groups in combination with `not-*`, `has-*`, and `in-*` ([#19100](https://github.com/tailwindlabs/tailwindcss/pull/19100)) - Prevent important utilities from affecting other utilities ([#19110](https://github.com/tailwindlabs/tailwindcss/pull/19110)) +- Don’t index into strings with the `theme(…)` function ([#19111](https://github.com/tailwindlabs/tailwindcss/pull/19111)) - Upgrade: Canonicalize utilities containing `0` values ([#19095](https://github.com/tailwindlabs/tailwindcss/pull/19095)) ## [4.1.14] - 2025-10-01 diff --git a/packages/tailwindcss/src/compat/config.test.ts b/packages/tailwindcss/src/compat/config.test.ts index 8256f663c..d86d5304b 100644 --- a/packages/tailwindcss/src/compat/config.test.ts +++ b/packages/tailwindcss/src/compat/config.test.ts @@ -1687,3 +1687,35 @@ test('handles setting theme keys to null', async () => { " `) }) + +test('The theme() function does not try indexing into strings', async () => { + let compiler = await compile(css` + @theme default { + --color-what-50: #f00; + --color-what-950: #f00; + } + + @theme { + /* + * The value of this theme variable is > 50 chars because colors.what.50 was previously + * indexing the string: "light-dark(theme(colors.what.950), theme(colors.what.50))" + * because the resolved config contained an object with a "what" key that was that string + */ + --color-what: light-dark(theme(colors.what.950), theme(colors.what.50)); + } + + @source inline("text-what"); + + @tailwind utilities; + `) + + expect(compiler.build([])).toMatchInlineSnapshot(` + ":root, :host { + --color-what: light-dark(#f00, #f00); + } + .text-what { + color: var(--color-what); + } + " + `) +}) diff --git a/packages/tailwindcss/src/compat/plugin-functions.ts b/packages/tailwindcss/src/compat/plugin-functions.ts index 339bd11c6..f311ad1e8 100644 --- a/packages/tailwindcss/src/compat/plugin-functions.ts +++ b/packages/tailwindcss/src/compat/plugin-functions.ts @@ -233,6 +233,11 @@ function get(obj: any, path: string[]) { continue } + // We never want to index into strings + if (typeof obj === 'string') { + return undefined + } + obj = obj[key] }