@music-vine/cadence 3.1.0 → 3.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.
@@ -33,12 +33,12 @@ const buttonVariants = cva(
33
33
  variants: {
34
34
  variant: {
35
35
  // BRANDING VARIANTS (use CSS variables for themeability)
36
- subtle: "border-1 border-gray-150 border-solid bg-white text-gray-950 hover:bg-gray-50 active:border-brand-primary-hover active:bg-brand-secondary active:text-brand-primary-hover disabled:bg-gray-50 active:disabled:border-gray-150 active:disabled:bg-gray-50 active:disabled:text-gray-950 dark:border-gray-800 dark:bg-gray-950 dark:text-white dark:disabled:border-gray-900 dark:disabled:bg-gray-950 dark:disabled:text-white dark:active:border-white dark:active:text-white dark:disabled:active:border-gray-900 dark:disabled:active:bg-gray-950 dark:disabled:active:text-white dark:hover:bg-gray-900",
36
+ subtle: "border-1 border-gray-150 border-solid bg-white text-gray-950 hover:bg-gray-50 active:border-brand-primary-hover active:bg-brand-secondary active:text-brand-primary-hover disabled:bg-gray-50 active:disabled:border-gray-150 active:disabled:bg-gray-50 active:disabled:text-gray-950 dark:border-gray-800 dark:bg-gray-950 dark:text-white dark:disabled:border-gray-900 dark:disabled:bg-gray-950 dark:disabled:text-white dark:active:border-white dark:active:text-white dark:disabled:active:border-gray-900 dark:disabled:active:bg-gray-950 dark:disabled:active:text-white dark:hover:bg-gray-800",
37
37
  brand: "border-1 border-brand-primary border-solid bg-brand-primary font-semibold text-white hover:border-brand-primary-hover hover:bg-brand-primary-hover disabled:hover:bg-brand-primary-hover",
38
- brandSecondary: "border-1 border-brand-secondary border-solid bg-brand-secondary font-semibold text-brand-primary-hover hover:border-brand-secondary-hover hover:bg-brand-secondary-hover disabled:hover:bg-brand-secondary-hover dark:border-gray-900 dark:bg-gray-900 hover:dark:border-gray-800 hover:dark:bg-gray-800 dark:text-white dark:disabled:hover:border-gray-900 dark:disabled:hover:bg-gray-900",
38
+ brandSecondary: "border-1 border-brand-secondary border-solid bg-brand-secondary font-semibold text-brand-primary-hover hover:border-brand-secondary-hover hover:bg-brand-secondary-hover disabled:hover:bg-brand-secondary-hover dark:border-gray-900 dark:bg-gray-900 hover:dark:border-gray-800 hover:dark:bg-gray-800 dark:text-white dark:disabled:hover:border-gray-900 dark:disabled:hover:bg-gray-800",
39
39
  bold: "border-gray-950 bg-gray-950 font-semibold text-white dark:border-white dark:bg-white dark:text-gray-950",
40
40
  light: "border-gray-150 bg-gray-100 font-semibold text-gray-950 hover:border-gray-200 hover:bg-gray-200 disabled:hover:border-gray-150 disabled:hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 dark:disabled:hover:bg-gray-800",
41
- transparent: "border-transparent! bg-transparent font-semibold text-gray-950 hover:border-gray-50 hover:bg-gray-50 disabled:hover:border-transparent disabled:hover:bg-transparent dark:text-white dark:hover:border-gray-900 dark:hover:bg-gray-900",
41
+ transparent: "border-transparent! bg-transparent font-semibold text-gray-950 hover:border-gray-50 hover:bg-gray-50 disabled:hover:border-transparent disabled:hover:bg-transparent dark:text-white dark:hover:border-gray-900 dark:hover:bg-gray-800",
42
42
  contrast: "border-white bg-white font-semibold text-gray-950 hover:bg-gray-50 hover:disabled:border-white hover:disabled:bg-white",
43
43
  success: "border-green-600 bg-green-600 font-semibold text-white hover:border-green-700 hover:bg-green-700 disabled:hover:border-green-600 disabled:hover:bg-green-600 dark:border-green-500 dark:bg-green-500 dark:hover:border-green-650 dark:hover:bg-green-650 dark:disabled:hover:border-green-500 dark:disabled:hover:bg-green-500",
44
44
  error: "border-red-600 bg-red-600 font-semibold text-white hover:bg-red-700 disabled:hover:border-red-700 disabled:hover:bg-red-700 dark:border-red-500 dark:bg-red-500 dark:hover:border-red-600 dark:hover:bg-red-600 dark:disabled:hover:border-red-600 dark:disabled:hover:bg-red-600"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/button.tsx"],
4
- "sourcesContent": ["/**\n * @module Button\n *\n * Versatile button component with multiple variants, sizes, and responsive options.\n * Supports all standard button interactions plus loading states and icon-only modes.\n *\n * @see {@link https://ui.shadcn.com/docs/components/button Shadcn Button}\n *\n * @example\n * // Basic buttons\n * <Button>Default</Button>\n * <Button variant=\"brand\">Primary Action</Button>\n * <Button variant=\"subtle\">Secondary</Button>\n *\n * @example\n * // Sizes\n * <Button size=\"xs\">Extra Small</Button>\n * <Button size=\"sm\">Small</Button>\n * <Button size=\"lg\">Large</Button>\n * <Button size=\"xl\">Extra Large</Button>\n *\n * @example\n * // Responsive size (changes at breakpoints)\n * <Button size={{ default: \"sm\", md: \"default\", lg: \"lg\" }}>\n * Responsive Button\n * </Button>\n *\n * @example\n * // Icon button\n * <Button icon size=\"default\"><PlayIcon /></Button>\n *\n * @example\n * // As link (using asChild)\n * <Button asChild variant=\"brand\">\n * <a href=\"/signup\">Sign Up</a>\n * </Button>\n *\n * @example\n * // With loading state\n * <Button disabled>\n * <Loading visible={isLoading} />\n * {isLoading ? \"Loading...\" : \"Submit\"}\n * </Button>\n */\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { LoaderCircle } from \"lucide-react\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport type { ButtonHTMLAttributes, Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Size classes for consistent button dimensions */\n// Define size classes once as a single source of truth (including leading but not font sizes)\nconst sizeClasses = {\n default: \"h-8 px-4 py-2 leading-8\",\n xs: \"h-5 gap-1.5 px-2 leading-5\",\n sm: \"h-7 px-3 py-2 leading-6\",\n lg: \"h-10 px-4 py-2 leading-8\",\n xl: \"h-12 px-8 py-3 leading-10\",\n icon: \"h-8 w-8 leading-8\",\n} as const;\n\nconst fontSizeClasses = {\n xxs: \"text-xxs\",\n xs: \"text-xs\",\n sm: \"text-sm\",\n base: \"text-base\",\n lg: \"text-lg\",\n xl: \"text-xl\",\n} as const;\n\n// Type definitions for responsive sizes\ntype ButtonSize = \"default\" | \"xs\" | \"sm\" | \"lg\" | \"icon\" | \"xl\";\n\ntype ButtonFontSize = \"xxs\" | \"xs\" | \"sm\" | \"base\" | \"lg\" | \"xl\" | null;\n\n// Default font size mapping based on button size (excludes null)\nconst defaultFontSizeForSize: Record<\n ButtonSize,\n Exclude<ButtonFontSize, null>\n> = {\n xs: \"xs\",\n sm: \"xs\",\n default: \"base\",\n lg: \"base\",\n icon: \"base\",\n xl: \"lg\",\n};\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap font-normal font-sans text-base transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--focus-ring-offset)] enabled:cursor-pointer disabled:cursor-not-allowed disabled:opacity-50\",\n {\n variants: {\n variant: {\n // BRANDING VARIANTS (use CSS variables for themeability)\n subtle:\n \"border-1 border-gray-150 border-solid bg-white text-gray-950 hover:bg-gray-50 active:border-brand-primary-hover active:bg-brand-secondary active:text-brand-primary-hover disabled:bg-gray-50 active:disabled:border-gray-150 active:disabled:bg-gray-50 active:disabled:text-gray-950 dark:border-gray-800 dark:bg-gray-950 dark:text-white dark:disabled:border-gray-900 dark:disabled:bg-gray-950 dark:disabled:text-white dark:active:border-white dark:active:text-white dark:disabled:active:border-gray-900 dark:disabled:active:bg-gray-950 dark:disabled:active:text-white dark:hover:bg-gray-900\",\n brand:\n \"border-1 border-brand-primary border-solid bg-brand-primary font-semibold text-white hover:border-brand-primary-hover hover:bg-brand-primary-hover disabled:hover:bg-brand-primary-hover\",\n brandSecondary:\n \"border-1 border-brand-secondary border-solid bg-brand-secondary font-semibold text-brand-primary-hover hover:border-brand-secondary-hover hover:bg-brand-secondary-hover disabled:hover:bg-brand-secondary-hover dark:border-gray-900 dark:bg-gray-900 hover:dark:border-gray-800 hover:dark:bg-gray-800 dark:text-white dark:disabled:hover:border-gray-900 dark:disabled:hover:bg-gray-900\",\n bold: \"border-gray-950 bg-gray-950 font-semibold text-white dark:border-white dark:bg-white dark:text-gray-950\",\n light:\n \"border-gray-150 bg-gray-100 font-semibold text-gray-950 hover:border-gray-200 hover:bg-gray-200 disabled:hover:border-gray-150 disabled:hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 dark:disabled:hover:bg-gray-800\",\n transparent:\n \"border-transparent! bg-transparent font-semibold text-gray-950 hover:border-gray-50 hover:bg-gray-50 disabled:hover:border-transparent disabled:hover:bg-transparent dark:text-white dark:hover:border-gray-900 dark:hover:bg-gray-900\",\n contrast:\n \"border-white bg-white font-semibold text-gray-950 hover:bg-gray-50 hover:disabled:border-white hover:disabled:bg-white\",\n success:\n \"border-green-600 bg-green-600 font-semibold text-white hover:border-green-700 hover:bg-green-700 disabled:hover:border-green-600 disabled:hover:bg-green-600 dark:border-green-500 dark:bg-green-500 dark:hover:border-green-650 dark:hover:bg-green-650 dark:disabled:hover:border-green-500 dark:disabled:hover:bg-green-500\",\n error:\n \"border-red-600 bg-red-600 font-semibold text-white hover:bg-red-700 disabled:hover:border-red-700 disabled:hover:bg-red-700 dark:border-red-500 dark:bg-red-500 dark:hover:border-red-600 dark:hover:bg-red-600 dark:disabled:hover:border-red-600 dark:disabled:hover:bg-red-600\",\n },\n\n size: sizeClasses,\n fontSize: {\n ...fontSizeClasses,\n null: \"\", // No font size class, relies on compound variants\n },\n borderRadius: {\n default: \"rounded-lg\",\n sm: \"rounded\",\n full: \"rounded-full\",\n rounded: \"rounded-[100px]\",\n },\n border: {\n true: \"border border-solid\",\n false: \"border-none\",\n },\n noFeedback: {\n true: \"\",\n false: \"transition-transform active:scale-[0.97] disabled:scale-100\",\n },\n },\n compoundVariants: [\n // Size + BorderRadius compounds\n {\n size: \"sm\",\n borderRadius: \"default\",\n className: \"rounded-lg\",\n },\n {\n size: \"xs\",\n borderRadius: \"default\",\n className: \"rounded-md\",\n },\n\n // Variant + Border compounds\n {\n variant: \"transparent\",\n border: true,\n className: \"border-gray-200 dark:border-gray-800\",\n },\n {\n variant: \"success\",\n border: true,\n className: \"border-green-dark\",\n },\n {\n variant: \"contrast\",\n border: true,\n className: \"border-white\",\n },\n // Size + fontSize compounds (default font size for each size when no explicit fontSize)\n { size: \"xs\", fontSize: null, className: \"text-xs\" },\n { size: \"sm\", fontSize: null, className: \"text-xs\" },\n { size: \"default\", fontSize: null, className: \"text-base\" },\n { size: \"lg\", fontSize: null, className: \"text-base\" },\n { size: \"icon\", fontSize: null, className: \"text-base\" },\n { size: \"xl\", fontSize: null, className: \"text-lg\" },\n ],\n defaultVariants: {\n variant: \"bold\",\n size: \"default\",\n fontSize: null,\n borderRadius: \"default\",\n border: false,\n noFeedback: false,\n },\n }\n);\n\ntype ResponsiveButtonSize =\n | ButtonSize\n | {\n default: ButtonSize;\n xxs?: ButtonSize;\n xs?: ButtonSize;\n sm?: ButtonSize;\n md?: ButtonSize;\n lg?: ButtonSize;\n xl?: ButtonSize;\n \"2xl\"?: ButtonSize;\n };\n\ntype ResponsiveButtonFontSize =\n | Exclude<ButtonFontSize, null>\n | {\n default?: Exclude<ButtonFontSize, null>;\n xxs?: Exclude<ButtonFontSize, null>;\n xs?: Exclude<ButtonFontSize, null>;\n sm?: Exclude<ButtonFontSize, null>;\n md?: Exclude<ButtonFontSize, null>;\n lg?: Exclude<ButtonFontSize, null>;\n xl?: Exclude<ButtonFontSize, null>;\n \"2xl\"?: Exclude<ButtonFontSize, null>;\n };\n\nconst getSizeClasses = (sizeKey: ButtonSize): string =>\n sizeClasses[sizeKey] || \"\";\n\nconst getFontSizeClasses = (\n fontSizeKey: Exclude<ButtonFontSize, null>\n): string => fontSizeClasses[fontSizeKey] || \"\";\n\n// Function to generate responsive Tailwind classes\nfunction generateResponsiveSizeClasses(size?: ResponsiveButtonSize): string {\n if (!size || typeof size === \"string\") {\n return \"\";\n }\n\n const classes: string[] = [];\n\n // Add responsive classes for each breakpoint\n const breakpoints = [\n { prefix: \"xxs\", value: size.xxs },\n { prefix: \"xs\", value: size.xs },\n { prefix: \"sm\", value: size.sm },\n { prefix: \"md\", value: size.md },\n { prefix: \"lg\", value: size.lg },\n { prefix: \"xl\", value: size.xl },\n { prefix: \"2xl\", value: size[\"2xl\"] },\n ];\n\n for (const breakpoint of breakpoints) {\n if (breakpoint.value) {\n const breakpointSizeClasses = getSizeClasses(breakpoint.value);\n const responsiveClasses = breakpointSizeClasses\n .split(\" \")\n .filter((cls) => cls.trim() !== \"\")\n .map((cls) => `${breakpoint.prefix}:${cls}`)\n .join(\" \");\n if (responsiveClasses) {\n classes.push(responsiveClasses);\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nfunction generateResponsiveFontSizeClasses(\n fontSize?: ResponsiveButtonFontSize\n): string {\n if (!fontSize || typeof fontSize === \"string\") {\n return \"\";\n }\n\n const classes: string[] = [];\n\n // Add responsive classes for each breakpoint\n const breakpoints = [\n { prefix: \"xxs\", value: fontSize.xxs },\n { prefix: \"xs\", value: fontSize.xs },\n { prefix: \"sm\", value: fontSize.sm },\n { prefix: \"md\", value: fontSize.md },\n { prefix: \"lg\", value: fontSize.lg },\n { prefix: \"xl\", value: fontSize.xl },\n { prefix: \"2xl\", value: fontSize[\"2xl\"] },\n ];\n\n for (const breakpoint of breakpoints) {\n if (breakpoint.value) {\n const breakpointFontSizeClasses = getFontSizeClasses(breakpoint.value);\n const responsiveClasses = breakpointFontSizeClasses\n .split(\" \")\n .filter((cls) => cls.trim() !== \"\")\n .map((cls) => `${breakpoint.prefix}:${cls}`)\n .join(\" \");\n if (responsiveClasses) {\n classes.push(responsiveClasses);\n }\n }\n }\n\n return classes.join(\" \");\n}\n\n/**\n * Props for the Button component.\n *\n * @property variant - Color/style: `brand`, `brandSecondary`, `subtle`, `bold`, `light`, `transparent`, `contrast`, `success`, `error`\n * @property size - Size or responsive size object: `xs`, `sm`, `default`, `lg`, `xl`, `icon`\n * @property fontSize - Override font size or responsive font size object\n * @property borderRadius - Corner style: `default`, `sm`, `full`, `rounded`\n * @property border - Show border when true\n * @property noFeedback - Disable scale animation on click\n * @property asChild - Render as child element (e.g., for links)\n * @property icon - Square aspect ratio with no padding (for icon-only buttons)\n */\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>,\n Omit<VariantProps<typeof buttonVariants>, \"size\" | \"fontSize\"> {\n asChild?: boolean;\n fontSize?: ResponsiveButtonFontSize;\n icon?: boolean;\n ref?: Ref<HTMLButtonElement>;\n size?: ResponsiveButtonSize;\n}\n\nconst Button = ({\n className,\n variant,\n size,\n borderRadius,\n border,\n noFeedback,\n asChild = false,\n type,\n children,\n fontSize,\n icon = false,\n ref,\n ...props\n}: ButtonProps) => {\n const Comp = asChild ? SlotPrimitive.Slot : \"button\";\n const buttonType = asChild ? undefined : (type ?? \"button\");\n\n // Generate responsive classes or use the cva size variant\n const responsiveClasses =\n typeof size === \"object\" ? generateResponsiveSizeClasses(size) : undefined;\n\n // Generate responsive font size classes\n let responsiveFontSizeClasses: string | undefined;\n if (typeof fontSize === \"object\") {\n // User provided explicit responsive font sizes\n responsiveFontSizeClasses = generateResponsiveFontSizeClasses(fontSize);\n } else if (typeof size === \"object\" && fontSize === undefined) {\n // Auto-generate responsive font sizes based on responsive sizes\n const autoFontSize: ResponsiveButtonFontSize = {\n default: defaultFontSizeForSize[size.default] || \"base\",\n };\n\n // Map over the size object to create the auto font size object\n const breakpointMappings = [\n { sizeKey: \"xxs\" as const, fontKey: \"xxs\" as const },\n { sizeKey: \"xs\" as const, fontKey: \"xs\" as const },\n { sizeKey: \"sm\" as const, fontKey: \"sm\" as const },\n { sizeKey: \"md\" as const, fontKey: \"md\" as const },\n { sizeKey: \"lg\" as const, fontKey: \"lg\" as const },\n { sizeKey: \"xl\" as const, fontKey: \"xl\" as const },\n { sizeKey: \"2xl\" as const, fontKey: \"2xl\" as const },\n ];\n\n for (const { sizeKey, fontKey } of breakpointMappings) {\n const sizeValue = (size as Record<string, ButtonSize>)[sizeKey];\n if (sizeValue && sizeValue in defaultFontSizeForSize) {\n (autoFontSize as Record<string, Exclude<ButtonFontSize, null>>)[\n fontKey\n ] = defaultFontSizeForSize[sizeValue as ButtonSize];\n }\n }\n\n responsiveFontSizeClasses = generateResponsiveFontSizeClasses(autoFontSize);\n }\n\n // For object sizes, use the default size in the cva\n const cvaSize = typeof size === \"object\" ? size.default : size;\n\n // Determine font size for cva\n let cvaFontSize: ButtonFontSize = null;\n if (fontSize) {\n if (typeof fontSize === \"object\") {\n // If fontSize object has no default, let compound variants handle it (null)\n cvaFontSize = fontSize.default || null;\n } else {\n cvaFontSize = fontSize;\n }\n } // null triggers compound variants for default font sizes\n\n // Generate icon classes if icon prop is true\n let iconClasses = \"\";\n if (icon) {\n iconClasses = \"aspect-square p-0\";\n\n // If using responsive sizes, we need to override padding at each breakpoint\n if (typeof size === \"object\") {\n const iconResponsiveClasses: string[] = [];\n\n const breakpointMappings = [\n { sizeKey: \"xxs\" as const, prefix: \"xxs\" },\n { sizeKey: \"xs\" as const, prefix: \"xs\" },\n { sizeKey: \"sm\" as const, prefix: \"sm\" },\n { sizeKey: \"md\" as const, prefix: \"md\" },\n { sizeKey: \"lg\" as const, prefix: \"lg\" },\n { sizeKey: \"xl\" as const, prefix: \"xl\" },\n { sizeKey: \"2xl\" as const, prefix: \"2xl\" },\n ];\n\n for (const { sizeKey, prefix } of breakpointMappings) {\n const sizeValue = (size as Record<string, ButtonSize>)[sizeKey];\n if (sizeValue) {\n iconResponsiveClasses.push(`${prefix}:p-0`);\n }\n }\n\n if (iconResponsiveClasses.length > 0) {\n iconClasses += ` ${iconResponsiveClasses.join(\" \")}`;\n }\n }\n }\n\n return (\n <Comp\n className={cn(\n buttonVariants({\n variant,\n size: cvaSize,\n fontSize: cvaFontSize,\n borderRadius,\n border,\n noFeedback,\n }),\n responsiveClasses,\n responsiveFontSizeClasses,\n iconClasses,\n className\n )}\n ref={ref}\n type={buttonType}\n {...props}\n >\n {children}\n </Comp>\n );\n};\n\n/** CVA variants for Loading spinner sizing */\nconst loadingVariants = cva(\n \"hidden w-4 animate-[spin_0.75s_linear_infinite] text-inherit\",\n {\n variants: {\n size: {\n default: \"w-4\",\n lg: \"w-5\",\n sm: \"w-3\",\n xs: \"w-3\",\n icon: \"w-4\",\n xl: \"w-5\",\n },\n visible: {\n true: \"block\",\n false: \"hidden\",\n },\n },\n defaultVariants: {\n size: \"default\",\n visible: false,\n },\n }\n);\n\n/**\n * Animated loading spinner for use inside buttons.\n *\n * @param size - Match parent button size for proper scaling\n * @param visible - Show/hide the spinner\n *\n * @example\n * <Button disabled={isLoading}>\n * <Loading visible={isLoading} size=\"default\" />\n * Submit\n * </Button>\n */\nconst Loading = ({ size, visible }: VariantProps<typeof loadingVariants>) => (\n <LoaderCircle className={cn(loadingVariants({ size, visible }))} />\n);\n\nexport type {\n ButtonFontSize,\n ButtonSize,\n ResponsiveButtonFontSize,\n ResponsiveButtonSize,\n};\nexport { Button, buttonVariants, Loading, loadingVariants };\n"],
4
+ "sourcesContent": ["/**\n * @module Button\n *\n * Versatile button component with multiple variants, sizes, and responsive options.\n * Supports all standard button interactions plus loading states and icon-only modes.\n *\n * @see {@link https://ui.shadcn.com/docs/components/button Shadcn Button}\n *\n * @example\n * // Basic buttons\n * <Button>Default</Button>\n * <Button variant=\"brand\">Primary Action</Button>\n * <Button variant=\"subtle\">Secondary</Button>\n *\n * @example\n * // Sizes\n * <Button size=\"xs\">Extra Small</Button>\n * <Button size=\"sm\">Small</Button>\n * <Button size=\"lg\">Large</Button>\n * <Button size=\"xl\">Extra Large</Button>\n *\n * @example\n * // Responsive size (changes at breakpoints)\n * <Button size={{ default: \"sm\", md: \"default\", lg: \"lg\" }}>\n * Responsive Button\n * </Button>\n *\n * @example\n * // Icon button\n * <Button icon size=\"default\"><PlayIcon /></Button>\n *\n * @example\n * // As link (using asChild)\n * <Button asChild variant=\"brand\">\n * <a href=\"/signup\">Sign Up</a>\n * </Button>\n *\n * @example\n * // With loading state\n * <Button disabled>\n * <Loading visible={isLoading} />\n * {isLoading ? \"Loading...\" : \"Submit\"}\n * </Button>\n */\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { LoaderCircle } from \"lucide-react\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport type { ButtonHTMLAttributes, Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Size classes for consistent button dimensions */\n// Define size classes once as a single source of truth (including leading but not font sizes)\nconst sizeClasses = {\n default: \"h-8 px-4 py-2 leading-8\",\n xs: \"h-5 gap-1.5 px-2 leading-5\",\n sm: \"h-7 px-3 py-2 leading-6\",\n lg: \"h-10 px-4 py-2 leading-8\",\n xl: \"h-12 px-8 py-3 leading-10\",\n icon: \"h-8 w-8 leading-8\",\n} as const;\n\nconst fontSizeClasses = {\n xxs: \"text-xxs\",\n xs: \"text-xs\",\n sm: \"text-sm\",\n base: \"text-base\",\n lg: \"text-lg\",\n xl: \"text-xl\",\n} as const;\n\n// Type definitions for responsive sizes\ntype ButtonSize = \"default\" | \"xs\" | \"sm\" | \"lg\" | \"icon\" | \"xl\";\n\ntype ButtonFontSize = \"xxs\" | \"xs\" | \"sm\" | \"base\" | \"lg\" | \"xl\" | null;\n\n// Default font size mapping based on button size (excludes null)\nconst defaultFontSizeForSize: Record<\n ButtonSize,\n Exclude<ButtonFontSize, null>\n> = {\n xs: \"xs\",\n sm: \"xs\",\n default: \"base\",\n lg: \"base\",\n icon: \"base\",\n xl: \"lg\",\n};\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap font-normal font-sans text-base transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--focus-ring-offset)] enabled:cursor-pointer disabled:cursor-not-allowed disabled:opacity-50\",\n {\n variants: {\n variant: {\n // BRANDING VARIANTS (use CSS variables for themeability)\n subtle:\n \"border-1 border-gray-150 border-solid bg-white text-gray-950 hover:bg-gray-50 active:border-brand-primary-hover active:bg-brand-secondary active:text-brand-primary-hover disabled:bg-gray-50 active:disabled:border-gray-150 active:disabled:bg-gray-50 active:disabled:text-gray-950 dark:border-gray-800 dark:bg-gray-950 dark:text-white dark:disabled:border-gray-900 dark:disabled:bg-gray-950 dark:disabled:text-white dark:active:border-white dark:active:text-white dark:disabled:active:border-gray-900 dark:disabled:active:bg-gray-950 dark:disabled:active:text-white dark:hover:bg-gray-800\",\n brand:\n \"border-1 border-brand-primary border-solid bg-brand-primary font-semibold text-white hover:border-brand-primary-hover hover:bg-brand-primary-hover disabled:hover:bg-brand-primary-hover\",\n brandSecondary:\n \"border-1 border-brand-secondary border-solid bg-brand-secondary font-semibold text-brand-primary-hover hover:border-brand-secondary-hover hover:bg-brand-secondary-hover disabled:hover:bg-brand-secondary-hover dark:border-gray-900 dark:bg-gray-900 hover:dark:border-gray-800 hover:dark:bg-gray-800 dark:text-white dark:disabled:hover:border-gray-900 dark:disabled:hover:bg-gray-800\",\n bold: \"border-gray-950 bg-gray-950 font-semibold text-white dark:border-white dark:bg-white dark:text-gray-950\",\n light:\n \"border-gray-150 bg-gray-100 font-semibold text-gray-950 hover:border-gray-200 hover:bg-gray-200 disabled:hover:border-gray-150 disabled:hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 dark:disabled:hover:bg-gray-800\",\n transparent:\n \"border-transparent! bg-transparent font-semibold text-gray-950 hover:border-gray-50 hover:bg-gray-50 disabled:hover:border-transparent disabled:hover:bg-transparent dark:text-white dark:hover:border-gray-900 dark:hover:bg-gray-800\",\n contrast:\n \"border-white bg-white font-semibold text-gray-950 hover:bg-gray-50 hover:disabled:border-white hover:disabled:bg-white\",\n success:\n \"border-green-600 bg-green-600 font-semibold text-white hover:border-green-700 hover:bg-green-700 disabled:hover:border-green-600 disabled:hover:bg-green-600 dark:border-green-500 dark:bg-green-500 dark:hover:border-green-650 dark:hover:bg-green-650 dark:disabled:hover:border-green-500 dark:disabled:hover:bg-green-500\",\n error:\n \"border-red-600 bg-red-600 font-semibold text-white hover:bg-red-700 disabled:hover:border-red-700 disabled:hover:bg-red-700 dark:border-red-500 dark:bg-red-500 dark:hover:border-red-600 dark:hover:bg-red-600 dark:disabled:hover:border-red-600 dark:disabled:hover:bg-red-600\",\n },\n\n size: sizeClasses,\n fontSize: {\n ...fontSizeClasses,\n null: \"\", // No font size class, relies on compound variants\n },\n borderRadius: {\n default: \"rounded-lg\",\n sm: \"rounded\",\n full: \"rounded-full\",\n rounded: \"rounded-[100px]\",\n },\n border: {\n true: \"border border-solid\",\n false: \"border-none\",\n },\n noFeedback: {\n true: \"\",\n false: \"transition-transform active:scale-[0.97] disabled:scale-100\",\n },\n },\n compoundVariants: [\n // Size + BorderRadius compounds\n {\n size: \"sm\",\n borderRadius: \"default\",\n className: \"rounded-lg\",\n },\n {\n size: \"xs\",\n borderRadius: \"default\",\n className: \"rounded-md\",\n },\n\n // Variant + Border compounds\n {\n variant: \"transparent\",\n border: true,\n className: \"border-gray-200 dark:border-gray-800\",\n },\n {\n variant: \"success\",\n border: true,\n className: \"border-green-dark\",\n },\n {\n variant: \"contrast\",\n border: true,\n className: \"border-white\",\n },\n // Size + fontSize compounds (default font size for each size when no explicit fontSize)\n { size: \"xs\", fontSize: null, className: \"text-xs\" },\n { size: \"sm\", fontSize: null, className: \"text-xs\" },\n { size: \"default\", fontSize: null, className: \"text-base\" },\n { size: \"lg\", fontSize: null, className: \"text-base\" },\n { size: \"icon\", fontSize: null, className: \"text-base\" },\n { size: \"xl\", fontSize: null, className: \"text-lg\" },\n ],\n defaultVariants: {\n variant: \"bold\",\n size: \"default\",\n fontSize: null,\n borderRadius: \"default\",\n border: false,\n noFeedback: false,\n },\n }\n);\n\ntype ResponsiveButtonSize =\n | ButtonSize\n | {\n default: ButtonSize;\n xxs?: ButtonSize;\n xs?: ButtonSize;\n sm?: ButtonSize;\n md?: ButtonSize;\n lg?: ButtonSize;\n xl?: ButtonSize;\n \"2xl\"?: ButtonSize;\n };\n\ntype ResponsiveButtonFontSize =\n | Exclude<ButtonFontSize, null>\n | {\n default?: Exclude<ButtonFontSize, null>;\n xxs?: Exclude<ButtonFontSize, null>;\n xs?: Exclude<ButtonFontSize, null>;\n sm?: Exclude<ButtonFontSize, null>;\n md?: Exclude<ButtonFontSize, null>;\n lg?: Exclude<ButtonFontSize, null>;\n xl?: Exclude<ButtonFontSize, null>;\n \"2xl\"?: Exclude<ButtonFontSize, null>;\n };\n\nconst getSizeClasses = (sizeKey: ButtonSize): string =>\n sizeClasses[sizeKey] || \"\";\n\nconst getFontSizeClasses = (\n fontSizeKey: Exclude<ButtonFontSize, null>\n): string => fontSizeClasses[fontSizeKey] || \"\";\n\n// Function to generate responsive Tailwind classes\nfunction generateResponsiveSizeClasses(size?: ResponsiveButtonSize): string {\n if (!size || typeof size === \"string\") {\n return \"\";\n }\n\n const classes: string[] = [];\n\n // Add responsive classes for each breakpoint\n const breakpoints = [\n { prefix: \"xxs\", value: size.xxs },\n { prefix: \"xs\", value: size.xs },\n { prefix: \"sm\", value: size.sm },\n { prefix: \"md\", value: size.md },\n { prefix: \"lg\", value: size.lg },\n { prefix: \"xl\", value: size.xl },\n { prefix: \"2xl\", value: size[\"2xl\"] },\n ];\n\n for (const breakpoint of breakpoints) {\n if (breakpoint.value) {\n const breakpointSizeClasses = getSizeClasses(breakpoint.value);\n const responsiveClasses = breakpointSizeClasses\n .split(\" \")\n .filter((cls) => cls.trim() !== \"\")\n .map((cls) => `${breakpoint.prefix}:${cls}`)\n .join(\" \");\n if (responsiveClasses) {\n classes.push(responsiveClasses);\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nfunction generateResponsiveFontSizeClasses(\n fontSize?: ResponsiveButtonFontSize\n): string {\n if (!fontSize || typeof fontSize === \"string\") {\n return \"\";\n }\n\n const classes: string[] = [];\n\n // Add responsive classes for each breakpoint\n const breakpoints = [\n { prefix: \"xxs\", value: fontSize.xxs },\n { prefix: \"xs\", value: fontSize.xs },\n { prefix: \"sm\", value: fontSize.sm },\n { prefix: \"md\", value: fontSize.md },\n { prefix: \"lg\", value: fontSize.lg },\n { prefix: \"xl\", value: fontSize.xl },\n { prefix: \"2xl\", value: fontSize[\"2xl\"] },\n ];\n\n for (const breakpoint of breakpoints) {\n if (breakpoint.value) {\n const breakpointFontSizeClasses = getFontSizeClasses(breakpoint.value);\n const responsiveClasses = breakpointFontSizeClasses\n .split(\" \")\n .filter((cls) => cls.trim() !== \"\")\n .map((cls) => `${breakpoint.prefix}:${cls}`)\n .join(\" \");\n if (responsiveClasses) {\n classes.push(responsiveClasses);\n }\n }\n }\n\n return classes.join(\" \");\n}\n\n/**\n * Props for the Button component.\n *\n * @property variant - Color/style: `brand`, `brandSecondary`, `subtle`, `bold`, `light`, `transparent`, `contrast`, `success`, `error`\n * @property size - Size or responsive size object: `xs`, `sm`, `default`, `lg`, `xl`, `icon`\n * @property fontSize - Override font size or responsive font size object\n * @property borderRadius - Corner style: `default`, `sm`, `full`, `rounded`\n * @property border - Show border when true\n * @property noFeedback - Disable scale animation on click\n * @property asChild - Render as child element (e.g., for links)\n * @property icon - Square aspect ratio with no padding (for icon-only buttons)\n */\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>,\n Omit<VariantProps<typeof buttonVariants>, \"size\" | \"fontSize\"> {\n asChild?: boolean;\n fontSize?: ResponsiveButtonFontSize;\n icon?: boolean;\n ref?: Ref<HTMLButtonElement>;\n size?: ResponsiveButtonSize;\n}\n\nconst Button = ({\n className,\n variant,\n size,\n borderRadius,\n border,\n noFeedback,\n asChild = false,\n type,\n children,\n fontSize,\n icon = false,\n ref,\n ...props\n}: ButtonProps) => {\n const Comp = asChild ? SlotPrimitive.Slot : \"button\";\n const buttonType = asChild ? undefined : (type ?? \"button\");\n\n // Generate responsive classes or use the cva size variant\n const responsiveClasses =\n typeof size === \"object\" ? generateResponsiveSizeClasses(size) : undefined;\n\n // Generate responsive font size classes\n let responsiveFontSizeClasses: string | undefined;\n if (typeof fontSize === \"object\") {\n // User provided explicit responsive font sizes\n responsiveFontSizeClasses = generateResponsiveFontSizeClasses(fontSize);\n } else if (typeof size === \"object\" && fontSize === undefined) {\n // Auto-generate responsive font sizes based on responsive sizes\n const autoFontSize: ResponsiveButtonFontSize = {\n default: defaultFontSizeForSize[size.default] || \"base\",\n };\n\n // Map over the size object to create the auto font size object\n const breakpointMappings = [\n { sizeKey: \"xxs\" as const, fontKey: \"xxs\" as const },\n { sizeKey: \"xs\" as const, fontKey: \"xs\" as const },\n { sizeKey: \"sm\" as const, fontKey: \"sm\" as const },\n { sizeKey: \"md\" as const, fontKey: \"md\" as const },\n { sizeKey: \"lg\" as const, fontKey: \"lg\" as const },\n { sizeKey: \"xl\" as const, fontKey: \"xl\" as const },\n { sizeKey: \"2xl\" as const, fontKey: \"2xl\" as const },\n ];\n\n for (const { sizeKey, fontKey } of breakpointMappings) {\n const sizeValue = (size as Record<string, ButtonSize>)[sizeKey];\n if (sizeValue && sizeValue in defaultFontSizeForSize) {\n (autoFontSize as Record<string, Exclude<ButtonFontSize, null>>)[\n fontKey\n ] = defaultFontSizeForSize[sizeValue as ButtonSize];\n }\n }\n\n responsiveFontSizeClasses = generateResponsiveFontSizeClasses(autoFontSize);\n }\n\n // For object sizes, use the default size in the cva\n const cvaSize = typeof size === \"object\" ? size.default : size;\n\n // Determine font size for cva\n let cvaFontSize: ButtonFontSize = null;\n if (fontSize) {\n if (typeof fontSize === \"object\") {\n // If fontSize object has no default, let compound variants handle it (null)\n cvaFontSize = fontSize.default || null;\n } else {\n cvaFontSize = fontSize;\n }\n } // null triggers compound variants for default font sizes\n\n // Generate icon classes if icon prop is true\n let iconClasses = \"\";\n if (icon) {\n iconClasses = \"aspect-square p-0\";\n\n // If using responsive sizes, we need to override padding at each breakpoint\n if (typeof size === \"object\") {\n const iconResponsiveClasses: string[] = [];\n\n const breakpointMappings = [\n { sizeKey: \"xxs\" as const, prefix: \"xxs\" },\n { sizeKey: \"xs\" as const, prefix: \"xs\" },\n { sizeKey: \"sm\" as const, prefix: \"sm\" },\n { sizeKey: \"md\" as const, prefix: \"md\" },\n { sizeKey: \"lg\" as const, prefix: \"lg\" },\n { sizeKey: \"xl\" as const, prefix: \"xl\" },\n { sizeKey: \"2xl\" as const, prefix: \"2xl\" },\n ];\n\n for (const { sizeKey, prefix } of breakpointMappings) {\n const sizeValue = (size as Record<string, ButtonSize>)[sizeKey];\n if (sizeValue) {\n iconResponsiveClasses.push(`${prefix}:p-0`);\n }\n }\n\n if (iconResponsiveClasses.length > 0) {\n iconClasses += ` ${iconResponsiveClasses.join(\" \")}`;\n }\n }\n }\n\n return (\n <Comp\n className={cn(\n buttonVariants({\n variant,\n size: cvaSize,\n fontSize: cvaFontSize,\n borderRadius,\n border,\n noFeedback,\n }),\n responsiveClasses,\n responsiveFontSizeClasses,\n iconClasses,\n className\n )}\n ref={ref}\n type={buttonType}\n {...props}\n >\n {children}\n </Comp>\n );\n};\n\n/** CVA variants for Loading spinner sizing */\nconst loadingVariants = cva(\n \"hidden w-4 animate-[spin_0.75s_linear_infinite] text-inherit\",\n {\n variants: {\n size: {\n default: \"w-4\",\n lg: \"w-5\",\n sm: \"w-3\",\n xs: \"w-3\",\n icon: \"w-4\",\n xl: \"w-5\",\n },\n visible: {\n true: \"block\",\n false: \"hidden\",\n },\n },\n defaultVariants: {\n size: \"default\",\n visible: false,\n },\n }\n);\n\n/**\n * Animated loading spinner for use inside buttons.\n *\n * @param size - Match parent button size for proper scaling\n * @param visible - Show/hide the spinner\n *\n * @example\n * <Button disabled={isLoading}>\n * <Loading visible={isLoading} size=\"default\" />\n * Submit\n * </Button>\n */\nconst Loading = ({ size, visible }: VariantProps<typeof loadingVariants>) => (\n <LoaderCircle className={cn(loadingVariants({ size, visible }))} />\n);\n\nexport type {\n ButtonFontSize,\n ButtonSize,\n ResponsiveButtonFontSize,\n ResponsiveButtonSize,\n};\nexport { Button, buttonVariants, Loading, loadingVariants };\n"],
5
5
  "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -4,7 +4,7 @@ const Card = ({ className, ref, ...props }) => /* @__PURE__ */ jsx(
4
4
  "div",
5
5
  {
6
6
  className: cn(
7
- "rounded-lg border border-gray-150 border-solid bg-white text-gray-950 shadow-card dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50",
7
+ "rounded-lg border border-gray-150 border-solid bg-white text-gray-950 shadow-card dark:border-gray-800 dark:bg-gray-875 dark:text-gray-50",
8
8
  className
9
9
  ),
10
10
  ref,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/card.tsx"],
4
- "sourcesContent": ["/**\n * @module Card\n *\n * Container component for grouping related content with consistent styling.\n * Provides structured sections for headers, content, and footers.\n *\n * @see {@link https://ui.shadcn.com/docs/components/card Shadcn Card}\n *\n * @example\n * // Basic card\n * <Card>\n * <CardHeader>\n * <CardTitle>Subscription</CardTitle>\n * <CardDescription>Your current plan details</CardDescription>\n * </CardHeader>\n * <CardContent>\n * <p>Premium Plan - $9.99/month</p>\n * </CardContent>\n * <CardFooter>\n * <Button>Manage</Button>\n * </CardFooter>\n * </Card>\n */\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Main card container with border, shadow, and rounded corners. */\ninterface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst Card = ({ className, ref, ...props }: CardProps) => (\n <div\n className={cn(\n \"rounded-lg border border-gray-150 border-solid bg-white text-gray-950 shadow-card dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Card header section with vertical spacing for title and description. */\ninterface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardHeader = ({ className, ref, ...props }: CardHeaderProps) => (\n <div\n className={cn(\"flex flex-col space-y-1.5 p-6\", className)}\n ref={ref}\n {...props}\n />\n);\n\n/** Card title with semibold weight and larger text. */\ninterface CardTitleProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardTitle = ({ className, ref, ...props }: CardTitleProps) => (\n <div\n className={cn(\n \"font-semibold text-2xl leading-none tracking-tight\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Muted description text below the card title. */\ninterface CardDescriptionProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardDescription = ({\n className,\n ref,\n ...props\n}: CardDescriptionProps) => (\n <div\n className={cn(\"text-gray-600 text-sm dark:text-gray-400\", className)}\n ref={ref}\n {...props}\n />\n);\n\n/** Main content area of the card. */\ninterface CardContentProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardContent = ({ className, ref, ...props }: CardContentProps) => (\n <div className={cn(\"p-6 pt-0\", className)} ref={ref} {...props} />\n);\n\n/** Card footer section, typically for actions/buttons. */\ninterface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardFooter = ({ className, ref, ...props }: CardFooterProps) => (\n <div\n className={cn(\"flex items-center p-6 pt-0\", className)}\n ref={ref}\n {...props}\n />\n);\n\nexport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n};\n"],
4
+ "sourcesContent": ["/**\n * @module Card\n *\n * Container component for grouping related content with consistent styling.\n * Provides structured sections for headers, content, and footers.\n *\n * @see {@link https://ui.shadcn.com/docs/components/card Shadcn Card}\n *\n * @example\n * // Basic card\n * <Card>\n * <CardHeader>\n * <CardTitle>Subscription</CardTitle>\n * <CardDescription>Your current plan details</CardDescription>\n * </CardHeader>\n * <CardContent>\n * <p>Premium Plan - $9.99/month</p>\n * </CardContent>\n * <CardFooter>\n * <Button>Manage</Button>\n * </CardFooter>\n * </Card>\n */\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Main card container with border, shadow, and rounded corners. */\ninterface CardProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst Card = ({ className, ref, ...props }: CardProps) => (\n <div\n className={cn(\n \"rounded-lg border border-gray-150 border-solid bg-white text-gray-950 shadow-card dark:border-gray-800 dark:bg-gray-875 dark:text-gray-50\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Card header section with vertical spacing for title and description. */\ninterface CardHeaderProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardHeader = ({ className, ref, ...props }: CardHeaderProps) => (\n <div\n className={cn(\"flex flex-col space-y-1.5 p-6\", className)}\n ref={ref}\n {...props}\n />\n);\n\n/** Card title with semibold weight and larger text. */\ninterface CardTitleProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardTitle = ({ className, ref, ...props }: CardTitleProps) => (\n <div\n className={cn(\n \"font-semibold text-2xl leading-none tracking-tight\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Muted description text below the card title. */\ninterface CardDescriptionProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardDescription = ({\n className,\n ref,\n ...props\n}: CardDescriptionProps) => (\n <div\n className={cn(\"text-gray-600 text-sm dark:text-gray-400\", className)}\n ref={ref}\n {...props}\n />\n);\n\n/** Main content area of the card. */\ninterface CardContentProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardContent = ({ className, ref, ...props }: CardContentProps) => (\n <div className={cn(\"p-6 pt-0\", className)} ref={ref} {...props} />\n);\n\n/** Card footer section, typically for actions/buttons. */\ninterface CardFooterProps extends React.HTMLAttributes<HTMLDivElement> {\n ref?: Ref<HTMLDivElement>;\n}\n\nconst CardFooter = ({ className, ref, ...props }: CardFooterProps) => (\n <div\n className={cn(\"flex items-center p-6 pt-0\", className)}\n ref={ref}\n {...props}\n />\n);\n\nexport {\n Card,\n CardContent,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n};\n"],
5
5
  "mappings": "AAiCE;AARF,SAAS,UAAU;AAOnB,MAAM,OAAO,CAAC,EAAE,WAAW,KAAK,GAAG,MAAM,MACvC;AAAA,EAAC;AAAA;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN;AAQF,MAAM,aAAa,CAAC,EAAE,WAAW,KAAK,GAAG,MAAM,MAC7C;AAAA,EAAC;AAAA;AAAA,IACC,WAAW,GAAG,iCAAiC,SAAS;AAAA,IACxD;AAAA,IACC,GAAG;AAAA;AACN;AAQF,MAAM,YAAY,CAAC,EAAE,WAAW,KAAK,GAAG,MAAM,MAC5C;AAAA,EAAC;AAAA;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN;AAQF,MAAM,kBAAkB,CAAC;AAAA,EACvB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC;AAAA;AAAA,IACC,WAAW,GAAG,4CAA4C,SAAS;AAAA,IACnE;AAAA,IACC,GAAG;AAAA;AACN;AAQF,MAAM,cAAc,CAAC,EAAE,WAAW,KAAK,GAAG,MAAM,MAC9C,oBAAC,SAAI,WAAW,GAAG,YAAY,SAAS,GAAG,KAAW,GAAG,OAAO;AAQlE,MAAM,aAAa,CAAC,EAAE,WAAW,KAAK,GAAG,MAAM,MAC7C;AAAA,EAAC;AAAA;AAAA,IACC,WAAW,GAAG,8BAA8B,SAAS;AAAA,IACrD;AAAA,IACC,GAAG;AAAA;AACN;",
6
6
  "names": []
7
7
  }
@@ -35,7 +35,7 @@ const DialogContent = ({
35
35
  DialogPrimitive.Content,
36
36
  {
37
37
  className: cn(
38
- "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-150 border-solid bg-white p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:rounded-lg dark:border-gray-800 dark:bg-gray-950",
38
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-150 border-solid bg-white p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:rounded-lg dark:border-gray-800 dark:bg-gray-875",
39
39
  className
40
40
  ),
41
41
  ref,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/dialog.tsx"],
4
- "sourcesContent": ["/**\n * @module Dialog\n *\n * Modal dialog overlay for focused user interaction. Built on Radix UI Dialog primitive.\n * Includes backdrop overlay, close button, and focus trapping.\n *\n * @see {@link https://ui.shadcn.com/docs/components/dialog Shadcn Dialog}\n * @see {@link https://www.radix-ui.com/primitives/docs/components/dialog Radix Dialog}\n *\n * @example\n * // Basic dialog\n * <Dialog>\n * <DialogTrigger asChild>\n * <Button>Open Dialog</Button>\n * </DialogTrigger>\n * <DialogContent>\n * <DialogHeader>\n * <DialogTitle>Are you sure?</DialogTitle>\n * <DialogDescription>\n * This action cannot be undone.\n * </DialogDescription>\n * </DialogHeader>\n * <DialogFooter>\n * <Button>Confirm</Button>\n * </DialogFooter>\n * </DialogContent>\n * </Dialog>\n *\n * @example\n * // Controlled dialog\n * const [open, setOpen] = useState(false);\n *\n * <Dialog open={open} onOpenChange={setOpen}>\n * <DialogContent>\n * <DialogHeader>\n * <DialogTitle>Edit Profile</DialogTitle>\n * </DialogHeader>\n * <form onSubmit={() => setOpen(false)}>\n * ...\n * </form>\n * </DialogContent>\n * </Dialog>\n */\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"./button\";\nimport { headingVariants } from \"./typography/heading\";\nimport { textVariants } from \"./typography/text\";\n\n/** Root component that manages dialog open/closed state. */\nconst Dialog = DialogPrimitive.Root;\n\n/** Element that opens the dialog when clicked. Use `asChild` to wrap custom buttons. */\nconst DialogTrigger = DialogPrimitive.Trigger;\n\n/** Portal for rendering dialog outside the DOM hierarchy. */\nconst DialogPortal = DialogPrimitive.Portal;\n\n/** Closes the dialog when clicked. Use `asChild` to wrap custom close buttons. */\nconst DialogClose = DialogPrimitive.Close;\n\n/** Semi-transparent backdrop behind the dialog. Animated fade on open/close. */\nconst DialogOverlay = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> & {\n ref?: Ref<React.ElementRef<typeof DialogPrimitive.Overlay>>;\n}) => (\n <DialogPrimitive.Overlay\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/60 data-[state=closed]:animate-out data-[state=open]:animate-in\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/**\n * Main dialog container. Centered on screen with close button.\n * Includes overlay, focus trap, and Escape key handling.\n */\nconst DialogContent = ({\n className,\n children,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {\n ref?: Ref<React.ElementRef<typeof DialogPrimitive.Content>>;\n}) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-150 border-solid bg-white p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:rounded-lg dark:border-gray-800 dark:bg-gray-950\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n <DialogPrimitive.Close\n asChild\n className=\"absolute top-4 right-4 opacity-100 ring-offset-white transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-gray-100 data-[state=open]:text-gray-500 dark:ring-offset-gray-950 dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:text-gray-400 dark:focus:ring-gray-300\"\n >\n <Button aria-label=\"Close\" size=\"icon\" variant=\"transparent\">\n <X className=\"h-5 w-5\" />\n <span className=\"sr-only\">Close</span>\n </Button>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n);\n\n/** Header section for dialog title and description. Centered on mobile, left-aligned on desktop. */\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-1.5 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n);\nDialogHeader.displayName = \"DialogHeader\";\n\n/** Footer section for action buttons. Stacks on mobile, horizontal on desktop. */\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n);\nDialogFooter.displayName = \"DialogFooter\";\n\n/** Dialog title with Echo typography. Required for accessibility. */\nconst DialogTitle = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> & {\n ref?: Ref<React.ElementRef<typeof DialogPrimitive.Title>>;\n}) => (\n <DialogPrimitive.Title\n className={cn(\n headingVariants({ variant: \"h5\", fontFamily: \"brand\" }),\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Accessible description text below the title. */\nconst DialogDescription = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> & {\n ref?: Ref<React.ElementRef<typeof DialogPrimitive.Description>>;\n}) => (\n <DialogPrimitive.Description\n className={cn(textVariants(), \"text-sm\", className)}\n ref={ref}\n {...props}\n />\n);\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n"],
4
+ "sourcesContent": ["/**\n * @module Dialog\n *\n * Modal dialog overlay for focused user interaction. Built on Radix UI Dialog primitive.\n * Includes backdrop overlay, close button, and focus trapping.\n *\n * @see {@link https://ui.shadcn.com/docs/components/dialog Shadcn Dialog}\n * @see {@link https://www.radix-ui.com/primitives/docs/components/dialog Radix Dialog}\n *\n * @example\n * // Basic dialog\n * <Dialog>\n * <DialogTrigger asChild>\n * <Button>Open Dialog</Button>\n * </DialogTrigger>\n * <DialogContent>\n * <DialogHeader>\n * <DialogTitle>Are you sure?</DialogTitle>\n * <DialogDescription>\n * This action cannot be undone.\n * </DialogDescription>\n * </DialogHeader>\n * <DialogFooter>\n * <Button>Confirm</Button>\n * </DialogFooter>\n * </DialogContent>\n * </Dialog>\n *\n * @example\n * // Controlled dialog\n * const [open, setOpen] = useState(false);\n *\n * <Dialog open={open} onOpenChange={setOpen}>\n * <DialogContent>\n * <DialogHeader>\n * <DialogTitle>Edit Profile</DialogTitle>\n * </DialogHeader>\n * <form onSubmit={() => setOpen(false)}>\n * ...\n * </form>\n * </DialogContent>\n * </Dialog>\n */\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\nimport { Button } from \"./button\";\nimport { headingVariants } from \"./typography/heading\";\nimport { textVariants } from \"./typography/text\";\n\n/** Root component that manages dialog open/closed state. */\nconst Dialog = DialogPrimitive.Root;\n\n/** Element that opens the dialog when clicked. Use `asChild` to wrap custom buttons. */\nconst DialogTrigger = DialogPrimitive.Trigger;\n\n/** Portal for rendering dialog outside the DOM hierarchy. */\nconst DialogPortal = DialogPrimitive.Portal;\n\n/** Closes the dialog when clicked. Use `asChild` to wrap custom close buttons. */\nconst DialogClose = DialogPrimitive.Close;\n\n/** Semi-transparent backdrop behind the dialog. Animated fade on open/close. */\nconst DialogOverlay = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> & {\n ref?: Ref<React.ElementRef<typeof DialogPrimitive.Overlay>>;\n}) => (\n <DialogPrimitive.Overlay\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/60 data-[state=closed]:animate-out data-[state=open]:animate-in\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/**\n * Main dialog container. Centered on screen with close button.\n * Includes overlay, focus trap, and Escape key handling.\n */\nconst DialogContent = ({\n className,\n children,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {\n ref?: Ref<React.ElementRef<typeof DialogPrimitive.Content>>;\n}) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-gray-150 border-solid bg-white p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:rounded-lg dark:border-gray-800 dark:bg-gray-875\",\n className\n )}\n ref={ref}\n {...props}\n >\n {children}\n <DialogPrimitive.Close\n asChild\n className=\"absolute top-4 right-4 opacity-100 ring-offset-white transition-opacity hover:opacity-100 focus:outline-hidden focus:ring-2 focus:ring-gray-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-gray-100 data-[state=open]:text-gray-500 dark:ring-offset-gray-950 dark:data-[state=open]:bg-gray-800 dark:data-[state=open]:text-gray-400 dark:focus:ring-gray-300\"\n >\n <Button aria-label=\"Close\" size=\"icon\" variant=\"transparent\">\n <X className=\"h-5 w-5\" />\n <span className=\"sr-only\">Close</span>\n </Button>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n);\n\n/** Header section for dialog title and description. Centered on mobile, left-aligned on desktop. */\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-1.5 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n);\nDialogHeader.displayName = \"DialogHeader\";\n\n/** Footer section for action buttons. Stacks on mobile, horizontal on desktop. */\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n);\nDialogFooter.displayName = \"DialogFooter\";\n\n/** Dialog title with Echo typography. Required for accessibility. */\nconst DialogTitle = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title> & {\n ref?: Ref<React.ElementRef<typeof DialogPrimitive.Title>>;\n}) => (\n <DialogPrimitive.Title\n className={cn(\n headingVariants({ variant: \"h5\", fontFamily: \"brand\" }),\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Accessible description text below the title. */\nconst DialogDescription = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description> & {\n ref?: Ref<React.ElementRef<typeof DialogPrimitive.Description>>;\n}) => (\n <DialogPrimitive.Description\n className={cn(textVariants(), \"text-sm\", className)}\n ref={ref}\n {...props}\n />\n);\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n"],
5
5
  "mappings": "AAyEE,cAqCM,YArCN;AA9BF,YAAY,qBAAqB;AACjC,SAAS,SAAS;AAIlB,SAAS,UAAU;AACnB,SAAS,cAAc;AACvB,SAAS,uBAAuB;AAChC,SAAS,oBAAoB;AAG7B,MAAM,SAAS,gBAAgB;AAG/B,MAAM,gBAAgB,gBAAgB;AAGtC,MAAM,eAAe,gBAAgB;AAGrC,MAAM,cAAc,gBAAgB;AAGpC,MAAM,gBAAgB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN;AAOF,MAAM,gBAAgB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE,qBAAC,gBACC;AAAA,sBAAC,iBAAc;AAAA,EACf;AAAA,IAAC,gBAAgB;AAAA,IAAhB;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,QACD;AAAA,UAAC,gBAAgB;AAAA,UAAhB;AAAA,YACC,SAAO;AAAA,YACP,WAAU;AAAA,YAEV,+BAAC,UAAO,cAAW,SAAQ,MAAK,QAAO,SAAQ,eAC7C;AAAA,kCAAC,KAAE,WAAU,WAAU;AAAA,cACvB,oBAAC,UAAK,WAAU,WAAU,mBAAK;AAAA,eACjC;AAAA;AAAA,QACF;AAAA;AAAA;AAAA,EACF;AAAA,GACF;AAIF,MAAM,eAAe,CAAC;AAAA,EACpB;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC;AAAA;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN;AAEF,aAAa,cAAc;AAG3B,MAAM,eAAe,CAAC;AAAA,EACpB;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC;AAAA;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN;AAEF,aAAa,cAAc;AAG3B,MAAM,cAAc,CAAC;AAAA,EACnB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT,gBAAgB,EAAE,SAAS,MAAM,YAAY,QAAQ,CAAC;AAAA,MACtD;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN;AAIF,MAAM,oBAAoB,CAAC;AAAA,EACzB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW,GAAG,aAAa,GAAG,WAAW,SAAS;AAAA,IAClD;AAAA,IACC,GAAG;AAAA;AACN;",
6
6
  "names": []
7
7
  }
@@ -54,7 +54,7 @@ const DrawerContent = ({
54
54
  DrawerPrimitive.Content,
55
55
  {
56
56
  className: cn(
57
- "fixed inset-x-0 bottom-0 z-[9999] mt-16 flex h-auto flex-col rounded-t-[20px] border border-gray-200 bg-white px-6 pt-10 pb-12 dark:border-gray-800 dark:bg-gray-900",
57
+ "fixed inset-x-0 bottom-0 z-[9999] mt-16 flex h-auto flex-col rounded-t-[20px] border border-gray-200 bg-white px-6 pt-10 pb-12 dark:border-gray-800 dark:bg-gray-875",
58
58
  className
59
59
  ),
60
60
  ref,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/drawer.tsx"],
4
- "sourcesContent": ["/**\n * @module Drawer\n *\n * Bottom sheet drawer for mobile-friendly interactions. Built on Vaul library.\n * Supports drag-to-dismiss, snap points, and background scaling.\n *\n * @see {@link https://ui.shadcn.com/docs/components/drawer Shadcn Drawer}\n * @see {@link https://vaul.emilkowal.ski/ Vaul Documentation}\n *\n * @example\n * // Basic drawer\n * <Drawer>\n * <DrawerTrigger asChild>\n * <Button>Open Drawer</Button>\n * </DrawerTrigger>\n * <DrawerContent>\n * <DrawerHeader>\n * <DrawerTitle>Settings</DrawerTitle>\n * <DrawerDescription>Adjust your preferences</DrawerDescription>\n * </DrawerHeader>\n * <div className=\"p-4\">Content here</div>\n * <DrawerFooter>\n * <DrawerClose asChild>\n * <Button variant=\"outline\">Close</Button>\n * </DrawerClose>\n * </DrawerFooter>\n * </DrawerContent>\n * </Drawer>\n *\n * @example\n * // Controlled drawer with custom handle\n * const [open, setOpen] = useState(false);\n *\n * <Drawer open={open} onOpenChange={setOpen}>\n * <DrawerContent handleColor=\"dark\">\n * <DrawerTitle>Menu</DrawerTitle>\n * </DrawerContent>\n * </Drawer>\n */\n\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\n\nimport { cn } from \"../lib/utils\";\n\n/**\n * Root drawer component. Scales background by default.\n * @param shouldScaleBackground - Scale page content when drawer opens (default: true)\n */\nconst Drawer = ({\n shouldScaleBackground = true,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (\n <DrawerPrimitive.Root\n shouldScaleBackground={shouldScaleBackground}\n {...props}\n />\n);\n\nDrawer.displayName = \"Drawer\";\n\n/** Element that opens the drawer when clicked. Use `asChild` to wrap custom triggers. */\nconst DrawerTrigger = DrawerPrimitive.Trigger;\n\n/** Portal for rendering drawer outside the DOM hierarchy. */\nconst DrawerPortal = DrawerPrimitive.Portal;\n\n/** Closes the drawer when clicked. Use `asChild` to wrap custom close buttons. */\nconst DrawerClose = DrawerPrimitive.Close;\n\n/** Semi-transparent backdrop behind the drawer. */\nconst DrawerOverlay = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay> & {\n ref?: Ref<React.ElementRef<typeof DrawerPrimitive.Overlay>>;\n}) => (\n <DrawerPrimitive.Overlay\n className={cn(\"fixed inset-0 z-50 bg-black/80\", className)}\n ref={ref}\n {...props}\n />\n);\n\n/**\n * Main drawer content container. Slides up from bottom with drag handle.\n * @param handleColor - Handle bar color: `\"default\"`, `\"dark\"`, `\"light\"`, or `\"custom\"`\n * @param customHandleColor - Tailwind class for custom handle color (when handleColor=\"custom\")\n */\nconst DrawerContent = ({\n className,\n children,\n handleColor = \"default\",\n customHandleColor,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & {\n handleColor?: \"default\" | \"dark\" | \"light\" | \"custom\";\n customHandleColor?: string;\n ref?: Ref<React.ElementRef<typeof DrawerPrimitive.Content>>;\n}) => {\n const getHandleColorClass = () => {\n switch (handleColor) {\n case \"dark\":\n return \"bg-gray-300 dark:bg-gray-700\";\n case \"light\":\n return \"bg-gray-100 dark:bg-gray-900\";\n case \"custom\":\n return customHandleColor ?? \"bg-gray-200 dark:bg-gray-800\";\n case \"default\":\n default:\n return \"bg-gray-200 dark:bg-gray-800\";\n }\n };\n\n return (\n <DrawerPortal>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n className={cn(\n \"fixed inset-x-0 bottom-0 z-[9999] mt-16 flex h-auto flex-col rounded-t-[20px] border border-gray-200 bg-white px-6 pt-10 pb-12 dark:border-gray-800 dark:bg-gray-900\",\n className\n )}\n ref={ref}\n {...props}\n >\n <div\n className={cn(\n \"absolute top-2 right-0 left-0 mx-auto h-1 w-[100px] rounded-full\",\n getHandleColorClass()\n )}\n />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n};\n\n/** Header section for drawer title and description. Centered on mobile, left-aligned on desktop. */\nconst DrawerHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"grid gap-1.5 p-4 text-center sm:text-left\", className)}\n {...props}\n />\n);\n\nDrawerHeader.displayName = \"DrawerHeader\";\n\n/** Footer section for action buttons, pushed to bottom of drawer. */\nconst DrawerFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n);\n\nDrawerFooter.displayName = \"DrawerFooter\";\n\n/** Drawer title with semibold weight. Required for accessibility. */\nconst DrawerTitle = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title> & {\n ref?: Ref<React.ElementRef<typeof DrawerPrimitive.Title>>;\n}) => (\n <DrawerPrimitive.Title\n className={cn(\n \"font-semibold text-lg leading-none tracking-tight\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Muted description text below the title. */\nconst DrawerDescription = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description> & {\n ref?: Ref<React.ElementRef<typeof DrawerPrimitive.Description>>;\n}) => (\n <DrawerPrimitive.Description\n className={cn(\"text-gray-500 text-sm dark:text-gray-400\", className)}\n ref={ref}\n {...props}\n />\n);\n\nexport {\n Drawer,\n DrawerClose,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerOverlay,\n DrawerPortal,\n DrawerTitle,\n DrawerTrigger,\n};\n"],
4
+ "sourcesContent": ["/**\n * @module Drawer\n *\n * Bottom sheet drawer for mobile-friendly interactions. Built on Vaul library.\n * Supports drag-to-dismiss, snap points, and background scaling.\n *\n * @see {@link https://ui.shadcn.com/docs/components/drawer Shadcn Drawer}\n * @see {@link https://vaul.emilkowal.ski/ Vaul Documentation}\n *\n * @example\n * // Basic drawer\n * <Drawer>\n * <DrawerTrigger asChild>\n * <Button>Open Drawer</Button>\n * </DrawerTrigger>\n * <DrawerContent>\n * <DrawerHeader>\n * <DrawerTitle>Settings</DrawerTitle>\n * <DrawerDescription>Adjust your preferences</DrawerDescription>\n * </DrawerHeader>\n * <div className=\"p-4\">Content here</div>\n * <DrawerFooter>\n * <DrawerClose asChild>\n * <Button variant=\"outline\">Close</Button>\n * </DrawerClose>\n * </DrawerFooter>\n * </DrawerContent>\n * </Drawer>\n *\n * @example\n * // Controlled drawer with custom handle\n * const [open, setOpen] = useState(false);\n *\n * <Drawer open={open} onOpenChange={setOpen}>\n * <DrawerContent handleColor=\"dark\">\n * <DrawerTitle>Menu</DrawerTitle>\n * </DrawerContent>\n * </Drawer>\n */\n\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\nimport { Drawer as DrawerPrimitive } from \"vaul\";\n\nimport { cn } from \"../lib/utils\";\n\n/**\n * Root drawer component. Scales background by default.\n * @param shouldScaleBackground - Scale page content when drawer opens (default: true)\n */\nconst Drawer = ({\n shouldScaleBackground = true,\n ...props\n}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (\n <DrawerPrimitive.Root\n shouldScaleBackground={shouldScaleBackground}\n {...props}\n />\n);\n\nDrawer.displayName = \"Drawer\";\n\n/** Element that opens the drawer when clicked. Use `asChild` to wrap custom triggers. */\nconst DrawerTrigger = DrawerPrimitive.Trigger;\n\n/** Portal for rendering drawer outside the DOM hierarchy. */\nconst DrawerPortal = DrawerPrimitive.Portal;\n\n/** Closes the drawer when clicked. Use `asChild` to wrap custom close buttons. */\nconst DrawerClose = DrawerPrimitive.Close;\n\n/** Semi-transparent backdrop behind the drawer. */\nconst DrawerOverlay = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay> & {\n ref?: Ref<React.ElementRef<typeof DrawerPrimitive.Overlay>>;\n}) => (\n <DrawerPrimitive.Overlay\n className={cn(\"fixed inset-0 z-50 bg-black/80\", className)}\n ref={ref}\n {...props}\n />\n);\n\n/**\n * Main drawer content container. Slides up from bottom with drag handle.\n * @param handleColor - Handle bar color: `\"default\"`, `\"dark\"`, `\"light\"`, or `\"custom\"`\n * @param customHandleColor - Tailwind class for custom handle color (when handleColor=\"custom\")\n */\nconst DrawerContent = ({\n className,\n children,\n handleColor = \"default\",\n customHandleColor,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & {\n handleColor?: \"default\" | \"dark\" | \"light\" | \"custom\";\n customHandleColor?: string;\n ref?: Ref<React.ElementRef<typeof DrawerPrimitive.Content>>;\n}) => {\n const getHandleColorClass = () => {\n switch (handleColor) {\n case \"dark\":\n return \"bg-gray-300 dark:bg-gray-700\";\n case \"light\":\n return \"bg-gray-100 dark:bg-gray-900\";\n case \"custom\":\n return customHandleColor ?? \"bg-gray-200 dark:bg-gray-800\";\n case \"default\":\n default:\n return \"bg-gray-200 dark:bg-gray-800\";\n }\n };\n\n return (\n <DrawerPortal>\n <DrawerOverlay />\n <DrawerPrimitive.Content\n className={cn(\n \"fixed inset-x-0 bottom-0 z-[9999] mt-16 flex h-auto flex-col rounded-t-[20px] border border-gray-200 bg-white px-6 pt-10 pb-12 dark:border-gray-800 dark:bg-gray-875\",\n className\n )}\n ref={ref}\n {...props}\n >\n <div\n className={cn(\n \"absolute top-2 right-0 left-0 mx-auto h-1 w-[100px] rounded-full\",\n getHandleColorClass()\n )}\n />\n {children}\n </DrawerPrimitive.Content>\n </DrawerPortal>\n );\n};\n\n/** Header section for drawer title and description. Centered on mobile, left-aligned on desktop. */\nconst DrawerHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"grid gap-1.5 p-4 text-center sm:text-left\", className)}\n {...props}\n />\n);\n\nDrawerHeader.displayName = \"DrawerHeader\";\n\n/** Footer section for action buttons, pushed to bottom of drawer. */\nconst DrawerFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n);\n\nDrawerFooter.displayName = \"DrawerFooter\";\n\n/** Drawer title with semibold weight. Required for accessibility. */\nconst DrawerTitle = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title> & {\n ref?: Ref<React.ElementRef<typeof DrawerPrimitive.Title>>;\n}) => (\n <DrawerPrimitive.Title\n className={cn(\n \"font-semibold text-lg leading-none tracking-tight\",\n className\n )}\n ref={ref}\n {...props}\n />\n);\n\n/** Muted description text below the title. */\nconst DrawerDescription = ({\n className,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description> & {\n ref?: Ref<React.ElementRef<typeof DrawerPrimitive.Description>>;\n}) => (\n <DrawerPrimitive.Description\n className={cn(\"text-gray-500 text-sm dark:text-gray-400\", className)}\n ref={ref}\n {...props}\n />\n);\n\nexport {\n Drawer,\n DrawerClose,\n DrawerContent,\n DrawerDescription,\n DrawerFooter,\n DrawerHeader,\n DrawerOverlay,\n DrawerPortal,\n DrawerTitle,\n DrawerTrigger,\n};\n"],
5
5
  "mappings": "AAsDE,cAkEI,YAlEJ;AAZF,SAAS,UAAU,uBAAuB;AAE1C,SAAS,UAAU;AAMnB,MAAM,SAAS,CAAC;AAAA,EACd,wBAAwB;AAAA,EACxB,GAAG;AACL,MACE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC;AAAA,IACC,GAAG;AAAA;AACN;AAGF,OAAO,cAAc;AAGrB,MAAM,gBAAgB,gBAAgB;AAGtC,MAAM,eAAe,gBAAgB;AAGrC,MAAM,cAAc,gBAAgB;AAGpC,MAAM,gBAAgB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW,GAAG,kCAAkC,SAAS;AAAA,IACzD;AAAA,IACC,GAAG;AAAA;AACN;AAQF,MAAM,gBAAgB,CAAC;AAAA,EACrB;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAIM;AACJ,QAAM,sBAAsB,MAAM;AAChC,YAAQ,aAAa;AAAA,MACnB,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO,qBAAqB;AAAA,MAC9B,KAAK;AAAA,MACL;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAEA,SACE,qBAAC,gBACC;AAAA,wBAAC,iBAAc;AAAA,IACf;AAAA,MAAC,gBAAgB;AAAA,MAAhB;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACA;AAAA,QACC,GAAG;AAAA,QAEJ;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT;AAAA,gBACA,oBAAoB;AAAA,cACtB;AAAA;AAAA,UACF;AAAA,UACC;AAAA;AAAA;AAAA,IACH;AAAA,KACF;AAEJ;AAGA,MAAM,eAAe,CAAC;AAAA,EACpB;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC;AAAA;AAAA,IACC,WAAW,GAAG,6CAA6C,SAAS;AAAA,IACnE,GAAG;AAAA;AACN;AAGF,aAAa,cAAc;AAG3B,MAAM,eAAe,CAAC;AAAA,EACpB;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC;AAAA;AAAA,IACC,WAAW,GAAG,mCAAmC,SAAS;AAAA,IACzD,GAAG;AAAA;AACN;AAGF,aAAa,cAAc;AAG3B,MAAM,cAAc,CAAC;AAAA,EACnB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN;AAIF,MAAM,oBAAoB,CAAC;AAAA,EACzB;AAAA,EACA;AAAA,EACA,GAAG;AACL,MAGE;AAAA,EAAC,gBAAgB;AAAA,EAAhB;AAAA,IACC,WAAW,GAAG,4CAA4C,SAAS;AAAA,IACnE;AAAA,IACC,GAAG;AAAA;AACN;",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/components/input.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAkBjC,QAAA,MAAM,aAAa;;;;;8EAgElB,CAAC;AAEF,KAAK,qBAAqB,GAAG,IAAI,CAC/B,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,MAAM,CACP,CAAC;AAEF,KAAK,kBAAkB,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAK7D,KAAK,wBAAwB,GAAG,IAAI,CAClC,kBAAkB,EAClB,aAAa,GAAG,cAAc,GAAG,MAAM,CACxC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,UACf,SAAQ,qBAAqB,EAC3B,wBAAwB;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,QAAA,MAAM,KAAK,GAAI,sFAUZ,UAAU,4CAwDZ,CAAC;AAEF,UAAU,qBACR,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACrD,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;CAC9B;AAED;;;GAGG;AACH,QAAA,MAAM,gBAAgB,GAAI,8BAIvB,qBAAqB,4CAcvB,CAAC;AAEF,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/components/input.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAkBjC,QAAA,MAAM,aAAa;;;;;8EAkElB,CAAC;AAEF,KAAK,qBAAqB,GAAG,IAAI,CAC/B,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAC3C,MAAM,CACP,CAAC;AAEF,KAAK,kBAAkB,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,CAAC;AAK7D,KAAK,wBAAwB,GAAG,IAAI,CAClC,kBAAkB,EAClB,aAAa,GAAG,cAAc,GAAG,MAAM,CACxC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,UACf,SAAQ,qBAAqB,EAC3B,wBAAwB;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,GAAG,CAAC,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC5B,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC7B;AAED,QAAA,MAAM,KAAK,GAAI,sFAUZ,UAAU,4CAwDZ,CAAC;AAEF,UAAU,qBACR,SAAQ,KAAK,CAAC,oBAAoB,CAAC,iBAAiB,CAAC;IACrD,GAAG,CAAC,EAAE,GAAG,CAAC,iBAAiB,CAAC,CAAC;CAC9B;AAED;;;GAGG;AACH,QAAA,MAAM,gBAAgB,GAAI,8BAIvB,qBAAqB,4CAcvB,CAAC;AAEF,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC"}
@@ -23,8 +23,10 @@ const inputVariants = cva(
23
23
  "font-sans text-base text-black antialiased disabled:text-black/60 dark:text-white dark:disabled:text-white/60",
24
24
  // placeholder
25
25
  "placeholder-gray-700 placeholder:font-sans dark:placeholder-gray-200",
26
- // background
27
- "bg-white focus:bg-white enabled:hover:bg-white disabled:bg-white/60 dark:bg-black dark:disabled:bg-black/60 dark:focus:bg-black dark:enabled:hover:bg-black",
26
+ // background — transparent across interactive states so the input
27
+ // inherits whatever surface it sits on (page, card, modal, drawer).
28
+ // Disabled keeps a faded fill so the state stays distinguishable.
29
+ "bg-transparent focus:bg-transparent enabled:hover:bg-transparent disabled:bg-white/60 dark:disabled:bg-black/60",
28
30
  // borders (light)
29
31
  "rounded-lg border border-gray-150 border-solid invalid:border-red invalid:hover:border-red focus:border-brand-primary enabled:hover:border-gray-200 enabled:focus:hover:border-brand-primary disabled:border-gray-150/60",
30
32
  // borders light
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/input.tsx"],
4
- "sourcesContent": ["/**\n * @module Input\n *\n * Styled text input with support for icons, sizes, and clear button.\n * Includes focus state management and dark mode support.\n *\n * @example\n * // Basic input\n * <Input placeholder=\"Enter your email\" type=\"email\" />\n *\n * @example\n * // With icons\n * <Input\n * leftIcon={<Search />}\n * rightIcon={<ClearInputButton onClick={clearValue} />}\n * placeholder=\"Search...\"\n * />\n *\n * @example\n * // Small size\n * <Input size=\"sm\" placeholder=\"Compact input\" />\n *\n * @example\n * // With clear button\n * const [value, setValue] = useState('');\n *\n * <Input\n * value={value}\n * onChange={(e) => setValue(e.target.value)}\n * rightIcon={\n * value && <ClearInputButton onClick={() => setValue('')} />\n * }\n * />\n */\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport type { Ref } from \"react\";\nimport { useState } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\nconst inputWrapperVariants = cva(\"relative flex w-full items-center\", {\n variants: {\n size: {\n auto: \"h-auto w-auto\",\n default: \"h-10\",\n sm: \"h-7\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n});\n\nconst inputVariants = cva(\n [\n \"w-full touch-manipulation self-stretch shadow-none outline-hidden outline-0 transition duration-200 ease-out disabled:cursor-not-allowed dark:disabled:cursor-not-allowed\",\n // text\n \"font-sans text-base text-black antialiased disabled:text-black/60 dark:text-white dark:disabled:text-white/60\",\n // placeholder\n \"placeholder-gray-700 placeholder:font-sans dark:placeholder-gray-200\",\n // background\n \"bg-white focus:bg-white enabled:hover:bg-white disabled:bg-white/60 dark:bg-black dark:disabled:bg-black/60 dark:focus:bg-black dark:enabled:hover:bg-black\",\n // borders (light)\n \"rounded-lg border border-gray-150 border-solid invalid:border-red invalid:hover:border-red focus:border-brand-primary enabled:hover:border-gray-200 enabled:focus:hover:border-brand-primary disabled:border-gray-150/60\", // borders light\n // borders (dark)\n \"dark:border-gray-800 dark:disabled:border-gray-900 dark:focus:border-white dark:enabled:hover:border-gray-700 dark:enabled:focus:hover:border-white dark:invalid:border-red dark:invalid:hover:border-red\", // borders dark\n ],\n {\n variants: {\n size: {\n default: \"px-4\",\n sm: \"px-3\",\n auto: \"px-4\",\n },\n hasLeftIcon: {\n true: \"\",\n false: \"\",\n },\n hasRightIcon: {\n true: \"\",\n false: \"\",\n },\n // Scoped to type=\"search\" only: the Keeper password manager extension\n // misidentifies inputs that style ::-webkit-search-cancel-button as\n // search fields and blocks paste, so non-search inputs must not match.\n type: {\n search: \"dark:[&::-webkit-search-cancel-button]:hidden\",\n },\n },\n compoundVariants: [\n {\n hasLeftIcon: true,\n size: \"default\",\n class: \"pl-10\",\n },\n {\n hasLeftIcon: true,\n size: \"sm\",\n class: \"pl-8\",\n },\n {\n hasRightIcon: true,\n size: \"default\",\n class: \"pr-10\",\n },\n {\n hasRightIcon: true,\n size: \"sm\",\n class: \"pr-8\",\n },\n ],\n defaultVariants: {\n size: \"default\",\n hasLeftIcon: false,\n hasRightIcon: false,\n },\n }\n);\n\ntype InputPropsWithoutSize = Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"size\"\n>;\n\ntype InputVariantsProps = VariantProps<typeof inputVariants>;\n\n// Omit internal-only variants from the public API. `type` is excluded so\n// consumers keep the full HTMLInputTypeAttribute union; CVA still picks up\n// the runtime value via the `type` argument passed in inputVariants().\ntype PublicInputVariantsProps = Omit<\n InputVariantsProps,\n \"hasLeftIcon\" | \"hasRightIcon\" | \"type\"\n>;\n\n/**\n * Props for the Input component.\n * @property size - Input size: `\"default\"`, `\"sm\"`, or `\"auto\"`\n * @property htmlSize - Native HTML size attribute for input width\n * @property leftIcon - Icon element displayed on the left side\n * @property rightIcon - Icon element displayed on the right side\n */\nexport interface InputProps\n extends InputPropsWithoutSize,\n PublicInputVariantsProps {\n htmlSize?: number;\n leftIcon?: React.ReactNode;\n ref?: Ref<HTMLInputElement>;\n rightIcon?: React.ReactNode;\n}\n\nconst Input = ({\n className,\n size,\n type = \"text\",\n htmlSize,\n leftIcon,\n rightIcon,\n placeholder,\n ref,\n ...props\n}: InputProps) => {\n const [isFocused, setIsFocused] = useState(false);\n\n return (\n // biome-ignore lint/a11y/noNoninteractiveElementInteractions: container needs onBlur to coordinate focus state across child input and icon slots\n // biome-ignore lint/a11y/noStaticElementInteractions: container needs onBlur to coordinate focus state across child input and icon slots\n <div\n className={cn(inputWrapperVariants({ size }))}\n onBlur={() => {\n setTimeout(() => {\n setIsFocused(false);\n }, 50);\n }}\n >\n {!!leftIcon && (\n <SlotPrimitive.Slot\n className={cn(\n \"absolute top-1/2 -translate-y-1/2 text-gray-950 dark:text-gray-300 [&:is(svg)]:pointer-events-none\",\n size === \"sm\" ? \"left-3 size-3\" : \"left-4 size-4\"\n )}\n >\n {leftIcon}\n </SlotPrimitive.Slot>\n )}\n <input\n className={cn(\n inputVariants({\n size,\n hasLeftIcon: !!leftIcon,\n hasRightIcon: !!rightIcon,\n type: type === \"search\" ? \"search\" : undefined,\n className,\n })\n )}\n onFocus={(e) => {\n setIsFocused(true);\n props.onFocus?.(e);\n }}\n placeholder={isFocused ? \" \" : placeholder}\n ref={ref}\n size={htmlSize}\n type={type}\n {...props}\n />\n {!!rightIcon && (\n <SlotPrimitive.Slot\n className={cn(\n \"absolute top-1/2 -translate-y-1/2 text-gray-950 dark:text-gray-300 [&:is(svg)]:pointer-events-none\",\n size === \"sm\" ? \"right-3 size-3\" : \"right-4 size-4\"\n )}\n >\n {rightIcon}\n </SlotPrimitive.Slot>\n )}\n </div>\n );\n};\n\ninterface ClearInputButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * Clear/reset button designed for use as Input's rightIcon.\n * Displays an X icon and handles focus states.\n */\nconst ClearInputButton = ({\n className,\n ref,\n ...props\n}: ClearInputButtonProps) => (\n <button\n className={cn(\n \"absolute top-1/2 -translate-y-1/2 text-gray-950 dark:text-gray-300\",\n \"focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-gray-100\",\n \"rounded-full focus-visible:outline-hidden dark:focus-visible:ring-[var(--focus-ring)] dark:focus-visible:ring-offset-gray-800\",\n className\n )}\n ref={ref}\n type=\"button\"\n {...props}\n >\n <X className=\"absolute inset-0 size-full\" strokeWidth={3} />\n </button>\n);\n\nexport { ClearInputButton, Input, inputVariants };\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * @module Input\n *\n * Styled text input with support for icons, sizes, and clear button.\n * Includes focus state management and dark mode support.\n *\n * @example\n * // Basic input\n * <Input placeholder=\"Enter your email\" type=\"email\" />\n *\n * @example\n * // With icons\n * <Input\n * leftIcon={<Search />}\n * rightIcon={<ClearInputButton onClick={clearValue} />}\n * placeholder=\"Search...\"\n * />\n *\n * @example\n * // Small size\n * <Input size=\"sm\" placeholder=\"Compact input\" />\n *\n * @example\n * // With clear button\n * const [value, setValue] = useState('');\n *\n * <Input\n * value={value}\n * onChange={(e) => setValue(e.target.value)}\n * rightIcon={\n * value && <ClearInputButton onClick={() => setValue('')} />\n * }\n * />\n */\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { X } from \"lucide-react\";\nimport { Slot as SlotPrimitive } from \"radix-ui\";\nimport type { Ref } from \"react\";\nimport { useState } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\nconst inputWrapperVariants = cva(\"relative flex w-full items-center\", {\n variants: {\n size: {\n auto: \"h-auto w-auto\",\n default: \"h-10\",\n sm: \"h-7\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n});\n\nconst inputVariants = cva(\n [\n \"w-full touch-manipulation self-stretch shadow-none outline-hidden outline-0 transition duration-200 ease-out disabled:cursor-not-allowed dark:disabled:cursor-not-allowed\",\n // text\n \"font-sans text-base text-black antialiased disabled:text-black/60 dark:text-white dark:disabled:text-white/60\",\n // placeholder\n \"placeholder-gray-700 placeholder:font-sans dark:placeholder-gray-200\",\n // background \u2014 transparent across interactive states so the input\n // inherits whatever surface it sits on (page, card, modal, drawer).\n // Disabled keeps a faded fill so the state stays distinguishable.\n \"bg-transparent focus:bg-transparent enabled:hover:bg-transparent disabled:bg-white/60 dark:disabled:bg-black/60\",\n // borders (light)\n \"rounded-lg border border-gray-150 border-solid invalid:border-red invalid:hover:border-red focus:border-brand-primary enabled:hover:border-gray-200 enabled:focus:hover:border-brand-primary disabled:border-gray-150/60\", // borders light\n // borders (dark)\n \"dark:border-gray-800 dark:disabled:border-gray-900 dark:focus:border-white dark:enabled:hover:border-gray-700 dark:enabled:focus:hover:border-white dark:invalid:border-red dark:invalid:hover:border-red\", // borders dark\n ],\n {\n variants: {\n size: {\n default: \"px-4\",\n sm: \"px-3\",\n auto: \"px-4\",\n },\n hasLeftIcon: {\n true: \"\",\n false: \"\",\n },\n hasRightIcon: {\n true: \"\",\n false: \"\",\n },\n // Scoped to type=\"search\" only: the Keeper password manager extension\n // misidentifies inputs that style ::-webkit-search-cancel-button as\n // search fields and blocks paste, so non-search inputs must not match.\n type: {\n search: \"dark:[&::-webkit-search-cancel-button]:hidden\",\n },\n },\n compoundVariants: [\n {\n hasLeftIcon: true,\n size: \"default\",\n class: \"pl-10\",\n },\n {\n hasLeftIcon: true,\n size: \"sm\",\n class: \"pl-8\",\n },\n {\n hasRightIcon: true,\n size: \"default\",\n class: \"pr-10\",\n },\n {\n hasRightIcon: true,\n size: \"sm\",\n class: \"pr-8\",\n },\n ],\n defaultVariants: {\n size: \"default\",\n hasLeftIcon: false,\n hasRightIcon: false,\n },\n }\n);\n\ntype InputPropsWithoutSize = Omit<\n React.InputHTMLAttributes<HTMLInputElement>,\n \"size\"\n>;\n\ntype InputVariantsProps = VariantProps<typeof inputVariants>;\n\n// Omit internal-only variants from the public API. `type` is excluded so\n// consumers keep the full HTMLInputTypeAttribute union; CVA still picks up\n// the runtime value via the `type` argument passed in inputVariants().\ntype PublicInputVariantsProps = Omit<\n InputVariantsProps,\n \"hasLeftIcon\" | \"hasRightIcon\" | \"type\"\n>;\n\n/**\n * Props for the Input component.\n * @property size - Input size: `\"default\"`, `\"sm\"`, or `\"auto\"`\n * @property htmlSize - Native HTML size attribute for input width\n * @property leftIcon - Icon element displayed on the left side\n * @property rightIcon - Icon element displayed on the right side\n */\nexport interface InputProps\n extends InputPropsWithoutSize,\n PublicInputVariantsProps {\n htmlSize?: number;\n leftIcon?: React.ReactNode;\n ref?: Ref<HTMLInputElement>;\n rightIcon?: React.ReactNode;\n}\n\nconst Input = ({\n className,\n size,\n type = \"text\",\n htmlSize,\n leftIcon,\n rightIcon,\n placeholder,\n ref,\n ...props\n}: InputProps) => {\n const [isFocused, setIsFocused] = useState(false);\n\n return (\n // biome-ignore lint/a11y/noNoninteractiveElementInteractions: container needs onBlur to coordinate focus state across child input and icon slots\n // biome-ignore lint/a11y/noStaticElementInteractions: container needs onBlur to coordinate focus state across child input and icon slots\n <div\n className={cn(inputWrapperVariants({ size }))}\n onBlur={() => {\n setTimeout(() => {\n setIsFocused(false);\n }, 50);\n }}\n >\n {!!leftIcon && (\n <SlotPrimitive.Slot\n className={cn(\n \"absolute top-1/2 -translate-y-1/2 text-gray-950 dark:text-gray-300 [&:is(svg)]:pointer-events-none\",\n size === \"sm\" ? \"left-3 size-3\" : \"left-4 size-4\"\n )}\n >\n {leftIcon}\n </SlotPrimitive.Slot>\n )}\n <input\n className={cn(\n inputVariants({\n size,\n hasLeftIcon: !!leftIcon,\n hasRightIcon: !!rightIcon,\n type: type === \"search\" ? \"search\" : undefined,\n className,\n })\n )}\n onFocus={(e) => {\n setIsFocused(true);\n props.onFocus?.(e);\n }}\n placeholder={isFocused ? \" \" : placeholder}\n ref={ref}\n size={htmlSize}\n type={type}\n {...props}\n />\n {!!rightIcon && (\n <SlotPrimitive.Slot\n className={cn(\n \"absolute top-1/2 -translate-y-1/2 text-gray-950 dark:text-gray-300 [&:is(svg)]:pointer-events-none\",\n size === \"sm\" ? \"right-3 size-3\" : \"right-4 size-4\"\n )}\n >\n {rightIcon}\n </SlotPrimitive.Slot>\n )}\n </div>\n );\n};\n\ninterface ClearInputButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n ref?: Ref<HTMLButtonElement>;\n}\n\n/**\n * Clear/reset button designed for use as Input's rightIcon.\n * Displays an X icon and handles focus states.\n */\nconst ClearInputButton = ({\n className,\n ref,\n ...props\n}: ClearInputButtonProps) => (\n <button\n className={cn(\n \"absolute top-1/2 -translate-y-1/2 text-gray-950 dark:text-gray-300\",\n \"focus-visible:ring-2 focus-visible:ring-[var(--focus-ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-gray-100\",\n \"rounded-full focus-visible:outline-hidden dark:focus-visible:ring-[var(--focus-ring)] dark:focus-visible:ring-offset-gray-800\",\n className\n )}\n ref={ref}\n type=\"button\"\n {...props}\n >\n <X className=\"absolute inset-0 size-full\" strokeWidth={3} />\n </button>\n);\n\nexport { ClearInputButton, Input, inputVariants };\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -16,7 +16,7 @@ const PopoverContent = ({
16
16
  {
17
17
  align,
18
18
  className: cn(
19
- "data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 z-50 origin-[var(--radix-popover-content-transform-origin)] rounded-md border border-gray-150 border-solid bg-white fill-mode-forwards p-4 text-gray-950 shadow-md outline-hidden data-[state=closed]:animate-out data-[state=open]:animate-in data-[state=closed]:opacity-0 data-[state=open]:opacity-100 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50",
19
+ "data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 z-50 origin-[var(--radix-popover-content-transform-origin)] rounded-md border border-gray-150 border-solid bg-white fill-mode-forwards p-4 text-gray-950 shadow-md outline-hidden data-[state=closed]:animate-out data-[state=open]:animate-in data-[state=closed]:opacity-0 data-[state=open]:opacity-100 dark:border-gray-800 dark:bg-gray-875 dark:text-gray-50",
20
20
  className
21
21
  ),
22
22
  ref,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/components/popover.tsx"],
4
- "sourcesContent": ["/**\n * @module Popover\n *\n * Floating content panel anchored to a trigger element. Built on Radix UI Popover primitive.\n * Supports controlled/uncontrolled modes, custom positioning, and focus management.\n *\n * @see {@link https://ui.shadcn.com/docs/components/popover Shadcn Popover}\n * @see {@link https://www.radix-ui.com/primitives/docs/components/popover Radix Popover}\n *\n * @example\n * // Basic popover\n * <Popover>\n * <PopoverTrigger asChild>\n * <Button>Open Popover</Button>\n * </PopoverTrigger>\n * <PopoverContent>\n * <p>Popover content here</p>\n * </PopoverContent>\n * </Popover>\n *\n * @example\n * // Controlled popover with custom positioning\n * const [open, setOpen] = useState(false);\n *\n * <Popover open={open} onOpenChange={setOpen}>\n * <PopoverTrigger>Settings</PopoverTrigger>\n * <PopoverContent align=\"start\" sideOffset={8}>\n * <SettingsForm onSave={() => setOpen(false)} />\n * </PopoverContent>\n * </Popover>\n *\n * @example\n * // With custom anchor point\n * <Popover>\n * <PopoverAnchor asChild>\n * <div>Anchor element (popover positions relative to this)</div>\n * </PopoverAnchor>\n * <PopoverTrigger>Open</PopoverTrigger>\n * <PopoverContent>Content</PopoverContent>\n * </Popover>\n */\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Root component that manages popover open/closed state. */\nconst Popover = PopoverPrimitive.Root;\n\n/** Element that toggles the popover when clicked. Use `asChild` to wrap custom elements. */\nconst PopoverTrigger = PopoverPrimitive.Trigger;\n\n/** Alternative anchor point for popover positioning (separate from trigger). */\nconst PopoverAnchor = PopoverPrimitive.Anchor;\n\n/**\n * Popover content container. Animated on open/close.\n * @param align - Horizontal alignment: `\"start\"`, `\"center\"` (default), or `\"end\"`\n * @param sideOffset - Distance from anchor in pixels (default: 4)\n * @param forceMount - Keep mounted in DOM even when closed\n */\nconst PopoverContent = ({\n className,\n forceMount,\n align = \"center\",\n sideOffset = 4,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {\n ref?: Ref<React.ElementRef<typeof PopoverPrimitive.Content>>;\n}) => (\n <PopoverPrimitive.Portal forceMount={forceMount}>\n <PopoverPrimitive.Content\n align={align}\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 z-50 origin-[var(--radix-popover-content-transform-origin)] rounded-md border border-gray-150 border-solid bg-white fill-mode-forwards p-4 text-gray-950 shadow-md outline-hidden data-[state=closed]:animate-out data-[state=open]:animate-in data-[state=closed]:opacity-0 data-[state=open]:opacity-100 dark:border-gray-800 dark:bg-gray-950 dark:text-gray-50\",\n className\n )}\n ref={ref}\n sideOffset={sideOffset}\n {...props}\n />\n </PopoverPrimitive.Portal>\n);\n\nexport { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };\n"],
4
+ "sourcesContent": ["/**\n * @module Popover\n *\n * Floating content panel anchored to a trigger element. Built on Radix UI Popover primitive.\n * Supports controlled/uncontrolled modes, custom positioning, and focus management.\n *\n * @see {@link https://ui.shadcn.com/docs/components/popover Shadcn Popover}\n * @see {@link https://www.radix-ui.com/primitives/docs/components/popover Radix Popover}\n *\n * @example\n * // Basic popover\n * <Popover>\n * <PopoverTrigger asChild>\n * <Button>Open Popover</Button>\n * </PopoverTrigger>\n * <PopoverContent>\n * <p>Popover content here</p>\n * </PopoverContent>\n * </Popover>\n *\n * @example\n * // Controlled popover with custom positioning\n * const [open, setOpen] = useState(false);\n *\n * <Popover open={open} onOpenChange={setOpen}>\n * <PopoverTrigger>Settings</PopoverTrigger>\n * <PopoverContent align=\"start\" sideOffset={8}>\n * <SettingsForm onSave={() => setOpen(false)} />\n * </PopoverContent>\n * </Popover>\n *\n * @example\n * // With custom anchor point\n * <Popover>\n * <PopoverAnchor asChild>\n * <div>Anchor element (popover positions relative to this)</div>\n * </PopoverAnchor>\n * <PopoverTrigger>Open</PopoverTrigger>\n * <PopoverContent>Content</PopoverContent>\n * </Popover>\n */\nimport { Popover as PopoverPrimitive } from \"radix-ui\";\nimport type * as React from \"react\";\nimport type { Ref } from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\n/** Root component that manages popover open/closed state. */\nconst Popover = PopoverPrimitive.Root;\n\n/** Element that toggles the popover when clicked. Use `asChild` to wrap custom elements. */\nconst PopoverTrigger = PopoverPrimitive.Trigger;\n\n/** Alternative anchor point for popover positioning (separate from trigger). */\nconst PopoverAnchor = PopoverPrimitive.Anchor;\n\n/**\n * Popover content container. Animated on open/close.\n * @param align - Horizontal alignment: `\"start\"`, `\"center\"` (default), or `\"end\"`\n * @param sideOffset - Distance from anchor in pixels (default: 4)\n * @param forceMount - Keep mounted in DOM even when closed\n */\nconst PopoverContent = ({\n className,\n forceMount,\n align = \"center\",\n sideOffset = 4,\n ref,\n ...props\n}: React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> & {\n ref?: Ref<React.ElementRef<typeof PopoverPrimitive.Content>>;\n}) => (\n <PopoverPrimitive.Portal forceMount={forceMount}>\n <PopoverPrimitive.Content\n align={align}\n className={cn(\n \"data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 z-50 origin-[var(--radix-popover-content-transform-origin)] rounded-md border border-gray-150 border-solid bg-white fill-mode-forwards p-4 text-gray-950 shadow-md outline-hidden data-[state=closed]:animate-out data-[state=open]:animate-in data-[state=closed]:opacity-0 data-[state=open]:opacity-100 dark:border-gray-800 dark:bg-gray-875 dark:text-gray-50\",\n className\n )}\n ref={ref}\n sideOffset={sideOffset}\n {...props}\n />\n </PopoverPrimitive.Portal>\n);\n\nexport { Popover, PopoverAnchor, PopoverContent, PopoverTrigger };\n"],
5
5
  "mappings": "AAyEI;AAhCJ,SAAS,WAAW,wBAAwB;AAI5C,SAAS,UAAU;AAGnB,MAAM,UAAU,iBAAiB;AAGjC,MAAM,iBAAiB,iBAAiB;AAGxC,MAAM,gBAAgB,iBAAiB;AAQvC,MAAM,iBAAiB,CAAC;AAAA,EACtB;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,aAAa;AAAA,EACb;AAAA,EACA,GAAG;AACL,MAGE,oBAAC,iBAAiB,QAAjB,EAAwB,YACvB;AAAA,EAAC,iBAAiB;AAAA,EAAjB;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACC,GAAG;AAAA;AACN,GACF;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  @import "tailwindcss";
2
- @import "../../tailwind.config.v4.css";
3
- @import "./index.v4.css";
2
+ @import "../../tailwind.config.css";
3
+ @import "./index.css";
4
4
 
5
5
  /* Storybook-specific global styles */
6
6
  body {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@music-vine/cadence",
3
- "version": "3.1.0",
3
+ "version": "3.1.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "sideEffects": false,