@srcroot/ui 0.0.3 → 0.0.5

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.
package/dist/index.js CHANGED
@@ -18,54 +18,255 @@ export function cn(...inputs: ClassValue[]) {
18
18
  return twMerge(clsx(inputs))
19
19
  }
20
20
  `;
21
- var CSS_VARIABLES = `@tailwind base;
21
+ var THEMES = {
22
+ slate: {
23
+ name: "Slate",
24
+ description: "Cool gray with strong blue undertones (default)",
25
+ light: {
26
+ background: "0 0% 100%",
27
+ foreground: "222.2 84% 4.9%",
28
+ card: "0 0% 100%",
29
+ "card-foreground": "222.2 84% 4.9%",
30
+ popover: "0 0% 100%",
31
+ "popover-foreground": "222.2 84% 4.9%",
32
+ primary: "222.2 47.4% 11.2%",
33
+ "primary-foreground": "210 40% 98%",
34
+ secondary: "210 40% 96.1%",
35
+ "secondary-foreground": "222.2 47.4% 11.2%",
36
+ muted: "210 40% 96.1%",
37
+ "muted-foreground": "215.4 16.3% 46.9%",
38
+ accent: "210 40% 96.1%",
39
+ "accent-foreground": "222.2 47.4% 11.2%",
40
+ destructive: "0 84.2% 60.2%",
41
+ "destructive-foreground": "210 40% 98%",
42
+ border: "214.3 31.8% 91.4%",
43
+ input: "214.3 31.8% 91.4%",
44
+ ring: "222.2 84% 4.9%"
45
+ },
46
+ dark: {
47
+ background: "222.2 84% 4.9%",
48
+ foreground: "210 40% 98%",
49
+ card: "222.2 84% 4.9%",
50
+ "card-foreground": "210 40% 98%",
51
+ popover: "222.2 84% 4.9%",
52
+ "popover-foreground": "210 40% 98%",
53
+ primary: "210 40% 98%",
54
+ "primary-foreground": "222.2 47.4% 11.2%",
55
+ secondary: "217.2 32.6% 17.5%",
56
+ "secondary-foreground": "210 40% 98%",
57
+ muted: "217.2 32.6% 17.5%",
58
+ "muted-foreground": "215 20.2% 65.1%",
59
+ accent: "217.2 32.6% 17.5%",
60
+ "accent-foreground": "210 40% 98%",
61
+ destructive: "0 62.8% 30.6%",
62
+ "destructive-foreground": "210 40% 98%",
63
+ border: "217.2 32.6% 17.5%",
64
+ input: "217.2 32.6% 17.5%",
65
+ ring: "212.7 26.8% 83.9%"
66
+ }
67
+ },
68
+ neutral: {
69
+ name: "Neutral",
70
+ description: "Pure gray, no undertones",
71
+ light: {
72
+ background: "0 0% 100%",
73
+ foreground: "0 0% 3.9%",
74
+ card: "0 0% 100%",
75
+ "card-foreground": "0 0% 3.9%",
76
+ popover: "0 0% 100%",
77
+ "popover-foreground": "0 0% 3.9%",
78
+ primary: "0 0% 9%",
79
+ "primary-foreground": "0 0% 98%",
80
+ secondary: "0 0% 96.1%",
81
+ "secondary-foreground": "0 0% 9%",
82
+ muted: "0 0% 96.1%",
83
+ "muted-foreground": "0 0% 45.1%",
84
+ accent: "0 0% 96.1%",
85
+ "accent-foreground": "0 0% 9%",
86
+ destructive: "0 84.2% 60.2%",
87
+ "destructive-foreground": "0 0% 98%",
88
+ border: "0 0% 89.8%",
89
+ input: "0 0% 89.8%",
90
+ ring: "0 0% 3.9%"
91
+ },
92
+ dark: {
93
+ background: "0 0% 3.9%",
94
+ foreground: "0 0% 98%",
95
+ card: "0 0% 3.9%",
96
+ "card-foreground": "0 0% 98%",
97
+ popover: "0 0% 3.9%",
98
+ "popover-foreground": "0 0% 98%",
99
+ primary: "0 0% 98%",
100
+ "primary-foreground": "0 0% 9%",
101
+ secondary: "0 0% 14.9%",
102
+ "secondary-foreground": "0 0% 98%",
103
+ muted: "0 0% 14.9%",
104
+ "muted-foreground": "0 0% 63.9%",
105
+ accent: "0 0% 14.9%",
106
+ "accent-foreground": "0 0% 98%",
107
+ destructive: "0 62.8% 30.6%",
108
+ "destructive-foreground": "0 0% 98%",
109
+ border: "0 0% 14.9%",
110
+ input: "0 0% 14.9%",
111
+ ring: "0 0% 83.1%"
112
+ }
113
+ },
114
+ stone: {
115
+ name: "Stone",
116
+ description: "Warm gray with brown undertones",
117
+ light: {
118
+ background: "0 0% 100%",
119
+ foreground: "24 9.8% 10%",
120
+ card: "0 0% 100%",
121
+ "card-foreground": "24 9.8% 10%",
122
+ popover: "0 0% 100%",
123
+ "popover-foreground": "24 9.8% 10%",
124
+ primary: "24 9.8% 10%",
125
+ "primary-foreground": "60 9.1% 97.8%",
126
+ secondary: "60 4.8% 95.9%",
127
+ "secondary-foreground": "24 9.8% 10%",
128
+ muted: "60 4.8% 95.9%",
129
+ "muted-foreground": "25 5.3% 44.7%",
130
+ accent: "60 4.8% 95.9%",
131
+ "accent-foreground": "24 9.8% 10%",
132
+ destructive: "0 84.2% 60.2%",
133
+ "destructive-foreground": "60 9.1% 97.8%",
134
+ border: "20 5.9% 90%",
135
+ input: "20 5.9% 90%",
136
+ ring: "24 9.8% 10%"
137
+ },
138
+ dark: {
139
+ background: "24 9.8% 10%",
140
+ foreground: "60 9.1% 97.8%",
141
+ card: "24 9.8% 10%",
142
+ "card-foreground": "60 9.1% 97.8%",
143
+ popover: "24 9.8% 10%",
144
+ "popover-foreground": "60 9.1% 97.8%",
145
+ primary: "60 9.1% 97.8%",
146
+ "primary-foreground": "24 9.8% 10%",
147
+ secondary: "12 6.5% 15.1%",
148
+ "secondary-foreground": "60 9.1% 97.8%",
149
+ muted: "12 6.5% 15.1%",
150
+ "muted-foreground": "24 5.4% 63.9%",
151
+ accent: "12 6.5% 15.1%",
152
+ "accent-foreground": "60 9.1% 97.8%",
153
+ destructive: "0 62.8% 30.6%",
154
+ "destructive-foreground": "60 9.1% 97.8%",
155
+ border: "12 6.5% 15.1%",
156
+ input: "12 6.5% 15.1%",
157
+ ring: "24 5.7% 82.9%"
158
+ }
159
+ },
160
+ zinc: {
161
+ name: "Zinc",
162
+ description: "Cool gray with subtle blue undertones",
163
+ light: {
164
+ background: "0 0% 100%",
165
+ foreground: "240 10% 3.9%",
166
+ card: "0 0% 100%",
167
+ "card-foreground": "240 10% 3.9%",
168
+ popover: "0 0% 100%",
169
+ "popover-foreground": "240 10% 3.9%",
170
+ primary: "240 5.9% 10%",
171
+ "primary-foreground": "0 0% 98%",
172
+ secondary: "240 4.8% 95.9%",
173
+ "secondary-foreground": "240 5.9% 10%",
174
+ muted: "240 4.8% 95.9%",
175
+ "muted-foreground": "240 3.8% 46.1%",
176
+ accent: "240 4.8% 95.9%",
177
+ "accent-foreground": "240 5.9% 10%",
178
+ destructive: "0 84.2% 60.2%",
179
+ "destructive-foreground": "0 0% 98%",
180
+ border: "240 5.9% 90%",
181
+ input: "240 5.9% 90%",
182
+ ring: "240 10% 3.9%"
183
+ },
184
+ dark: {
185
+ background: "240 10% 3.9%",
186
+ foreground: "0 0% 98%",
187
+ card: "240 10% 3.9%",
188
+ "card-foreground": "0 0% 98%",
189
+ popover: "240 10% 3.9%",
190
+ "popover-foreground": "0 0% 98%",
191
+ primary: "0 0% 98%",
192
+ "primary-foreground": "240 5.9% 10%",
193
+ secondary: "240 3.7% 15.9%",
194
+ "secondary-foreground": "0 0% 98%",
195
+ muted: "240 3.7% 15.9%",
196
+ "muted-foreground": "240 5% 64.9%",
197
+ accent: "240 3.7% 15.9%",
198
+ "accent-foreground": "0 0% 98%",
199
+ destructive: "0 62.8% 30.6%",
200
+ "destructive-foreground": "0 0% 98%",
201
+ border: "240 3.7% 15.9%",
202
+ input: "240 3.7% 15.9%",
203
+ ring: "240 4.9% 83.9%"
204
+ }
205
+ },
206
+ gray: {
207
+ name: "Gray",
208
+ description: "True neutral gray",
209
+ light: {
210
+ background: "0 0% 100%",
211
+ foreground: "224 71.4% 4.1%",
212
+ card: "0 0% 100%",
213
+ "card-foreground": "224 71.4% 4.1%",
214
+ popover: "0 0% 100%",
215
+ "popover-foreground": "224 71.4% 4.1%",
216
+ primary: "220.9 39.3% 11%",
217
+ "primary-foreground": "210 20% 98%",
218
+ secondary: "220 14.3% 95.9%",
219
+ "secondary-foreground": "220.9 39.3% 11%",
220
+ muted: "220 14.3% 95.9%",
221
+ "muted-foreground": "220 8.9% 46.1%",
222
+ accent: "220 14.3% 95.9%",
223
+ "accent-foreground": "220.9 39.3% 11%",
224
+ destructive: "0 84.2% 60.2%",
225
+ "destructive-foreground": "210 20% 98%",
226
+ border: "220 13% 91%",
227
+ input: "220 13% 91%",
228
+ ring: "224 71.4% 4.1%"
229
+ },
230
+ dark: {
231
+ background: "224 71.4% 4.1%",
232
+ foreground: "210 20% 98%",
233
+ card: "224 71.4% 4.1%",
234
+ "card-foreground": "210 20% 98%",
235
+ popover: "224 71.4% 4.1%",
236
+ "popover-foreground": "210 20% 98%",
237
+ primary: "210 20% 98%",
238
+ "primary-foreground": "220.9 39.3% 11%",
239
+ secondary: "215 27.9% 16.9%",
240
+ "secondary-foreground": "210 20% 98%",
241
+ muted: "215 27.9% 16.9%",
242
+ "muted-foreground": "217.9 10.6% 64.9%",
243
+ accent: "215 27.9% 16.9%",
244
+ "accent-foreground": "210 20% 98%",
245
+ destructive: "0 62.8% 30.6%",
246
+ "destructive-foreground": "210 20% 98%",
247
+ border: "215 27.9% 16.9%",
248
+ input: "215 27.9% 16.9%",
249
+ ring: "216 12.2% 83.9%"
250
+ }
251
+ }
252
+ };
253
+ function generateCssVariables(themeName) {
254
+ const theme = THEMES[themeName];
255
+ if (!theme) return "";
256
+ const lightVars = Object.entries(theme.light).map(([key, value]) => ` --${key}: ${value};`).join("\n");
257
+ const darkVars = Object.entries(theme.dark).map(([key, value]) => ` --${key}: ${value};`).join("\n");
258
+ return `@tailwind base;
22
259
  @tailwind components;
23
260
  @tailwind utilities;
24
261
 
25
262
  @layer base {
26
263
  :root {
27
- --background: 0 0% 100%;
28
- --foreground: 222.2 84% 4.9%;
29
- --card: 0 0% 100%;
30
- --card-foreground: 222.2 84% 4.9%;
31
- --popover: 0 0% 100%;
32
- --popover-foreground: 222.2 84% 4.9%;
33
- --primary: 222.2 47.4% 11.2%;
34
- --primary-foreground: 210 40% 98%;
35
- --secondary: 210 40% 96.1%;
36
- --secondary-foreground: 222.2 47.4% 11.2%;
37
- --muted: 210 40% 96.1%;
38
- --muted-foreground: 215.4 16.3% 46.9%;
39
- --accent: 210 40% 96.1%;
40
- --accent-foreground: 222.2 47.4% 11.2%;
41
- --destructive: 0 84.2% 60.2%;
42
- --destructive-foreground: 210 40% 98%;
43
- --border: 214.3 31.8% 91.4%;
44
- --input: 214.3 31.8% 91.4%;
45
- --ring: 222.2 84% 4.9%;
264
+ ${lightVars}
46
265
  --radius: 0.5rem;
47
266
  }
48
267
 
49
268
  .dark {
50
- --background: 222.2 84% 4.9%;
51
- --foreground: 210 40% 98%;
52
- --card: 222.2 84% 4.9%;
53
- --card-foreground: 210 40% 98%;
54
- --popover: 222.2 84% 4.9%;
55
- --popover-foreground: 210 40% 98%;
56
- --primary: 210 40% 98%;
57
- --primary-foreground: 222.2 47.4% 11.2%;
58
- --secondary: 217.2 32.6% 17.5%;
59
- --secondary-foreground: 210 40% 98%;
60
- --muted: 217.2 32.6% 17.5%;
61
- --muted-foreground: 215 20.2% 65.1%;
62
- --accent: 217.2 32.6% 17.5%;
63
- --accent-foreground: 210 40% 98%;
64
- --destructive: 0 62.8% 30.6%;
65
- --destructive-foreground: 210 40% 98%;
66
- --border: 217.2 32.6% 17.5%;
67
- --input: 217.2 32.6% 17.5%;
68
- --ring: 212.7 26.8% 83.9%;
269
+ ${darkVars}
69
270
  }
70
271
  }
71
272
 
@@ -77,7 +278,25 @@ var CSS_VARIABLES = `@tailwind base;
77
278
  @apply bg-background text-foreground;
78
279
  }
