From bb23dc112ee9e27cbf460bba3c532e1806845bd6 Mon Sep 17 00:00:00 2001
From: maiieul <maieul.chevalier@rosa.be>
Date: Fri, 7 Mar 2025 18:52:42 +0100
Subject: [PATCH] refactor(utils): move extract-theme to a separate package

---
 apps/component-tests/src/root.tsx             |  2 +-
 .../copy-css-config/copy-css-config.tsx       |  2 +-
 .../make-it-yours/make-it-yours.tsx           |  4 +-
 apps/website/src/root.tsx                     |  2 +-
 packages/cli/bin/index.ts                     |  2 +-
 .../src/generators/setup-tailwind/schema.d.ts |  2 +-
 .../setup-tailwind-generator.spec.ts          |  6 +-
 .../setup-tailwind-generator.ts               |  2 +-
 packages/extract-theme/.eslintrc.json         | 18 +++++
 packages/extract-theme/CHANGELOG.md           | 42 ++++++++++
 packages/extract-theme/LICENSE                | 21 +++++
 packages/extract-theme/README.md              | 20 +++++
 packages/extract-theme/package.json           | 34 +++++++++
 packages/extract-theme/project.json           | 61 +++++++++++++++
 .../src}/extract-between-comments.ts          |  0
 .../src}/extract-theme-css.spec.ts            |  0
 .../src}/extract-theme-css.ts                 |  0
 packages/extract-theme/src/index.ts           |  9 +++
 packages/extract-theme/src/root.tsx           |  4 +
 .../src}/theme-base-colors.ts                 |  2 +-
 .../src}/theme-border-radiuses.ts             |  2 +-
 .../src}/theme-config.type.ts                 |  0
 .../src}/theme-fonts.ts                       |  2 +-
 .../src}/theme-modes.ts                       |  2 +-
 .../src}/theme-primary-colors.ts              |  2 +-
 .../src}/theme-styles.ts                      |  2 +-
 packages/extract-theme/src/type-utils.ts      |  1 +
 packages/extract-theme/tsconfig.json          | 25 ++++++
 packages/extract-theme/tsconfig.lib.json      | 10 +++
 packages/extract-theme/tsconfig.spec.json     | 19 +++++
 packages/extract-theme/vite.config.ts         | 76 +++++++++++++++++++
 packages/utils/src/index.ts                   | 13 +---
 pnpm-lock.yaml                                | 32 +++++---
 tsconfig.base.json                            |  1 +
 34 files changed, 384 insertions(+), 36 deletions(-)
 create mode 100644 packages/extract-theme/.eslintrc.json
 create mode 100644 packages/extract-theme/CHANGELOG.md
 create mode 100644 packages/extract-theme/LICENSE
 create mode 100644 packages/extract-theme/README.md
 create mode 100644 packages/extract-theme/package.json
 create mode 100644 packages/extract-theme/project.json
 rename packages/{utils/src/theme => extract-theme/src}/extract-between-comments.ts (100%)
 rename packages/{utils/src/theme => extract-theme/src}/extract-theme-css.spec.ts (100%)
 rename packages/{utils/src/theme => extract-theme/src}/extract-theme-css.ts (100%)
 create mode 100644 packages/extract-theme/src/index.ts
 create mode 100644 packages/extract-theme/src/root.tsx
 rename packages/{utils/src/theme => extract-theme/src}/theme-base-colors.ts (83%)
 rename packages/{utils/src/theme => extract-theme/src}/theme-border-radiuses.ts (88%)
 rename packages/{utils/src/theme => extract-theme/src}/theme-config.type.ts (100%)
 rename packages/{utils/src/theme => extract-theme/src}/theme-fonts.ts (86%)
 rename packages/{utils/src/theme => extract-theme/src}/theme-modes.ts (74%)
 rename packages/{utils/src/theme => extract-theme/src}/theme-primary-colors.ts (99%)
 rename packages/{utils/src/theme => extract-theme/src}/theme-styles.ts (79%)
 create mode 100644 packages/extract-theme/src/type-utils.ts
 create mode 100644 packages/extract-theme/tsconfig.json
 create mode 100644 packages/extract-theme/tsconfig.lib.json
 create mode 100644 packages/extract-theme/tsconfig.spec.json
 create mode 100644 packages/extract-theme/vite.config.ts

