@autono/create-open-pages 0.4.0 → 0.6.0

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 (63) hide show
  1. package/dist/cli.js +30 -2
  2. package/package.json +1 -1
  3. package/template/.agents/skills/create-page/SKILL.md +1 -1
  4. package/template/.agents/skills/page-authoring/SKILL.md +2 -2
  5. package/template/.agents/skills/page-authoring/references/interactivity.md +13 -21
  6. package/template/README.md +2 -1
  7. package/template/components.json +19 -3
  8. package/template/package.json +7 -7
  9. package/template/styles/globals.css +83 -66
  10. package/template/ui/accordion.tsx +22 -7
  11. package/template/ui/alert-dialog.tsx +22 -21
  12. package/template/ui/alert.tsx +15 -5
  13. package/template/ui/avatar.tsx +7 -4
  14. package/template/ui/badge.tsx +8 -7
  15. package/template/ui/breadcrumb.tsx +21 -8
  16. package/template/ui/button-group.tsx +5 -5
  17. package/template/ui/button.tsx +18 -15
  18. package/template/ui/calendar.tsx +28 -24
  19. package/template/ui/card.tsx +17 -6
  20. package/template/ui/carousel.tsx +10 -11
  21. package/template/ui/chart.tsx +1 -2
  22. package/template/ui/checkbox.tsx +5 -6
  23. package/template/ui/collapsible.tsx +2 -0
  24. package/template/ui/combobox.tsx +9 -20
  25. package/template/ui/command.tsx +39 -26
  26. package/template/ui/context-menu.tsx +69 -60
  27. package/template/ui/dialog.tsx +26 -14
  28. package/template/ui/drawer.tsx +8 -9
  29. package/template/ui/dropdown-menu.tsx +34 -22
  30. package/template/ui/empty.tsx +8 -8
  31. package/template/ui/field.tsx +15 -25
  32. package/template/ui/hover-card.tsx +1 -1
  33. package/template/ui/input-group.tsx +10 -24
  34. package/template/ui/input-otp.tsx +16 -6
  35. package/template/ui/input.tsx +1 -3
  36. package/template/ui/item.tsx +17 -14
  37. package/template/ui/kbd.tsx +1 -3
  38. package/template/ui/menubar.tsx +28 -24
  39. package/template/ui/message-scroller.tsx +5 -4
  40. package/template/ui/native-select.tsx +13 -14
  41. package/template/ui/navigation-menu.tsx +8 -12
  42. package/template/ui/pagination.tsx +35 -33
  43. package/template/ui/popover.tsx +5 -5
  44. package/template/ui/progress.tsx +2 -2
  45. package/template/ui/radio-group.tsx +4 -5
  46. package/template/ui/resizable.tsx +2 -5
  47. package/template/ui/scroll-area.tsx +2 -5
  48. package/template/ui/select.tsx +26 -24
  49. package/template/ui/separator.tsx +1 -1
  50. package/template/ui/sheet.tsx +20 -16
  51. package/template/ui/sidebar.tsx +39 -63
  52. package/template/ui/skeleton.tsx +1 -1
  53. package/template/ui/slider.tsx +6 -8
  54. package/template/ui/sonner.tsx +21 -14
  55. package/template/ui/spinner.tsx +2 -8
  56. package/template/ui/switch.tsx +4 -4
  57. package/template/ui/table.tsx +2 -4
  58. package/template/ui/tabs.tsx +9 -8
  59. package/template/ui/textarea.tsx +1 -1
  60. package/template/ui/toggle-group.tsx +14 -8
  61. package/template/ui/toggle.tsx +8 -10
  62. package/template/ui/tooltip.tsx +4 -2
  63. package/template/ui/form.tsx +0 -167
package/dist/cli.js CHANGED
@@ -1,11 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import chalk from "chalk";
3
3
  import { cp, mkdir, readFile, readdir, rm, symlink, writeFile } from "node:fs/promises";
4
- import { basename, dirname, join, resolve } from "node:path";
4
+ import { basename, dirname, join, resolve, sep } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { Command } from "commander";
7
7
  import prompts from "prompts";
8
8
  import { spawn } from "node:child_process";
9
+ import { createHash } from "node:crypto";
9
10
  import { existsSync } from "node:fs";