79
280
  }
281
+
282
+ @layer utilities {
283
+ @keyframes accordion-down {
284
+ from { height: 0; }
285
+ to { height: var(--radix-accordion-content-height); }
286
+ }
287
+ @keyframes accordion-up {
288
+ from { height: var(--radix-accordion-content-height); }
289
+ to { height: 0; }
290
+ }
291
+ .animate-accordion-down {
292
+ animation: accordion-down 0.2s ease-out;
293
+ }
294
+ .animate-accordion-up {
295
+ animation: accordion-up 0.2s ease-out;
296
+ }
297
+ }
80
298
  `;
299
+ }
81
300
  var TAILWIND_CONFIG = `import type { Config } from "tailwindcss"
82
301
 
83
302
  const config: Config = {
@@ -150,12 +369,30 @@ async function init(options) {
150
369
  const libDir = path.join(srcDir, "lib");
151
370
  const componentsDir = path.join(srcDir, "components", "ui");
152
371
  const globalsPath = isAppRouter ? path.join(appDir, "globals.css") : path.join(srcDir, "styles", "globals.css");
372
+ let selectedTheme = options.theme || "slate";
373
+ if (!options.yes && !options.theme) {
374
+ const themeChoices = Object.entries(THEMES).map(([key, theme]) => ({
375
+ title: `${theme.name} - ${chalk.dim(theme.description)}`,
376
+ value: key
377
+ }));
378
+ const themeResponse = await prompts({
379
+ type: "select",
380
+ name: "theme",
381
+ message: "Which color theme would you like to use?",
382
+ choices: themeChoices,
383
+ initial: 0
384
+ // slate is first
385
+ });
386
+ if (themeResponse.theme) {
387
+ selectedTheme = themeResponse.theme;
388
+ }
389
+ }
153
390
  if (!options.yes) {
154
391
  const response = await prompts([
155
392
  {
156
393
  type: "confirm",
157
394
  name: "proceed",
158
- message: `This will create files in ${chalk.cyan(cwd)}. Continue?`,
395
+ message: `This will create files in ${chalk.cyan(cwd)} with ${chalk.cyan(THEMES[selectedTheme].name)} theme. Continue?`,
159
396
  initial: true
160
397
  }
161
398
  ]);
@@ -171,20 +408,21 @@ async function init(options) {
171
408
  const utilsPath = path.join(libDir, "utils.ts");
172
409
  await fs.writeFile(utilsPath, UTILS_CONTENT);
173
410
  spinner.succeed(`Created ${chalk.cyan("src/lib/utils.ts")}`);
174
- spinner.start("Setting up CSS variables...");
411
+ spinner.start(`Setting up ${chalk.cyan(THEMES[selectedTheme].name)} theme...`);
175
412
  const stylesDir = path.dirname(globalsPath);
176
413
  await fs.ensureDir(stylesDir);
414
+ const cssContent = generateCssVariables(selectedTheme);
177
415
  if (fs.existsSync(globalsPath)) {
178
416
  const existingCss = await fs.readFile(globalsPath, "utf-8");
179
417
  if (!existingCss.includes("--background:")) {
180
- await fs.writeFile(globalsPath, CSS_VARIABLES + "\n" + existingCss);
181
- spinner.succeed(`Updated ${chalk.cyan(path.relative(cwd, globalsPath))}`);
418
+ await fs.writeFile(globalsPath, cssContent + "\n" + existingCss);
419
+ spinner.succeed(`Updated ${chalk.cyan(path.relative(cwd, globalsPath))} with ${chalk.cyan(THEMES[selectedTheme].name)} theme`);
182
420
  } else {
183
421
  spinner.info(`CSS variables already exist in ${chalk.cyan(path.relative(cwd, globalsPath))}`);
184
422
  }
185
423
  } else {
186
- await fs.writeFile(globalsPath, CSS_VARIABLES);
187
- spinner.succeed(`Created ${chalk.cyan(path.relative(cwd, globalsPath))}`);
424
+ await fs.writeFile(globalsPath, cssContent);
425
+ spinner.succeed(`Created ${chalk.cyan(path.relative(cwd, globalsPath))} with ${chalk.cyan(THEMES[selectedTheme].name)} theme`);
188
426
  }
189
427
  spinner.start("Setting up Tailwind config...");
190
428
  const tailwindConfigPath = path.join(cwd, "tailwind.config.ts");
@@ -232,7 +470,8 @@ Please manually run: npm install ${missing.join(" ")}
232
470
  spinner.succeed("All dependencies installed");
233
471
  }
234
472
  console.log(chalk.green("\n\u2705 Project initialized successfully!\n"));
235
- console.log("Next steps:");
473
+ console.log(`Theme: ${chalk.cyan(THEMES[selectedTheme].name)}`);
474
+ console.log("\nNext steps:");
236
475
  console.log(chalk.dim(" 1. Install missing dependencies (if any)"));
237
476
  console.log(chalk.dim(" 2. Run: npx srcroot-ui add button"));
238
477
  console.log();
@@ -725,7 +964,7 @@ async function list() {
725
964
  // src/cli/index.ts
726
965
  var program = new Command();
727
966
  program.name("@srcroot/ui").description("Add polymorphic, accessible UI components to your project").version("0.0.1");
728
- program.command("init").description("Initialize your project with srcroot-ui").option("-y, --yes", "Skip confirmation prompts", false).option("--cwd <path>", "Working directory", process.cwd()).action(init);
967
+ program.command("init").description("Initialize your project with srcroot-ui").option("-y, --yes", "Skip confirmation prompts", false).option("-t, --theme <theme>", "Color theme (slate, neutral, stone, zinc, gray)").option("--cwd <path>", "Working directory", process.cwd()).action(init);
729
968
  program.command("add").description("Add components to your project").argument("[components...]", "Components to add").option("-y, --yes", "Skip confirmation prompts", false).option("-o, --overwrite", "Overwrite existing files", false).option("-a, --all", "Add all available components", false).option("--cwd <path>", "Working directory", process.cwd()).action(add);
730
969
  program.command("list").description("List all available components").action(list);
731
970
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@srcroot/ui",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "A shadcn-style CLI UI library with polymorphic, accessible React components",
5
5
  "type": "module",
6
6
  "bin": {
@@ -5,6 +5,7 @@ interface AccordionContextValue {
5
5
  value: string[]
6
6
  onValueChange: (value: string[]) => void
7
7
  type: "single" | "multiple"
8
+ collapsible: boolean
8
9
  }
9
10
 
10
11
  const AccordionContext = React.createContext<AccordionContextValue | null>(null)
@@ -37,14 +38,14 @@ interface AccordionProps extends React.HTMLAttributes<HTMLDivElement> {
37
38
  * </Accordion>
38
39
  */
39
40
  const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
40
- ({ className, type = "single", value: controlledValue, onValueChange, defaultValue = [], children, ...props }, ref) => {
41
+ ({ className, type = "single", value: controlledValue, onValueChange, defaultValue = [], collapsible = false, children, ...props }, ref) => {
41
42
  const [uncontrolledValue, setUncontrolledValue] = React.useState(defaultValue)
42
43
 
43
44
  const value = controlledValue !== undefined ? controlledValue : uncontrolledValue
44
45
  const setValue = onValueChange || setUncontrolledValue
45
46
 
46
47
  return (
47
- <AccordionContext.Provider value={{ value, onValueChange: setValue, type }}>
48
+ <AccordionContext.Provider value={{ value, onValueChange: setValue, type, collapsible }}>
48
49
  <div ref={ref} className={cn("", className)} {...props}>
49
50
  {children}
50
51
  </div>
@@ -54,6 +55,7 @@ const Accordion = React.forwardRef<HTMLDivElement, AccordionProps>(
54
55
  )
55
56
  Accordion.displayName = "Accordion"
56
57
 
58
+
57
59
  interface AccordionItemProps extends React.HTMLAttributes<HTMLDivElement> {
58
60
  value: string
59
61
  }
@@ -67,6 +69,8 @@ const AccordionItem = React.forwardRef<HTMLDivElement, AccordionItemProps>(
67
69
 
68
70
  const toggle = () => {
69
71
  if (context.type === "single") {
72
+ // In single mode with collapsible=false, don't allow closing
73
+ if (isOpen && !context.collapsible) return
70
74
  context.onValueChange(isOpen ? [] : [value])
71
75
  } else {
72
76
  context.onValueChange(
@@ -0,0 +1,197 @@
1
+ /**
2
+ * @srcroot/ui Design Tokens
3
+ *
4
+ * A comprehensive design token system for consistent styling.
5
+ * Copy this file to your project and import it in your main CSS file.
6
+ */
7
+
8
+ @tailwind base;
9
+ @tailwind components;
10
+ @tailwind utilities;
11
+
12
+ @layer base {
13
+ :root {
14
+ /* ========================================
15
+ COLOR TOKENS (HSL format for Tailwind)
16
+ ======================================== */
17
+
18
+ /* Semantic Colors */
19
+ --background: 0 0% 100%;
20
+ --foreground: 222.2 84% 4.9%;
21
+
22
+ /* Surfaces */
23
+ --card: 0 0% 100%;
24
+ --card-foreground: 222.2 84% 4.9%;
25
+ --popover: 0 0% 100%;
26
+ --popover-foreground: 222.2 84% 4.9%;
27
+
28
+ /* Brand Colors */
29
+ --primary: 222.2 47.4% 11.2%;
30
+ --primary-foreground: 210 40% 98%;
31
+ --secondary: 210 40% 96.1%;
32
+ --secondary-foreground: 222.2 47.4% 11.2%;
33
+
34
+ /* UI Colors */
35
+ --muted: 210 40% 96.1%;
36
+ --muted-foreground: 215.4 16.3% 46.9%;
37
+ --accent: 210 40% 96.1%;
38
+ --accent-foreground: 222.2 47.4% 11.2%;
39
+
40
+ /* Status Colors */
41
+ --destructive: 0 84.2% 60.2%;
42
+ --destructive-foreground: 210 40% 98%;
43
+ --success: 142.1 76.2% 36.3%;
44
+ --success-foreground: 355.7 100% 97.3%;
45
+ --warning: 45.4 93.4% 47.5%;
46
+ --warning-foreground: 26 83.3% 14.1%;
47
+ --info: 201.3 96.3% 32.2%;
48
+ --info-foreground: 210 40% 98%;
49
+
50
+ /* Form & Border Colors */
51
+ --border: 214.3 31.8% 91.4%;
52
+ --input: 214.3 31.8% 91.4%;
53
+ --ring: 222.2 84% 4.9%;
54
+
55
+ /* ========================================
56
+ RADIUS TOKENS
57
+ ======================================== */
58
+ --radius: 0.5rem;
59
+ --radius-sm: 0.25rem;
60
+ --radius-md: 0.5rem;
61
+ --radius-lg: 0.75rem;
62
+ --radius-xl: 1rem;
63
+ --radius-2xl: 1.5rem;
64
+ --radius-full: 9999px;
65
+
66
+ /* ========================================
67
+ SPACING TOKENS
68
+ ======================================== */
69
+ --spacing-0: 0;
70
+ --spacing-1: 0.25rem;
71
+ --spacing-2: 0.5rem;
72
+ --spacing-3: 0.75rem;
73
+ --spacing-4: 1rem;
74
+ --spacing-6: 1.5rem;
75
+ --spacing-8: 2rem;
76
+ --spacing-12: 3rem;
77
+ --spacing-16: 4rem;
78
+
79
+ /* ========================================
80
+ TYPOGRAPHY TOKENS
81
+ ======================================== */
82
+ --font-sans: ui-sans-serif, system-ui, sans-serif;
83
+ --font-mono: ui-monospace, monospace;
84
+
85
+ --text-xs: 0.75rem;
86
+ --text-sm: 0.875rem;
87
+ --text-base: 1rem;
88
+ --text-lg: 1.125rem;
89
+ --text-xl: 1.25rem;
90
+ --text-2xl: 1.5rem;
91
+ --text-3xl: 1.875rem;
92
+ --text-4xl: 2.25rem;
93
+
94
+ /* ========================================
95
+ SHADOW TOKENS
96
+ ======================================== */
97
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
98
+ --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
99
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
100
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
101
+
102
+ /* ========================================
103
+ COMPONENT TOKENS
104
+ ======================================== */
105
+ --sidebar-width: 16rem;
106
+ --sidebar-width-collapsed: 3.5rem;
107
+ --header-height: 3.5rem;
108
+ }
109
+
110
+ /* ========================================
111
+ DARK MODE TOKENS
112
+ ======================================== */
113
+ .dark {
114
+ --background: 222.2 84% 4.9%;
115
+ --foreground: 210 40% 98%;
116
+
117
+ --card: 222.2 84% 4.9%;
118
+ --card-foreground: 210 40% 98%;
119
+ --popover: 222.2 84% 4.9%;
120
+ --popover-foreground: 210 40% 98%;
121
+
122
+ --primary: 210 40% 98%;
123
+ --primary-foreground: 222.2 47.4% 11.2%;
124
+ --secondary: 217.2 32.6% 17.5%;
125
+ --secondary-foreground: 210 40% 98%;
126
+
127
+ --muted: 217.2 32.6% 17.5%;
128
+ --muted-foreground: 215 20.2% 65.1%;
129
+ --accent: 217.2 32.6% 17.5%;
130
+ --accent-foreground: 210 40% 98%;
131
+
132
+ --destructive: 0 62.8% 30.6%;
133
+ --destructive-foreground: 210 40% 98%;
134
+ --success: 142.1 70.6% 45.3%;
135
+ --success-foreground: 144.9 80.4% 10%;
136
+ --warning: 48 96.5% 53.1%;
137
+ --warning-foreground: 20.9 91.7% 14.1%;
138
+ --info: 199.4 95.5% 53.8%;
139
+ --info-foreground: 204.9 73.1% 12.9%;
140
+
141
+ --border: 217.2 32.6% 17.5%;
142
+ --input: 217.2 32.6% 17.5%;
143
+ --ring: 212.7 26.8% 83.9%;
144
+
145
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.3);
146
+ --shadow: 0 1px 3px 0 rgb(0 0 0 / 0.4);
147
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.4);
148
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.4);
149
+ }
150
+ }
151
+
152
+ /* ========================================
153
+ BASE STYLES
154
+ ======================================== */
155
+ @layer base {
156
+ * {
157
+ @apply border-border;
158
+ }
159
+
160
+ body {
161
+ @apply bg-background text-foreground;
162
+ font-feature-settings: "rlig" 1, "calt" 1;
163
+ }
164
+ }
165
+
166
+ /* ========================================
167
+ ACCORDION ANIMATION (required)
168
+ ======================================== */
169
+ @layer utilities {
170
+ @keyframes accordion-down {
171
+ from {
172
+ height: 0;
173
+ }
174
+
175
+ to {
176
+ height: var(--radix-accordion-content-height);
177
+ }
178
+ }
179
+
180
+ @keyframes accordion-up {
181
+ from {
182
+ height: var(--radix-accordion-content-height);
183
+ }
184
+
185
+ to {
186
+ height: 0;
187
+ }
188
+ }
189
+
190
+ .animate-accordion-down {
191
+ animation: accordion-down 0.2s ease-out;
192
+ }
193
+
194
+ .animate-accordion-up {
195
+ animation: accordion-up 0.2s ease-out;
196
+ }
197
+ }