@snow-labs/brutal-ui 0.1.1 → 0.2.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.
- package/dist/components/brutal/cta-section.d.ts +7 -2
- package/dist/components/brutal/cta-section.js +136 -32
- package/dist/components/brutal/cta-section.js.map +1 -1
- package/dist/components/brutal/faq.d.ts +16 -0
- package/dist/components/brutal/faq.js +163 -0
- package/dist/components/brutal/faq.js.map +1 -0
- package/dist/components/brutal/feature-grid.d.ts +6 -2
- package/dist/components/brutal/feature-grid.js +73 -10
- package/dist/components/brutal/feature-grid.js.map +1 -1
- package/dist/components/brutal/footer.d.ts +14 -1
- package/dist/components/brutal/footer.js +181 -18
- package/dist/components/brutal/footer.js.map +1 -1
- package/dist/components/brutal/hero.d.ts +9 -2
- package/dist/components/brutal/hero.js +67 -22
- package/dist/components/brutal/hero.js.map +1 -1
- package/dist/components/brutal/index.d.ts +7 -2
- package/dist/components/brutal/index.js +1035 -128
- package/dist/components/brutal/index.js.map +1 -1
- package/dist/components/brutal/integration-grid.d.ts +1 -1
- package/dist/components/brutal/integration-grid.js +13 -5
- package/dist/components/brutal/integration-grid.js.map +1 -1
- package/dist/components/brutal/logo-cloud.d.ts +17 -0
- package/dist/components/brutal/logo-cloud.js +93 -0
- package/dist/components/brutal/logo-cloud.js.map +1 -0
- package/dist/components/brutal/nav.d.ts +3 -1
- package/dist/components/brutal/nav.js +166 -9
- package/dist/components/brutal/nav.js.map +1 -1
- package/dist/components/brutal/newsletter.d.ts +14 -0
- package/dist/components/brutal/newsletter.js +169 -0
- package/dist/components/brutal/newsletter.js.map +1 -0
- package/dist/components/brutal/pricing-table.d.ts +27 -0
- package/dist/components/brutal/pricing-table.js +251 -0
- package/dist/components/brutal/pricing-table.js.map +1 -0
- package/dist/components/brutal/section-divider.d.ts +14 -0
- package/dist/components/brutal/section-divider.js +70 -0
- package/dist/components/brutal/section-divider.js.map +1 -0
- package/dist/components/brutal/section.d.ts +7 -3
- package/dist/components/brutal/section.js +13 -5
- package/dist/components/brutal/section.js.map +1 -1
- package/dist/components/brutal/stats-bar.d.ts +16 -0
- package/dist/components/brutal/stats-bar.js +127 -0
- package/dist/components/brutal/stats-bar.js.map +1 -0
- package/dist/components/brutal/testimonials.d.ts +11 -3
- package/dist/components/brutal/testimonials.js +126 -33
- package/dist/components/brutal/testimonials.js.map +1 -1
- package/dist/components/brutal/wave-divider.d.ts +2 -12
- package/dist/components/brutal/wave-divider.js +54 -26
- package/dist/components/brutal/wave-divider.js.map +1 -1
- package/dist/components/dashboard/activity-feed.d.ts +18 -0
- package/dist/components/dashboard/activity-feed.js +105 -0
- package/dist/components/dashboard/activity-feed.js.map +1 -0
- package/dist/components/dashboard/app-shell.d.ts +19 -0
- package/dist/components/dashboard/app-shell.js +206 -0
- package/dist/components/dashboard/app-shell.js.map +1 -0
- package/dist/components/dashboard/empty-state.d.ts +14 -0
- package/dist/components/dashboard/empty-state.js +96 -0
- package/dist/components/dashboard/empty-state.js.map +1 -0
- package/dist/components/dashboard/file-upload.d.ts +12 -0
- package/dist/components/dashboard/file-upload.js +86 -0
- package/dist/components/dashboard/file-upload.js.map +1 -0
- package/dist/components/dashboard/index.d.ts +10 -0
- package/dist/components/dashboard/index.js +755 -0
- package/dist/components/dashboard/index.js.map +1 -0
- package/dist/components/dashboard/search-bar.d.ts +12 -0
- package/dist/components/dashboard/search-bar.js +49 -0
- package/dist/components/dashboard/search-bar.js.map +1 -0
- package/dist/components/dashboard/sidebar.d.ts +23 -0
- package/dist/components/dashboard/sidebar.js +113 -0
- package/dist/components/dashboard/sidebar.js.map +1 -0
- package/dist/components/dashboard/stat-card.d.ts +13 -0
- package/dist/components/dashboard/stat-card.js +55 -0
- package/dist/components/dashboard/stat-card.js.map +1 -0
- package/dist/components/dashboard/user-menu.d.ts +16 -0
- package/dist/components/dashboard/user-menu.js +179 -0
- package/dist/components/dashboard/user-menu.js.map +1 -0
- package/dist/components/dashboard/view-switcher.d.ts +12 -0
- package/dist/components/dashboard/view-switcher.js +130 -0
- package/dist/components/dashboard/view-switcher.js.map +1 -0
- package/dist/components/views/calendar-view.d.ts +17 -0
- package/dist/components/views/calendar-view.js +182 -0
- package/dist/components/views/calendar-view.js.map +1 -0
- package/dist/components/views/data-table.d.ts +15 -0
- package/dist/components/views/data-table.js +204 -0
- package/dist/components/views/data-table.js.map +1 -0
- package/dist/components/views/grid-view.d.ts +11 -0
- package/dist/components/views/grid-view.js +31 -0
- package/dist/components/views/grid-view.js.map +1 -0
- package/dist/components/views/index.d.ts +7 -0
- package/dist/components/views/index.js +542 -0
- package/dist/components/views/index.js.map +1 -0
- package/dist/components/views/kanban-board.d.ts +21 -0
- package/dist/components/views/kanban-board.js +153 -0
- package/dist/components/views/kanban-board.js.map +1 -0
- package/dist/components/views/list-view.d.ts +19 -0
- package/dist/components/views/list-view.js +96 -0
- package/dist/components/views/list-view.js.map +1 -0
- package/dist/index.d.ts +27 -3
- package/dist/index.js +1881 -142
- package/dist/index.js.map +1 -1
- package/dist/lib/animations.d.ts +68 -0
- package/dist/lib/animations.js +44 -0
- package/dist/lib/animations.js.map +1 -0
- package/dist/templates/dashboard.d.ts +40 -0
- package/dist/templates/dashboard.js +658 -0
- package/dist/templates/dashboard.js.map +1 -0
- package/dist/templates/index.d.ts +4 -0
- package/dist/templates/index.js +2001 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/saas-launch.d.ts +113 -0
- package/dist/templates/saas-launch.js +1394 -0
- package/dist/templates/saas-launch.js.map +1 -0
- package/dist/templates/studio.d.ts +72 -0
- package/dist/templates/studio.js +1099 -0
- package/dist/templates/studio.js.map +1 -0
- package/dist/theme.css +58 -15
- package/package.json +48 -2
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { Button as Button$1 } from '@base-ui/react/button';
|
|
5
|
+
import { cva } from 'class-variance-authority';
|
|
6
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
7
|
+
import { mergeProps } from '@base-ui/react/merge-props';
|
|
8
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
9
|
+
import { useState } from 'react';
|
|
10
|
+
|
|
11
|
+
function cn(...inputs) {
|
|
12
|
+
return twMerge(clsx(inputs));
|
|
13
|
+
}
|
|
14
|
+
var buttonVariants = cva(
|
|
15
|
+
"group/button inline-flex shrink-0 items-center justify-center rounded-lg whitespace-nowrap font-bold transition-all duration-150 select-none outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
16
|
+
{
|
|
17
|
+
variants: {
|
|
18
|
+
variant: {
|
|
19
|
+
// Primary: Black bg, white text, offset shadow
|
|
20
|
+
default: "border-brutal border-foreground bg-primary text-primary-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
21
|
+
// CTA: Mint green bg — the Bannerbear signature
|
|
22
|
+
cta: "border-brutal border-foreground bg-cta text-cta-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
23
|
+
// Brand: Dynamic brand color bg
|
|
24
|
+
brand: "border-brutal border-foreground bg-brand text-brand-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
25
|
+
// Outline: White bg, black border, offset shadow
|
|
26
|
+
outline: "border-brutal border-foreground bg-background text-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
27
|
+
// Secondary: Light bg, border, smaller shadow
|
|
28
|
+
secondary: "border-brutal border-foreground bg-secondary text-secondary-foreground shadow-brutal-sm hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal active:translate-x-px active:translate-y-px active:shadow-none",
|
|
29
|
+
// Ghost: No border/shadow, subtle hover
|
|
30
|
+
ghost: "hover:bg-secondary hover:text-foreground",
|
|
31
|
+
// Link: Text only
|
|
32
|
+
link: "text-foreground underline-offset-4 hover:underline",
|
|
33
|
+
// Destructive
|
|
34
|
+
destructive: "border-brutal border-destructive bg-destructive text-destructive-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm",
|
|
35
|
+
// Nav: Thin border, no shadow (for nav Sign In buttons)
|
|
36
|
+
nav: "border border-foreground bg-background text-foreground hover:bg-foreground hover:text-background"
|
|
37
|
+
},
|
|
38
|
+
size: {
|
|
39
|
+
xs: "h-7 gap-1 px-2.5 text-xs",
|
|
40
|
+
sm: "h-8 gap-1.5 px-3 text-sm",
|
|
41
|
+
default: "h-10 gap-2 px-5 text-sm",
|
|
42
|
+
lg: "h-12 gap-2 px-7 text-base",
|
|
43
|
+
xl: "h-14 gap-2.5 px-9 text-lg",
|
|
44
|
+
icon: "size-10",
|
|
45
|
+
"icon-sm": "size-8",
|
|
46
|
+
"icon-lg": "size-12"
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
defaultVariants: {
|
|
50
|
+
variant: "default",
|
|
51
|
+
size: "default"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
function Button({
|
|
56
|
+
className,
|
|
57
|
+
variant = "default",
|
|
58
|
+
size = "default",
|
|
59
|
+
...props
|
|
60
|
+
}) {
|
|
61
|
+
return /* @__PURE__ */ jsx(
|
|
62
|
+
Button$1,
|
|
63
|
+
{
|
|
64
|
+
"data-slot": "button",
|
|
65
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
66
|
+
...props
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
var badgeVariants = cva(
|
|
71
|
+
"group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 rounded-md whitespace-nowrap font-bold transition-all [&>svg]:pointer-events-none [&>svg]:size-3!",
|
|
72
|
+
{
|
|
73
|
+
variants: {
|
|
74
|
+
variant: {
|
|
75
|
+
default: "border-brutal border-foreground bg-primary px-3 py-1 text-xs text-primary-foreground shadow-brutal-sm",
|
|
76
|
+
secondary: "border-brutal border-foreground bg-secondary px-3 py-1 text-xs text-secondary-foreground shadow-brutal-sm",
|
|
77
|
+
brand: "border-brutal border-foreground bg-brand px-3 py-1 text-xs text-brand-foreground shadow-brutal-sm",
|
|
78
|
+
cta: "border-brutal border-foreground bg-cta px-3 py-1 text-xs text-cta-foreground shadow-brutal-sm",
|
|
79
|
+
outline: "border-brutal border-foreground bg-background px-3 py-1 text-xs text-foreground",
|
|
80
|
+
destructive: "border-brutal border-destructive bg-destructive/10 px-3 py-1 text-xs text-destructive",
|
|
81
|
+
ghost: "px-2 py-0.5 text-xs text-muted-foreground"
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
defaultVariants: {
|
|
85
|
+
variant: "default"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
function Badge({
|
|
90
|
+
className,
|
|
91
|
+
variant = "default",
|
|
92
|
+
render,
|
|
93
|
+
...props
|
|
94
|
+
}) {
|
|
95
|
+
return useRender({
|
|
96
|
+
defaultTagName: "span",
|
|
97
|
+
props: mergeProps(
|
|
98
|
+
{
|
|
99
|
+
className: cn(badgeVariants({ variant }), className)
|
|
100
|
+
},
|
|
101
|
+
props
|
|
102
|
+
),
|
|
103
|
+
render,
|
|
104
|
+
state: {
|
|
105
|
+
slot: "badge",
|
|
106
|
+
variant
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
var colorMap = {
|
|
111
|
+
white: "bg-background text-foreground",
|
|
112
|
+
brand: "bg-brand",
|
|
113
|
+
"brand-muted": "bg-brand-muted text-foreground",
|
|
114
|
+
black: "bg-foreground text-background",
|
|
115
|
+
cta: "bg-cta"
|
|
116
|
+
};
|
|
117
|
+
var containerMap = {
|
|
118
|
+
sm: "brutal-container-sm",
|
|
119
|
+
default: "brutal-container",
|
|
120
|
+
lg: "brutal-container-lg"
|
|
121
|
+
};
|
|
122
|
+
var paddingMap = {
|
|
123
|
+
sm: "brutal-section-sm",
|
|
124
|
+
default: "brutal-section",
|
|
125
|
+
lg: "brutal-section py-28 sm:py-36 md:py-44"
|
|
126
|
+
};
|
|
127
|
+
var patternMap = {
|
|
128
|
+
dots: "brutal-dots",
|
|
129
|
+
stripes: "brutal-stripes",
|
|
130
|
+
noise: "brutal-noise",
|
|
131
|
+
grain: "brutal-grain",
|
|
132
|
+
crosshatch: "brutal-crosshatch",
|
|
133
|
+
"grid-dots": "brutal-grid-dots",
|
|
134
|
+
"gradient-mesh": "brutal-gradient-mesh",
|
|
135
|
+
none: ""
|
|
136
|
+
};
|
|
137
|
+
function BrutalSection({
|
|
138
|
+
children,
|
|
139
|
+
color = "white",
|
|
140
|
+
className,
|
|
141
|
+
containerSize = "default",
|
|
142
|
+
padding = "default",
|
|
143
|
+
pattern,
|
|
144
|
+
dots = false,
|
|
145
|
+
stripes = false,
|
|
146
|
+
id
|
|
147
|
+
}) {
|
|
148
|
+
const resolvedPattern = pattern ?? (dots ? "dots" : void 0) ?? (stripes ? "stripes" : void 0) ?? "none";
|
|
149
|
+
return /* @__PURE__ */ jsx(
|
|
150
|
+
"section",
|
|
151
|
+
{
|
|
152
|
+
id,
|
|
153
|
+
className: cn(
|
|
154
|
+
paddingMap[padding],
|
|
155
|
+
colorMap[color],
|
|
156
|
+
patternMap[resolvedPattern],
|
|
157
|
+
className
|
|
158
|
+
),
|
|
159
|
+
children: /* @__PURE__ */ jsx("div", { className: containerMap[containerSize], children })
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
function PricingTable({
|
|
164
|
+
badge,
|
|
165
|
+
headline,
|
|
166
|
+
description,
|
|
167
|
+
tiers,
|
|
168
|
+
popularIndex,
|
|
169
|
+
billingToggle = true,
|
|
170
|
+
color = "white",
|
|
171
|
+
className
|
|
172
|
+
}) {
|
|
173
|
+
const [annual, setAnnual] = useState(false);
|
|
174
|
+
return /* @__PURE__ */ jsxs(BrutalSection, { color, className, children: [
|
|
175
|
+
(headline || badge) && /* @__PURE__ */ jsxs("div", { className: "mb-8 text-center", children: [
|
|
176
|
+
badge && /* @__PURE__ */ jsx("p", { className: "brutal-label mb-4 text-muted-foreground", children: badge }),
|
|
177
|
+
headline && /* @__PURE__ */ jsx("h2", { className: "brutal-h2 mb-4", children: headline }),
|
|
178
|
+
description && /* @__PURE__ */ jsx("p", { className: "brutal-body mx-auto max-w-lg text-muted-foreground", children: description })
|
|
179
|
+
] }),
|
|
180
|
+
billingToggle && /* @__PURE__ */ jsxs("div", { className: "mb-10 flex items-center justify-center gap-3", children: [
|
|
181
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-sm font-bold", !annual && "text-brand"), children: "Monthly" }),
|
|
182
|
+
/* @__PURE__ */ jsx(
|
|
183
|
+
"button",
|
|
184
|
+
{
|
|
185
|
+
onClick: () => setAnnual(!annual),
|
|
186
|
+
className: cn(
|
|
187
|
+
"relative h-8 w-14 border-brutal border-foreground transition-colors",
|
|
188
|
+
annual ? "bg-brand" : "bg-secondary"
|
|
189
|
+
),
|
|
190
|
+
children: /* @__PURE__ */ jsx(
|
|
191
|
+
"span",
|
|
192
|
+
{
|
|
193
|
+
className: cn(
|
|
194
|
+
"absolute top-1 size-6 border border-foreground bg-background transition-transform",
|
|
195
|
+
annual ? "left-7" : "left-1"
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
),
|
|
201
|
+
/* @__PURE__ */ jsx("span", { className: cn("text-sm font-bold", annual && "text-brand"), children: "Annual" }),
|
|
202
|
+
annual && /* @__PURE__ */ jsx(Badge, { variant: "cta", children: "Save 20%" })
|
|
203
|
+
] }),
|
|
204
|
+
/* @__PURE__ */ jsx("div", { className: cn(
|
|
205
|
+
"grid gap-6",
|
|
206
|
+
tiers.length === 2 && "mx-auto max-w-2xl sm:grid-cols-2",
|
|
207
|
+
tiers.length === 3 && "lg:grid-cols-3",
|
|
208
|
+
tiers.length >= 4 && "sm:grid-cols-2 lg:grid-cols-4"
|
|
209
|
+
), children: tiers.map((tier, i) => {
|
|
210
|
+
const isPopular = i === popularIndex;
|
|
211
|
+
const price = annual ? tier.price.annual : tier.price.monthly;
|
|
212
|
+
return /* @__PURE__ */ jsxs(
|
|
213
|
+
"div",
|
|
214
|
+
{
|
|
215
|
+
className: cn(
|
|
216
|
+
"relative flex flex-col border-brutal border-foreground bg-background p-6 shadow-brutal",
|
|
217
|
+
isPopular && "border-brand shadow-brutal-lg ring-2 ring-brand z-10 scale-105"
|
|
218
|
+
),
|
|
219
|
+
children: [
|
|
220
|
+
isPopular && /* @__PURE__ */ jsx(Badge, { variant: "brand", className: "absolute -top-3 left-1/2 -translate-x-1/2", children: "Popular" }),
|
|
221
|
+
/* @__PURE__ */ jsx("h3", { className: "brutal-h4 mb-1", children: tier.name }),
|
|
222
|
+
tier.description && /* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: tier.description }),
|
|
223
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
|
|
224
|
+
/* @__PURE__ */ jsx("span", { className: "brutal-h1", children: typeof price === "number" ? `$${price}` : price }),
|
|
225
|
+
typeof price === "number" && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "/mo" })
|
|
226
|
+
] }),
|
|
227
|
+
/* @__PURE__ */ jsx("ul", { className: "mb-8 flex flex-1 flex-col gap-2", children: tier.features.map((feature) => /* @__PURE__ */ jsxs("li", { className: "flex items-start gap-2 text-sm", children: [
|
|
228
|
+
/* @__PURE__ */ jsx("span", { className: "mt-0.5 text-brand", children: "\u2713" }),
|
|
229
|
+
feature
|
|
230
|
+
] }, feature)) }),
|
|
231
|
+
/* @__PURE__ */ jsx(
|
|
232
|
+
Button,
|
|
233
|
+
{
|
|
234
|
+
variant: tier.ctaVariant || (isPopular ? "cta" : "outline"),
|
|
235
|
+
size: "lg",
|
|
236
|
+
className: "w-full",
|
|
237
|
+
render: tier.ctaHref ? /* @__PURE__ */ jsx("a", { href: tier.ctaHref }) : void 0,
|
|
238
|
+
children: tier.ctaText
|
|
239
|
+
}
|
|
240
|
+
)
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
tier.name
|
|
244
|
+
);
|
|
245
|
+
}) })
|
|
246
|
+
] });
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export { PricingTable };
|
|
250
|
+
//# sourceMappingURL=pricing-table.js.map
|
|
251
|
+
//# sourceMappingURL=pricing-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/ui/button.tsx","../../../src/components/ui/badge.tsx","../../../src/components/brutal/section.tsx","../../../src/components/brutal/pricing-table.tsx"],"names":["ButtonPrimitive","cva","jsx"],"mappings":"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","file":"pricing-table.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","\"use client\";\n\nimport { Button as ButtonPrimitive } from \"@base-ui/react/button\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst buttonVariants = cva(\n \"group/button inline-flex shrink-0 items-center justify-center rounded-lg whitespace-nowrap font-bold transition-all duration-150 select-none outline-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4\",\n {\n variants: {\n variant: {\n // Primary: Black bg, white text, offset shadow\n default:\n \"border-brutal border-foreground bg-primary text-primary-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // CTA: Mint green bg — the Bannerbear signature\n cta: \"border-brutal border-foreground bg-cta text-cta-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // Brand: Dynamic brand color bg\n brand:\n \"border-brutal border-foreground bg-brand text-brand-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // Outline: White bg, black border, offset shadow\n outline:\n \"border-brutal border-foreground bg-background text-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // Secondary: Light bg, border, smaller shadow\n secondary:\n \"border-brutal border-foreground bg-secondary text-secondary-foreground shadow-brutal-sm hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal active:translate-x-px active:translate-y-px active:shadow-none\",\n // Ghost: No border/shadow, subtle hover\n ghost: \"hover:bg-secondary hover:text-foreground\",\n // Link: Text only\n link: \"text-foreground underline-offset-4 hover:underline\",\n // Destructive\n destructive:\n \"border-brutal border-destructive bg-destructive text-destructive-foreground shadow-brutal hover:-translate-x-0.5 hover:-translate-y-0.5 hover:shadow-brutal-lg active:translate-x-px active:translate-y-px active:shadow-brutal-sm\",\n // Nav: Thin border, no shadow (for nav Sign In buttons)\n nav: \"border border-foreground bg-background text-foreground hover:bg-foreground hover:text-background\",\n },\n size: {\n xs: \"h-7 gap-1 px-2.5 text-xs\",\n sm: \"h-8 gap-1.5 px-3 text-sm\",\n default: \"h-10 gap-2 px-5 text-sm\",\n lg: \"h-12 gap-2 px-7 text-base\",\n xl: \"h-14 gap-2.5 px-9 text-lg\",\n icon: \"size-10\",\n \"icon-sm\": \"size-8\",\n \"icon-lg\": \"size-12\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n ...props\n}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {\n return (\n <ButtonPrimitive\n data-slot=\"button\"\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n );\n}\n\nexport { Button, buttonVariants };\n","import { mergeProps } from \"@base-ui/react/merge-props\";\nimport { useRender } from \"@base-ui/react/use-render\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"../../lib/utils\";\n\nconst badgeVariants = cva(\n \"group/badge inline-flex w-fit shrink-0 items-center justify-center gap-1 rounded-md whitespace-nowrap font-bold transition-all [&>svg]:pointer-events-none [&>svg]:size-3!\",\n {\n variants: {\n variant: {\n default:\n \"border-brutal border-foreground bg-primary px-3 py-1 text-xs text-primary-foreground shadow-brutal-sm\",\n secondary:\n \"border-brutal border-foreground bg-secondary px-3 py-1 text-xs text-secondary-foreground shadow-brutal-sm\",\n brand:\n \"border-brutal border-foreground bg-brand px-3 py-1 text-xs text-brand-foreground shadow-brutal-sm\",\n cta: \"border-brutal border-foreground bg-cta px-3 py-1 text-xs text-cta-foreground shadow-brutal-sm\",\n outline:\n \"border-brutal border-foreground bg-background px-3 py-1 text-xs text-foreground\",\n destructive:\n \"border-brutal border-destructive bg-destructive/10 px-3 py-1 text-xs text-destructive\",\n ghost: \"px-2 py-0.5 text-xs text-muted-foreground\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nfunction Badge({\n className,\n variant = \"default\",\n render,\n ...props\n}: useRender.ComponentProps<\"span\"> & VariantProps<typeof badgeVariants>) {\n return useRender({\n defaultTagName: \"span\",\n props: mergeProps<\"span\">(\n {\n className: cn(badgeVariants({ variant }), className),\n },\n props\n ),\n render,\n state: {\n slot: \"badge\",\n variant,\n },\n });\n}\n\nexport { Badge, badgeVariants };\n","import { cn } from \"../../lib/utils\";\n\nexport type SectionColor =\n | \"white\"\n | \"brand\"\n | \"brand-muted\"\n | \"black\"\n | \"cta\";\n\nexport type SectionPattern =\n | \"dots\"\n | \"stripes\"\n | \"noise\"\n | \"grain\"\n | \"crosshatch\"\n | \"grid-dots\"\n | \"gradient-mesh\"\n | \"none\";\n\ninterface BrutalSectionProps {\n children: React.ReactNode;\n color?: SectionColor;\n className?: string;\n containerSize?: \"sm\" | \"default\" | \"lg\";\n padding?: \"sm\" | \"default\" | \"lg\";\n pattern?: SectionPattern;\n /** @deprecated Use pattern=\"dots\" instead */\n dots?: boolean;\n /** @deprecated Use pattern=\"stripes\" instead */\n stripes?: boolean;\n id?: string;\n}\n\nconst colorMap: Record<SectionColor, string> = {\n white: \"bg-background text-foreground\",\n brand: \"bg-brand\",\n \"brand-muted\": \"bg-brand-muted text-foreground\",\n black: \"bg-foreground text-background\",\n cta: \"bg-cta\",\n};\n\nconst containerMap = {\n sm: \"brutal-container-sm\",\n default: \"brutal-container\",\n lg: \"brutal-container-lg\",\n};\n\nconst paddingMap = {\n sm: \"brutal-section-sm\",\n default: \"brutal-section\",\n lg: \"brutal-section py-28 sm:py-36 md:py-44\",\n};\n\nconst patternMap: Record<SectionPattern, string> = {\n dots: \"brutal-dots\",\n stripes: \"brutal-stripes\",\n noise: \"brutal-noise\",\n grain: \"brutal-grain\",\n crosshatch: \"brutal-crosshatch\",\n \"grid-dots\": \"brutal-grid-dots\",\n \"gradient-mesh\": \"brutal-gradient-mesh\",\n none: \"\",\n};\n\nexport function BrutalSection({\n children,\n color = \"white\",\n className,\n containerSize = \"default\",\n padding = \"default\",\n pattern,\n dots = false,\n stripes = false,\n id,\n}: BrutalSectionProps) {\n const resolvedPattern = pattern\n ?? (dots ? \"dots\" : undefined)\n ?? (stripes ? \"stripes\" : undefined)\n ?? \"none\";\n\n return (\n <section\n id={id}\n className={cn(\n paddingMap[padding],\n colorMap[color],\n patternMap[resolvedPattern],\n className\n )}\n >\n <div className={containerMap[containerSize]}>{children}</div>\n </section>\n );\n}\n","\"use client\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"../ui/button\";\nimport { Badge } from \"../ui/badge\";\nimport { BrutalSection } from \"./section\";\nimport { useState } from \"react\";\n\ninterface PricingTier {\n name: string;\n description?: string;\n price: { monthly: number | string; annual: number | string };\n features: string[];\n ctaText: string;\n ctaHref?: string;\n ctaVariant?: \"cta\" | \"brand\" | \"default\" | \"outline\";\n}\n\ninterface PricingTableProps {\n badge?: string;\n headline?: string;\n description?: string;\n tiers: PricingTier[];\n popularIndex?: number;\n billingToggle?: boolean;\n color?: \"white\" | \"black\";\n className?: string;\n}\n\nexport function PricingTable({\n badge,\n headline,\n description,\n tiers,\n popularIndex,\n billingToggle = true,\n color = \"white\",\n className,\n}: PricingTableProps) {\n const [annual, setAnnual] = useState(false);\n\n return (\n <BrutalSection color={color} className={className}>\n {(headline || badge) && (\n <div className=\"mb-8 text-center\">\n {badge && <p className=\"brutal-label mb-4 text-muted-foreground\">{badge}</p>}\n {headline && <h2 className=\"brutal-h2 mb-4\">{headline}</h2>}\n {description && <p className=\"brutal-body mx-auto max-w-lg text-muted-foreground\">{description}</p>}\n </div>\n )}\n\n {billingToggle && (\n <div className=\"mb-10 flex items-center justify-center gap-3\">\n <span className={cn(\"text-sm font-bold\", !annual && \"text-brand\")}>Monthly</span>\n <button\n onClick={() => setAnnual(!annual)}\n className={cn(\n \"relative h-8 w-14 border-brutal border-foreground transition-colors\",\n annual ? \"bg-brand\" : \"bg-secondary\",\n )}\n >\n <span\n className={cn(\n \"absolute top-1 size-6 border border-foreground bg-background transition-transform\",\n annual ? \"left-7\" : \"left-1\",\n )}\n />\n </button>\n <span className={cn(\"text-sm font-bold\", annual && \"text-brand\")}>Annual</span>\n {annual && <Badge variant=\"cta\">Save 20%</Badge>}\n </div>\n )}\n\n <div className={cn(\n \"grid gap-6\",\n tiers.length === 2 && \"mx-auto max-w-2xl sm:grid-cols-2\",\n tiers.length === 3 && \"lg:grid-cols-3\",\n tiers.length >= 4 && \"sm:grid-cols-2 lg:grid-cols-4\",\n )}>\n {tiers.map((tier, i) => {\n const isPopular = i === popularIndex;\n const price = annual ? tier.price.annual : tier.price.monthly;\n\n return (\n <div\n key={tier.name}\n className={cn(\n \"relative flex flex-col border-brutal border-foreground bg-background p-6 shadow-brutal\",\n isPopular && \"border-brand shadow-brutal-lg ring-2 ring-brand z-10 scale-105\",\n )}\n >\n {isPopular && (\n <Badge variant=\"brand\" className=\"absolute -top-3 left-1/2 -translate-x-1/2\">\n Popular\n </Badge>\n )}\n <h3 className=\"brutal-h4 mb-1\">{tier.name}</h3>\n {tier.description && <p className=\"mb-4 text-sm text-muted-foreground\">{tier.description}</p>}\n <div className=\"mb-6\">\n <span className=\"brutal-h1\">{typeof price === \"number\" ? `$${price}` : price}</span>\n {typeof price === \"number\" && <span className=\"text-sm text-muted-foreground\">/mo</span>}\n </div>\n <ul className=\"mb-8 flex flex-1 flex-col gap-2\">\n {tier.features.map((feature) => (\n <li key={feature} className=\"flex items-start gap-2 text-sm\">\n <span className=\"mt-0.5 text-brand\">✓</span>\n {feature}\n </li>\n ))}\n </ul>\n <Button\n variant={tier.ctaVariant || (isPopular ? \"cta\" : \"outline\")}\n size=\"lg\"\n className=\"w-full\"\n render={tier.ctaHref ? <a href={tier.ctaHref} /> : undefined}\n >\n {tier.ctaText}\n </Button>\n </div>\n );\n })}\n </div>\n </BrutalSection>\n );\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
type DividerVariant = "wave" | "jagged" | "curve" | "castle" | "torn-paper" | "brush-stroke" | "geometric" | "blob" | "diagonal" | "zigzag" | "hand-drawn";
|
|
4
|
+
interface SectionDividerProps {
|
|
5
|
+
from?: string;
|
|
6
|
+
to?: string;
|
|
7
|
+
flip?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
variant?: DividerVariant;
|
|
10
|
+
layers?: boolean;
|
|
11
|
+
}
|
|
12
|
+
declare function SectionDivider({ from, to, flip, className, variant, layers, }: SectionDividerProps): react_jsx_runtime.JSX.Element;
|
|
13
|
+
|
|
14
|
+
export { SectionDivider };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/lib/utils.ts
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
var paths = {
|
|
11
|
+
wave: "M0,64 C160,128 320,0 480,64 C640,128 800,0 960,64 L960,160 L0,160 Z",
|
|
12
|
+
jagged: "M0,80 L80,40 L160,80 L240,20 L320,80 L400,40 L480,80 L560,20 L640,80 L720,40 L800,80 L880,20 L960,80 L960,160 L0,160 Z",
|
|
13
|
+
curve: "M0,128 Q480,-32 960,128 L960,160 L0,160 Z",
|
|
14
|
+
castle: "M0,80 L0,60 L80,60 L80,80 L120,80 L120,40 L200,40 L200,80 L240,80 L240,60 L320,60 L320,80 L360,80 L360,40 L440,40 L440,80 L480,80 L480,60 L560,60 L560,80 L600,80 L600,40 L680,40 L680,80 L720,80 L720,60 L800,60 L800,80 L840,80 L840,40 L920,40 L920,80 L960,80 L960,160 L0,160 Z",
|
|
15
|
+
"torn-paper": "M0,70 C30,65 45,85 80,72 C115,59 130,90 170,78 C210,66 225,88 270,75 C315,62 340,92 380,79 C420,66 445,86 490,73 C535,60 560,88 600,76 C640,64 665,90 710,77 C755,64 780,84 830,71 C875,58 900,86 960,80 L960,160 L0,160 Z",
|
|
16
|
+
"brush-stroke": "M0,90 C80,50 120,100 200,60 C280,20 360,110 480,70 C600,30 640,100 720,80 C800,60 880,100 960,70 L960,160 L0,160 Z",
|
|
17
|
+
geometric: "M0,100 L120,60 L240,80 L360,40 L480,90 L600,50 L720,70 L840,30 L960,80 L960,160 L0,160 Z",
|
|
18
|
+
blob: "M0,80 C80,120 160,40 280,80 C400,120 440,30 560,70 C680,110 760,40 960,80 L960,160 L0,160 Z",
|
|
19
|
+
diagonal: "M0,160 L960,60 L960,160 Z",
|
|
20
|
+
zigzag: "M0,80 L60,40 L120,80 L180,40 L240,80 L300,40 L360,80 L420,40 L480,80 L540,40 L600,80 L660,40 L720,80 L780,40 L840,80 L900,40 L960,80 L960,160 L0,160 Z",
|
|
21
|
+
"hand-drawn": "M0,72 C20,68 35,82 60,75 C85,68 95,84 125,76 C155,68 170,85 200,77 C230,69 245,83 275,75 C305,67 320,86 350,78 C380,70 395,82 425,74 C455,66 470,84 500,76 C530,68 545,83 575,75 C605,67 620,85 650,77 C680,69 695,82 725,74 C755,66 770,84 800,76 C830,68 845,83 875,75 C905,67 920,82 960,74 L960,160 L0,160 Z"
|
|
22
|
+
};
|
|
23
|
+
function SectionDivider({
|
|
24
|
+
from = "hsl(var(--background))",
|
|
25
|
+
to = "hsl(var(--background))",
|
|
26
|
+
flip = false,
|
|
27
|
+
className = "",
|
|
28
|
+
variant = "wave",
|
|
29
|
+
layers = false
|
|
30
|
+
}) {
|
|
31
|
+
return /* @__PURE__ */ jsxs(
|
|
32
|
+
"div",
|
|
33
|
+
{
|
|
34
|
+
className: cn("relative -my-px w-full overflow-hidden", className),
|
|
35
|
+
style: { backgroundColor: from },
|
|
36
|
+
children: [
|
|
37
|
+
layers && /* @__PURE__ */ jsx(
|
|
38
|
+
"svg",
|
|
39
|
+
{
|
|
40
|
+
viewBox: "0 0 960 160",
|
|
41
|
+
preserveAspectRatio: "none",
|
|
42
|
+
className: "absolute inset-0 block w-full opacity-30",
|
|
43
|
+
style: {
|
|
44
|
+
height: "clamp(40px, 6vw, 80px)",
|
|
45
|
+
transform: flip ? "scaleY(-1) translateY(4px)" : "translateY(-4px)"
|
|
46
|
+
},
|
|
47
|
+
children: /* @__PURE__ */ jsx("path", { d: paths[variant], fill: to })
|
|
48
|
+
}
|
|
49
|
+
),
|
|
50
|
+
/* @__PURE__ */ jsx(
|
|
51
|
+
"svg",
|
|
52
|
+
{
|
|
53
|
+
viewBox: "0 0 960 160",
|
|
54
|
+
preserveAspectRatio: "none",
|
|
55
|
+
className: "relative block w-full",
|
|
56
|
+
style: {
|
|
57
|
+
height: "clamp(40px, 6vw, 80px)",
|
|
58
|
+
transform: flip ? "scaleY(-1)" : void 0
|
|
59
|
+
},
|
|
60
|
+
children: /* @__PURE__ */ jsx("path", { d: paths[variant], fill: to })
|
|
61
|
+
}
|
|
62
|
+
)
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export { SectionDivider };
|
|
69
|
+
//# sourceMappingURL=section-divider.js.map
|
|
70
|
+
//# sourceMappingURL=section-divider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/brutal/section-divider.tsx"],"names":[],"mappings":";;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;ACmBA,IAAM,KAAA,GAAwC;AAAA,EAC5C,IAAA,EAAM,qEAAA;AAAA,EACN,MAAA,EAAQ,wHAAA;AAAA,EACR,KAAA,EAAO,2CAAA;AAAA,EACP,MAAA,EAAQ,qRAAA;AAAA,EACR,YAAA,EAAc,4NAAA;AAAA,EACd,cAAA,EAAgB,oHAAA;AAAA,EAChB,SAAA,EAAW,0FAAA;AAAA,EACX,IAAA,EAAM,6FAAA;AAAA,EACN,QAAA,EAAU,2BAAA;AAAA,EACV,MAAA,EAAQ,wJAAA;AAAA,EACR,YAAA,EAAc;AAChB,CAAA;AAEO,SAAS,cAAA,CAAe;AAAA,EAC7B,IAAA,GAAO,wBAAA;AAAA,EACP,EAAA,GAAK,wBAAA;AAAA,EACL,IAAA,GAAO,KAAA;AAAA,EACP,SAAA,GAAY,EAAA;AAAA,EACZ,OAAA,GAAU,MAAA;AAAA,EACV,MAAA,GAAS;AACX,CAAA,EAAwB;AACtB,EAAA,uBACE,IAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAW,EAAA,CAAG,wCAAA,EAA0C,SAAS,CAAA;AAAA,MACjE,KAAA,EAAO,EAAE,eAAA,EAAiB,IAAA,EAAK;AAAA,MAE9B,QAAA,EAAA;AAAA,QAAA,MAAA,oBACC,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAQ,aAAA;AAAA,YACR,mBAAA,EAAoB,MAAA;AAAA,YACpB,SAAA,EAAU,0CAAA;AAAA,YACV,KAAA,EAAO;AAAA,cACL,MAAA,EAAQ,wBAAA;AAAA,cACR,SAAA,EAAW,OAAO,4BAAA,GAA+B;AAAA,aACnD;AAAA,YAEA,8BAAC,MAAA,EAAA,EAAK,CAAA,EAAG,MAAM,OAAO,CAAA,EAAG,MAAM,EAAA,EAAI;AAAA;AAAA,SACrC;AAAA,wBAEF,GAAA;AAAA,UAAC,KAAA;AAAA,UAAA;AAAA,YACC,OAAA,EAAQ,aAAA;AAAA,YACR,mBAAA,EAAoB,MAAA;AAAA,YACpB,SAAA,EAAU,uBAAA;AAAA,YACV,KAAA,EAAO;AAAA,cACL,MAAA,EAAQ,wBAAA;AAAA,cACR,SAAA,EAAW,OAAO,YAAA,GAAe;AAAA,aACnC;AAAA,YAEA,8BAAC,MAAA,EAAA,EAAK,CAAA,EAAG,MAAM,OAAO,CAAA,EAAG,MAAM,EAAA,EAAI;AAAA;AAAA;AACrC;AAAA;AAAA,GACF;AAEJ","file":"section-divider.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { cn } from \"../../lib/utils\";\n\ntype DividerVariant =\n | \"wave\"\n | \"jagged\"\n | \"curve\"\n | \"castle\"\n | \"torn-paper\"\n | \"brush-stroke\"\n | \"geometric\"\n | \"blob\"\n | \"diagonal\"\n | \"zigzag\"\n | \"hand-drawn\";\n\ninterface SectionDividerProps {\n from?: string;\n to?: string;\n flip?: boolean;\n className?: string;\n variant?: DividerVariant;\n layers?: boolean;\n}\n\nconst paths: Record<DividerVariant, string> = {\n wave: \"M0,64 C160,128 320,0 480,64 C640,128 800,0 960,64 L960,160 L0,160 Z\",\n jagged: \"M0,80 L80,40 L160,80 L240,20 L320,80 L400,40 L480,80 L560,20 L640,80 L720,40 L800,80 L880,20 L960,80 L960,160 L0,160 Z\",\n curve: \"M0,128 Q480,-32 960,128 L960,160 L0,160 Z\",\n castle: \"M0,80 L0,60 L80,60 L80,80 L120,80 L120,40 L200,40 L200,80 L240,80 L240,60 L320,60 L320,80 L360,80 L360,40 L440,40 L440,80 L480,80 L480,60 L560,60 L560,80 L600,80 L600,40 L680,40 L680,80 L720,80 L720,60 L800,60 L800,80 L840,80 L840,40 L920,40 L920,80 L960,80 L960,160 L0,160 Z\",\n \"torn-paper\": \"M0,70 C30,65 45,85 80,72 C115,59 130,90 170,78 C210,66 225,88 270,75 C315,62 340,92 380,79 C420,66 445,86 490,73 C535,60 560,88 600,76 C640,64 665,90 710,77 C755,64 780,84 830,71 C875,58 900,86 960,80 L960,160 L0,160 Z\",\n \"brush-stroke\": \"M0,90 C80,50 120,100 200,60 C280,20 360,110 480,70 C600,30 640,100 720,80 C800,60 880,100 960,70 L960,160 L0,160 Z\",\n geometric: \"M0,100 L120,60 L240,80 L360,40 L480,90 L600,50 L720,70 L840,30 L960,80 L960,160 L0,160 Z\",\n blob: \"M0,80 C80,120 160,40 280,80 C400,120 440,30 560,70 C680,110 760,40 960,80 L960,160 L0,160 Z\",\n diagonal: \"M0,160 L960,60 L960,160 Z\",\n zigzag: \"M0,80 L60,40 L120,80 L180,40 L240,80 L300,40 L360,80 L420,40 L480,80 L540,40 L600,80 L660,40 L720,80 L780,40 L840,80 L900,40 L960,80 L960,160 L0,160 Z\",\n \"hand-drawn\": \"M0,72 C20,68 35,82 60,75 C85,68 95,84 125,76 C155,68 170,85 200,77 C230,69 245,83 275,75 C305,67 320,86 350,78 C380,70 395,82 425,74 C455,66 470,84 500,76 C530,68 545,83 575,75 C605,67 620,85 650,77 C680,69 695,82 725,74 C755,66 770,84 800,76 C830,68 845,83 875,75 C905,67 920,82 960,74 L960,160 L0,160 Z\",\n};\n\nexport function SectionDivider({\n from = \"hsl(var(--background))\",\n to = \"hsl(var(--background))\",\n flip = false,\n className = \"\",\n variant = \"wave\",\n layers = false,\n}: SectionDividerProps) {\n return (\n <div\n className={cn(\"relative -my-px w-full overflow-hidden\", className)}\n style={{ backgroundColor: from }}\n >\n {layers && (\n <svg\n viewBox=\"0 0 960 160\"\n preserveAspectRatio=\"none\"\n className=\"absolute inset-0 block w-full opacity-30\"\n style={{\n height: \"clamp(40px, 6vw, 80px)\",\n transform: flip ? \"scaleY(-1) translateY(4px)\" : \"translateY(-4px)\",\n }}\n >\n <path d={paths[variant]} fill={to} />\n </svg>\n )}\n <svg\n viewBox=\"0 0 960 160\"\n preserveAspectRatio=\"none\"\n className=\"relative block w-full\"\n style={{\n height: \"clamp(40px, 6vw, 80px)\",\n transform: flip ? \"scaleY(-1)\" : undefined,\n }}\n >\n <path d={paths[variant]} fill={to} />\n </svg>\n </div>\n );\n}\n"]}
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
|
|
3
|
-
type SectionColor = "white" | "brand" | "brand-muted" | "
|
|
3
|
+
type SectionColor = "white" | "brand" | "brand-muted" | "black" | "cta";
|
|
4
|
+
type SectionPattern = "dots" | "stripes" | "noise" | "grain" | "crosshatch" | "grid-dots" | "gradient-mesh" | "none";
|
|
4
5
|
interface BrutalSectionProps {
|
|
5
6
|
children: React.ReactNode;
|
|
6
7
|
color?: SectionColor;
|
|
7
8
|
className?: string;
|
|
8
9
|
containerSize?: "sm" | "default" | "lg";
|
|
9
10
|
padding?: "sm" | "default" | "lg";
|
|
11
|
+
pattern?: SectionPattern;
|
|
12
|
+
/** @deprecated Use pattern="dots" instead */
|
|
10
13
|
dots?: boolean;
|
|
14
|
+
/** @deprecated Use pattern="stripes" instead */
|
|
11
15
|
stripes?: boolean;
|
|
12
16
|
id?: string;
|
|
13
17
|
}
|
|
14
|
-
declare function BrutalSection({ children, color, className, containerSize, padding, dots, stripes, id, }: BrutalSectionProps): react_jsx_runtime.JSX.Element;
|
|
18
|
+
declare function BrutalSection({ children, color, className, containerSize, padding, pattern, dots, stripes, id, }: BrutalSectionProps): react_jsx_runtime.JSX.Element;
|
|
15
19
|
|
|
16
|
-
export { BrutalSection };
|
|
20
|
+
export { BrutalSection, type SectionColor, type SectionPattern };
|
|
@@ -11,9 +11,6 @@ var colorMap = {
|
|
|
11
11
|
white: "bg-background text-foreground",
|
|
12
12
|
brand: "bg-brand",
|
|
13
13
|
"brand-muted": "bg-brand-muted text-foreground",
|
|
14
|
-
blue: "bg-section-blue",
|
|
15
|
-
gray: "bg-section-gray text-foreground",
|
|
16
|
-
cream: "bg-section-cream text-foreground",
|
|
17
14
|
black: "bg-foreground text-background",
|
|
18
15
|
cta: "bg-cta"
|
|
19
16
|
};
|
|
@@ -27,16 +24,28 @@ var paddingMap = {
|
|
|
27
24
|
default: "brutal-section",
|
|
28
25
|
lg: "brutal-section py-28 sm:py-36 md:py-44"
|
|
29
26
|
};
|
|
27
|
+
var patternMap = {
|
|
28
|
+
dots: "brutal-dots",
|
|
29
|
+
stripes: "brutal-stripes",
|
|
30
|
+
noise: "brutal-noise",
|
|
31
|
+
grain: "brutal-grain",
|
|
32
|
+
crosshatch: "brutal-crosshatch",
|
|
33
|
+
"grid-dots": "brutal-grid-dots",
|
|
34
|
+
"gradient-mesh": "brutal-gradient-mesh",
|
|
35
|
+
none: ""
|
|
36
|
+
};
|
|
30
37
|
function BrutalSection({
|
|
31
38
|
children,
|
|
32
39
|
color = "white",
|
|
33
40
|
className,
|
|
34
41
|
containerSize = "default",
|
|
35
42
|
padding = "default",
|
|
43
|
+
pattern,
|
|
36
44
|
dots = false,
|
|
37
45
|
stripes = false,
|
|
38
46
|
id
|
|
39
47
|
}) {
|
|
48
|
+
const resolvedPattern = pattern ?? (dots ? "dots" : void 0) ?? (stripes ? "stripes" : void 0) ?? "none";
|
|
40
49
|
return /* @__PURE__ */ jsx(
|
|
41
50
|
"section",
|
|
42
51
|
{
|
|
@@ -44,8 +53,7 @@ function BrutalSection({
|
|
|
44
53
|
className: cn(
|
|
45
54
|
paddingMap[padding],
|
|
46
55
|
colorMap[color],
|
|
47
|
-
|
|
48
|
-
stripes && "brutal-stripes",
|
|
56
|
+
patternMap[resolvedPattern],
|
|
49
57
|
className
|
|
50
58
|
),
|
|
51
59
|
children: /* @__PURE__ */ jsx("div", { className: containerMap[containerSize], children })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/brutal/section.tsx"],"names":[],"mappings":";;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;
|
|
1
|
+
{"version":3,"sources":["../../../src/lib/utils.ts","../../../src/components/brutal/section.tsx"],"names":[],"mappings":";;;;;AAGO,SAAS,MAAM,MAAA,EAAsB;AAC1C,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAC,CAAA;AAC7B;AC4BA,IAAM,QAAA,GAAyC;AAAA,EAC7C,KAAA,EAAO,+BAAA;AAAA,EACP,KAAA,EAAO,UAAA;AAAA,EACP,aAAA,EAAe,gCAAA;AAAA,EACf,KAAA,EAAO,+BAAA;AAAA,EACP,GAAA,EAAK;AACP,CAAA;AAEA,IAAM,YAAA,GAAe;AAAA,EACnB,EAAA,EAAI,qBAAA;AAAA,EACJ,OAAA,EAAS,kBAAA;AAAA,EACT,EAAA,EAAI;AACN,CAAA;AAEA,IAAM,UAAA,GAAa;AAAA,EACjB,EAAA,EAAI,mBAAA;AAAA,EACJ,OAAA,EAAS,gBAAA;AAAA,EACT,EAAA,EAAI;AACN,CAAA;AAEA,IAAM,UAAA,GAA6C;AAAA,EACjD,IAAA,EAAM,aAAA;AAAA,EACN,OAAA,EAAS,gBAAA;AAAA,EACT,KAAA,EAAO,cAAA;AAAA,EACP,KAAA,EAAO,cAAA;AAAA,EACP,UAAA,EAAY,mBAAA;AAAA,EACZ,WAAA,EAAa,kBAAA;AAAA,EACb,eAAA,EAAiB,sBAAA;AAAA,EACjB,IAAA,EAAM;AACR,CAAA;AAEO,SAAS,aAAA,CAAc;AAAA,EAC5B,QAAA;AAAA,EACA,KAAA,GAAQ,OAAA;AAAA,EACR,SAAA;AAAA,EACA,aAAA,GAAgB,SAAA;AAAA,EAChB,OAAA,GAAU,SAAA;AAAA,EACV,OAAA;AAAA,EACA,IAAA,GAAO,KAAA;AAAA,EACP,OAAA,GAAU,KAAA;AAAA,EACV;AACF,CAAA,EAAuB;AACrB,EAAA,MAAM,kBAAkB,OAAA,KAClB,IAAA,GAAO,SAAS,MAAA,CAAA,KAChB,OAAA,GAAU,YAAY,MAAA,CAAA,IACvB,MAAA;AAEL,EAAA,uBACE,GAAA;AAAA,IAAC,SAAA;AAAA,IAAA;AAAA,MACC,EAAA;AAAA,MACA,SAAA,EAAW,EAAA;AAAA,QACT,WAAW,OAAO,CAAA;AAAA,QAClB,SAAS,KAAK,CAAA;AAAA,QACd,WAAW,eAAe,CAAA;AAAA,QAC1B;AAAA,OACF;AAAA,MAEA,8BAAC,KAAA,EAAA,EAAI,SAAA,EAAW,YAAA,CAAa,aAAa,GAAI,QAAA,EAAS;AAAA;AAAA,GACzD;AAEJ","file":"section.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import { cn } from \"../../lib/utils\";\n\nexport type SectionColor =\n | \"white\"\n | \"brand\"\n | \"brand-muted\"\n | \"black\"\n | \"cta\";\n\nexport type SectionPattern =\n | \"dots\"\n | \"stripes\"\n | \"noise\"\n | \"grain\"\n | \"crosshatch\"\n | \"grid-dots\"\n | \"gradient-mesh\"\n | \"none\";\n\ninterface BrutalSectionProps {\n children: React.ReactNode;\n color?: SectionColor;\n className?: string;\n containerSize?: \"sm\" | \"default\" | \"lg\";\n padding?: \"sm\" | \"default\" | \"lg\";\n pattern?: SectionPattern;\n /** @deprecated Use pattern=\"dots\" instead */\n dots?: boolean;\n /** @deprecated Use pattern=\"stripes\" instead */\n stripes?: boolean;\n id?: string;\n}\n\nconst colorMap: Record<SectionColor, string> = {\n white: \"bg-background text-foreground\",\n brand: \"bg-brand\",\n \"brand-muted\": \"bg-brand-muted text-foreground\",\n black: \"bg-foreground text-background\",\n cta: \"bg-cta\",\n};\n\nconst containerMap = {\n sm: \"brutal-container-sm\",\n default: \"brutal-container\",\n lg: \"brutal-container-lg\",\n};\n\nconst paddingMap = {\n sm: \"brutal-section-sm\",\n default: \"brutal-section\",\n lg: \"brutal-section py-28 sm:py-36 md:py-44\",\n};\n\nconst patternMap: Record<SectionPattern, string> = {\n dots: \"brutal-dots\",\n stripes: \"brutal-stripes\",\n noise: \"brutal-noise\",\n grain: \"brutal-grain\",\n crosshatch: \"brutal-crosshatch\",\n \"grid-dots\": \"brutal-grid-dots\",\n \"gradient-mesh\": \"brutal-gradient-mesh\",\n none: \"\",\n};\n\nexport function BrutalSection({\n children,\n color = \"white\",\n className,\n containerSize = \"default\",\n padding = \"default\",\n pattern,\n dots = false,\n stripes = false,\n id,\n}: BrutalSectionProps) {\n const resolvedPattern = pattern\n ?? (dots ? \"dots\" : undefined)\n ?? (stripes ? \"stripes\" : undefined)\n ?? \"none\";\n\n return (\n <section\n id={id}\n className={cn(\n paddingMap[padding],\n colorMap[color],\n patternMap[resolvedPattern],\n className\n )}\n >\n <div className={containerMap[containerSize]}>{children}</div>\n </section>\n );\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
interface Stat {
|
|
4
|
+
value: number;
|
|
5
|
+
label: string;
|
|
6
|
+
prefix?: string;
|
|
7
|
+
suffix?: string;
|
|
8
|
+
}
|
|
9
|
+
interface StatsBarProps {
|
|
10
|
+
stats: Stat[];
|
|
11
|
+
color?: "white" | "brand" | "black";
|
|
12
|
+
className?: string;
|
|
13
|
+
}
|
|
14
|
+
declare function StatsBar({ stats, color, className }: StatsBarProps): react_jsx_runtime.JSX.Element;
|
|
15
|
+
|
|
16
|
+
export { StatsBar };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
import { useRef, useState, useEffect } from 'react';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
var colorMap = {
|
|
11
|
+
white: "bg-background text-foreground",
|
|
12
|
+
brand: "bg-brand",
|
|
13
|
+
"brand-muted": "bg-brand-muted text-foreground",
|
|
14
|
+
black: "bg-foreground text-background",
|
|
15
|
+
cta: "bg-cta"
|
|
16
|
+
};
|
|
17
|
+
var containerMap = {
|
|
18
|
+
sm: "brutal-container-sm",
|
|
19
|
+
default: "brutal-container",
|
|
20
|
+
lg: "brutal-container-lg"
|
|
21
|
+
};
|
|
22
|
+
var paddingMap = {
|
|
23
|
+
sm: "brutal-section-sm",
|
|
24
|
+
default: "brutal-section",
|
|
25
|
+
lg: "brutal-section py-28 sm:py-36 md:py-44"
|
|
26
|
+
};
|
|
27
|
+
var patternMap = {
|
|
28
|
+
dots: "brutal-dots",
|
|
29
|
+
stripes: "brutal-stripes",
|
|
30
|
+
noise: "brutal-noise",
|
|
31
|
+
grain: "brutal-grain",
|
|
32
|
+
crosshatch: "brutal-crosshatch",
|
|
33
|
+
"grid-dots": "brutal-grid-dots",
|
|
34
|
+
"gradient-mesh": "brutal-gradient-mesh",
|
|
35
|
+
none: ""
|
|
36
|
+
};
|
|
37
|
+
function BrutalSection({
|
|
38
|
+
children,
|
|
39
|
+
color = "white",
|
|
40
|
+
className,
|
|
41
|
+
containerSize = "default",
|
|
42
|
+
padding = "default",
|
|
43
|
+
pattern,
|
|
44
|
+
dots = false,
|
|
45
|
+
stripes = false,
|
|
46
|
+
id
|
|
47
|
+
}) {
|
|
48
|
+
const resolvedPattern = pattern ?? (dots ? "dots" : void 0) ?? (stripes ? "stripes" : void 0) ?? "none";
|
|
49
|
+
return /* @__PURE__ */ jsx(
|
|
50
|
+
"section",
|
|
51
|
+
{
|
|
52
|
+
id,
|
|
53
|
+
className: cn(
|
|
54
|
+
paddingMap[padding],
|
|
55
|
+
colorMap[color],
|
|
56
|
+
patternMap[resolvedPattern],
|
|
57
|
+
className
|
|
58
|
+
),
|
|
59
|
+
children: /* @__PURE__ */ jsx("div", { className: containerMap[containerSize], children })
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function useCountUp(target, inView) {
|
|
64
|
+
const [count, setCount] = useState(0);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!inView) return;
|
|
67
|
+
let frame;
|
|
68
|
+
const duration = 1500;
|
|
69
|
+
const start = performance.now();
|
|
70
|
+
function animate(now) {
|
|
71
|
+
const elapsed = now - start;
|
|
72
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
73
|
+
const eased = 1 - Math.pow(1 - progress, 3);
|
|
74
|
+
setCount(Math.round(eased * target));
|
|
75
|
+
if (progress < 1) frame = requestAnimationFrame(animate);
|
|
76
|
+
}
|
|
77
|
+
frame = requestAnimationFrame(animate);
|
|
78
|
+
return () => cancelAnimationFrame(frame);
|
|
79
|
+
}, [target, inView]);
|
|
80
|
+
return count;
|
|
81
|
+
}
|
|
82
|
+
function StatItem({ stat, inView }) {
|
|
83
|
+
const count = useCountUp(stat.value, inView);
|
|
84
|
+
return /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
85
|
+
/* @__PURE__ */ jsxs("p", { className: "brutal-display", children: [
|
|
86
|
+
stat.prefix,
|
|
87
|
+
count.toLocaleString(),
|
|
88
|
+
stat.suffix
|
|
89
|
+
] }),
|
|
90
|
+
/* @__PURE__ */ jsx("p", { className: "brutal-label mt-2 text-muted-foreground", children: stat.label })
|
|
91
|
+
] });
|
|
92
|
+
}
|
|
93
|
+
function StatsBar({ stats, color = "white", className }) {
|
|
94
|
+
const ref = useRef(null);
|
|
95
|
+
const [inView, setInView] = useState(false);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (!ref.current) return;
|
|
98
|
+
const observer = new IntersectionObserver(
|
|
99
|
+
([entry]) => {
|
|
100
|
+
if (entry.isIntersecting) {
|
|
101
|
+
setInView(true);
|
|
102
|
+
observer.disconnect();
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
{ threshold: 0.3 }
|
|
106
|
+
);
|
|
107
|
+
observer.observe(ref.current);
|
|
108
|
+
return () => observer.disconnect();
|
|
109
|
+
}, []);
|
|
110
|
+
return /* @__PURE__ */ jsx(BrutalSection, { color, padding: "sm", className, children: /* @__PURE__ */ jsx(
|
|
111
|
+
"div",
|
|
112
|
+
{
|
|
113
|
+
ref,
|
|
114
|
+
className: cn(
|
|
115
|
+
"grid gap-8",
|
|
116
|
+
stats.length === 2 && "grid-cols-2",
|
|
117
|
+
stats.length === 3 && "grid-cols-3",
|
|
118
|
+
stats.length >= 4 && "grid-cols-2 sm:grid-cols-4"
|
|
119
|
+
),
|
|
120
|
+
children: stats.map((stat) => /* @__PURE__ */ jsx(StatItem, { stat, inView }, stat.label))
|
|
121
|
+
}
|
|
122
|
+
) });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export { StatsBar };
|
|
126
|
+
//# sourceMappingURL=stats-bar.js.map
|
|
127
|
+
//# sourceMappingURL=stats-bar.js.map
|