create-reactivite 1.6.0 → 1.7.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.
@@ -0,0 +1,58 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+
3
+ import { PricingCard } from "./pricing-card";
4
+
5
+ const meta = {
6
+ title: "UI Library/PricingCard",
7
+ component: PricingCard,
8
+ tags: ["autodocs"],
9
+ } satisfies Meta<typeof PricingCard>;
10
+
11
+ export default meta;
12
+ type Story = StoryObj<typeof meta>;
13
+
14
+ export const Default: Story = {
15
+ args: {
16
+ plan: "Pro",
17
+ price: "$29",
18
+ description: "For growing teams that need more power.",
19
+ features: ["Unlimited projects", "Priority support", "Advanced analytics", "Custom domains"],
20
+ featured: false,
21
+ },
22
+ };
23
+
24
+ export const Featured: Story = {
25
+ args: {
26
+ ...Default.args,
27
+ featured: true,
28
+ },
29
+ };
30
+
31
+ export const Tiers: Story = {
32
+ args: { plan: "Pro", price: "$29", features: [] },
33
+ render: () => (
34
+ <div className="grid w-full max-w-4xl grid-cols-1 gap-6 sm:grid-cols-3">
35
+ <PricingCard
36
+ plan="Starter"
37
+ price="$0"
38
+ description="For side projects."
39
+ features={["1 project", "Community support", "1 GB storage"]}
40
+ cta="Start free"
41
+ />
42
+ <PricingCard
43
+ plan="Pro"
44
+ price="$29"
45
+ description="For growing teams."
46
+ features={["Unlimited projects", "Priority support", "Analytics"]}
47
+ featured
48
+ />
49
+ <PricingCard
50
+ plan="Enterprise"
51
+ price="$99"
52
+ description="For large orgs."
53
+ features={["SSO + SAML", "Dedicated support", "SLA", "Audit logs"]}
54
+ cta="Contact sales"
55
+ />
56
+ </div>
57
+ ),
58
+ };
@@ -0,0 +1,86 @@
1
+ import * as React from "react"
2
+ import { Check } from "lucide-react"
3
+
4
+ import { cn } from "@/lib/utils"
5
+ import { Badge } from "@/components/ui/badge"
6
+ import { Button } from "@/components/ui/button"
7
+ import {
8
+ Card,
9
+ CardContent,
10
+ CardDescription,
11
+ CardFooter,
12
+ CardHeader,
13
+ CardTitle,
14
+ } from "@/components/ui/card"
15
+
16
+ export interface PricingCardProps extends React.ComponentProps<typeof Card> {
17
+ plan: string
18
+ price: string
19
+ period?: string
20
+ description?: string
21
+ features: string[]
22
+ cta?: string
23
+ /** Highlights the card with a ring + badge — the "recommended" plan. */
24
+ featured?: boolean
25
+ onSelect?: () => void
26
+ }
27
+
28
+ /**
29
+ * PricingCard — pricing tier composed from Card + Badge + Button + check list.
30
+ */
31
+ export function PricingCard({
32
+ plan,
33
+ price,
34
+ period = "/mo",
35
+ description,
36
+ features,
37
+ cta = "Get started",
38
+ featured = false,
39
+ onSelect,
40
+ className,
41
+ ...props
42
+ }: PricingCardProps) {
43
+ return (
44
+ <Card
45
+ className={cn(
46
+ "relative gap-6",
47
+ featured && "ring-primary ring-2",
48
+ className
49
+ )}
50
+ {...props}
51
+ >
52
+ {featured && (
53
+ <Badge className="absolute -top-3 left-6">Most popular</Badge>
54
+ )}
55
+ <CardHeader>
56
+ <CardDescription>{plan}</CardDescription>
57
+ <CardTitle className="flex items-baseline gap-1">
58
+ <span className="text-4xl font-bold tracking-tight">{price}</span>
59
+ <span className="text-muted-foreground text-sm font-normal">
60
+ {period}
61
+ </span>
62
+ </CardTitle>
63
+ {description && <CardDescription>{description}</CardDescription>}
64
+ </CardHeader>
65
+ <CardContent>
66
+ <ul className="space-y-2.5 text-sm">
67
+ {features.map((f) => (
68
+ <li key={f} className="flex items-center gap-2">
69
+ <Check className="text-primary size-4 shrink-0" />
70
+ <span>{f}</span>
71
+ </li>
72
+ ))}
73
+ </ul>
74
+ </CardContent>
75
+ <CardFooter>
76
+ <Button
77
+ className="w-full"
78
+ variant={featured ? "default" : "outline"}
79
+ onClick={onSelect}
80
+ >
81
+ {cta}
82
+ </Button>
83
+ </CardFooter>
84
+ </Card>
85
+ )
86
+ }
@@ -0,0 +1,55 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { DollarSign, Users, Activity } from "lucide-react";
3
+
4
+ import { StatCard } from "./stat-card";
5
+
6
+ const meta = {
7
+ title: "UI Library/StatCard",
8
+ component: StatCard,
9
+ tags: ["autodocs"],
10
+ argTypes: {
11
+ delta: { control: { type: "number" } },
12
+ },
13
+ } satisfies Meta<typeof StatCard>;
14
+
15
+ export default meta;
16
+ type Story = StoryObj<typeof meta>;
17
+
18
+ export const Default: Story = {
19
+ args: {
20
+ label: "Total revenue",
21
+ value: "$48,120",
22
+ delta: 12.4,
23
+ hint: "vs last month",
24
+ icon: DollarSign,
25
+ },
26
+ };
27
+
28
+ export const Negative: Story = {
29
+ args: {
30
+ label: "Churn rate",
31
+ value: "3.8%",
32
+ delta: -2.1,
33
+ hint: "vs last month",
34
+ icon: Activity,
35
+ },
36
+ };
37
+
38
+ export const NoDelta: Story = {
39
+ args: {
40
+ label: "Active users",
41
+ value: "12,840",
42
+ icon: Users,
43
+ },
44
+ };
45
+
46
+ export const Grid: Story = {
47
+ args: { label: "Total revenue", value: "$48,120", delta: 12.4, icon: DollarSign },
48
+ render: () => (
49
+ <div className="grid w-full max-w-3xl grid-cols-1 gap-4 sm:grid-cols-3">
50
+ <StatCard label="Revenue" value="$48,120" delta={12.4} icon={DollarSign} />
51
+ <StatCard label="Users" value="12,840" delta={4.2} icon={Users} />
52
+ <StatCard label="Churn" value="3.8%" delta={-2.1} icon={Activity} />
53
+ </div>
54
+ ),
55
+ };
@@ -0,0 +1,73 @@
1
+ import * as React from "react"
2
+ import { type LucideIcon, TrendingDown, TrendingUp } from "lucide-react"
3
+
4
+ import { cn } from "@/lib/utils"
5
+ import { Badge } from "@/components/ui/badge"
6
+ import {
7
+ Card,
8
+ CardAction,
9
+ CardContent,
10
+ CardDescription,
11
+ CardHeader,
12
+ CardTitle,
13
+ } from "@/components/ui/card"
14
+
15
+ export interface StatCardProps extends React.ComponentProps<typeof Card> {
16
+ /** Metric label, e.g. "Total revenue". */
17
+ label: string
18
+ /** Big value, e.g. "$48,120". */
19
+ value: React.ReactNode
20
+ /** Optional period / context line under the value. */
21
+ hint?: string
22
+ /** Signed percentage change. Positive renders up + accent, negative down + destructive. */
23
+ delta?: number
24
+ /** Icon shown top-right. */
25
+ icon?: LucideIcon
26
+ }
27
+
28
+ /**
29
+ * StatCard — a dashboard KPI tile composed from Card + Badge.
30
+ * Purely var-driven, so it adopts whatever `data-theme` is active.
31
+ */
32
+ export function StatCard({
33
+ label,
34
+ value,
35
+ hint,
36
+ delta,
37
+ icon: Icon,
38
+ className,
39
+ ...props
40
+ }: StatCardProps) {
41
+ const up = (delta ?? 0) >= 0
42
+ return (
43
+ <Card className={cn("gap-3", className)} {...props}>
44
+ <CardHeader>
45
+ <CardDescription>{label}</CardDescription>
46
+ <CardTitle className="text-3xl font-semibold tabular-nums tracking-tight">
47
+ {value}
48
+ </CardTitle>
49
+ {Icon ? (
50
+ <CardAction>
51
+ <span className="bg-muted text-muted-foreground flex size-9 items-center justify-center rounded-md">
52
+ <Icon className="size-4" />
53
+ </span>
54
+ </CardAction>
55
+ ) : null}
56
+ </CardHeader>
57
+ {(delta !== undefined || hint) && (
58
+ <CardContent className="flex items-center gap-2">
59
+ {delta !== undefined && (
60
+ <Badge variant={up ? "default" : "destructive"}>
61
+ {up ? <TrendingUp /> : <TrendingDown />}
62
+ {up ? "+" : ""}
63
+ {delta}%
64
+ </Badge>
65
+ )}
66
+ {hint && (
67
+ <span className="text-muted-foreground text-xs">{hint}</span>
68
+ )}
69
+ </CardContent>
70
+ )}
71
+ </Card>
72
+ )
73
+ }
@@ -0,0 +1,68 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { DollarSign, Zap } from "lucide-react";
3
+
4
+ import { StatCard } from "./stat-card";
5
+ import { FeatureCard } from "./feature-card";
6
+ import { PricingCard } from "./pricing-card";
7
+ import { Button } from "@/components/ui/button";
8
+ import { Badge } from "@/components/ui/badge";
9
+
10
+ const THEMES = ["minimal", "brutalist", "aurora"] as const;
11
+
12
+ /** A compact sampler rendered once per theme, side by side. */
13
+ function Sampler() {
14
+ return (
15
+ <div className="grid w-72 gap-4">
16
+ <div className="flex flex-wrap items-center gap-2">
17
+ <Button size="sm">Primary</Button>
18
+ <Button size="sm" variant="outline">
19
+ Outline
20
+ </Button>
21
+ <Badge>New</Badge>
22
+ </div>
23
+ <StatCard label="Revenue" value="$48,120" delta={12.4} icon={DollarSign} />
24
+ <FeatureCard
25
+ icon={Zap}
26
+ title="Themeable"
27
+ description="One attribute re-skins everything."
28
+ />
29
+ <PricingCard
30
+ plan="Pro"
31
+ price="$29"
32
+ features={["Unlimited", "Support", "Analytics"]}
33
+ featured
34
+ />
35
+ </div>
36
+ );
37
+ }
38
+
39
+ const meta = {
40
+ title: "UI Library/Themes",
41
+ parameters: { layout: "fullscreen" },
42
+ } satisfies Meta;
43
+
44
+ export default meta;
45
+ type Story = StoryObj<typeof meta>;
46
+
47
+ /**
48
+ * All three themes at once. Each column hard-sets its own `data-theme`,
49
+ * overriding the toolbar selection — this is the "3 variants" overview.
50
+ */
51
+ export const AllThemes: Story = {
52
+ render: () => (
53
+ <div className="flex flex-wrap gap-6 p-2">
54
+ {THEMES.map((t) => (
55
+ <div
56
+ key={t}
57
+ data-theme={t}
58
+ className="bg-background text-foreground rounded-xl border p-5"
59
+ >
60
+ <p className="text-muted-foreground mb-4 text-xs font-medium tracking-wide uppercase">
61
+ {t}
62
+ </p>
63
+ <Sampler />
64
+ </div>
65
+ ))}
66
+ </div>
67
+ ),
68
+ };
@@ -74,6 +74,128 @@
74
74
  --sidebar-ring: oklch(0.439 0 0);