diff --git a/apps/component-tests/src/root.tsx b/apps/component-tests/src/root.tsx
index e6e890a4d..35f603951 100644
--- a/apps/component-tests/src/root.tsx
+++ b/apps/component-tests/src/root.tsx
@@ -13,7 +13,7 @@ import {
   ThemeModes,
   ThemePrimaryColors,
   ThemeStyles,
-} from '@qwik-ui/utils';
+} from '@qwik-ui/extract-theme';
 
 export default component$(() => {
   /**
diff --git a/apps/website/src/components/copy-css-config/copy-css-config.tsx b/apps/website/src/components/copy-css-config/copy-css-config.tsx
index f4ef794e6..8c239f5ef 100644
--- a/apps/website/src/components/copy-css-config/copy-css-config.tsx
+++ b/apps/website/src/components/copy-css-config/copy-css-config.tsx
@@ -1,7 +1,7 @@
 import { component$, useSignal } from '@builder.io/qwik';
 import { Modal } from '@qwik-ui/headless';
 import { Button } from '~/components/ui';
-import { extractThemeCSS } from '@qwik-ui/utils';
+import { extractThemeCSS } from '@qwik-ui/extract-theme';
 import { LuX } from '@qwikest/icons/lucide';
 import { useTheme } from '@qwik-ui/themes';
 import globalCSS from '~/global.css?raw';
diff --git a/apps/website/src/components/make-it-yours/make-it-yours.tsx b/apps/website/src/components/make-it-yours/make-it-yours.tsx
index 2bda6fe25..ef8e337f9 100644
--- a/apps/website/src/components/make-it-yours/make-it-yours.tsx
+++ b/apps/website/src/components/make-it-yours/make-it-yours.tsx
@@ -7,8 +7,8 @@ import {
   ThemeModes,
   ThemePrimaryColors,
   ThemeStyles,
-  cn,
-} from '@qwik-ui/utils';
+} from '@qwik-ui/extract-theme';
+import { cn } from '@qwik-ui/utils';
 import { LuSlidersHorizontal, LuX } from '@qwikest/icons/lucide';
 import { useTheme } from '@qwik-ui/themes';
 
diff --git a/apps/website/src/root.tsx b/apps/website/src/root.tsx
index 2be94acad..6c92e47ca 100644
--- a/apps/website/src/root.tsx
+++ b/apps/website/src/root.tsx
@@ -15,7 +15,7 @@ import {
   ThemeModes,
   ThemePrimaryColors,
   ThemeStyles,
-} from '@qwik-ui/utils';
+} from '@qwik-ui/extract-theme';
 import { ModulePreload } from './components/module-preload/module-preload';
 
 export default component$(() => {
diff --git a/packages/cli/bin/index.ts b/packages/cli/bin/index.ts
index 61ce28a79..dce30b2cd 100644
--- a/packages/cli/bin/index.ts
+++ b/packages/cli/bin/index.ts
@@ -27,7 +27,7 @@ import {
   ThemePrimaryColors,
   ThemeStyle,
   ThemeStyles,
-} from '@qwik-ui/utils';
+} from '@qwik-ui/extract-theme';
 import { bgRgb, bold, cyan, green, red } from 'ansis';
 import { execSync } from 'child_process';
 import { existsSync, readFileSync, writeFileSync } from 'fs';
diff --git a/packages/cli/src/generators/setup-tailwind/schema.d.ts b/packages/cli/src/generators/setup-tailwind/schema.d.ts
index 2649cf238..c2b65bd2d 100644
--- a/packages/cli/src/generators/setup-tailwind/schema.d.ts
+++ b/packages/cli/src/generators/setup-tailwind/schema.d.ts
@@ -1,4 +1,4 @@
-import type { ThemeConfig } from '@qwik-ui/utils';
+import type { ThemeConfig } from '@qwik-ui/extract-theme';
 
 export interface SetupTailwindGeneratorSchema extends ThemeConfig {
   projectRoot?: string;
diff --git a/packages/cli/src/generators/setup-tailwind/setup-tailwind-generator.spec.ts b/packages/cli/src/generators/setup-tailwind/setup-tailwind-generator.spec.ts
index 12bfa6e36..c52364555 100644
--- a/packages/cli/src/generators/setup-tailwind/setup-tailwind-generator.spec.ts
+++ b/packages/cli/src/generators/setup-tailwind/setup-tailwind-generator.spec.ts
@@ -1,5 +1,9 @@
 import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
-import { ThemeBorderRadiuses, ThemePrimaryColors, ThemeStyles } from '@qwik-ui/utils';
+import {
+  ThemeBorderRadiuses,
+  ThemePrimaryColors,
+  ThemeStyles,
+} from '@qwik-ui/extract-theme';
 import { SetupTailwindGeneratorSchema } from './schema';
 import { setupTailwindGenerator } from './setup-tailwind-generator';
 
diff --git a/packages/cli/src/generators/setup-tailwind/setup-tailwind-generator.ts b/packages/cli/src/generators/setup-tailwind/setup-tailwind-generator.ts
index 50751c232..0bfad212b 100644
--- a/packages/cli/src/generators/setup-tailwind/setup-tailwind-generator.ts
+++ b/packages/cli/src/generators/setup-tailwind/setup-tailwind-generator.ts
@@ -7,7 +7,7 @@ import {
   extractBetweenComments,
   extractThemeCSS,
   type ThemeConfig,
-} from '@qwik-ui/utils';
+} from '@qwik-ui/extract-theme';
 import { readFileSync } from 'fs';
 import { join } from 'path';
 import { getKitRoot } from '../../_shared/get-kit-root';
diff --git a/packages/extract-theme/.eslintrc.json b/packages/extract-theme/.eslintrc.json
new file mode 100644
index 000000000..9d9c0db55
--- /dev/null
+++ b/packages/extract-theme/.eslintrc.json
@@ -0,0 +1,18 @@
+{
+  "extends": ["../../.eslintrc.json"],
+  "ignorePatterns": ["!**/*"],
+  "overrides": [
+    {
+      "files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+      "rules": {}
+    },
+    {
+      "files": ["*.ts", "*.tsx"],
+      "rules": {}
+    },
+    {
+      "files": ["*.js", "*.jsx"],
+      "rules": {}
+    }
+  ]
+}
diff --git a/packages/extract-theme/CHANGELOG.md b/packages/extract-theme/CHANGELOG.md
new file mode 100644
index 000000000..9a2defb00
--- /dev/null
+++ b/packages/extract-theme/CHANGELOG.md
@@ -0,0 +1,42 @@
+# Changelog
+
+## 0.3.1
+
+### Patch Changes
+
+- ✨ new inline component utilities (by [@thejackshelton](https://github.com/thejackshelton) in [#937](https://github.com/qwikifiers/qwik-ui/pull/937))
+
+## 0.3.0
+
+### Minor Changes
+
+- Changed enums to const maps in utils (by [@shairez](https://github.com/shairez) in [#914](https://github.com/qwikifiers/qwik-ui/pull/914))
+
+## 0.2.1
+
+### Patch Changes
+
+- ✨ added `extractThemeCSS` (by [@shairez](https://github.com/shairez) in [#604](https://github.com/qwikifiers/qwik-ui/pull/604))
+
+## 0.2.0
+
+### Minor Changes
+
+- Removed `useOrdinal` (by [@shairez](https://github.com/shairez) in [`4043d29`](https://github.com/qwikifiers/qwik-ui/commit/4043d29dcc39b03f16c79d659da592af3fbeafeb))
+
+This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
+
+## [0.1.1](https://github.com/qwikifiers/qwik-ui/compare/utils-0.1.0...utils-0.1.1) (2024-01-09)
+
+# [0.1.0](https://github.com/qwikifiers/qwik-ui/compare/utils-0.0.3...utils-0.1.0) (2023-12-16)
+
+### Bug Fixes
+
+- **docs:** add install / related docs / merge with main ([bb1b62c](https://github.com/qwikifiers/qwik-ui/commit/bb1b62cd87d376858fd706e9b5344603be87127c))
+- **everything:** fix 1.3 craziness ([0b20d97](https://github.com/qwikifiers/qwik-ui/commit/0b20d97af41f75bc7e1215391fd1c202ee8a9366))
+
+## [0.0.3](https://github.com/qwikifiers/qwik-ui/compare/utils-0.0.2...utils-0.0.3) (2023-12-01)
+
+## [0.0.2](https://github.com/qwikifiers/qwik-ui/compare/utils-0.0.1...utils-0.0.2) (2023-12-01)
+
+## 0.0.1 (2023-11-30)
diff --git a/packages/extract-theme/LICENSE b/packages/extract-theme/LICENSE
new file mode 100644
index 000000000..3f40df030
--- /dev/null
+++ b/packages/extract-theme/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2023 HiRez.io, Qwikifiers
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/packages/extract-theme/README.md b/packages/extract-theme/README.md
new file mode 100644
index 000000000..5dc7517d1
--- /dev/null
+++ b/packages/extract-theme/README.md
@@ -0,0 +1,20 @@
+# Qwik UI extract-theme
+
+## `stringifyClassList`
+
+A function that takes a Qwik class list and returns a string.
+Good for cases you are working with other libraries that require a string as an input.
+
+## `cva`
+
+This is a version of the original code from [cva](https://cva.style/) but optimized for Qwik apps.
+
+Thanks to [Joe Bell](https://twitter.com/joebell_) for creating the original library.
+
+## `tcva`
+
+CVA with Tailwind merge built in.
+
+# License
+
+MIT
diff --git a/packages/extract-theme/package.json b/packages/extract-theme/package.json
new file mode 100644
index 000000000..9852523f4
--- /dev/null
+++ b/packages/extract-theme/package.json
@@ -0,0 +1,34 @@
+{
+  "name": "@qwik-ui/extract-theme",
+  "version": "0.3.1",
+  "description": "Qwik UI extract-theme",
+  "publishConfig": {
+    "access": "public"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/qwikifiers/qwik-ui",
+    "directory": "packages/extract-theme"
+  },
+  "main": "./index.qwik.cjs",
+  "qwik": "./index.qwik.mjs",
+  "module": "./index.qwik.mjs",
+  "types": "./index.d.ts",
+  "type": "module",
+  "exports": {
+    ".": {
+      "types": "./index.d.ts",
+      "import": "./index.qwik.mjs",
+      "require": "./index.qwik.cjs"
+    }
+  },
+  "engines": {
+    "node": ">=16.0.0"
+  },
+  "private": false,
+  "dependencies": {
+    "tailwind-merge": "^1.14.0",
+    "clsx": "^2.0.0",
+    "qwik-themes": "^0.2.0"
+  }
+}
diff --git a/packages/extract-theme/project.json b/packages/extract-theme/project.json
new file mode 100644
index 000000000..c03b23615
--- /dev/null
+++ b/packages/extract-theme/project.json
@@ -0,0 +1,61 @@
+{
+  "name": "extract-theme",
+  "$schema": "../../node_modules/nx/schemas/project-schema.json",
+  "sourceRoot": "packages/extract-theme/src",
+  "projectType": "library",
+  "targets": {
+    "build": {
+      "executor": "@nx/vite:build",
+      "outputs": ["{options.outputPath}"],
+      "defaultConfiguration": "production",
+      "options": {
+        "outputPath": "dist/packages/extract-theme",
+        "configFile": "packages/extract-theme/vite.config.ts",
+        "mode": "lib"
+      },
+      "configurations": {
+        "development": {},
+        "production": {}
+      }
+    },
+    "lint": {
+      "executor": "@nx/eslint:lint",
+      "outputs": ["{options.outputFile}"],
+      "options": {
+        "lintFilePatterns": ["packages/extract-theme/**/*.{ts,tsx,js,jsx}"]
+      }
+    },
+    "e2e": {
+      "executor": "@nx/playwright:playwright",
+      "outputs": ["{workspaceRoot}/dist/.playwright/packages/extract-theme"],
+      "options": {
+        "config": "packages/extract-theme/playwright.config.ts",
+        "project": ["logic"]
+      }
+    },
+    "setup-chrome-108": {
+      "executor": "nx:run-script",
+      "options": {
+        "script": "setup.chrome.108"
+      }
+    },
+    "e2e-chrome-108": {
+      "executor": "@nx/playwright:playwright",
+      "outputs": ["{workspaceRoot}/dist/.playwright/packages/extract-theme"],
+      "dependsOn": ["setup-chrome-108"],
+      "options": {
+        "config": "packages/extract-theme/playwright.config.ts",
+        "project": ["popover-chrome-108"]
+      }
+    },
+    "visual-test": {
+      "executor": "@nx/playwright:playwright",
+      "outputs": ["{workspaceRoot}/dist/.playwright/packages/extract-theme"],
+      "options": {
+        "config": "packages/extract-theme/playwright.config.ts",
+        "project": ["visual"]
+      }
+    }
+  },
+  "tags": []
+}
diff --git a/packages/utils/src/theme/extract-between-comments.ts b/packages/extract-theme/src/extract-between-comments.ts
similarity index 100%
rename from packages/utils/src/theme/extract-between-comments.ts
rename to packages/extract-theme/src/extract-between-comments.ts
diff --git a/packages/utils/src/theme/extract-theme-css.spec.ts b/packages/extract-theme/src/extract-theme-css.spec.ts
similarity index 100%
rename from packages/utils/src/theme/extract-theme-css.spec.ts
rename to packages/extract-theme/src/extract-theme-css.spec.ts
diff --git a/packages/utils/src/theme/extract-theme-css.ts b/packages/extract-theme/src/extract-theme-css.ts
similarity index 100%
rename from packages/utils/src/theme/extract-theme-css.ts
rename to packages/extract-theme/src/extract-theme-css.ts
diff --git a/packages/extract-theme/src/index.ts b/packages/extract-theme/src/index.ts
new file mode 100644
index 000000000..67188da82
--- /dev/null
+++ b/packages/extract-theme/src/index.ts
@@ -0,0 +1,9 @@
+export { extractBetweenComments } from './extract-between-comments';
+export { extractThemeCSS } from './extract-theme-css';
+export { ThemeBaseColors, type ThemeBaseColor } from './theme-base-colors';
+export { ThemeBorderRadiuses, type ThemeBorderRadius } from './theme-border-radiuses';
+export { type ThemeConfig } from './theme-config.type';
+export { ThemeFonts, type ThemeFont } from './theme-fonts';
+export { ThemeModes, type ThemeMode } from './theme-modes';
+export { ThemePrimaryColors, type ThemePrimaryColor } from './theme-primary-colors';
+export { ThemeStyles, type ThemeStyle } from './theme-styles';
diff --git a/packages/extract-theme/src/root.tsx b/packages/extract-theme/src/root.tsx
new file mode 100644
index 000000000..218a3ba6b
--- /dev/null
+++ b/packages/extract-theme/src/root.tsx
@@ -0,0 +1,4 @@
+// eslint-disable-next-line @typescript-eslint/no-empty-function
+export default () => {};
+
+// THIS FILE IS ONLY HERE TO SATISFY QWIK BUILD
diff --git a/packages/utils/src/theme/theme-base-colors.ts b/packages/extract-theme/src/theme-base-colors.ts
similarity index 83%
rename from packages/utils/src/theme/theme-base-colors.ts
rename to packages/extract-theme/src/theme-base-colors.ts
index 53d6297af..882389cb6 100644
--- a/packages/utils/src/theme/theme-base-colors.ts
+++ b/packages/extract-theme/src/theme-base-colors.ts
@@ -1,4 +1,4 @@
-import { ObjectValues } from '../type-utils';
+import { ObjectValues } from './type-utils';
 
 export const ThemeBaseColors = {
   SLATE: 'base-slate',
diff --git a/packages/utils/src/theme/theme-border-radiuses.ts b/packages/extract-theme/src/theme-border-radiuses.ts
similarity index 88%
rename from packages/utils/src/theme/theme-border-radiuses.ts
rename to packages/extract-theme/src/theme-border-radiuses.ts
index fce211896..612fc4a72 100644
--- a/packages/utils/src/theme/theme-border-radiuses.ts
+++ b/packages/extract-theme/src/theme-border-radiuses.ts
@@ -1,4 +1,4 @@
-import { ObjectValues } from '../type-utils';
+import { ObjectValues } from './type-utils';
 
 export const ThemeBorderRadiuses = {
   'BORDER-RADIUS-0': 'border-radius-0',
diff --git a/packages/utils/src/theme/theme-config.type.ts b/packages/extract-theme/src/theme-config.type.ts
similarity index 100%
rename from packages/utils/src/theme/theme-config.type.ts
rename to packages/extract-theme/src/theme-config.type.ts
diff --git a/packages/utils/src/theme/theme-fonts.ts b/packages/extract-theme/src/theme-fonts.ts
similarity index 86%
rename from packages/utils/src/theme/theme-fonts.ts
rename to packages/extract-theme/src/theme-fonts.ts
index 32ed95605..58e2e5703 100644
--- a/packages/utils/src/theme/theme-fonts.ts
+++ b/packages/extract-theme/src/theme-fonts.ts
@@ -1,4 +1,4 @@
-import { ObjectValues } from '../type-utils';
+import { ObjectValues } from './type-utils';
 
 export const ThemeFonts = {
   MONO: 'font-mono',
diff --git a/packages/utils/src/theme/theme-modes.ts b/packages/extract-theme/src/theme-modes.ts
similarity index 74%
rename from packages/utils/src/theme/theme-modes.ts
rename to packages/extract-theme/src/theme-modes.ts
index 7e64a3e86..7ea18f8c1 100644
--- a/packages/utils/src/theme/theme-modes.ts
+++ b/packages/extract-theme/src/theme-modes.ts
@@ -1,4 +1,4 @@
-import { ObjectValues } from '../type-utils';
+import { ObjectValues } from './type-utils';
 
 export const ThemeModes = {
   LIGHT: 'light',
diff --git a/packages/utils/src/theme/theme-primary-colors.ts b/packages/extract-theme/src/theme-primary-colors.ts
similarity index 99%
rename from packages/utils/src/theme/theme-primary-colors.ts
rename to packages/extract-theme/src/theme-primary-colors.ts
index 3887741fb..290ad549c 100644
--- a/packages/utils/src/theme/theme-primary-colors.ts
+++ b/packages/extract-theme/src/theme-primary-colors.ts
@@ -1,4 +1,4 @@
-import { ObjectValues } from '../type-utils';
+import { ObjectValues } from './type-utils';
 
 export const ThemePrimaryColors = {
   SLATE100: 'primary-slate-100',
diff --git a/packages/utils/src/theme/theme-styles.ts b/packages/extract-theme/src/theme-styles.ts
similarity index 79%
rename from packages/utils/src/theme/theme-styles.ts
rename to packages/extract-theme/src/theme-styles.ts
index 65e469524..a53658036 100644
--- a/packages/utils/src/theme/theme-styles.ts
+++ b/packages/extract-theme/src/theme-styles.ts
@@ -1,4 +1,4 @@
-import { ObjectValues } from '../type-utils';
+import { ObjectValues } from './type-utils';
 
 export const ThemeStyles = {
   SIMPLE: 'simple',
diff --git a/packages/extract-theme/src/type-utils.ts b/packages/extract-theme/src/type-utils.ts
new file mode 100644
index 000000000..e21529c98
--- /dev/null
+++ b/packages/extract-theme/src/type-utils.ts
@@ -0,0 +1 @@
+export type ObjectValues<T> = T[keyof T];
diff --git a/packages/extract-theme/tsconfig.json b/packages/extract-theme/tsconfig.json
new file mode 100644
index 000000000..47dad120a
--- /dev/null
+++ b/packages/extract-theme/tsconfig.json
@@ -0,0 +1,25 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "module": "ES2022",
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "noImplicitOverride": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true,
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "types": ["vitest"]
+  },
+  "files": [],
+  "include": [],
+  "references": [
+    {
+      "path": "./tsconfig.lib.json"
+    },
+    {
+      "path": "./tsconfig.spec.json"
+    }
+  ]
+}
diff --git a/packages/extract-theme/tsconfig.lib.json b/packages/extract-theme/tsconfig.lib.json
new file mode 100644
index 000000000..33eca2c2c
--- /dev/null
+++ b/packages/extract-theme/tsconfig.lib.json
@@ -0,0 +1,10 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../dist/out-tsc",
+    "declaration": true,
+    "types": ["node"]
+  },
+  "include": ["src/**/*.ts"],
+  "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"]
+}
diff --git a/packages/extract-theme/tsconfig.spec.json b/packages/extract-theme/tsconfig.spec.json
new file mode 100644
index 000000000..6a4c53c4f
--- /dev/null
+++ b/packages/extract-theme/tsconfig.spec.json
@@ -0,0 +1,19 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "outDir": "../../dist/out-tsc",
+    "types": ["vitest/globals", "vitest/importMeta", "vite/client", "node", "vitest"]
+  },
+  "include": [
+    "vite.config.ts",
+    "src/**/*.test.ts",
+    "src/**/*.spec.ts",
+    "src/**/*.test.tsx",
+    "src/**/*.spec.tsx",
+    "src/**/*.test.js",
+    "src/**/*.spec.js",
+    "src/**/*.test.jsx",
+    "src/**/*.spec.jsx",
+    "src/**/*.d.ts"
+  ]
+}
diff --git a/packages/extract-theme/vite.config.ts b/packages/extract-theme/vite.config.ts
new file mode 100644
index 000000000..6a7333369
--- /dev/null
+++ b/packages/extract-theme/vite.config.ts
@@ -0,0 +1,76 @@
+/// <reference types="vitest" />
+import { qwikVite } from '@builder.io/qwik/optimizer';
+import { dirname, join } from 'path';
+import { fileURLToPath } from 'url';
+import { defineConfig } from 'vite';
+import dts from 'vite-plugin-dts';
+import { viteStaticCopy } from 'vite-plugin-static-copy';
+import tsconfigPaths from 'vite-tsconfig-paths';
+import pkg from './package.json';
+
+const { dependencies = {}, peerDependencies = {} } = pkg as any;
+const makeRegex = (dep: any) => new RegExp(`^${dep}(/.*)?$`);
+const excludeAll = (obj: any) => Object.keys(obj).map(makeRegex);
+
+export default defineConfig({
+  plugins: [
+    qwikVite(),
+    tsconfigPaths({ root: '../../' }),
+    dts({
+      tsconfigPath: join(dirname(fileURLToPath(import.meta.url)), 'tsconfig.lib.json'),
+      entryRoot: 'src',
+      afterDiagnostic(ds) {
+        // ensure DTS errors are still visible - otherwise get swallowed and silent
+        console.log((ds ?? []).map((d) => d.messageText));
+
+        const nonPortableTypeErrors = ds.filter((d) => d.code === 2742);
+        if (nonPortableTypeErrors.length > 0) {
+          // stop the build for 2742 specifically
+          return Promise.reject(nonPortableTypeErrors);
+        }
+
+        return;
+      },
+    }),
+    viteStaticCopy({
+      targets: [{ src: './README.md', dest: './' }],
+    }),
+  ],
+  server: {
+    fs: {
+      // Allow serving files from the project root
+      allow: ['../../'],
+    },
+  },
+
+  // Configuration for building your library.
+  // See: https://vitejs.dev/guide/build.html#library-mode
+  optimizeDeps: {
+    include: ['css-tree'],
+  },
+  build: {
+    target: 'es2022',
+    lib: {
+      entry: './src/index.ts',
+      // Could also be a dictionary or array of multiple entry points.
+      name: 'extract-theme',
+      fileName: (format, entryName) =>
+        `${entryName}.qwik.${format === 'es' ? 'mjs' : 'cjs'}`,
+      // fileName: 'index',
+      // Change this to the formats you want to support.
+      // Don't forget to update your package.json as well.
+      formats: ['es', 'cjs'],
+    },
+    rollupOptions: {
+      external: [
+        /^node:.*/,
+        ...excludeAll(dependencies),
+        ...excludeAll(peerDependencies),
+      ],
+      output: {
+        preserveModules: true,
+        preserveModulesRoot: 'packages/extract-theme/src',
+      },
+    },
+  },
+});
diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts
index 84f350e8c..bb7522240 100644
--- a/packages/utils/src/index.ts
+++ b/packages/utils/src/index.ts
@@ -1,11 +1,2 @@
-export * from './cn';
-export * from './theme/extract-between-comments';
-export * from './theme/extract-theme-css';
-export * from './theme/theme-base-colors';
-export * from './theme/theme-border-radiuses';
-export * from './theme/theme-config.type';
-export * from './theme/theme-fonts';
-export * from './theme/theme-modes';
-export * from './theme/theme-primary-colors';
-export * from './theme/theme-styles';
-export * from './inline-component';
+export { cn } from './cn';
+export { processChildren, findComponent } from './inline-component';
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 13aa54883..bd3009235 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -67,7 +67,7 @@ importers:
         version: 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(@zkochan/js-yaml@0.0.7)(eslint@8.57.0)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(ts-node@10.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(typescript@5.4.5))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))
       '@nx/vite':
         specifier: 19.4.2
-        version: 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.0.0)(sass@1.77.4))
+        version: 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0)
       '@nx/workspace':
         specifier: 19.4.2
         version: 19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))
@@ -109,7 +109,7 @@ importers:
         version: 7.9.0(eslint@8.57.0)(typescript@5.4.5)
       '@vitest/coverage-v8':
         specifier: ^1.6.0
-        version: 1.6.0(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.0.0)(sass@1.77.4))
+        version: 1.6.0(vitest@1.6.0)
       '@vitest/ui':
         specifier: ^1.6.0
         version: 1.6.0(vitest@1.6.0)
@@ -202,7 +202,7 @@ importers:
         version: 4.0.0(prettier@3.2.5)
       qwik-nx:
         specifier: ^2.3.0
-        version: 2.3.0(6rocepx3yltcemdfak7x4h4way)
+        version: 2.3.0(yfeuwrzyzw4p6vciz27cdhykpq)
       qwik-themes:
         specifier: ^0.2.0
         version: 0.2.0
@@ -310,6 +310,18 @@ importers:
         specifier: 0.3.0
         version: link:../kit-styled
 
+  packages/extract-theme:
+    dependencies:
+      clsx:
+        specifier: ^2.0.0
+        version: 2.1.1
+      qwik-themes:
+        specifier: ^0.2.0
+        version: 0.2.0
+      tailwind-merge:
+        specifier: ^1.14.0
+        version: 1.14.0
+
   packages/kit-headless:
     dependencies:
       '@builder.io/qwik':
@@ -9889,9 +9901,9 @@ snapshots:
       - '@swc/core'
       - debug
 
-  '@nrwl/vite@19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.0.0)(sass@1.77.4))':
+  '@nrwl/vite@19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0)':
     dependencies:
-      '@nx/vite': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.0.0)(sass@1.77.4))
+      '@nx/vite': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0)
     transitivePeerDependencies:
       - '@babel/traverse'
       - '@swc-node/register'
@@ -10365,9 +10377,9 @@ snapshots:
       - typescript
       - verdaccio
 
-  '@nx/vite@19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.0.0)(sass@1.77.4))':
+  '@nx/vite@19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0)':
     dependencies:
-      '@nrwl/vite': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.0.0)(sass@1.77.4))
+      '@nrwl/vite': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0)
       '@nx/devkit': 19.4.2(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))
       '@nx/js': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))
       '@phenomnomnominal/tsquery': 5.0.1(typescript@5.4.5)
