@opensite/ui 0.1.0 → 0.1.2

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.
@@ -36,34 +36,148 @@ var useEmblaCarousel__default = /*#__PURE__*/_interopDefault(useEmblaCarousel);
36
36
  function cn(...inputs) {
37
37
  return tailwindMerge.twMerge(clsx.clsx(inputs));
38
38
  }
39
- var buttonVariants = classVarianceAuthority.cva(
40
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
41
- {
42
- variants: {
43
- variant: {
44
- default: "bg-[var(--button-default-bg,hsl(var(--primary)))] text-[var(--button-default-fg,hsl(var(--primary-foreground)))] hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
45
- destructive: "bg-[var(--button-destructive-bg,hsl(var(--destructive)))] text-[var(--button-destructive-fg,white)] hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
46
- outline: "border-[var(--button-outline-border-width,1px)] border-[var(--button-outline-border,hsl(var(--border)))] bg-[var(--button-outline-bg,hsl(var(--background)))] text-[var(--button-outline-fg,inherit)] shadow-xs hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))] hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))] dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
47
- secondary: "bg-[var(--button-secondary-bg,hsl(var(--secondary)))] text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))] hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
48
- ghost: "bg-[var(--button-ghost-bg,transparent)] text-[var(--button-ghost-fg,inherit)] hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))] hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))] dark:hover:bg-accent/50",
49
- link: "text-[var(--button-link-fg,hsl(var(--primary)))] underline-offset-4 hover:underline"
50
- },
51
- size: {
52
- default: "h-[var(--button-height-md,2.25rem)] px-[var(--button-padding-x-md,1rem)] py-2 has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]",
53
- sm: "h-[var(--button-height-sm,2rem)] rounded-button gap-1.5 px-[var(--button-padding-x-sm,0.75rem)] has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]",
54
- md: "h-[var(--button-height-md,2.25rem)] px-[var(--button-padding-x-md,1rem)] py-2 has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]",
55
- lg: "h-[var(--button-height-lg,2.5rem)] rounded-button px-[var(--button-padding-x-lg,1.5rem)] has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]",
56
- icon: "size-[var(--button-height-md,2.25rem)]",
57
- "icon-sm": "size-[var(--button-height-sm,2rem)]",
58
- "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
59
- }
39
+ var baseStyles = [
40
+ // Layout
41
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
42
+ // Typography - using CSS variables with sensible defaults
43
+ "font-[var(--button-font-family,inherit)]",
44
+ "font-[var(--button-font-weight,500)]",
45
+ "tracking-[var(--button-letter-spacing,0)]",
46
+ "leading-[var(--button-line-height,1.25)]",
47
+ "[text-transform:var(--button-text-transform,none)]",
48
+ "text-sm",
49
+ // Border radius
50
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
51
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
52
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
53
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
54
+ "[box-shadow:var(--button-shadow,none)]",
55
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
56
+ // Disabled state
57
+ "disabled:pointer-events-none disabled:opacity-50",
58
+ // SVG handling
59
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
60
+ // Focus styles
61
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
62
+ // Invalid state
63
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
64
+ ].join(" ");
65
+ var buttonVariants = classVarianceAuthority.cva(baseStyles, {
66
+ variants: {
67
+ variant: {
68
+ // Default (Primary) variant - full customization
69
+ default: [
70
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
71
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
72
+ "border-[length:var(--button-default-border-width,0px)]",
73
+ "border-[color:var(--button-default-border,transparent)]",
74
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
75
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
76
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
77
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
78
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
79
+ ].join(" "),
80
+ // Destructive variant - full customization
81
+ destructive: [
82
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
83
+ "text-[var(--button-destructive-fg,white)]",
84
+ "border-[length:var(--button-destructive-border-width,0px)]",
85
+ "border-[color:var(--button-destructive-border,transparent)]",
86
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
87
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
88
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
89
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
90
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
91
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
92
+ "dark:bg-destructive/60"
93
+ ].join(" "),
94
+ // Outline variant - full customization with proper border handling
95
+ outline: [
96
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
97
+ "text-[var(--button-outline-fg,inherit)]",
98
+ "border-[length:var(--button-outline-border-width,1px)]",
99
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
100
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
101
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
102
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
103
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
104
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
105
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
106
+ ].join(" "),
107
+ // Secondary variant - full customization
108
+ secondary: [
109
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
110
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
111
+ "border-[length:var(--button-secondary-border-width,0px)]",
112
+ "border-[color:var(--button-secondary-border,transparent)]",
113
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
114
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
115
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
116
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
117
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
118
+ ].join(" "),
119
+ // Ghost variant - full customization
120
+ ghost: [
121
+ "bg-[var(--button-ghost-bg,transparent)]",
122
+ "text-[var(--button-ghost-fg,inherit)]",
123
+ "border-[length:var(--button-ghost-border-width,0px)]",
124
+ "border-[color:var(--button-ghost-border,transparent)]",
125
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
126
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
127
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
128
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
129
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
130
+ "dark:hover:bg-accent/50"
131
+ ].join(" "),
132
+ // Link variant - full customization
133
+ link: [
134
+ "bg-[var(--button-link-bg,transparent)]",
135
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
136
+ "border-[length:var(--button-link-border-width,0px)]",
137
+ "border-[color:var(--button-link-border,transparent)]",
138
+ "[box-shadow:var(--button-link-shadow,none)]",
139
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
140
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
141
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
142
+ "underline-offset-4 hover:underline"
143
+ ].join(" ")
60
144
  },
61
- defaultVariants: {
62
- variant: "default",
63
- size: "default"
145
+ size: {
146
+ default: [
147
+ "h-[var(--button-height-md,2.25rem)]",
148
+ "px-[var(--button-padding-x-md,1rem)]",
149
+ "py-[var(--button-padding-y-md,0.5rem)]",
150
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
151
+ ].join(" "),
152
+ sm: [
153
+ "h-[var(--button-height-sm,2rem)]",
154
+ "px-[var(--button-padding-x-sm,0.75rem)]",
155
+ "py-[var(--button-padding-y-sm,0.25rem)]",
156
+ "gap-1.5",
157
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
158
+ ].join(" "),
159
+ md: [
160
+ "h-[var(--button-height-md,2.25rem)]",
161
+ "px-[var(--button-padding-x-md,1rem)]",
162
+ "py-[var(--button-padding-y-md,0.5rem)]",
163
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
164
+ ].join(" "),
165
+ lg: [
166
+ "h-[var(--button-height-lg,2.5rem)]",
167
+ "px-[var(--button-padding-x-lg,1.5rem)]",
168
+ "py-[var(--button-padding-y-lg,0.5rem)]",
169
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
170
+ ].join(" "),
171
+ icon: "size-[var(--button-height-md,2.25rem)]",
172
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
173
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
64
174
  }
175
+ },
176
+ defaultVariants: {
177
+ variant: "default",
178
+ size: "default"
65
179
  }
66
- );
180
+ });
67
181
  function Button({
68
182
  className,
69
183
  variant = "default",
@@ -1 +1 @@
1
- {"version":3,"sources":["../lib/utils.ts","../lib/button-variants.ts","../components/ui/button.tsx","../components/ui/dynamic-icon.tsx","../components/ui/carousel.tsx","../components/blocks/features/feature-showcase.tsx"],"names":["twMerge","clsx","cva","Slot","jsx","React","iconName","React2","useEmblaCarousel","api","jsxs","useState","useRef","useEffect"],"mappings":"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","file":"feature-showcase.cjs","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 { cva } from \"class-variance-authority\";\n\n/**\n * Button variants using class-variance-authority (cva).\n * \n * This is extracted to a separate file to avoid importing @radix-ui/react-slot\n * when only the variants are needed (e.g., in Pressable component).\n */\nexport const buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n {\n variants: {\n variant: {\n default: \"bg-[var(--button-default-bg,hsl(var(--primary)))] text-[var(--button-default-fg,hsl(var(--primary-foreground)))] hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]\",\n destructive:\n \"bg-[var(--button-destructive-bg,hsl(var(--destructive)))] text-[var(--button-destructive-fg,white)] hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60\",\n outline:\n \"border-[var(--button-outline-border-width,1px)] border-[var(--button-outline-border,hsl(var(--border)))] bg-[var(--button-outline-bg,hsl(var(--background)))] text-[var(--button-outline-fg,inherit)] shadow-xs hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))] hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))] dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n secondary:\n \"bg-[var(--button-secondary-bg,hsl(var(--secondary)))] text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))] hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]\",\n ghost:\n \"bg-[var(--button-ghost-bg,transparent)] text-[var(--button-ghost-fg,inherit)] hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))] hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))] dark:hover:bg-accent/50\",\n link: \"text-[var(--button-link-fg,hsl(var(--primary)))] underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-[var(--button-height-md,2.25rem)] px-[var(--button-padding-x-md,1rem)] py-2 has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]\",\n sm: \"h-[var(--button-height-sm,2rem)] rounded-button gap-1.5 px-[var(--button-padding-x-sm,0.75rem)] has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]\",\n md: \"h-[var(--button-height-md,2.25rem)] px-[var(--button-padding-x-md,1rem)] py-2 has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]\",\n lg: \"h-[var(--button-height-lg,2.5rem)] rounded-button px-[var(--button-padding-x-lg,1.5rem)] has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]\",\n icon: \"size-[var(--button-height-md,2.25rem)]\",\n \"icon-sm\": \"size-[var(--button-height-sm,2rem)]\",\n \"icon-lg\": \"size-[var(--button-height-lg,2.5rem)]\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\n","import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"../../lib/utils\"\nimport { buttonVariants } from \"../../lib/button-variants\"\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\ninterface DynamicIconProps {\n /**\n * Icon name in format: prefix/name or prefix:name\n * Examples: \"lucide/home\", \"mdi:account\", \"heroicons/check\"\n */\n name: string;\n /**\n * Icon size in pixels\n * @default 28\n */\n size?: number;\n /**\n * Icon color - accepts any valid CSS color\n * Note: When not specified, the icon inherits color from parent via CSS currentColor\n */\n color?: string;\n /**\n * Additional CSS classes\n */\n className?: string;\n /**\n * Alt text for accessibility\n */\n alt?: string;\n}\n\n// Simple in-memory cache for fetched SVGs\nconst svgCache = new Map<string, string>();\n\n/**\n * Lightweight icon component that dynamically loads SVG icons from icons.opensite.ai API.\n *\n * Features:\n * - Pulls SVGs from https://icons.opensite.ai API and inlines them for CSS color inheritance\n * - Supports currentColor - icons inherit color from parent element\n * - Accepts prefix/name or prefix:name format\n * - Customizable size and explicit color via props\n * - In-memory caching to prevent duplicate fetches\n *\n * @example\n * ```tsx\n * // Icon inherits color from parent (recommended for hover states, etc.)\n * <span className=\"text-white hover:text-red-500\">\n * <DynamicIcon name=\"lucide/home\" size={24} />\n * </span>\n *\n * // Icon with explicit color\n * <DynamicIcon name=\"mdi:account\" size={32} color=\"#ff0000\" />\n * ```\n */\nexport function DynamicIcon({\n name,\n size = 28,\n color,\n className,\n alt,\n}: DynamicIconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n const [error, setError] = React.useState<string | null>(null);\n\n const { url, iconName } = React.useMemo(() => {\n const separator = name.includes(\"/\") ? \"/\" : \":\";\n const [prefix, iconName] = name.split(separator);\n // Don't pass color to API - we'll handle it via CSS\n const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName}?format=svg&width=${size}&height=${size}`;\n\n return {\n url: baseUrl,\n iconName,\n };\n }, [name, size]);\n\n React.useEffect(() => {\n let isMounted = true;\n\n const fetchSvg = async () => {\n // Check cache first\n const cached = svgCache.get(url);\n if (cached) {\n if (isMounted) {\n setSvgContent(cached);\n setIsLoading(false);\n }\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n let svg = await response.text();\n\n // Process SVG to ensure currentColor works:\n // 1. Replace any hardcoded colors with currentColor\n // 2. Ensure stroke/fill use currentColor where appropriate\n svg = processSvgForCurrentColor(svg);\n\n // Cache the processed SVG\n svgCache.set(url, svg);\n\n if (isMounted) {\n setSvgContent(svg);\n setIsLoading(false);\n }\n } catch (err) {\n if (isMounted) {\n setError(err instanceof Error ? err.message : \"Failed to load icon\");\n setIsLoading(false);\n }\n }\n };\n\n fetchSvg();\n\n return () => {\n isMounted = false;\n };\n }, [url]);\n\n // Loading state - show placeholder with same dimensions\n if (isLoading) {\n return (\n <span\n className={cn(\"inline-block\", className)}\n style={{ width: size, height: size }}\n aria-hidden=\"true\"\n />\n );\n }\n\n // Error state - show nothing or fallback\n if (error || !svgContent) {\n return (\n <span\n className={cn(\"inline-block\", className)}\n style={{ width: size, height: size }}\n role=\"img\"\n aria-label={alt || iconName}\n />\n );\n }\n\n // Render inline SVG\n // The color prop applies an explicit color, otherwise inherits from parent via currentColor\n return (\n <span\n className={cn(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\",\n }}\n role=\"img\"\n aria-label={alt || iconName}\n dangerouslySetInnerHTML={{ __html: svgContent }}\n />\n );\n}\n\n/**\n * Process SVG to ensure it uses currentColor for proper CSS inheritance.\n * This handles various icon libraries that may use different color approaches.\n */\nfunction processSvgForCurrentColor(svg: string): string {\n // Replace stroke=\"currentColor\" is already correct, but ensure fill also works\n // Some icons use fill=\"none\" with stroke, others use fill with no stroke\n\n // Ensure the SVG doesn't have hardcoded colors that should be currentColor\n // Common patterns to replace:\n // - stroke=\"#000\" or stroke=\"#000000\" or stroke=\"black\" -> stroke=\"currentColor\"\n // - fill=\"#000\" or fill=\"#000000\" or fill=\"black\" -> fill=\"currentColor\"\n\n let processed = svg;\n\n // Replace common black color values with currentColor for stroke\n processed = processed.replace(\n /stroke=[\"'](#000000|#000|black)[\"']/gi,\n 'stroke=\"currentColor\"'\n );\n\n // Replace common black color values with currentColor for fill\n // But be careful not to replace fill=\"none\"\n processed = processed.replace(\n /fill=[\"'](#000000|#000|black)[\"']/gi,\n 'fill=\"currentColor\"'\n );\n\n return processed;\n}\n","import * as React from \"react\";\nimport useEmblaCarousel, {\n type UseEmblaCarouselType,\n} from \"embla-carousel-react\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"./button\";\nimport { DynamicIcon } from \"./dynamic-icon\";\n\ntype CarouselApi = UseEmblaCarouselType[1];\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>;\ntype CarouselOptions = UseCarouselParameters[0];\ntype CarouselPlugin = UseCarouselParameters[1];\n\ntype CarouselProps = {\n opts?: CarouselOptions;\n plugins?: CarouselPlugin;\n orientation?: \"horizontal\" | \"vertical\";\n setApi?: (api: CarouselApi) => void;\n};\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0];\n api: ReturnType<typeof useEmblaCarousel>[1];\n scrollPrev: () => void;\n scrollNext: () => void;\n canScrollPrev: boolean;\n canScrollNext: boolean;\n} & CarouselProps;\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null);\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext);\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a <Carousel />\");\n }\n\n return context;\n}\n\nfunction Carousel({\n orientation = \"horizontal\",\n opts,\n setApi,\n plugins,\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & CarouselProps) {\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n },\n plugins\n );\n const [canScrollPrev, setCanScrollPrev] = React.useState(false);\n const [canScrollNext, setCanScrollNext] = React.useState(false);\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) return;\n setCanScrollPrev(api.canScrollPrev());\n setCanScrollNext(api.canScrollNext());\n }, []);\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev();\n }, [api]);\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext();\n }, [api]);\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n scrollPrev();\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault();\n scrollNext();\n }\n },\n [scrollPrev, scrollNext]\n );\n\n React.useEffect(() => {\n if (!api || !setApi) return;\n setApi(api);\n }, [api, setApi]);\n\n React.useEffect(() => {\n if (!api) return;\n onSelect(api);\n api.on(\"reInit\", onSelect);\n api.on(\"select\", onSelect);\n\n return () => {\n api?.off(\"select\", onSelect);\n };\n }, [api, onSelect]);\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation:\n orientation || (opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n }}\n >\n <div\n onKeyDownCapture={handleKeyDown}\n className={cn(\"relative\", className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n data-slot=\"carousel\"\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n );\n}\n\nfunction CarouselContent({ className, ...props }: React.ComponentProps<\"div\">) {\n const { carouselRef, orientation } = useCarousel();\n\n return (\n <div\n ref={carouselRef}\n className=\"overflow-hidden\"\n data-slot=\"carousel-content\"\n >\n <div\n className={cn(\n \"flex\",\n orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction CarouselItem({ className, ...props }: React.ComponentProps<\"div\">) {\n const { orientation } = useCarousel();\n\n return (\n <div\n role=\"group\"\n aria-roledescription=\"slide\"\n data-slot=\"carousel-item\"\n className={cn(\n \"min-w-0 shrink-0 grow-0 basis-full\",\n orientation === \"horizontal\" ? \"pl-4\" : \"pt-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction CarouselPrevious({\n className,\n variant = \"outline\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel();\n\n return (\n <Button\n data-slot=\"carousel-previous\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -left-12 -translate-y-1/2\"\n : \"-top-12 left-1/2 -translate-x-1/2 rotate-90\",\n className\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n {...props}\n >\n <DynamicIcon name=\"lucide/arrow-left\" />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n );\n}\n\nfunction CarouselNext({\n className,\n variant = \"outline\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollNext, canScrollNext } = useCarousel();\n\n return (\n <Button\n data-slot=\"carousel-next\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -right-12 -translate-y-1/2\"\n : \"-bottom-12 left-1/2 -translate-x-1/2 rotate-90\",\n className\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n {...props}\n >\n <DynamicIcon name=\"lucide/arrow-right\" />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n );\n}\n\nexport {\n type CarouselApi,\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n};\n","\"use client\";\n\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../../../lib/utils\";\nimport {\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselNext,\n CarouselPrevious,\n} from \"../../ui/carousel\";\n\nexport interface FeatureShowcaseItem {\n content: ReactNode;\n mediaComponent: ReactNode;\n}\n\nexport interface FeatureShowcaseProps {\n items: FeatureShowcaseItem[];\n children?: ReactNode;\n className?: string;\n carouselClassName?: string;\n slideClassName?: string;\n contentClassName?: string;\n mediaClassName?: string;\n arrowClassName?: string;\n equalizeOnMobile?: boolean;\n stretchMediaOnMobile?: boolean;\n}\n\n/**\n * Feature Showcase component with carousel navigation\n *\n * Displays feature content with media in a carousel format. Each slide shows\n * content (text, headings) alongside media (images, videos). Features mobile\n * height equalization for consistent slide heights and customizable styling.\n *\n * @example\n * ```tsx\n * <FeatureShowcase\n * items={[\n * {\n * content: <div><h3>Feature 1</h3><p>Description</p></div>,\n * mediaComponent: <img src=\"/feature1.jpg\" alt=\"Feature 1\" />\n * },\n * {\n * content: <div><h3>Feature 2</h3><p>Description</p></div>,\n * mediaComponent: <img src=\"/feature2.jpg\" alt=\"Feature 2\" />\n * }\n * ]}\n * />\n * ```\n */\nexport function FeatureShowcase({\n items,\n children,\n className,\n carouselClassName,\n slideClassName,\n contentClassName,\n mediaClassName,\n arrowClassName,\n equalizeOnMobile = true,\n stretchMediaOnMobile = true,\n}: FeatureShowcaseProps) {\n const baseArrowClassName =\n \"bottom-4 top-auto size-12 translate-y-0 rounded-full border border-current bg-transparent text-current shadow-sm focus:ring-current focus:ring-offset-2 focus:ring-offset-transparent hover:bg-current/10 md:bottom-6\";\n const [mobileSlideHeight, setMobileSlideHeight] = useState<number | null>(\n null\n );\n const slideRefs = useRef<Array<HTMLDivElement | null>>([]);\n const mediaWrapperClassName =\n equalizeOnMobile && stretchMediaOnMobile\n ? \"flex-1 min-h-0 md:flex-none\"\n : \"\";\n\n useEffect(() => {\n if (!equalizeOnMobile) {\n setMobileSlideHeight(null);\n return;\n }\n\n const updateHeights = () => {\n if (typeof window === \"undefined\") return;\n const isMobile = window.innerWidth < 768;\n\n if (!isMobile) {\n setMobileSlideHeight(null);\n return;\n }\n\n const heights = slideRefs.current\n .slice(0, items.length)\n .map((node) => node?.offsetHeight ?? 0);\n const maxHeight = Math.max(...heights, 0);\n\n if (maxHeight > 0) {\n setMobileSlideHeight((prev) =>\n prev === maxHeight ? prev : maxHeight\n );\n }\n };\n\n updateHeights();\n window.addEventListener(\"resize\", updateHeights);\n\n let resizeObserver: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(updateHeights);\n slideRefs.current.slice(0, items.length).forEach((node) => {\n if (node) resizeObserver?.observe(node);\n });\n }\n\n return () => {\n window.removeEventListener(\"resize\", updateHeights);\n resizeObserver?.disconnect();\n };\n }, [equalizeOnMobile, items.length]);\n\n return (\n <div className={className}>\n {children}\n <Carousel className={carouselClassName}>\n <div className=\"pb-18 md:pb-24\">\n <CarouselContent className=\"ease-in\">\n {items.map((item, itemIndex) => (\n <CarouselItem key={`slide-${itemIndex}`}>\n <div\n ref={(node) => {\n slideRefs.current[itemIndex] = node;\n }}\n style={\n equalizeOnMobile && mobileSlideHeight\n ? { minHeight: mobileSlideHeight }\n : undefined\n }\n className={cn(\n \"flex flex-col gap-8 md:gap-14 md:flex-row md:items-center md:justify-between\",\n slideClassName\n )}\n >\n <div className={cn(\"w-full\", contentClassName)}>\n {item.content}\n </div>\n <div\n className={cn(\n \"w-full\",\n mediaWrapperClassName,\n mediaClassName\n )}\n >\n {item.mediaComponent}\n </div>\n </div>\n </CarouselItem>\n ))}\n </CarouselContent>\n </div>\n <CarouselPrevious\n className={cn(baseArrowClassName, \"left-4 md:left-6\", arrowClassName)}\n />\n <CarouselNext\n className={cn(baseArrowClassName, \"right-4 md:right-6\", arrowClassName)}\n />\n </Carousel>\n </div>\n );\n}\n"]}
1
+ {"version":3,"sources":["../lib/utils.ts","../lib/button-variants.ts","../components/ui/button.tsx","../components/ui/dynamic-icon.tsx","../components/ui/carousel.tsx","../components/blocks/features/feature-showcase.tsx"],"names":["twMerge","clsx","cva","Slot","jsx","React","iconName","React2","useEmblaCarousel","api","jsxs","useState","useRef","useEffect"],"mappings":"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","file":"feature-showcase.cjs","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 { cva } from \"class-variance-authority\";\n\n/**\n * Button variants using class-variance-authority (cva).\n *\n * This is extracted to a separate file to avoid importing @radix-ui/react-slot\n * when only the variants are needed (e.g., in Pressable component).\n *\n * ## CSS Variable Reference\n *\n * ### Master Button Variables (apply to all variants)\n * - `--button-font-family` - Font family (default: inherit)\n * - `--button-font-weight` - Font weight (default: 500)\n * - `--button-letter-spacing` - Letter spacing (default: 0)\n * - `--button-line-height` - Line height (default: 1.25)\n * - `--button-text-transform` - Text transform (default: none)\n * - `--button-transition` - Transition timing (default: all 250ms cubic-bezier(0.4, 0, 0.2, 1))\n * - `--button-radius` - Border radius (default: var(--radius, 0.375rem))\n * - `--button-shadow` - Default box shadow (default: none)\n * - `--button-shadow-hover` - Hover box shadow (default: none)\n *\n * ### Size Variables\n * - `--button-height-sm/md/lg` - Button heights\n * - `--button-padding-x-sm/md/lg` - Horizontal padding\n * - `--button-padding-y-sm/md/lg` - Vertical padding\n *\n * ### Per-Variant Variables (replace {variant} with: default, destructive, outline, secondary, ghost, link)\n * - `--button-{variant}-bg` - Background color\n * - `--button-{variant}-fg` - Text/foreground color\n * - `--button-{variant}-border` - Border color\n * - `--button-{variant}-border-width` - Border width\n * - `--button-{variant}-hover-bg` - Hover background color\n * - `--button-{variant}-hover-fg` - Hover text color\n * - `--button-{variant}-hover-border` - Hover border color\n * - `--button-{variant}-shadow` - Box shadow (overrides master)\n * - `--button-{variant}-shadow-hover` - Hover box shadow (overrides master)\n */\n\n// Base styles applied to all buttons - includes master typography, transition, and layout\nconst baseStyles = [\n // Layout\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0\",\n // Typography - using CSS variables with sensible defaults\n \"font-[var(--button-font-family,inherit)]\",\n \"font-[var(--button-font-weight,500)]\",\n \"tracking-[var(--button-letter-spacing,0)]\",\n \"leading-[var(--button-line-height,1.25)]\",\n \"[text-transform:var(--button-text-transform,none)]\",\n \"text-sm\",\n // Border radius\n \"rounded-[var(--button-radius,var(--radius,0.375rem))]\",\n // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)\n \"[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]\",\n // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows\n \"[box-shadow:var(--button-shadow,none)]\",\n \"hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]\",\n // Disabled state\n \"disabled:pointer-events-none disabled:opacity-50\",\n // SVG handling\n \"[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0\",\n // Focus styles\n \"outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]\",\n // Invalid state\n \"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive\",\n].join(\" \");\n\nexport const buttonVariants = cva(baseStyles, {\n variants: {\n variant: {\n // Default (Primary) variant - full customization\n default: [\n \"bg-[var(--button-default-bg,hsl(var(--primary)))]\",\n \"text-[var(--button-default-fg,hsl(var(--primary-foreground)))]\",\n \"border-[length:var(--button-default-border-width,0px)]\",\n \"border-[color:var(--button-default-border,transparent)]\",\n \"[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]\",\n \"hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]\",\n \"hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]\",\n \"hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]\",\n \"hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]\",\n ].join(\" \"),\n\n // Destructive variant - full customization\n destructive: [\n \"bg-[var(--button-destructive-bg,hsl(var(--destructive)))]\",\n \"text-[var(--button-destructive-fg,white)]\",\n \"border-[length:var(--button-destructive-border-width,0px)]\",\n \"border-[color:var(--button-destructive-border,transparent)]\",\n \"[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]\",\n \"hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]\",\n \"hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]\",\n \"hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]\",\n \"hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]\",\n \"focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40\",\n \"dark:bg-destructive/60\",\n ].join(\" \"),\n\n // Outline variant - full customization with proper border handling\n outline: [\n \"bg-[var(--button-outline-bg,hsl(var(--background)))]\",\n \"text-[var(--button-outline-fg,inherit)]\",\n \"border-[length:var(--button-outline-border-width,1px)]\",\n \"border-[color:var(--button-outline-border,hsl(var(--border)))]\",\n \"[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]\",\n \"hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]\",\n \"hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]\",\n \"hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]\",\n \"hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]\",\n \"dark:bg-input/30 dark:border-input dark:hover:bg-input/50\",\n ].join(\" \"),\n\n // Secondary variant - full customization\n secondary: [\n \"bg-[var(--button-secondary-bg,hsl(var(--secondary)))]\",\n \"text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]\",\n \"border-[length:var(--button-secondary-border-width,0px)]\",\n \"border-[color:var(--button-secondary-border,transparent)]\",\n \"[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]\",\n \"hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]\",\n \"hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]\",\n \"hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]\",\n \"hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]\",\n ].join(\" \"),\n\n // Ghost variant - full customization\n ghost: [\n \"bg-[var(--button-ghost-bg,transparent)]\",\n \"text-[var(--button-ghost-fg,inherit)]\",\n \"border-[length:var(--button-ghost-border-width,0px)]\",\n \"border-[color:var(--button-ghost-border,transparent)]\",\n \"[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]\",\n \"hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]\",\n \"hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]\",\n \"hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]\",\n \"hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]\",\n \"dark:hover:bg-accent/50\",\n ].join(\" \"),\n\n // Link variant - full customization\n link: [\n \"bg-[var(--button-link-bg,transparent)]\",\n \"text-[var(--button-link-fg,hsl(var(--primary)))]\",\n \"border-[length:var(--button-link-border-width,0px)]\",\n \"border-[color:var(--button-link-border,transparent)]\",\n \"[box-shadow:var(--button-link-shadow,none)]\",\n \"hover:bg-[var(--button-link-hover-bg,transparent)]\",\n \"hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]\",\n \"hover:[box-shadow:var(--button-link-shadow-hover,none)]\",\n \"underline-offset-4 hover:underline\",\n ].join(\" \"),\n },\n size: {\n default: [\n \"h-[var(--button-height-md,2.25rem)]\",\n \"px-[var(--button-padding-x-md,1rem)]\",\n \"py-[var(--button-padding-y-md,0.5rem)]\",\n \"has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]\",\n ].join(\" \"),\n sm: [\n \"h-[var(--button-height-sm,2rem)]\",\n \"px-[var(--button-padding-x-sm,0.75rem)]\",\n \"py-[var(--button-padding-y-sm,0.25rem)]\",\n \"gap-1.5\",\n \"has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]\",\n ].join(\" \"),\n md: [\n \"h-[var(--button-height-md,2.25rem)]\",\n \"px-[var(--button-padding-x-md,1rem)]\",\n \"py-[var(--button-padding-y-md,0.5rem)]\",\n \"has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]\",\n ].join(\" \"),\n lg: [\n \"h-[var(--button-height-lg,2.5rem)]\",\n \"px-[var(--button-padding-x-lg,1.5rem)]\",\n \"py-[var(--button-padding-y-lg,0.5rem)]\",\n \"has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]\",\n ].join(\" \"),\n icon: \"size-[var(--button-height-md,2.25rem)]\",\n \"icon-sm\": \"size-[var(--button-height-sm,2rem)]\",\n \"icon-lg\": \"size-[var(--button-height-lg,2.5rem)]\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n});\n\n","import * as React from \"react\"\nimport { Slot } from \"@radix-ui/react-slot\"\nimport { type VariantProps } from \"class-variance-authority\"\n\nimport { cn } from \"../../lib/utils\"\nimport { buttonVariants } from \"../../lib/button-variants\"\n\nfunction Button({\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n ...props\n}: React.ComponentProps<\"button\"> &\n VariantProps<typeof buttonVariants> & {\n asChild?: boolean\n }) {\n const Comp = asChild ? Slot : \"button\"\n\n return (\n <Comp\n data-slot=\"button\"\n data-variant={variant}\n data-size={size}\n className={cn(buttonVariants({ variant, size, className }))}\n {...props}\n />\n )\n}\n\nexport { Button, buttonVariants }\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\ninterface DynamicIconProps {\n /**\n * Icon name in format: prefix/name or prefix:name\n * Examples: \"lucide/home\", \"mdi:account\", \"heroicons/check\"\n */\n name: string;\n /**\n * Icon size in pixels\n * @default 28\n */\n size?: number;\n /**\n * Icon color - accepts any valid CSS color\n * Note: When not specified, the icon inherits color from parent via CSS currentColor\n */\n color?: string;\n /**\n * Additional CSS classes\n */\n className?: string;\n /**\n * Alt text for accessibility\n */\n alt?: string;\n}\n\n// Simple in-memory cache for fetched SVGs\nconst svgCache = new Map<string, string>();\n\n/**\n * Lightweight icon component that dynamically loads SVG icons from icons.opensite.ai API.\n *\n * Features:\n * - Pulls SVGs from https://icons.opensite.ai API and inlines them for CSS color inheritance\n * - Supports currentColor - icons inherit color from parent element\n * - Accepts prefix/name or prefix:name format\n * - Customizable size and explicit color via props\n * - In-memory caching to prevent duplicate fetches\n *\n * @example\n * ```tsx\n * // Icon inherits color from parent (recommended for hover states, etc.)\n * <span className=\"text-white hover:text-red-500\">\n * <DynamicIcon name=\"lucide/home\" size={24} />\n * </span>\n *\n * // Icon with explicit color\n * <DynamicIcon name=\"mdi:account\" size={32} color=\"#ff0000\" />\n * ```\n */\nexport function DynamicIcon({\n name,\n size = 28,\n color,\n className,\n alt,\n}: DynamicIconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n const [error, setError] = React.useState<string | null>(null);\n\n const { url, iconName } = React.useMemo(() => {\n const separator = name.includes(\"/\") ? \"/\" : \":\";\n const [prefix, iconName] = name.split(separator);\n // Don't pass color to API - we'll handle it via CSS\n const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName}?format=svg&width=${size}&height=${size}`;\n\n return {\n url: baseUrl,\n iconName,\n };\n }, [name, size]);\n\n React.useEffect(() => {\n let isMounted = true;\n\n const fetchSvg = async () => {\n // Check cache first\n const cached = svgCache.get(url);\n if (cached) {\n if (isMounted) {\n setSvgContent(cached);\n setIsLoading(false);\n }\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n let svg = await response.text();\n\n // Process SVG to ensure currentColor works:\n // 1. Replace any hardcoded colors with currentColor\n // 2. Ensure stroke/fill use currentColor where appropriate\n svg = processSvgForCurrentColor(svg);\n\n // Cache the processed SVG\n svgCache.set(url, svg);\n\n if (isMounted) {\n setSvgContent(svg);\n setIsLoading(false);\n }\n } catch (err) {\n if (isMounted) {\n setError(err instanceof Error ? err.message : \"Failed to load icon\");\n setIsLoading(false);\n }\n }\n };\n\n fetchSvg();\n\n return () => {\n isMounted = false;\n };\n }, [url]);\n\n // Loading state - show placeholder with same dimensions\n if (isLoading) {\n return (\n <span\n className={cn(\"inline-block\", className)}\n style={{ width: size, height: size }}\n aria-hidden=\"true\"\n />\n );\n }\n\n // Error state - show nothing or fallback\n if (error || !svgContent) {\n return (\n <span\n className={cn(\"inline-block\", className)}\n style={{ width: size, height: size }}\n role=\"img\"\n aria-label={alt || iconName}\n />\n );\n }\n\n // Render inline SVG\n // The color prop applies an explicit color, otherwise inherits from parent via currentColor\n return (\n <span\n className={cn(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\",\n }}\n role=\"img\"\n aria-label={alt || iconName}\n dangerouslySetInnerHTML={{ __html: svgContent }}\n />\n );\n}\n\n/**\n * Process SVG to ensure it uses currentColor for proper CSS inheritance.\n * This handles various icon libraries that may use different color approaches.\n */\nfunction processSvgForCurrentColor(svg: string): string {\n // Replace stroke=\"currentColor\" is already correct, but ensure fill also works\n // Some icons use fill=\"none\" with stroke, others use fill with no stroke\n\n // Ensure the SVG doesn't have hardcoded colors that should be currentColor\n // Common patterns to replace:\n // - stroke=\"#000\" or stroke=\"#000000\" or stroke=\"black\" -> stroke=\"currentColor\"\n // - fill=\"#000\" or fill=\"#000000\" or fill=\"black\" -> fill=\"currentColor\"\n\n let processed = svg;\n\n // Replace common black color values with currentColor for stroke\n processed = processed.replace(\n /stroke=[\"'](#000000|#000|black)[\"']/gi,\n 'stroke=\"currentColor\"'\n );\n\n // Replace common black color values with currentColor for fill\n // But be careful not to replace fill=\"none\"\n processed = processed.replace(\n /fill=[\"'](#000000|#000|black)[\"']/gi,\n 'fill=\"currentColor\"'\n );\n\n return processed;\n}\n","import * as React from \"react\";\nimport useEmblaCarousel, {\n type UseEmblaCarouselType,\n} from \"embla-carousel-react\";\n\nimport { cn } from \"../../lib/utils\";\nimport { Button } from \"./button\";\nimport { DynamicIcon } from \"./dynamic-icon\";\n\ntype CarouselApi = UseEmblaCarouselType[1];\ntype UseCarouselParameters = Parameters<typeof useEmblaCarousel>;\ntype CarouselOptions = UseCarouselParameters[0];\ntype CarouselPlugin = UseCarouselParameters[1];\n\ntype CarouselProps = {\n opts?: CarouselOptions;\n plugins?: CarouselPlugin;\n orientation?: \"horizontal\" | \"vertical\";\n setApi?: (api: CarouselApi) => void;\n};\n\ntype CarouselContextProps = {\n carouselRef: ReturnType<typeof useEmblaCarousel>[0];\n api: ReturnType<typeof useEmblaCarousel>[1];\n scrollPrev: () => void;\n scrollNext: () => void;\n canScrollPrev: boolean;\n canScrollNext: boolean;\n} & CarouselProps;\n\nconst CarouselContext = React.createContext<CarouselContextProps | null>(null);\n\nfunction useCarousel() {\n const context = React.useContext(CarouselContext);\n\n if (!context) {\n throw new Error(\"useCarousel must be used within a <Carousel />\");\n }\n\n return context;\n}\n\nfunction Carousel({\n orientation = \"horizontal\",\n opts,\n setApi,\n plugins,\n className,\n children,\n ...props\n}: React.ComponentProps<\"div\"> & CarouselProps) {\n const [carouselRef, api] = useEmblaCarousel(\n {\n ...opts,\n axis: orientation === \"horizontal\" ? \"x\" : \"y\",\n },\n plugins\n );\n const [canScrollPrev, setCanScrollPrev] = React.useState(false);\n const [canScrollNext, setCanScrollNext] = React.useState(false);\n\n const onSelect = React.useCallback((api: CarouselApi) => {\n if (!api) return;\n setCanScrollPrev(api.canScrollPrev());\n setCanScrollNext(api.canScrollNext());\n }, []);\n\n const scrollPrev = React.useCallback(() => {\n api?.scrollPrev();\n }, [api]);\n\n const scrollNext = React.useCallback(() => {\n api?.scrollNext();\n }, [api]);\n\n const handleKeyDown = React.useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === \"ArrowLeft\") {\n event.preventDefault();\n scrollPrev();\n } else if (event.key === \"ArrowRight\") {\n event.preventDefault();\n scrollNext();\n }\n },\n [scrollPrev, scrollNext]\n );\n\n React.useEffect(() => {\n if (!api || !setApi) return;\n setApi(api);\n }, [api, setApi]);\n\n React.useEffect(() => {\n if (!api) return;\n onSelect(api);\n api.on(\"reInit\", onSelect);\n api.on(\"select\", onSelect);\n\n return () => {\n api?.off(\"select\", onSelect);\n };\n }, [api, onSelect]);\n\n return (\n <CarouselContext.Provider\n value={{\n carouselRef,\n api: api,\n opts,\n orientation:\n orientation || (opts?.axis === \"y\" ? \"vertical\" : \"horizontal\"),\n scrollPrev,\n scrollNext,\n canScrollPrev,\n canScrollNext,\n }}\n >\n <div\n onKeyDownCapture={handleKeyDown}\n className={cn(\"relative\", className)}\n role=\"region\"\n aria-roledescription=\"carousel\"\n data-slot=\"carousel\"\n {...props}\n >\n {children}\n </div>\n </CarouselContext.Provider>\n );\n}\n\nfunction CarouselContent({ className, ...props }: React.ComponentProps<\"div\">) {\n const { carouselRef, orientation } = useCarousel();\n\n return (\n <div\n ref={carouselRef}\n className=\"overflow-hidden\"\n data-slot=\"carousel-content\"\n >\n <div\n className={cn(\n \"flex\",\n orientation === \"horizontal\" ? \"-ml-4\" : \"-mt-4 flex-col\",\n className\n )}\n {...props}\n />\n </div>\n );\n}\n\nfunction CarouselItem({ className, ...props }: React.ComponentProps<\"div\">) {\n const { orientation } = useCarousel();\n\n return (\n <div\n role=\"group\"\n aria-roledescription=\"slide\"\n data-slot=\"carousel-item\"\n className={cn(\n \"min-w-0 shrink-0 grow-0 basis-full\",\n orientation === \"horizontal\" ? \"pl-4\" : \"pt-4\",\n className\n )}\n {...props}\n />\n );\n}\n\nfunction CarouselPrevious({\n className,\n variant = \"outline\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollPrev, canScrollPrev } = useCarousel();\n\n return (\n <Button\n data-slot=\"carousel-previous\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -left-12 -translate-y-1/2\"\n : \"-top-12 left-1/2 -translate-x-1/2 rotate-90\",\n className\n )}\n disabled={!canScrollPrev}\n onClick={scrollPrev}\n {...props}\n >\n <DynamicIcon name=\"lucide/arrow-left\" />\n <span className=\"sr-only\">Previous slide</span>\n </Button>\n );\n}\n\nfunction CarouselNext({\n className,\n variant = \"outline\",\n size = \"icon\",\n ...props\n}: React.ComponentProps<typeof Button>) {\n const { orientation, scrollNext, canScrollNext } = useCarousel();\n\n return (\n <Button\n data-slot=\"carousel-next\"\n variant={variant}\n size={size}\n className={cn(\n \"absolute size-8 rounded-full\",\n orientation === \"horizontal\"\n ? \"top-1/2 -right-12 -translate-y-1/2\"\n : \"-bottom-12 left-1/2 -translate-x-1/2 rotate-90\",\n className\n )}\n disabled={!canScrollNext}\n onClick={scrollNext}\n {...props}\n >\n <DynamicIcon name=\"lucide/arrow-right\" />\n <span className=\"sr-only\">Next slide</span>\n </Button>\n );\n}\n\nexport {\n type CarouselApi,\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselPrevious,\n CarouselNext,\n};\n","\"use client\";\n\nimport { useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { cn } from \"../../../lib/utils\";\nimport {\n Carousel,\n CarouselContent,\n CarouselItem,\n CarouselNext,\n CarouselPrevious,\n} from \"../../ui/carousel\";\n\nexport interface FeatureShowcaseItem {\n content: ReactNode;\n mediaComponent: ReactNode;\n}\n\nexport interface FeatureShowcaseProps {\n items: FeatureShowcaseItem[];\n children?: ReactNode;\n className?: string;\n carouselClassName?: string;\n slideClassName?: string;\n contentClassName?: string;\n mediaClassName?: string;\n arrowClassName?: string;\n equalizeOnMobile?: boolean;\n stretchMediaOnMobile?: boolean;\n}\n\n/**\n * Feature Showcase component with carousel navigation\n *\n * Displays feature content with media in a carousel format. Each slide shows\n * content (text, headings) alongside media (images, videos). Features mobile\n * height equalization for consistent slide heights and customizable styling.\n *\n * @example\n * ```tsx\n * <FeatureShowcase\n * items={[\n * {\n * content: <div><h3>Feature 1</h3><p>Description</p></div>,\n * mediaComponent: <img src=\"/feature1.jpg\" alt=\"Feature 1\" />\n * },\n * {\n * content: <div><h3>Feature 2</h3><p>Description</p></div>,\n * mediaComponent: <img src=\"/feature2.jpg\" alt=\"Feature 2\" />\n * }\n * ]}\n * />\n * ```\n */\nexport function FeatureShowcase({\n items,\n children,\n className,\n carouselClassName,\n slideClassName,\n contentClassName,\n mediaClassName,\n arrowClassName,\n equalizeOnMobile = true,\n stretchMediaOnMobile = true,\n}: FeatureShowcaseProps) {\n const baseArrowClassName =\n \"bottom-4 top-auto size-12 translate-y-0 rounded-full border border-current bg-transparent text-current shadow-sm focus:ring-current focus:ring-offset-2 focus:ring-offset-transparent hover:bg-current/10 md:bottom-6\";\n const [mobileSlideHeight, setMobileSlideHeight] = useState<number | null>(\n null\n );\n const slideRefs = useRef<Array<HTMLDivElement | null>>([]);\n const mediaWrapperClassName =\n equalizeOnMobile && stretchMediaOnMobile\n ? \"flex-1 min-h-0 md:flex-none\"\n : \"\";\n\n useEffect(() => {\n if (!equalizeOnMobile) {\n setMobileSlideHeight(null);\n return;\n }\n\n const updateHeights = () => {\n if (typeof window === \"undefined\") return;\n const isMobile = window.innerWidth < 768;\n\n if (!isMobile) {\n setMobileSlideHeight(null);\n return;\n }\n\n const heights = slideRefs.current\n .slice(0, items.length)\n .map((node) => node?.offsetHeight ?? 0);\n const maxHeight = Math.max(...heights, 0);\n\n if (maxHeight > 0) {\n setMobileSlideHeight((prev) =>\n prev === maxHeight ? prev : maxHeight\n );\n }\n };\n\n updateHeights();\n window.addEventListener(\"resize\", updateHeights);\n\n let resizeObserver: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(updateHeights);\n slideRefs.current.slice(0, items.length).forEach((node) => {\n if (node) resizeObserver?.observe(node);\n });\n }\n\n return () => {\n window.removeEventListener(\"resize\", updateHeights);\n resizeObserver?.disconnect();\n };\n }, [equalizeOnMobile, items.length]);\n\n return (\n <div className={className}>\n {children}\n <Carousel className={carouselClassName}>\n <div className=\"pb-18 md:pb-24\">\n <CarouselContent className=\"ease-in\">\n {items.map((item, itemIndex) => (\n <CarouselItem key={`slide-${itemIndex}`}>\n <div\n ref={(node) => {\n slideRefs.current[itemIndex] = node;\n }}\n style={\n equalizeOnMobile && mobileSlideHeight\n ? { minHeight: mobileSlideHeight }\n : undefined\n }\n className={cn(\n \"flex flex-col gap-8 md:gap-14 md:flex-row md:items-center md:justify-between\",\n slideClassName\n )}\n >\n <div className={cn(\"w-full\", contentClassName)}>\n {item.content}\n </div>\n <div\n className={cn(\n \"w-full\",\n mediaWrapperClassName,\n mediaClassName\n )}\n >\n {item.mediaComponent}\n </div>\n </div>\n </CarouselItem>\n ))}\n </CarouselContent>\n </div>\n <CarouselPrevious\n className={cn(baseArrowClassName, \"left-4 md:left-6\", arrowClassName)}\n />\n <CarouselNext\n className={cn(baseArrowClassName, \"right-4 md:right-6\", arrowClassName)}\n />\n </Carousel>\n </div>\n );\n}\n"]}
@@ -12,34 +12,148 @@ import { jsxs, jsx } from 'react/jsx-runtime';
12
12
  function cn(...inputs) {
13
13
  return twMerge(clsx(inputs));
14
14
  }
15
- var buttonVariants = cva(
16
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-button text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
17
- {
18
- variants: {
19
- variant: {
20
- default: "bg-[var(--button-default-bg,hsl(var(--primary)))] text-[var(--button-default-fg,hsl(var(--primary-foreground)))] hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
21
- destructive: "bg-[var(--button-destructive-bg,hsl(var(--destructive)))] text-[var(--button-destructive-fg,white)] hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
22
- outline: "border-[var(--button-outline-border-width,1px)] border-[var(--button-outline-border,hsl(var(--border)))] bg-[var(--button-outline-bg,hsl(var(--background)))] text-[var(--button-outline-fg,inherit)] shadow-xs hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))] hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))] dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
23
- secondary: "bg-[var(--button-secondary-bg,hsl(var(--secondary)))] text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))] hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
24
- ghost: "bg-[var(--button-ghost-bg,transparent)] text-[var(--button-ghost-fg,inherit)] hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))] hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))] dark:hover:bg-accent/50",
25
- link: "text-[var(--button-link-fg,hsl(var(--primary)))] underline-offset-4 hover:underline"
26
- },
27
- size: {
28
- default: "h-[var(--button-height-md,2.25rem)] px-[var(--button-padding-x-md,1rem)] py-2 has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]",
29
- sm: "h-[var(--button-height-sm,2rem)] rounded-button gap-1.5 px-[var(--button-padding-x-sm,0.75rem)] has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]",
30
- md: "h-[var(--button-height-md,2.25rem)] px-[var(--button-padding-x-md,1rem)] py-2 has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]",
31
- lg: "h-[var(--button-height-lg,2.5rem)] rounded-button px-[var(--button-padding-x-lg,1.5rem)] has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]",
32
- icon: "size-[var(--button-height-md,2.25rem)]",
33
- "icon-sm": "size-[var(--button-height-sm,2rem)]",
34
- "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
35
- }
15
+ var baseStyles = [
16
+ // Layout
17
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
18
+ // Typography - using CSS variables with sensible defaults
19
+ "font-[var(--button-font-family,inherit)]",
20
+ "font-[var(--button-font-weight,500)]",
21
+ "tracking-[var(--button-letter-spacing,0)]",
22
+ "leading-[var(--button-line-height,1.25)]",
23
+ "[text-transform:var(--button-text-transform,none)]",
24
+ "text-sm",
25
+ // Border radius
26
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
27
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
28
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
29
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
30
+ "[box-shadow:var(--button-shadow,none)]",
31
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
32
+ // Disabled state
33
+ "disabled:pointer-events-none disabled:opacity-50",
34
+ // SVG handling
35
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
36
+ // Focus styles
37
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
38
+ // Invalid state
39
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
40
+ ].join(" ");
41
+ var buttonVariants = cva(baseStyles, {
42
+ variants: {
43
+ variant: {
44
+ // Default (Primary) variant - full customization
45
+ default: [
46
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
47
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
48
+ "border-[length:var(--button-default-border-width,0px)]",
49
+ "border-[color:var(--button-default-border,transparent)]",
50
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
51
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
52
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
53
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
54
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
55
+ ].join(" "),
56
+ // Destructive variant - full customization
57
+ destructive: [
58
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
59
+ "text-[var(--button-destructive-fg,white)]",
60
+ "border-[length:var(--button-destructive-border-width,0px)]",
61
+ "border-[color:var(--button-destructive-border,transparent)]",
62
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
63
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
64
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
65
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
66
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
67
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
68
+ "dark:bg-destructive/60"
69
+ ].join(" "),
70
+ // Outline variant - full customization with proper border handling
71
+ outline: [
72
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
73
+ "text-[var(--button-outline-fg,inherit)]",
74
+ "border-[length:var(--button-outline-border-width,1px)]",
75
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
76
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
77
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
78
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
79
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
80
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
81
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
82
+ ].join(" "),
83
+ // Secondary variant - full customization
84
+ secondary: [
85
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
86
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
87
+ "border-[length:var(--button-secondary-border-width,0px)]",
88
+ "border-[color:var(--button-secondary-border,transparent)]",
89
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
90
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
91
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
92
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
93
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
94
+ ].join(" "),
95
+ // Ghost variant - full customization
96
+ ghost: [
97
+ "bg-[var(--button-ghost-bg,transparent)]",
98
+ "text-[var(--button-ghost-fg,inherit)]",
99
+ "border-[length:var(--button-ghost-border-width,0px)]",
100
+ "border-[color:var(--button-ghost-border,transparent)]",
101
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
102
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
103
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
104
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
105
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
106
+ "dark:hover:bg-accent/50"
107
+ ].join(" "),
108
+ // Link variant - full customization
109
+ link: [
110
+ "bg-[var(--button-link-bg,transparent)]",
111
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
112
+ "border-[length:var(--button-link-border-width,0px)]",
113
+ "border-[color:var(--button-link-border,transparent)]",
114
+ "[box-shadow:var(--button-link-shadow,none)]",
115
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
116
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
117
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
118
+ "underline-offset-4 hover:underline"
119
+ ].join(" ")
36
120
  },
37
- defaultVariants: {
38
- variant: "default",
39
- size: "default"
121
+ size: {
122
+ default: [
123
+ "h-[var(--button-height-md,2.25rem)]",
124
+ "px-[var(--button-padding-x-md,1rem)]",
125
+ "py-[var(--button-padding-y-md,0.5rem)]",
126
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
127
+ ].join(" "),
128
+ sm: [
129
+ "h-[var(--button-height-sm,2rem)]",
130
+ "px-[var(--button-padding-x-sm,0.75rem)]",
131
+ "py-[var(--button-padding-y-sm,0.25rem)]",
132
+ "gap-1.5",
133
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
134
+ ].join(" "),
135
+ md: [
136
+ "h-[var(--button-height-md,2.25rem)]",
137
+ "px-[var(--button-padding-x-md,1rem)]",
138
+ "py-[var(--button-padding-y-md,0.5rem)]",
139
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
140
+ ].join(" "),
141
+ lg: [
142
+ "h-[var(--button-height-lg,2.5rem)]",
143
+ "px-[var(--button-padding-x-lg,1.5rem)]",
144
+ "py-[var(--button-padding-y-lg,0.5rem)]",
145
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
146
+ ].join(" "),
147
+ icon: "size-[var(--button-height-md,2.25rem)]",
148
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
149
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
40
150
  }
151
+ },
152
+ defaultVariants: {
153
+ variant: "default",
154
+ size: "default"
41
155
  }
42
- );
156
+ });
43
157
  function Button({
44
158
  className,
45
159
  variant = "default",