75
75
  }
76
76
 
77
+ /* ──────────────────────────────────────────────────────────────
78
+ UI LIBRARY THEMES
79
+ 3 distinct, var-driven looks. Switch by setting `data-theme` on
80
+ <html>: "minimal" | "brutalist" | "aurora". Every shadcn + ui-lib
81
+ component reads these vars, so themes need ZERO component changes.
82
+ Default theme is set in index.html. The base :root above stays the
83
+ neutral-dark fallback when no data-theme is present.
84
+ ────────────────────────────────────────────────────────────── */
85
+
86
+ /* Theme 1 — MINIMAL: light, calm neutrals, soft radius, editorial. */
87
+ [data-theme="minimal"] {
88
+ --background: oklch(1 0 0);
89
+ --foreground: oklch(0.18 0 0);
90
+ --card: oklch(1 0 0);
91
+ --card-foreground: oklch(0.18 0 0);
92
+ --popover: oklch(1 0 0);
93
+ --popover-foreground: oklch(0.18 0 0);
94
+ --primary: oklch(0.21 0 0);
95
+ --primary-foreground: oklch(0.98 0 0);
96
+ --secondary: oklch(0.97 0 0);
97
+ --secondary-foreground: oklch(0.21 0 0);
98
+ --muted: oklch(0.97 0 0);
99
+ --muted-foreground: oklch(0.52 0 0);
100
+ --accent: oklch(0.96 0 0);
101
+ --accent-foreground: oklch(0.21 0 0);
102
+ --destructive: oklch(0.577 0.245 27.325);
103
+ --destructive-foreground: oklch(0.98 0 0);
104
+ --border: oklch(0.92 0 0);
105
+ --input: oklch(0.92 0 0);
106
+ --ring: oklch(0.7 0 0);
107
+ --radius: 0.625rem;
108
+ --sidebar: oklch(0.985 0 0);
109
+ --sidebar-foreground: oklch(0.18 0 0);
110
+ --sidebar-primary: oklch(0.21 0 0);
111
+ --sidebar-primary-foreground: oklch(0.985 0 0);
112
+ --sidebar-accent: oklch(0.96 0 0);
113
+ --sidebar-accent-foreground: oklch(0.21 0 0);
114
+ --sidebar-border: oklch(0.92 0 0);
115
+ --sidebar-ring: oklch(0.7 0 0);
116
+ --chart-1: oklch(0.646 0.222 41.116);
117
+ --chart-2: oklch(0.6 0.118 184.704);
118
+ --chart-3: oklch(0.398 0.07 227.392);
119
+ --chart-4: oklch(0.828 0.189 84.429);
120
+ --chart-5: oklch(0.769 0.188 70.08);
121
+ }
122
+
123
+ /* Theme 2 — BRUTALIST: stark black/white, acid-yellow accent, 0 radius,
124
+ pure-black borders. High-contrast, raw, intentionally loud. */
125
+ [data-theme="brutalist"] {
126
+ --background: oklch(0.98 0 0);
127
+ --foreground: oklch(0 0 0);
128
+ --card: oklch(1 0 0);
129
+ --card-foreground: oklch(0 0 0);
130
+ --popover: oklch(1 0 0);
131
+ --popover-foreground: oklch(0 0 0);
132
+ --primary: oklch(0 0 0);
133
+ --primary-foreground: oklch(1 0 0);
134
+ --secondary: oklch(0.93 0.19 102);
135
+ --secondary-foreground: oklch(0 0 0);
136
+ --muted: oklch(0.95 0 0);
137
+ --muted-foreground: oklch(0.35 0 0);
138
+ --accent: oklch(0.93 0.19 102);
139
+ --accent-foreground: oklch(0 0 0);
140
+ --destructive: oklch(0.55 0.24 27);
141
+ --destructive-foreground: oklch(1 0 0);
142
+ --border: oklch(0 0 0);
143
+ --input: oklch(0 0 0);
144
+ --ring: oklch(0 0 0);
145
+ --radius: 0rem;
146
+ --sidebar: oklch(1 0 0);
147
+ --sidebar-foreground: oklch(0 0 0);
148
+ --sidebar-primary: oklch(0 0 0);
149
+ --sidebar-primary-foreground: oklch(1 0 0);
150
+ --sidebar-accent: oklch(0.93 0.19 102);
151
+ --sidebar-accent-foreground: oklch(0 0 0);
152
+ --sidebar-border: oklch(0 0 0);
153
+ --sidebar-ring: oklch(0 0 0);
154
+ --chart-1: oklch(0 0 0);
155
+ --chart-2: oklch(0.93 0.19 102);
156
+ --chart-3: oklch(0.55 0.24 27);
157
+ --chart-4: oklch(0.5 0 0);
158
+ --chart-5: oklch(0.75 0 0);
159
+ }
160
+
161
+ /* Theme 3 — AURORA: deep violet dark, neon violet/cyan accents,
162
+ generous radius. Glowy, premium, night-mode product feel. */
163
+ [data-theme="aurora"] {
164
+ --background: oklch(0.16 0.02 280);
165
+ --foreground: oklch(0.96 0.01 280);
166
+ --card: oklch(0.2 0.03 280);
167
+ --card-foreground: oklch(0.96 0.01 280);
168
+ --popover: oklch(0.2 0.03 280);
169
+ --popover-foreground: oklch(0.96 0.01 280);
170
+ --primary: oklch(0.7 0.18 295);
171
+ --primary-foreground: oklch(0.16 0.02 280);
172
+ --secondary: oklch(0.28 0.04 280);
173
+ --secondary-foreground: oklch(0.96 0.01 280);
174
+ --muted: oklch(0.26 0.03 280);
175
+ --muted-foreground: oklch(0.72 0.03 280);
176
+ --accent: oklch(0.55 0.16 230);
177
+ --accent-foreground: oklch(0.98 0 0);
178
+ --destructive: oklch(0.62 0.24 18);
179
+ --destructive-foreground: oklch(0.98 0 0);
180
+ --border: oklch(0.32 0.04 280);
181
+ --input: oklch(0.3 0.04 280);
182
+ --ring: oklch(0.7 0.18 295);
183
+ --radius: 1rem;
184
+ --sidebar: oklch(0.18 0.025 280);
185
+ --sidebar-foreground: oklch(0.96 0.01 280);
186
+ --sidebar-primary: oklch(0.7 0.18 295);
187
+ --sidebar-primary-foreground: oklch(0.16 0.02 280);
188
+ --sidebar-accent: oklch(0.28 0.04 280);
189
+ --sidebar-accent-foreground: oklch(0.96 0.01 280);
190
+ --sidebar-border: oklch(0.32 0.04 280);
191
+ --sidebar-ring: oklch(0.7 0.18 295);
192
+ --chart-1: oklch(0.7 0.18 295);
193
+ --chart-2: oklch(0.6 0.16 230);
194
+ --chart-3: oklch(0.75 0.17 160);
195
+ --chart-4: oklch(0.7 0.19 20);
196
+ --chart-5: oklch(0.8 0.16 90);
197
+ }
198
+
77
199
  @theme inline {
78
200
  --font-sans: var(--font-geist-sans);
79
201
  --font-mono: var(--font-geist-mono);