@@ -11206,7 +11218,7 @@ snapshots:
       minimatch: 7.4.6
       semver: 7.6.0
 
-  '@vitest/coverage-v8@1.6.0(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.0.0)(sass@1.77.4))':
+  '@vitest/coverage-v8@1.6.0(vitest@1.6.0)':
     dependencies:
       '@ampproject/remapping': 2.3.0
       '@bcoe/v8-coverage': 0.2.3
@@ -15871,12 +15883,12 @@ snapshots:
 
   quick-lru@5.1.1: {}
 
-  qwik-nx@2.3.0(6rocepx3yltcemdfak7x4h4way):
+  qwik-nx@2.3.0(yfeuwrzyzw4p6vciz27cdhykpq):
     dependencies:
       '@nx/devkit': 19.4.2(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))
       '@nx/eslint': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(@zkochan/js-yaml@0.0.7)(eslint@8.57.0)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(verdaccio@5.31.0(typanion@3.14.0))
       '@nx/js': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))
-      '@nx/vite': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0(@types/node@20.12.12)(@vitest/ui@1.6.0)(jsdom@24.0.0)(sass@1.77.4))
+      '@nx/vite': 19.4.2(@babel/traverse@7.24.6)(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11))(@types/node@20.12.12)(nx@19.4.2(@swc-node/register@1.9.1(@swc/core@1.5.24(@swc/helpers@0.5.11))(@swc/types@0.1.7)(typescript@5.4.5))(@swc/core@1.5.24(@swc/helpers@0.5.11)))(typescript@5.4.5)(verdaccio@5.31.0(typanion@3.14.0))(vite@5.2.11(@types/node@20.12.12)(sass@1.77.4))(vitest@1.6.0)
 
   qwik-themes@0.2.0: {}
 
diff --git a/tsconfig.base.json b/tsconfig.base.json
index e29e0cde3..ac751ce1e 100644
--- a/tsconfig.base.json
+++ b/tsconfig.base.json
@@ -19,6 +19,7 @@
       "@qwik-ui/styled": ["packages/kit-styled/src/index.ts"],
       "@qwik-ui/utils": ["packages/utils/src/index.ts"],
       "@qwik-ui/themes": ["packages/themes/src/index.ts"],
+      "@qwik-ui/extract-theme": ["packages/extract-theme/src/index.ts"],
       "~/*": ["apps/website/src/*"]
     }
   },