10
11
 
11
12
  //#region src/git.ts
@@ -102,7 +103,7 @@ async function isDirNonEmpty(target) {
102
103
  return entries.some((e) => !e.startsWith("."));
103
104
  }
104
105
  function coreVersionRange() {
105
- return `^0.4.0`;
106
+ return `^0.6.0`;
106
107
  }
107
108
  async function linkOrCopy(relSrc, dst) {
108
109
  await rm(dst, {
@@ -128,6 +129,32 @@ async function materializeTemplateLinks(target) {
128
129
  await linkOrCopy(join("..", "..", ".agents", "skills", entry.name), join(claudeSkills, entry.name));
129
130
  }
130
131
  }
132
+ async function writeUiManifest(target) {
133
+ const manifest = {};
134
+ for (const entry of [
135
+ "ui",
136
+ "lib",
137
+ "hooks"
138
+ ]) {
139
+ const root = join(target, entry);
140
+ if (!existsSync(root)) continue;
141
+ for (const rel of await collectFiles(root)) {
142
+ const wsRel = join(entry, rel).split(sep).join("/");
143
+ manifest[wsRel] = createHash("sha256").update(await readFile(join(root, rel))).digest("hex");
144
+ }
145
+ }
146
+ const sorted = Object.fromEntries(Object.entries(manifest).sort(([a], [b]) => a.localeCompare(b)));
147
+ await writeFile(join(target, "ui", ".sync-manifest.json"), `${JSON.stringify(sorted, null, 2)}\n`);
148
+ }
149
+ async function collectFiles(dir, prefix = "") {
150
+ const out = [];
151
+ for (const entry of await readdir(dir, { withFileTypes: true })) {
152
+ const rel = prefix ? join(prefix, entry.name) : entry.name;
153
+ if (entry.isDirectory()) out.push(...await collectFiles(join(dir, entry.name), rel));
154
+ else if (entry.isFile()) out.push(rel);
155
+ }
156
+ return out;
157
+ }
131
158
  async function runInstall(pm, cwd) {
132
159
  await new Promise((res, rej) => {
133
160
  const child = spawn(pm, ["install"], {
@@ -147,6 +174,7 @@ async function init(opts) {
147
174
  if (await isDirNonEmpty(target) && !force) throw new Error(`Target ${target} is not empty. Pass --force to scaffold into it anyway.`);
148
175
  await cp(TEMPLATE_DIR, target, { recursive: true });
149
176
  await materializeTemplateLinks(target);
177
+ await writeUiManifest(target);
150
178
  const pkgPath = join(target, "package.json");
151
179
  if (existsSync(pkgPath)) {
152
180
  const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autono/create-open-pages",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Scaffold an open-pages workspace — agent skills preconfigured, live real-PDF preview out of the box.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -77,7 +77,7 @@ Before writing code, map every planned section to the shadcn components it shoul
77
77
  | --- | --- |
78
78
  | Landing / marketing | `navigation-menu` or plain links + `sheet` for mobile nav, `button` (CTAs), `badge` (eyebrows), `card` (features, pricing tiers), `tabs` or `switch` (billing toggle), `accordion` (FAQ), `separator` |
79
79
  | Dashboard / app UI | `sidebar` (`SidebarProvider` + `SidebarInset`), `table`, `chart`, `card` (KPIs), `tabs`, `select`, `dropdown-menu`, `badge` (status), `skeleton`, `empty` |
80
- | Form / signup | `form` (react-hook-form + zod) or `field`, `input`, `select`, `checkbox`, `radio-group`, `switch`, `textarea`, `button`, `alert` |
80
+ | Form / signup | `field`, `input`, `select`, `checkbox`, `radio-group`, `switch`, `textarea`, `button`, `alert` |
81
81
  | Docs / long-form | `sidebar` or a sticky `scroll-area` TOC, `breadcrumb`, prose in a `max-w-[65ch]` column, `kbd`, `table`, `alert` (callouts) |
82
82
  | Portfolio / gallery | `carousel`, `aspect-ratio`, `card`, `dialog` (lightbox), `hover-card` |
83
83
  | Internal tool | `command` (palette), `combobox`, `resizable`, `context-menu`, `tooltip`, `sonner` (toasts), `alert-dialog` (confirm) |
@@ -25,10 +25,10 @@ Every workspace is a shadcn/ui project out of the box. Nothing needs installing:
25
25
  | `lib/utils.ts` | `cn()` for merging class names. Import as `@/lib/utils`. |
26
26
  | `hooks/use-mobile.ts` | `useIsMobile()` breakpoint hook. Import as `@/hooks/use-mobile`. |
27
27
  | `styles/globals.css` | Tailwind v4 entry: `@source` for `pages/ ui/ lib/ hooks/ themes/`, the `dark` variant, `:root`/`.dark` OKLCH tokens, `@theme inline`, base layer. Loaded into every page automatically. |
28
- | `components.json` | shadcn config (style `new-york`, base `radix`, aliases `@/ui`, `@/lib/utils`, `@/hooks`, `@/components`). Activates the `shadcn` skill and CLI lookups. |
28
+ | `components.json` | shadcn config (style `radix-nova`, aliases `@/ui`, `@/lib/utils`, `@/hooks`, `@/components`). Activates the `shadcn` skill and CLI lookups. |
29
29
  | `themes/<id>.css` | Optional token overrides a page opts into with `meta.theme` (see Themes). |
30
30
 
31
- Installed deps you may import directly: `lucide-react` (icons), `radix-ui`, `class-variance-authority`, `clsx`, `tailwind-merge`, `cmdk`, `sonner`, `vaul`, `recharts`, `react-day-picker`, `date-fns`, `react-hook-form`, `zod`, `@hookform/resolvers`, `input-otp`, `embla-carousel-react`, `react-resizable-panels`, `next-themes`. Nothing else.
31
+ Installed deps you may import directly: `lucide-react` (icons), `radix-ui`, `class-variance-authority`, `clsx`, `tailwind-merge`, `cmdk`, `sonner`, `vaul`, `recharts`, `react-day-picker`, `date-fns`, `input-otp`, `embla-carousel-react`, `react-resizable-panels`, `next-themes`. Nothing else.
32
32
 
33
33
  `npx shadcn@latest add <item>` is only for **blocks** (`dashboard-01`, `login-03`, …) or third-party registries; every core component is already under `ui/`. For lookups use `npx shadcn@latest docs <component> --json`, `view`, `search` — details in the `shadcn` skill.
34
34
 
@@ -35,32 +35,24 @@ const [billing, setBilling] = useState<'monthly' | 'yearly'>('monthly');
35
35
 
36
36
  Simple forms: `<form onSubmit>` with `@/ui/field`, `@/ui/input`, `@/ui/label`,
37
37
  `@/ui/select`, `@/ui/checkbox`, `@/ui/textarea`, `@/ui/button`. Validated
38
- forms: `react-hook-form` + `zod` through `@/ui/form`:
38
+ forms: native form state (or your own) presented with `@/ui/field`:
39
39
 
40
40
  ```tsx
41
- import { zodResolver } from '@hookform/resolvers/zod';
42
- import { useForm } from 'react-hook-form';
43
- import { z } from 'zod';
44
41
  import { Button } from '@/ui/button';
45
- import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/ui/form';
42
+ import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from '@/ui/field';
46
43
  import { Input } from '@/ui/input';
47
44
 
48
- const schema = z.object({ email: z.string().email('Enter a valid work email') });
49
-
50
- const form = useForm<z.infer<typeof schema>>({ resolver: zodResolver(schema), defaultValues: { email: '' } });
51
-
52
- <Form {...form}>
53
- <form onSubmit={form.handleSubmit((values) => setSubmitted(values))} className="flex flex-col gap-4">
54
- <FormField control={form.control} name="email" render={({ field }) => (
55
- <FormItem>
56
- <FormLabel>Work email</FormLabel>
57
- <FormControl><Input type="email" autoComplete="email" {...field} /></FormControl>
58
- <FormMessage />
59
- </FormItem>
60
- )} />
61
- <Button type="submit">Join the waitlist</Button>
62
- </form>
63
- </Form>
45
+ <form onSubmit={handleSubmit}>
46
+ <FieldGroup>
47
+ <Field>
48
+ <FieldLabel htmlFor="email">Work email</FieldLabel>
49
+ <Input id="email" name="email" type="email" required />
50
+ <FieldDescription>We never share it.</FieldDescription>
51
+ {error && <FieldError>{error}</FieldError>}
52
+ </Field>
53
+ <Button type="submit">Create account</Button>
54
+ </FieldGroup>
55
+ </form>
64
56
  ```
65
57
 
66
58
  - Every input has a label (`<Label htmlFor>` or `<FormLabel>`), a `name`, and
@@ -31,7 +31,8 @@ Then open `http://localhost:5173`, edit `pages/getting-started/index.tsx`, or cr
31
31
  | `npm run export` | Build pages into `export/<id>/`, one deployable folder per page. |
32
32
  | `npm run preview` | Preview the built workspace locally. |
33
33
  | `npm run sync:skills` | Sync the bundled agent skills into the workspace. |
34
- | `npm run update` | Update `@autono/open-pages` and sync skills. |
34
+ | `npx open-pages sync:ui` | Update `ui/`, `lib/`, and `hooks/` to the installed runtime's set; files you edited are kept (`--force` to overwrite). |
35
+ | `npm run update` | Update `@autono/open-pages` and sync skills and UI. |
35
36
 
36
37
  ## Authoring a page
37
38
 
@@ -1,9 +1,25 @@
1
1
  {
2
2
  "$schema": "https://ui.shadcn.com/schema.json",
3
- "style": "new-york",
3
+ "style": "radix-nova",
4
4
  "rsc": false,
5
5
  "tsx": true,
6
- "tailwind": { "config": "", "css": "styles/globals.css", "baseColor": "neutral", "cssVariables": true, "prefix": "" },
6
+ "tailwind": {
7
+ "config": "",
8
+ "css": "styles/globals.css",
9
+ "baseColor": "neutral",
10
+ "cssVariables": true,
11
+ "prefix": ""
12
+ },
7
13
  "iconLibrary": "lucide",
8
- "aliases": { "components": "@/components", "ui": "@/ui", "lib": "@/lib", "utils": "@/lib/utils", "hooks": "@/hooks" }
14
+ "rtl": false,
15
+ "aliases": {
16
+ "components": "@/components",
17
+ "utils": "@/lib/utils",
18
+ "ui": "@/ui",
19
+ "lib": "@/lib",
20
+ "hooks": "@/hooks"
21
+ },
22
+ "menuColor": "default",
23
+ "menuAccent": "subtle",
24
+ "registries": {}
9
25
  }
@@ -16,7 +16,7 @@
16
16
  "react": "^19.2.0",
17
17
  "react-dom": "^19.2.0",
18
18
  "@base-ui/react": "^1.7.0",
19
- "@hookform/resolvers": "^5.9.1",
19
+ "@fontsource-variable/geist": "^5.3.0",
20
20
  "@shadcn/react": "^0.3.0",
21
21
  "class-variance-authority": "^0.7.1",
22
22
  "clsx": "^2.1.1",
@@ -24,23 +24,23 @@
24
24
  "date-fns": "^4.4.0",
25
25
  "embla-carousel-react": "^8.6.0",
26
26
  "input-otp": "^1.5.0",
27
- "lucide-react": "^1.35.0",
27
+ "lucide-react": "^1.37.0",
28
28
  "next-themes": "^0.4.6",
29
29
  "radix-ui": "^1.6.7",
30
30
  "react-day-picker": "^10.0.1",
31
- "react-hook-form": "^7.86.0",
32
31
  "react-resizable-panels": "^4.12.3",
33
- "recharts": "3.8.0",
32
+ "recharts": "^3.8.0",
33
+ "shadcn": "^4.19.0",
34
34
  "sonner": "^2.0.8",
35
35
  "tailwind-merge": "^3.6.0",
36
- "vaul": "^1.1.2",
37
- "zod": "^4.5.1"
36
+ "tw-animate-css": "^1.4.0",
37
+ "vaul": "^1.1.2"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/react": "^19.2.0",
41
41
  "@types/react-dom": "^19.2.0",
42
42
  "vite": "^6.4.3",
43
- "tailwindcss": "^4.2.2",
43
+ "tailwindcss": "^4.3.3",
44
44
  "typescript": "^5.9.3"
45
45
  }
46
46
  }
@@ -1,4 +1,7 @@
1
1
  @import "tailwindcss" source(none);
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+ @import "@fontsource-variable/geist";
2
5
  @source "../pages";
3
6
  @source "../ui";
4
7
  @source "../lib";
@@ -7,15 +10,50 @@
7
10
 
8
11
  @custom-variant dark (&:is(.dark *));
9
12
 
13
+ @theme inline {
14
+ --font-heading: var(--font-sans);
15
+ --font-sans: 'Geist Variable', sans-serif;
16
+ --color-sidebar-ring: var(--sidebar-ring);
17
+ --color-sidebar-border: var(--sidebar-border);
18
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
19
+ --color-sidebar-accent: var(--sidebar-accent);
20
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
21
+ --color-sidebar-primary: var(--sidebar-primary);
22
+ --color-sidebar-foreground: var(--sidebar-foreground);
23
+ --color-sidebar: var(--sidebar);
24
+ --color-chart-5: var(--chart-5);
25
+ --color-chart-4: var(--chart-4);
26
+ --color-chart-3: var(--chart-3);
27
+ --color-chart-2: var(--chart-2);
28
+ --color-chart-1: var(--chart-1);
29
+ --color-ring: var(--ring);
30
+ --color-input: var(--input);
31
+ --color-border: var(--border);
32
+ --color-destructive: var(--destructive);
33
+ --color-accent-foreground: var(--accent-foreground);
34
+ --color-accent: var(--accent);
35
+ --color-muted-foreground: var(--muted-foreground);
36
+ --color-muted: var(--muted);
37
+ --color-secondary-foreground: var(--secondary-foreground);
38
+ --color-secondary: var(--secondary);
39
+ --color-primary-foreground: var(--primary-foreground);
40
+ --color-primary: var(--primary);
41
+ --color-popover-foreground: var(--popover-foreground);
42
+ --color-popover: var(--popover);
43
+ --color-card-foreground: var(--card-foreground);
44
+ --color-card: var(--card);
45
+ --color-foreground: var(--foreground);
46
+ --color-background: var(--background);
47
+ --radius-sm: calc(var(--radius) * 0.6);
48
+ --radius-md: calc(var(--radius) * 0.8);
49
+ --radius-lg: var(--radius);
50
+ --radius-xl: calc(var(--radius) * 1.4);
51
+ --radius-2xl: calc(var(--radius) * 1.8);
52
+ --radius-3xl: calc(var(--radius) * 2.2);
53
+ --radius-4xl: calc(var(--radius) * 2.6);
54
+ }
55
+
10
56
  :root {
11
- --sidebar: oklch(0.985 0 0);
12
- --sidebar-foreground: oklch(0.145 0 0);
13
- --sidebar-primary: oklch(0.205 0 0);
14
- --sidebar-primary-foreground: oklch(0.985 0 0);
15
- --sidebar-accent: oklch(0.97 0 0);
16
- --sidebar-accent-foreground: oklch(0.205 0 0);
17
- --sidebar-border: oklch(0.922 0 0);
18
- --sidebar-ring: oklch(0.708 0 0);
19
57
  --background: oklch(1 0 0);
20
58
  --foreground: oklch(0.145 0 0);
21
59
  --card: oklch(1 0 0);
@@ -34,23 +72,23 @@
34
72
  --border: oklch(0.922 0 0);
35
73
  --input: oklch(0.922 0 0);
36
74
  --ring: oklch(0.708 0 0);
37
- --chart-1: oklch(0.646 0.222 41.116);
38
- --chart-2: oklch(0.6 0.118 184.704);
39
- --chart-3: oklch(0.398 0.07 227.392);
40
- --chart-4: oklch(0.828 0.189 84.429);
41
- --chart-5: oklch(0.769 0.188 70.08);
75
+ --chart-1: oklch(0.87 0 0);
76
+ --chart-2: oklch(0.556 0 0);
77
+ --chart-3: oklch(0.439 0 0);
78
+ --chart-4: oklch(0.371 0 0);
79
+ --chart-5: oklch(0.269 0 0);
42
80
  --radius: 0.625rem;
81
+ --sidebar: oklch(0.985 0 0);
82
+ --sidebar-foreground: oklch(0.145 0 0);
83
+ --sidebar-primary: oklch(0.205 0 0);
84
+ --sidebar-primary-foreground: oklch(0.985 0 0);
85
+ --sidebar-accent: oklch(0.97 0 0);
86
+ --sidebar-accent-foreground: oklch(0.205 0 0);
87
+ --sidebar-border: oklch(0.922 0 0);
88
+ --sidebar-ring: oklch(0.708 0 0);
43
89
  }
44
90
 
45
91
  .dark {
46
- --sidebar: oklch(0.205 0 0);
47
- --sidebar-foreground: oklch(0.985 0 0);
48
- --sidebar-primary: oklch(0.488 0.243 264.376);
49
- --sidebar-primary-foreground: oklch(0.985 0 0);
50
- --sidebar-accent: oklch(0.269 0 0);
51
- --sidebar-accent-foreground: oklch(0.985 0 0);
52
- --sidebar-border: oklch(1 0 0 / 10%);
53
- --sidebar-ring: oklch(0.556 0 0);
54
92
  --background: oklch(0.145 0 0);
55
93
  --foreground: oklch(0.985 0 0);
56
94
  --card: oklch(0.205 0 0);
@@ -69,50 +107,29 @@
69
107
  --border: oklch(1 0 0 / 10%);
70
108
  --input: oklch(1 0 0 / 15%);
71
109
  --ring: oklch(0.556 0 0);
72
- --chart-1: oklch(0.488 0.243 264.376);
73
- --chart-2: oklch(0.696 0.17 162.48);
74
- --chart-3: oklch(0.769 0.188 70.08);
75
- --chart-4: oklch(0.627 0.265 303.9);
76
- --chart-5: oklch(0.645 0.246 16.439);
110
+ --chart-1: oklch(0.87 0 0);
111
+ --chart-2: oklch(0.556 0 0);
112
+ --chart-3: oklch(0.439 0 0);
113
+ --chart-4: oklch(0.371 0 0);
114
+ --chart-5: oklch(0.269 0 0);
115
+ --sidebar: oklch(0.205 0 0);
116
+ --sidebar-foreground: oklch(0.985 0 0);
117
+ --sidebar-primary: oklch(0.488 0.243 264.376);
118
+ --sidebar-primary-foreground: oklch(0.985 0 0);
119
+ --sidebar-accent: oklch(0.269 0 0);
120
+ --sidebar-accent-foreground: oklch(0.985 0 0);
121
+ --sidebar-border: oklch(1 0 0 / 10%);
122
+ --sidebar-ring: oklch(0.556 0 0);
77
123
  }
78
124
 
79
- @theme inline {
80
- --color-sidebar: var(--sidebar);
81
- --color-sidebar-foreground: var(--sidebar-foreground);
82
- --color-sidebar-primary: var(--sidebar-primary);
83
- --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
84
- --color-sidebar-accent: var(--sidebar-accent);
85
- --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
86
- --color-sidebar-border: var(--sidebar-border);
87
- --color-sidebar-ring: var(--sidebar-ring);
88
- --color-chart-5: var(--chart-5);
89
- --color-chart-4: var(--chart-4);
90
- --color-chart-3: var(--chart-3);
91
- --color-chart-2: var(--chart-2);
92
- --color-chart-1: var(--chart-1);
93
- --color-ring: var(--ring);
94
- --color-input: var(--input);
95
- --color-border: var(--border);
96
- --color-destructive: var(--destructive);
97
- --color-accent-foreground: var(--accent-foreground);
98
- --color-accent: var(--accent);
99
- --color-muted-foreground: var(--muted-foreground);
100
- --color-muted: var(--muted);
101
- --color-secondary-foreground: var(--secondary-foreground);
102
- --color-secondary: var(--secondary);
103
- --color-primary-foreground: var(--primary-foreground);
104
- --color-primary: var(--primary);
105
- --color-popover-foreground: var(--popover-foreground);
106
- --color-popover: var(--popover);
107
- --color-card-foreground: var(--card-foreground);
108
- --color-card: var(--card);
109
- --color-foreground: var(--foreground);
110
- --color-background: var(--background);
111
- --radius-sm: calc(var(--radius) * 0.6);
112
- --radius-md: calc(var(--radius) * 0.8);
113
- --radius-lg: var(--radius);
114
- --radius-xl: calc(var(--radius) * 1.4);
115
- --radius-2xl: calc(var(--radius) * 1.8);
116
- --radius-3xl: calc(var(--radius) * 2.2);
117
- --radius-4xl: calc(var(--radius) * 2.6);
118
- }
125
+ @layer base {
126
+ * {
127
+ @apply border-border outline-ring/50;
128
+ }
129
+ body {
130
+ @apply bg-background text-foreground;
131
+ }
132
+ html {
133
+ @apply font-sans;
134
+ }
135
+ }
@@ -1,13 +1,20 @@
1
1
  import * as React from "react"
2
- import { ChevronDownIcon } from "lucide-react"
3
2
  import { Accordion as AccordionPrimitive } from "radix-ui"
4
3
 
5
4
  import { cn } from "@/lib/utils"
5
+ import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
6
6
 
7
7
  function Accordion({
8
+ className,
8
9
  ...props
9
10
  }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
10
- return <AccordionPrimitive.Root data-slot="accordion" {...props} />
11
+ return (
12
+ <AccordionPrimitive.Root
13
+ data-slot="accordion"
14
+ className={cn("flex w-full flex-col", className)}
15
+ {...props}
16
+ />
17
+ )
11
18
  }
12
19
 
13
20
  function AccordionItem({
@@ -17,7 +24,7 @@ function AccordionItem({
17
24
  return (
18
25
  <AccordionPrimitive.Item
19
26
  data-slot="accordion-item"
20
- className={cn("border-b last:border-b-0", className)}
27
+ className={cn("not-last:border-b", className)}
21
28
  {...props}
22
29
  />
23
30
  )
@@ -33,13 +40,14 @@ function AccordionTrigger({
33
40
  <AccordionPrimitive.Trigger
34
41
  data-slot="accordion-trigger"
35
42
  className={cn(
36
- "flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180",
43
+ "group/accordion-trigger relative flex flex-1 items-start justify-between rounded-lg border border-transparent py-2.5 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:after:border-ring disabled:pointer-events-none disabled:opacity-50 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 **:data-[slot=accordion-trigger-icon]:text-muted-foreground",
37
44
  className
38
45
  )}
39
46
  {...props}
40
47
  >
41
48
  {children}
42
- <ChevronDownIcon className="pointer-events-none size-4 shrink-0 translate-y-0.5 text-muted-foreground transition-transform duration-200" />
49
+ <ChevronDownIcon data-slot="accordion-trigger-icon" className="pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
50
+ <ChevronUpIcon data-slot="accordion-trigger-icon" className="pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
43
51
  </AccordionPrimitive.Trigger>
44
52
  </AccordionPrimitive.Header>
45
53
  )
@@ -53,10 +61,17 @@ function AccordionContent({
53
61
  return (
54
62
  <AccordionPrimitive.Content
55
63
  data-slot="accordion-content"
56
- className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
64
+ className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up"
57
65
  {...props}
58
66
  >
59
- <div className={cn("pt-0 pb-4", className)}>{children}</div>
67
+ <div
68
+ className={cn(
69
+ "h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
70
+ className
71
+ )}
72
+ >
73
+ {children}
74
+ </div>
60
75
  </AccordionPrimitive.Content>
61
76
  )
62
77
  }
@@ -1,5 +1,3 @@
1
- "use client"
2
-
3
1
  import * as React from "react"
4
2
  import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
5
3
 
@@ -36,7 +34,7 @@ function AlertDialogOverlay({
36
34
  <AlertDialogPrimitive.Overlay
37
35
  data-slot="alert-dialog-overlay"
38
36
  className={cn(
39
- "fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
37
+ "fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
40
38
  className
41
39
  )}
42
40
  {...props}
@@ -58,7 +56,7 @@ function AlertDialogContent({
58
56
  data-slot="alert-dialog-content"
59
57
  data-size={size}
60
58
  className={cn(
61
- "group/alert-dialog-content fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[size=sm]:max-w-xs data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[size=default]:sm:max-w-lg",
59
+ "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
62
60
  className
63
61
  )}
64
62
  {...props}
@@ -75,7 +73,7 @@ function AlertDialogHeader({
75
73
  <div
76
74
  data-slot="alert-dialog-header"
77
75
  className={cn(
78
- "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
76
+ "grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
79
77
  className
80
78
  )}
81
79
  {...props}
@@ -91,7 +89,7 @@ function AlertDialogFooter({
91
89
  <div
92
90
  data-slot="alert-dialog-footer"
93
91
  className={cn(
94
- "flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
92
+ "-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
95
93
  className
96
94
  )}
97
95
  {...props}
@@ -99,15 +97,15 @@ function AlertDialogFooter({
99
97
  )
100
98
  }
101
99
 
102
- function AlertDialogTitle({
100
+ function AlertDialogMedia({
103
101
  className,
104
102
  ...props
105
- }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
103
+ }: React.ComponentProps<"div">) {
106
104
  return (
107
- <AlertDialogPrimitive.Title
108
- data-slot="alert-dialog-title"
105
+ <div
106
+ data-slot="alert-dialog-media"
109
107
  className={cn(
110
- "text-lg font-semibold sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
108
+ "mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
111
109
  className
112
110
  )}
113
111
  {...props}
@@ -115,28 +113,31 @@ function AlertDialogTitle({
115
113
  )
116
114
  }
117
115
 
118
- function AlertDialogDescription({
116
+ function AlertDialogTitle({
119
117
  className,
120
118
  ...props
121
- }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
119
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
122
120
  return (
123
- <AlertDialogPrimitive.Description
124
- data-slot="alert-dialog-description"
125
- className={cn("text-sm text-muted-foreground", className)}
121
+ <AlertDialogPrimitive.Title
122
+ data-slot="alert-dialog-title"
123
+ className={cn(
124
+ "font-heading text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
125
+ className
126
+ )}
126
127
  {...props}
127
128
  />
128
129
  )
129
130
  }
130
131
 
131
- function AlertDialogMedia({
132
+ function AlertDialogDescription({
132
133
  className,
133
134
  ...props
134
- }: React.ComponentProps<"div">) {
135
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
135
136
  return (
136
- <div
137
- data-slot="alert-dialog-media"
137
+ <AlertDialogPrimitive.Description
138
+ data-slot="alert-dialog-description"
138
139
  className={cn(
139
- "mb-2 inline-flex size-16 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
140
+ "text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
140
141
  className
141
142
  )}
142
143
  {...props}
@@ -4,13 +4,13 @@ import { cva, type VariantProps } from "class-variance-authority"
4
4
  import { cn } from "@/lib/utils"
5
5
 
6
6
  const alertVariants = cva(
7
- "relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
7
+ "group/alert relative grid w-full gap-0.5 rounded-lg border px-2.5 py-2 text-left text-sm has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0.5 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4",
8
8
  {
9
9
  variants: {
10
10
  variant: {
11
11
  default: "bg-card text-card-foreground",
12
12
  destructive:
13
- "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 [&>svg]:text-current",
13
+ "bg-card text-destructive *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
14
14
  },
15
15
  },
16
16
  defaultVariants: {
@@ -39,7 +39,7 @@ function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
39
39
  <div
40
40
  data-slot="alert-title"
41
41
  className={cn(
42
- "col-start-2 line-clamp-1 min-h-4 font-medium tracking-tight",
42
+ "font-medium group-has-[>svg]/alert:col-start-2 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground",
43
43
  className
44
44
  )}
45
45
  {...props}
@@ -55,7 +55,7 @@ function AlertDescription({
55
55
  <div
56
56
  data-slot="alert-description"
57
57
  className={cn(
58
- "col-start-2 grid justify-items-start gap-1 text-sm text-muted-foreground [&_p]:leading-relaxed",
58
+ "text-sm text-balance text-muted-foreground md:text-pretty [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
59
59
  className
60
60
  )}
61
61
  {...props}
@@ -63,4 +63,14 @@ function AlertDescription({
63
63
  )
64
64
  }
65
65
 
66
- export { Alert, AlertTitle, AlertDescription }
66
+ function AlertAction({ className, ...props }: React.ComponentProps<"div">) {
67
+ return (
68
+ <div
69
+ data-slot="alert-action"
70
+ className={cn("absolute top-2 right-2", className)}
71
+ {...props}
72
+ />
73
+ )
74
+ }
75
+
76
+ export { Alert, AlertTitle, AlertDescription, AlertAction }