create-lumen 2.0.0-alpha.1 → 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/README.md +38 -12
  2. package/bin/cli.js +3 -1
  3. package/package.json +3 -3
  4. package/schema/fixtures/v2/invalid/shadcn-without-tailwind.json +1 -1
  5. package/schema/fixtures/v2/valid/react-vite-type-based.json +1 -1
  6. package/schema/lumen.config.v2.json +0 -1
  7. package/src/cli-args.js +169 -7
  8. package/src/configure.js +12 -4
  9. package/src/css.js +14 -30
  10. package/src/dependencies.js +36 -20
  11. package/src/engine/capabilities.js +195 -4
  12. package/src/engine/composition.js +736 -0
  13. package/src/engine/index.js +1 -0
  14. package/src/engine/providers/base-provider.js +4 -4
  15. package/src/injector.js +294 -446
  16. package/src/main.js +38 -16
  17. package/src/manifest/emit.js +1 -1
  18. package/src/manifest/schema.js +1 -1
  19. package/src/prompts.js +581 -238
  20. package/src/readme.js +23 -2
  21. package/src/scaffold.js +17 -0
  22. package/templates/architectures/hybrid/main.jsx +10 -0
  23. package/templates/architectures/hybrid/main.tsx +10 -0
  24. package/templates/architectures/hybrid/src/App.jsx +20 -0
  25. package/templates/architectures/hybrid/src/App.tsx +20 -0
  26. package/templates/architectures/hybrid/src/shared/components/ui/Button.jsx +31 -0
  27. package/templates/architectures/hybrid/src/shared/components/ui/Button.tsx +36 -0
  28. package/templates/architectures/hybrid/src/shared/components/ui/index.js +1 -0
  29. package/templates/architectures/hybrid/src/shared/components/ui/index.ts +1 -0
  30. package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.jsx +28 -0
  31. package/templates/architectures/hybrid/src/shared/hooks/useLocalStorage.ts +28 -0
  32. package/templates/architectures/hybrid/src/shared/layouts/.gitkeep +0 -0
  33. package/templates/architectures/hybrid/src/shared/stores/.gitkeep +0 -0
  34. package/templates/architectures/hybrid/src/shared/styles/.gitkeep +0 -0
  35. package/templates/architectures/hybrid/src/shared/types/index.js +20 -0
  36. package/templates/architectures/hybrid/src/shared/types/index.ts +16 -0
  37. package/templates/architectures/hybrid/src/shared/utils/cn.jsx +3 -0
  38. package/templates/architectures/hybrid/src/shared/utils/cn.ts +5 -0
  39. package/templates/architectures/none/main.jsx +10 -0
  40. package/templates/architectures/none/main.tsx +10 -0
  41. package/templates/architectures/none/src/App.jsx +15 -0
  42. package/templates/architectures/none/src/App.tsx +15 -0
  43. package/templates/architectures/none/src/styles/.gitkeep +1 -0
  44. package/templates/architectures/none/src/types/index.js +20 -0
  45. package/templates/architectures/none/src/types/index.ts +16 -0
  46. package/templates/bases/next/app-router/js/.provenance.json +19 -0
  47. package/templates/bases/next/app-router/js/README.md +36 -0
  48. package/templates/bases/next/app-router/js/jsconfig.json +7 -0
  49. package/templates/bases/next/app-router/js/next.config.mjs +6 -0
  50. package/templates/bases/next/app-router/js/package.json +15 -0
  51. package/templates/bases/next/app-router/js/public/file.svg +1 -0
  52. package/templates/bases/next/app-router/js/public/globe.svg +1 -0
  53. package/templates/bases/next/app-router/js/public/next.svg +1 -0
  54. package/templates/bases/next/app-router/js/public/vercel.svg +1 -0
  55. package/templates/bases/next/app-router/js/public/window.svg +1 -0
  56. package/templates/bases/next/app-router/js/src/app/api/hello/route.js +5 -0
  57. package/templates/bases/next/app-router/js/src/app/blog/[slug]/page.js +33 -0
  58. package/templates/bases/next/app-router/js/src/app/favicon.ico +0 -0
  59. package/templates/bases/next/app-router/js/src/app/globals.css +49 -0
  60. package/templates/bases/next/app-router/js/src/app/layout.js +25 -0
  61. package/templates/bases/next/app-router/js/src/app/loading.js +15 -0
  62. package/templates/bases/next/app-router/js/src/app/not-found.js +29 -0
  63. package/templates/bases/next/app-router/js/src/app/page.js +69 -0
  64. package/templates/bases/next/app-router/js/src/app/page.module.css +150 -0
  65. package/templates/bases/next/app-router/ts/.provenance.json +19 -0
  66. package/templates/bases/next/app-router/ts/README.md +36 -0
  67. package/templates/bases/next/app-router/ts/next.config.ts +7 -0
  68. package/templates/bases/next/app-router/ts/package.json +21 -0
  69. package/templates/bases/next/app-router/ts/public/file.svg +1 -0
  70. package/templates/bases/next/app-router/ts/public/globe.svg +1 -0
  71. package/templates/bases/next/app-router/ts/public/next.svg +1 -0
  72. package/templates/bases/next/app-router/ts/public/vercel.svg +1 -0
  73. package/templates/bases/next/app-router/ts/public/window.svg +1 -0
  74. package/templates/bases/next/app-router/ts/src/app/api/hello/route.ts +5 -0
  75. package/templates/bases/next/app-router/ts/src/app/blog/[slug]/page.tsx +38 -0
  76. package/templates/bases/next/app-router/ts/src/app/favicon.ico +0 -0
  77. package/templates/bases/next/app-router/ts/src/app/globals.css +49 -0
  78. package/templates/bases/next/app-router/ts/src/app/layout.tsx +30 -0
  79. package/templates/bases/next/app-router/ts/src/app/loading.tsx +15 -0
  80. package/templates/bases/next/app-router/ts/src/app/not-found.tsx +29 -0
  81. package/templates/bases/next/app-router/ts/src/app/page.module.css +150 -0
  82. package/templates/bases/next/app-router/ts/src/app/page.tsx +69 -0
  83. package/templates/bases/next/app-router/ts/tsconfig.json +34 -0
  84. package/templates/bases/next/pages-router/js/.provenance.json +19 -0
  85. package/templates/bases/next/pages-router/js/README.md +40 -0
  86. package/templates/bases/next/pages-router/js/jsconfig.json +7 -0
  87. package/templates/bases/next/pages-router/js/next.config.mjs +7 -0
  88. package/templates/bases/next/pages-router/js/package.json +15 -0
  89. package/templates/bases/next/pages-router/js/public/favicon.ico +0 -0
  90. package/templates/bases/next/pages-router/js/public/file.svg +1 -0
  91. package/templates/bases/next/pages-router/js/public/globe.svg +1 -0
  92. package/templates/bases/next/pages-router/js/public/next.svg +1 -0
  93. package/templates/bases/next/pages-router/js/public/vercel.svg +1 -0
  94. package/templates/bases/next/pages-router/js/public/window.svg +1 -0
  95. package/templates/bases/next/pages-router/js/src/pages/404.js +35 -0
  96. package/templates/bases/next/pages-router/js/src/pages/500.js +35 -0
  97. package/templates/bases/next/pages-router/js/src/pages/_app.js +20 -0
  98. package/templates/bases/next/pages-router/js/src/pages/_document.js +15 -0
  99. package/templates/bases/next/pages-router/js/src/pages/api/hello.js +5 -0
  100. package/templates/bases/next/pages-router/js/src/pages/blog/[slug].js +41 -0
  101. package/templates/bases/next/pages-router/js/src/pages/index.js +77 -0
  102. package/templates/bases/next/pages-router/js/src/styles/Home.module.css +149 -0
  103. package/templates/bases/next/pages-router/js/src/styles/globals.css +42 -0
  104. package/templates/bases/next/pages-router/ts/.provenance.json +19 -0
  105. package/templates/bases/next/pages-router/ts/README.md +40 -0
  106. package/templates/bases/next/pages-router/ts/next.config.ts +8 -0
  107. package/templates/bases/next/pages-router/ts/package.json +21 -0
  108. package/templates/bases/next/pages-router/ts/public/favicon.ico +0 -0
  109. package/templates/bases/next/pages-router/ts/public/file.svg +1 -0
  110. package/templates/bases/next/pages-router/ts/public/globe.svg +1 -0
  111. package/templates/bases/next/pages-router/ts/public/next.svg +1 -0
  112. package/templates/bases/next/pages-router/ts/public/vercel.svg +1 -0
  113. package/templates/bases/next/pages-router/ts/public/window.svg +1 -0
  114. package/templates/bases/next/pages-router/ts/src/pages/404.tsx +35 -0
  115. package/templates/bases/next/pages-router/ts/src/pages/500.tsx +35 -0
  116. package/templates/bases/next/pages-router/ts/src/pages/_app.tsx +21 -0
  117. package/templates/bases/next/pages-router/ts/src/pages/_document.tsx +15 -0
  118. package/templates/bases/next/pages-router/ts/src/pages/api/hello.ts +13 -0
  119. package/templates/bases/next/pages-router/ts/src/pages/blog/[slug].tsx +46 -0
  120. package/templates/bases/next/pages-router/ts/src/pages/index.tsx +77 -0
  121. package/templates/bases/next/pages-router/ts/src/styles/Home.module.css +149 -0
  122. package/templates/bases/next/pages-router/ts/src/styles/globals.css +42 -0
  123. package/templates/bases/next/pages-router/ts/tsconfig.json +29 -0
  124. package/templates/conditional/linting/biome/biome.json +31 -0
  125. package/templates/conditional/next/config/cloudflare/next.config.mjs +6 -0
  126. package/templates/conditional/next/config/cloudflare/next.config.ts +7 -0
  127. package/templates/conditional/next/config/node/next.config.mjs +6 -0
  128. package/templates/conditional/next/config/node/next.config.ts +7 -0
  129. package/templates/conditional/next/config/static/next.config.mjs +6 -0
  130. package/templates/conditional/next/config/static/next.config.ts +7 -0
  131. package/templates/conditional/next/config/turbopack/next.config.mjs +7 -0
  132. package/templates/conditional/next/config/turbopack/next.config.ts +8 -0
  133. package/templates/conditional/next/config/vercel/next.config.mjs +6 -0
  134. package/templates/conditional/next/config/vercel/next.config.ts +7 -0
  135. package/templates/conditional/next/config/webpack/next.config.mjs +9 -0
  136. package/templates/conditional/next/config/webpack/next.config.ts +10 -0
  137. package/templates/conditional/next/font/BUILD_SIZE_NOTE.md +17 -0
  138. package/templates/conditional/next/font/app-router/src/app/layout.js +25 -0
  139. package/templates/conditional/next/font/app-router/src/app/layout.tsx +30 -0
  140. package/templates/conditional/next/font/pages-router/src/pages/_app.js +20 -0
  141. package/templates/conditional/next/font/pages-router/src/pages/_app.tsx +21 -0
  142. package/templates/conditional/next/font/pages-router/src/pages/_document.js +18 -0
  143. package/templates/conditional/next/font/pages-router/src/pages/_document.tsx +18 -0
  144. package/templates/conditional/next/font/src/app/layout.js +25 -0
  145. package/templates/conditional/next/font/src/app/layout.tsx +30 -0
  146. package/templates/conditional/next/font/src/lib/fonts.js +11 -0
  147. package/templates/conditional/next/font/src/lib/fonts.ts +11 -0
  148. package/templates/conditional/next/font/src/pages/_app.js +20 -0
  149. package/templates/conditional/next/font/src/pages/_app.tsx +21 -0
  150. package/templates/conditional/next/font/src/pages/_document.js +18 -0
  151. package/templates/conditional/next/font/src/pages/_document.tsx +18 -0
  152. package/templates/conditional/next/image/BUILD_SIZE_NOTE.md +15 -0
  153. package/templates/conditional/next/image/app-router/src/components/ImageExample.jsx +34 -0
  154. package/templates/conditional/next/image/app-router/src/components/ImageExample.tsx +44 -0
  155. package/templates/conditional/next/image/pages-router/src/components/ImageExample.jsx +34 -0
  156. package/templates/conditional/next/image/pages-router/src/components/ImageExample.tsx +44 -0
  157. package/templates/conditional/next/image/src/components/ImageExample.jsx +44 -0
  158. package/templates/conditional/next/image/src/components/ImageExample.tsx +54 -0
  159. package/templates/conditional/next/image/src/components/image/index.js +1 -0
  160. package/templates/conditional/next/image/src/components/image/index.ts +2 -0
  161. package/templates/conditional/next/server-components/README.md +13 -0
  162. package/templates/conditional/next/server-components/eslint.config.mjs +47 -0
  163. package/templates/conditional/next/server-components/rules/no-server-imports-in-client.js +89 -0
  164. package/templates/conditional/next/server-components/rules/no-server-imports-in-client.mjs +88 -0
  165. package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.jsx +48 -0
  166. package/templates/conditional/next/server-components/src/components/boundaries/ClientCounter.tsx +53 -0
  167. package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.jsx +41 -0
  168. package/templates/conditional/next/server-components/src/components/boundaries/ServerCard.tsx +46 -0
  169. package/templates/conditional/next/server-components/src/components/boundaries/index.js +2 -0
  170. package/templates/conditional/next/server-components/src/components/boundaries/index.ts +4 -0
  171. package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.jsx +11 -0
  172. package/templates/conditional/testing/jest/hybrid/src/__tests__/App.test.tsx +11 -0
  173. package/templates/conditional/testing/jest/none/src/__tests__/App.test.jsx +11 -0
  174. package/templates/conditional/testing/jest/none/src/__tests__/App.test.tsx +11 -0
  175. package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.jsx +10 -0
  176. package/templates/conditional/testing/vitest/hybrid/src/__tests__/App.test.tsx +10 -0
  177. package/templates/conditional/testing/vitest/none/src/__tests__/App.test.jsx +10 -0
  178. package/templates/conditional/testing/vitest/none/src/__tests__/App.test.tsx +10 -0
  179. package/templates/css/tailwind/src/globals.css +10 -0
  180. package/templates/css/tailwind/src/themes.css +86 -4
  181. package/templates/ui/shadcn/lib/utils.js +6 -0
  182. package/templates/ui/shadcn/lib/utils.ts +6 -0
  183. package/templates/ui/shadcn/primitives/Button.jsx +48 -0
  184. package/templates/ui/shadcn/primitives/Button.tsx +59 -0
  185. package/templates/ui/shadcn/primitives/Card.jsx +55 -0
  186. package/templates/ui/shadcn/primitives/Card.tsx +73 -0
  187. package/templates/ui/shadcn/primitives/index.js +9 -0
  188. package/templates/ui/shadcn/primitives/index.ts +9 -0
  189. package/templates/css/bootstrap/src/themes.css +0 -14
  190. /package/templates/{css/bootstrap/src/globals.css → architectures/hybrid/src/features/.gitkeep} +0 -0
