Repository: chakra-ui/panda (TypeScript)

Issue:
Theme filtering incorrectly matches partial theme names when only one is specified in `staticCss.themes`
### Description

When creating `themes` variants with similar names (like `design` and `design-legacy`) but only pre-generating `design` in the `staticCss.themes` array, the build tool incorrectly processes both variants instead of only the specified one.

### Link to Reproduction

https://play.panda-css.com/41FTSkx0R0

### Steps to reproduce

1. Go to https://play.panda-css.com/41FTSkx0R0
2. Go to `Config` tab
3. Verify `themes` variants, `design` and `design-legacy`
4. Verify `staticCss.themes` only has `design` to pre-generate tokens
5. Open CSS and Tokens tab a the bottom
6. See how both variants are pre-generated
7. If you were to swap `design` for `design-legacy` in `staticCss.themes`, it would only generate `design-legacy` tokens.

### JS Framework

React 18

### Panda CSS Version

0.53.6

### Browser

Latest Chrome

### Operating System

- [x] macOS
- [ ] Windows
- [ ] Linux

### Additional Information

I believe the issue is somewhere in this file when it uses `.some` and `.startsWith` methods.

https://github.com/chakra-ui/panda/blob/9453c9b8e5957b34efe9711c528a0d37b81025c0/packages/generator/src/artifacts/css/token-css.ts



Discussion:

Patch:
diff --git a/.changeset/warm-turtles-behave.md b/.changeset/warm-turtles-behave.md
new file mode 100644
index 0000000000..5ad6feb4cb
--- /dev/null
+++ b/.changeset/warm-turtles-behave.md
@@ -0,0 +1,6 @@
+---
+'@pandacss/generator': patch
+---
+
+This change fixes a issue where if two themes had shared a similar start name, both would be outputted in the token
+generation process
diff --git a/packages/generator/src/artifacts/css/token-css.ts b/packages/generator/src/artifacts/css/token-css.ts
index ed13831ec0..31bf150ed8 100644
--- a/packages/generator/src/artifacts/css/token-css.ts
+++ b/packages/generator/src/artifacts/css/token-css.ts
@@ -24,7 +24,7 @@ export function generateTokenCss(ctx: Context, sheet: Stylesheet) {
   const themePrefix = ctx.conditions.getThemeName('')
 
   for (const [key, values] of tokens.view.vars.entries()) {
-    const isThemeSkipped = key.startsWith(themePrefix) && !themeConds.some((condName) => key.startsWith(condName))
+    const isThemeSkipped = key.startsWith(themePrefix) && !themeConds.some((condName) => key === condName || key.startsWith(condName + ':'))
     if (isThemeSkipped) {
       continue
     }


Test patch:
diff --git a/packages/generator/__tests__/generate-token.test.ts b/packages/generator/__tests__/generate-token.test.ts
index e8103f73fe..01162416cf 100644
--- a/packages/generator/__tests__/generate-token.test.ts
+++ b/packages/generator/__tests__/generate-token.test.ts
@@ -1053,6 +1053,104 @@ describe('generator', () => {
     `)
   })
 
+  test('themes - staticCss with multiple themes ', () => {
+    const css = tokenCss({
+      eject: true,
+      conditions: {
+        osDark: '@media (prefers-color-scheme: dark)',
+      },
+      theme: {
+        tokens: {
+          colors: {
+            text: { value: 'blue' },
+          },
+        },
+        semanticTokens: {
+          colors: {
+            body: {
+              value: {
+                base: '{colors.blue.600}',
+                _osDark: '{colors.blue.400}',
+              },
+            },
+          },
+        },
+      },
+      // alternative theme variants
+      themes: {
+        primary: {
+          tokens: {
+            colors: {
+              text: { value: 'red' },
+            },
+          },
+          semanticTokens: {
+            colors: {
+              muted: { value: '{colors.red.200}' },
+              body: {
+                value: {
+                  base: '{colors.red.600}',
+                  _osDark: '{colors.red.400}',
+                },
+              },
+            },
+          },
+        },
+        'primary-legacy': {
+          tokens: {
+            colors: {
+              text: { value: 'blue' },
+            },
+          },
+          semanticTokens: {
+            colors: {
+              muted: { value: '{colors.blue.200}' },
+              body: {
+                value: {
+                  base: '{colors.blue.600}',
+                  _osDark: '{colors.blue.400}',
+                },
+              },
+            },
+          },
+        },
+      },
+      staticCss: {
+        // only generate the red in addition to the main one
+        themes: ['primary'],
+        // use  ['*'] to generate all themes
+      },
+      outdir: '',
+    })
+
+    expect(css).toMatchInlineSnapshot(`
+      "@layer tokens {
+        :where(html) {
+          --colors-text: blue;
+          --colors-body: var(--colors-blue-600);
+      }
+
+        [data-panda-theme=primary] {
+          --colors-text: red;
+          --colors-muted: var(--colors-red-200);
+          --colors-body: var(--colors-red-600)
+      }
+
+        @media (prefers-color-scheme: dark) {
+          :where(html) {
+            --colors-body: var(--colors-blue-400)
+              }
+          }
+
+        @media (prefers-color-scheme: dark) {
+          [data-panda-theme=primary] {
+            --colors-body: var(--colors-red-400)
+                  }
+              }
+      }"
+    `)
+  })
+
   test('themes - staticCss with *', () => {
     const css = tokenCss({
       eject: true,