@@ -0,0 +1,41 @@
1
+ import React from "react";
2
+ import { ClientCounter } from "./ClientCounter";
3
+
4
+ /**
5
+ * Server Component example for RSC boundary.
6
+ * Renders on the server by default (zero client-side bundle impact).
7
+ * Demonstrates composing a Client Component across the RSC boundary.
8
+ * Compatible with both Next.js App Router and Pages Router.
9
+ */
10
+ export function ServerCard({
11
+ title = "Server Component Shell",
12
+ serverMessage = "Rendered on the server with zero client bundle overhead for this card frame.",
13
+ }) {
14
+ return (
15
+ <div className="server-boundary p-6 rounded-xl border border-emerald-500/20 bg-emerald-500/5 my-4">
16
+ <div className="flex items-center justify-between mb-3">
17
+ <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-emerald-100 text-emerald-800 dark:bg-emerald-900/60 dark:text-emerald-300">
18
+ Server Component (Default RSC)
19
+ </span>
20
+ </div>
21
+ <h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2">
22
+ {title}
23
+ </h3>
24
+ <p className="text-sm text-gray-600 dark:text-gray-300 mb-4">
25
+ {serverMessage}
26
+ </p>
27
+ <div className="p-4 rounded-lg border border-dashed border-emerald-500/30 bg-white/40 dark:bg-black/20">
28
+ <p className="text-xs font-semibold text-emerald-700 dark:text-emerald-400 uppercase tracking-wider mb-2">
29
+ Server / Client Boundary
30
+ </p>
31
+ <ClientCounter initialCount={0}>
32
+ <p className="text-xs text-gray-500 dark:text-gray-400 italic">
33
+ Server-rendered child passed through client component boundary.
34
+ </p>
35
+ </ClientCounter>
36
+ </div>
37
+ </div>
38
+ );
39
+ }
40
+
41
+ export default ServerCard;
@@ -0,0 +1,46 @@
1
+ import React from "react";
2
+ import { ClientCounter } from "./ClientCounter";
3
+
4
+ export interface ServerCardProps {
5
+ title?: string;
6
+ serverMessage?: string;
7
+ }
8
+
9
+ /**
10
+ * Server Component example for RSC boundary.
11
+ * Renders on the server by default (zero client-side bundle impact).
12
+ * Demonstrates composing a Client Component across the RSC boundary.
13
+ * Compatible with both Next.js App Router and Pages Router.
14
+ */
15
+ export function ServerCard({
16
+ title = "Server Component Shell",
17
+ serverMessage = "Rendered on the server with zero client bundle overhead for this card frame.",
18
+ }: ServerCardProps) {
19
+ return (
20
+ <div className="server-boundary p-6 rounded-xl border border-emerald-500/20 bg-emerald-500/5 my-4">
21
+ <div className="flex items-center justify-between mb-3">
22
+ <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-semibold bg-emerald-100 text-emerald-800 dark:bg-emerald-900/60 dark:text-emerald-300">
23
+ Server Component (Default RSC)
24
+ </span>
25
+ </div>
26
+ <h3 className="text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2">
27
+ {title}
28
+ </h3>
29
+ <p className="text-sm text-gray-600 dark:text-gray-300 mb-4">
30
+ {serverMessage}
31
+ </p>
32
+ <div className="p-4 rounded-lg border border-dashed border-emerald-500/30 bg-white/40 dark:bg-black/20">
33
+ <p className="text-xs font-semibold text-emerald-700 dark:text-emerald-400 uppercase tracking-wider mb-2">
34
+ Server / Client Boundary
35
+ </p>
36
+ <ClientCounter initialCount={0}>
37
+ <p className="text-xs text-gray-500 dark:text-gray-400 italic">
38
+ Server-rendered child passed through client component boundary.
39
+ </p>
40
+ </ClientCounter>
41
+ </div>
42
+ </div>
43
+ );
44
+ }
45
+
46
+ export default ServerCard;
@@ -0,0 +1,2 @@
1
+ export { ClientCounter } from "./ClientCounter";
2
+ export { ServerCard } from "./ServerCard";
@@ -0,0 +1,4 @@
1
+ export { ClientCounter } from "./ClientCounter";
2
+ export type { ClientCounterProps } from "./ClientCounter";
3
+ export { ServerCard } from "./ServerCard";
4
+ export type { ServerCardProps } from "./ServerCard";
@@ -0,0 +1,11 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { render, screen } from "@testing-library/react";
3
+ import "@testing-library/jest-dom/jest-globals";
4
+ import App from "@/App";
5
+
6
+ describe("App", () => {
7
+ it("renders without crashing", () => {
8
+ render(<App />);
9
+ expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,11 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { render, screen } from "@testing-library/react";
3
+ import "@testing-library/jest-dom/jest-globals";
4
+ import App from "@/App";
5
+
6
+ describe("App", () => {
7
+ it("renders without crashing", () => {
8
+ render(<App />);
9
+ expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,11 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { render, screen } from "@testing-library/react";
3
+ import "@testing-library/jest-dom/jest-globals";
4
+ import App from "@/App";
5
+
6
+ describe("App", () => {
7
+ it("renders without crashing", () => {
8
+ render(<App />);
9
+ expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,11 @@
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { render, screen } from "@testing-library/react";
3
+ import "@testing-library/jest-dom/jest-globals";
4
+ import App from "@/App";
5
+
6
+ describe("App", () => {
7
+ it("renders without crashing", () => {
8
+ render(<App />);
9
+ expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
10
+ });
11
+ });
@@ -0,0 +1,10 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { describe, it, expect } from "vitest";
3
+ import App from "@/App";
4
+
5
+ describe("App", () => {
6
+ it("renders without crashing", () => {
7
+ render(<App />);
8
+ expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
9
+ });
10
+ });
@@ -0,0 +1,10 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { describe, it, expect } from "vitest";
3
+ import App from "@/App";
4
+
5
+ describe("App", () => {
6
+ it("renders without crashing", () => {
7
+ render(<App />);
8
+ expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
9
+ });
10
+ });
@@ -0,0 +1,10 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { describe, it, expect } from "vitest";
3
+ import App from "@/App";
4
+
5
+ describe("App", () => {
6
+ it("renders without crashing", () => {
7
+ render(<App />);
8
+ expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
9
+ });
10
+ });
@@ -0,0 +1,10 @@
1
+ import { render, screen } from "@testing-library/react";
2
+ import { describe, it, expect } from "vitest";
3
+ import App from "@/App";
4
+
5
+ describe("App", () => {
6
+ it("renders without crashing", () => {
7
+ render(<App />);
8
+ expect(screen.getByText(/Welcome/i)).toBeInTheDocument();
9
+ });
10
+ });
@@ -1 +1,11 @@
1
1
  @import "tailwindcss";
2
+ @import "./themes.css";
3
+
4
+ @layer base {
5
+ * {
6
+ @apply border-border;
7
+ }
8
+ body {
9
+ @apply bg-background text-foreground;
10
+ }
11
+ }
@@ -1,9 +1,91 @@
1
1
  /* themes.css — Tailwind v4 theme tokens
2
2
  * Synced with ThemeProvider / AppProvider via
3
- * document.documentElement.dataset.theme.
3
+ * document.documentElement.dataset.theme and .dark class.
4
4
  */
5
- @custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
5
+ @custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *, .dark, .dark *));
6
6
 
7
- @theme {
8
- --color-primary: #6366f1;
7
+ @theme inline {
8
+ --color-background: var(--background);
9
+ --color-foreground: var(--foreground);
10
+ --color-card: var(--card);
11
+ --color-card-foreground: var(--card-foreground);
12
+ --color-popover: var(--popover);
13
+ --color-popover-foreground: var(--popover-foreground);
14
+ --color-primary: var(--primary);
15
+ --color-primary-foreground: var(--primary-foreground);
16
+ --color-secondary: var(--secondary);
17
+ --color-secondary-foreground: var(--secondary-foreground);
18
+ --color-muted: var(--muted);
19
+ --color-muted-foreground: var(--muted-foreground);
20
+ --color-accent: var(--accent);
21
+ --color-accent-foreground: var(--accent-foreground);
22
+ --color-destructive: var(--destructive);
23
+ --color-destructive-foreground: var(--destructive-foreground);
24
+ --color-border: var(--border);
25
+ --color-input: var(--input);
26
+ --color-ring: var(--ring);
27
+ --color-chart-1: var(--chart-1);
28
+ --color-chart-2: var(--chart-2);
29
+ --color-chart-3: var(--chart-3);
30
+ --color-chart-4: var(--chart-4);
31
+ --color-chart-5: var(--chart-5);
32
+ --radius-sm: calc(var(--radius) - 4px);
33
+ --radius-md: calc(var(--radius) - 2px);
34
+ --radius-lg: var(--radius);
35
+ --radius-xl: calc(var(--radius) + 4px);
36
+ }
37
+
38
+ :root {
39
+ --background: oklch(1 0 0);
40
+ --foreground: oklch(0.145 0 0);
41
+ --card: oklch(1 0 0);
42
+ --card-foreground: oklch(0.145 0 0);
43
+ --popover: oklch(1 0 0);
44
+ --popover-foreground: oklch(0.145 0 0);
45
+ --primary: oklch(0.205 0 0);
46
+ --primary-foreground: oklch(0.985 0 0);
47
+ --secondary: oklch(0.97 0 0);
48
+ --secondary-foreground: oklch(0.205 0 0);
49
+ --muted: oklch(0.97 0 0);
50
+ --muted-foreground: oklch(0.556 0 0);
51
+ --accent: oklch(0.97 0 0);
52
+ --accent-foreground: oklch(0.205 0 0);
53
+ --destructive: oklch(0.577 0.245 27.325);
54
+ --destructive-foreground: oklch(0.577 0.245 27.325);
55
+ --border: oklch(0.922 0 0);
56
+ --input: oklch(0.922 0 0);
57
+ --ring: oklch(0.708 0 0);
58
+ --chart-1: oklch(0.646 0.222 41.116);
59
+ --chart-2: oklch(0.6 0.118 184.704);
60
+ --chart-3: oklch(0.398 0.07 227.392);
61
+ --chart-4: oklch(0.828 0.189 84.429);
62
+ --chart-5: oklch(0.769 0.188 70.08);
63
+ --radius: 0.625rem;
64
+ }
65
+
66
+ [data-theme="dark"], .dark {
67
+ --background: oklch(0.145 0 0);
68
+ --foreground: oklch(0.985 0 0);
69
+ --card: oklch(0.145 0 0);
70
+ --card-foreground: oklch(0.985 0 0);
71
+ --popover: oklch(0.145 0 0);
72
+ --popover-foreground: oklch(0.985 0 0);
73
+ --primary: oklch(0.985 0 0);
74
+ --primary-foreground: oklch(0.205 0 0);
75
+ --secondary: oklch(0.269 0 0);
76
+ --secondary-foreground: oklch(0.985 0 0);
77
+ --muted: oklch(0.269 0 0);
78
+ --muted-foreground: oklch(0.708 0 0);
79
+ --accent: oklch(0.269 0 0);
80
+ --accent-foreground: oklch(0.985 0 0);
81
+ --destructive: oklch(0.396 0.141 25.723);
82
+ --destructive-foreground: oklch(0.637 0.237 25.331);
83
+ --border: oklch(0.269 0 0);
84
+ --input: oklch(0.269 0 0);
85
+ --ring: oklch(0.439 0 0);
86
+ --chart-1: oklch(0.488 0.243 264.376);
87
+ --chart-2: oklch(0.696 0.17 162.48);
88
+ --chart-3: oklch(0.769 0.188 70.08);
89
+ --chart-4: oklch(0.627 0.265 303.9);
90
+ --chart-5: oklch(0.645 0.246 16.439);
9
91
  }
@@ -0,0 +1,6 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,48 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ const buttonVariants = ({
5
+ variant = "default",
6
+ size = "default",
7
+ className = "",
8
+ } = {}) => {
9
+ const base =
10
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
11
+
12
+ const variants = {
13
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14
+ destructive:
15
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16
+ outline:
17
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18
+ secondary:
19
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20
+ ghost: "hover:bg-accent hover:text-accent-foreground",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ };
23
+
24
+ const sizes = {
25
+ default: "h-9 px-4 py-2",
26
+ sm: "h-8 rounded-md px-3 text-xs",
27
+ lg: "h-10 rounded-md px-8",
28
+ icon: "h-9 w-9",
29
+ };
30
+
31
+ return cn(base, variants[variant] || variants.default, sizes[size] || sizes.default, className);
32
+ };
33
+
34
+ const Button = React.forwardRef(
35
+ ({ className, variant = "default", size = "default", ...props }, ref) => {
36
+ return (
37
+ <button
38
+ className={buttonVariants({ variant, size, className })}
39
+ ref={ref}
40
+ {...props}
41
+ />
42
+ );
43
+ }
44
+ );
45
+ Button.displayName = "Button";
46
+
47
+ // eslint-disable-next-line react-refresh/only-export-components
48
+ export { Button, buttonVariants };
@@ -0,0 +1,59 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ export interface ButtonProps
5
+ extends React.ButtonHTMLAttributes<HTMLButtonElement> {
6
+ variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link";
7
+ size?: "default" | "sm" | "lg" | "icon";
8
+ asChild?: boolean;
9
+ }
10
+
11
+ const buttonVariants = ({
12
+ variant = "default",
13
+ size = "default",
14
+ className = "",
15
+ }: {
16
+ variant?: ButtonProps["variant"];
17
+ size?: ButtonProps["size"];
18
+ className?: string;
19
+ } = {}) => {
20
+ const base =
21
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0";
22
+
23
+ const variants = {
24
+ default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
25
+ destructive:
26
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
27
+ outline:
28
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
29
+ secondary:
30
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
31
+ ghost: "hover:bg-accent hover:text-accent-foreground",
32
+ link: "text-primary underline-offset-4 hover:underline",
33
+ };
34
+
35
+ const sizes = {
36
+ default: "h-9 px-4 py-2",
37
+ sm: "h-8 rounded-md px-3 text-xs",
38
+ lg: "h-10 rounded-md px-8",
39
+ icon: "h-9 w-9",
40
+ };
41
+
42
+ return cn(base, variants[variant] || variants.default, sizes[size] || sizes.default, className);
43
+ };
44
+
45
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
46
+ ({ className, variant = "default", size = "default", ...props }, ref) => {
47
+ return (
48
+ <button
49
+ className={buttonVariants({ variant, size, className })}
50
+ ref={ref}
51
+ {...props}
52
+ />
53
+ );
54
+ }
55
+ );
56
+ Button.displayName = "Button";
57
+
58
+ // eslint-disable-next-line react-refresh/only-export-components
59
+ export { Button, buttonVariants };
@@ -0,0 +1,55 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ export const Card = React.forwardRef(({ className, ...props }, ref) => (
5
+ <div
6
+ ref={ref}
7
+ className={cn(
8
+ "rounded-xl border bg-card text-card-foreground shadow",
9
+ className
10
+ )}
11
+ {...props}
12
+ />
13
+ ));
14
+ Card.displayName = "Card";
15
+
16
+ export const CardHeader = React.forwardRef(({ className, ...props }, ref) => (
17
+ <div
18
+ ref={ref}
19
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
20
+ {...props}
21
+ />
22
+ ));
23
+ CardHeader.displayName = "CardHeader";
24
+
25
+ export const CardTitle = React.forwardRef(({ className, ...props }, ref) => (
26
+ <div
27
+ ref={ref}
28
+ className={cn("font-semibold leading-none tracking-tight", className)}
29
+ {...props}
30
+ />
31
+ ));
32
+ CardTitle.displayName = "CardTitle";
33
+
34
+ export const CardDescription = React.forwardRef(({ className, ...props }, ref) => (
35
+ <div
36
+ ref={ref}
37
+ className={cn("text-sm text-muted-foreground", className)}
38
+ {...props}
39
+ />
40
+ ));
41
+ CardDescription.displayName = "CardDescription";
42
+
43
+ export const CardContent = React.forwardRef(({ className, ...props }, ref) => (
44
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
45
+ ));
46
+ CardContent.displayName = "CardContent";
47
+
48
+ export const CardFooter = React.forwardRef(({ className, ...props }, ref) => (
49
+ <div
50
+ ref={ref}
51
+ className={cn("flex items-center p-6 pt-0", className)}
52
+ {...props}
53
+ />
54
+ ));
55
+ CardFooter.displayName = "CardFooter";
@@ -0,0 +1,73 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ export const Card = React.forwardRef<
5
+ HTMLDivElement,
6
+ React.HTMLAttributes<HTMLDivElement>
7
+ >(({ className, ...props }, ref) => (
8
+ <div
9
+ ref={ref}
10
+ className={cn(
11
+ "rounded-xl border bg-card text-card-foreground shadow",
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ ));
17
+ Card.displayName = "Card";
18
+
19
+ export const CardHeader = React.forwardRef<
20
+ HTMLDivElement,
21
+ React.HTMLAttributes<HTMLDivElement>
22
+ >(({ className, ...props }, ref) => (
23
+ <div
24
+ ref={ref}
25
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
26
+ {...props}
27
+ />
28
+ ));
29
+ CardHeader.displayName = "CardHeader";
30
+
31
+ export const CardTitle = React.forwardRef<
32
+ HTMLDivElement,
33
+ React.HTMLAttributes<HTMLDivElement>
34
+ >(({ className, ...props }, ref) => (
35
+ <div
36
+ ref={ref}
37
+ className={cn("font-semibold leading-none tracking-tight", className)}
38
+ {...props}
39
+ />
40
+ ));
41
+ CardTitle.displayName = "CardTitle";
42
+
43
+ export const CardDescription = React.forwardRef<
44
+ HTMLDivElement,
45
+ React.HTMLAttributes<HTMLDivElement>
46
+ >(({ className, ...props }, ref) => (
47
+ <div
48
+ ref={ref}
49
+ className={cn("text-sm text-muted-foreground", className)}
50
+ {...props}
51
+ />
52
+ ));
53
+ CardDescription.displayName = "CardDescription";
54
+
55
+ export const CardContent = React.forwardRef<
56
+ HTMLDivElement,
57
+ React.HTMLAttributes<HTMLDivElement>
58
+ >(({ className, ...props }, ref) => (
59
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
60
+ ));
61
+ CardContent.displayName = "CardContent";
62
+
63
+ export const CardFooter = React.forwardRef<
64
+ HTMLDivElement,
65
+ React.HTMLAttributes<HTMLDivElement>
66
+ >(({ className, ...props }, ref) => (
67
+ <div
68
+ ref={ref}
69
+ className={cn("flex items-center p-6 pt-0", className)}
70
+ {...props}
71
+ />
72
+ ));
73
+ CardFooter.displayName = "CardFooter";
@@ -0,0 +1,9 @@
1
+ export { Button, buttonVariants } from "./Button";
2
+ export {
3
+ Card,
4
+ CardHeader,
5
+ CardFooter,
6
+ CardTitle,
7
+ CardDescription,
8
+ CardContent,
9
+ } from "./Card";
@@ -0,0 +1,9 @@
1
+ export { Button, buttonVariants } from "./Button";
2
+ export {
3
+ Card,
4
+ CardHeader,
5
+ CardFooter,
6
+ CardTitle,
7
+ CardDescription,
8
+ CardContent,
9
+ } from "./Card";
@@ -1,14 +0,0 @@
1
- /* themes.css — Bootstrap 5.3+ theme overrides
2
- * Bootstrap 5.3+ reads data-bs-theme on a parent element to switch between
3
- * light/dark variants. The themes here customize the default tokens; the
4
- * data-bs-theme attribute is applied externally.
5
- */
6
- :root[data-bs-theme="light"] {
7
- --bs-primary: #6366f1;
8
- }
9
-
10
- :root[data-bs-theme="dark"] {
11
- --bs-body-bg: #111827;
12
- --bs-body-color: #f9fafb;
13
- --bs-primary: #818cf8;
14
- }