shadcn-glass-ui 1.0.10 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/CHANGELOG.md +259 -16
  2. package/README.md +248 -28
  3. package/context7.json +58 -0
  4. package/dist/cli/index.cjs +470 -0
  5. package/dist/cli/index.cjs.map +1 -0
  6. package/dist/cli/index.d.ts +18 -0
  7. package/dist/cli/index.js +469 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/components.cjs +4 -4
  10. package/dist/components.d.ts +153 -22
  11. package/dist/components.js +2 -2
  12. package/dist/hooks.cjs +2 -2
  13. package/dist/index.cjs +872 -5
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +0 -2390
  16. package/dist/index.js +844 -3
  17. package/dist/index.js.map +1 -1
  18. package/dist/r/ai-card-glass.json +5 -12
  19. package/dist/r/avatar-glass.json +5 -15
  20. package/dist/r/circular-metric-glass.json +5 -11
  21. package/dist/r/circular-progress-glass.json +5 -12
  22. package/dist/r/combobox-glass.json +5 -13
  23. package/dist/r/flag-alert-glass.json +5 -11
  24. package/dist/r/flags-section-glass.json +5 -12
  25. package/dist/r/insight-card-glass.json +17 -0
  26. package/dist/r/interactive-card.json +5 -12
  27. package/dist/r/language-bar-glass.json +5 -11
  28. package/dist/r/metric-card-glass.json +6 -13
  29. package/dist/r/metrics-grid-glass.json +5 -11
  30. package/dist/r/modal-glass.json +5 -15
  31. package/dist/r/popover-glass.json +1 -1
  32. package/dist/r/progress-glass.json +5 -13
  33. package/dist/r/registry.json +20 -2
  34. package/dist/r/segmented-control-glass.json +5 -11
  35. package/dist/r/sparkline-glass.json +17 -0
  36. package/dist/r/stepper-glass.json +33 -0
  37. package/dist/r/tabs-glass.json +4 -9
  38. package/dist/r/trust-score-card-glass.json +5 -12
  39. package/dist/r/year-card-glass.json +5 -13
  40. package/dist/shadcn-glass-ui.css +1 -1
  41. package/dist/{theme-context-XtasSxRT.cjs → theme-context-CVW50BKW.cjs} +2 -2
  42. package/dist/{theme-context-XtasSxRT.cjs.map → theme-context-CVW50BKW.cjs.map} +1 -1
  43. package/dist/themes.cjs +1 -1
  44. package/dist/trust-score-card-glass-BcZbul0P.js +26895 -0
  45. package/dist/trust-score-card-glass-BcZbul0P.js.map +1 -0
  46. package/dist/trust-score-card-glass-r3qM09Jp.cjs +27689 -0
  47. package/dist/trust-score-card-glass-r3qM09Jp.cjs.map +1 -0
  48. package/dist/{use-focus-BbpE2qGq.cjs → use-focus-ZE8ozmZE.cjs} +2 -2
  49. package/dist/{use-focus-BbpE2qGq.cjs.map → use-focus-ZE8ozmZE.cjs.map} +1 -1
  50. package/dist/{use-wallpaper-tint-CIqtoETa.cjs → use-wallpaper-tint-BuS80tbN.cjs} +2 -2
  51. package/dist/{use-wallpaper-tint-CIqtoETa.cjs.map → use-wallpaper-tint-BuS80tbN.cjs.map} +1 -1
  52. package/dist/{utils-CGCOTvxT.js → utils-CcyeqpKQ.js} +1 -1
  53. package/dist/{utils-CGCOTvxT.js.map → utils-CcyeqpKQ.js.map} +1 -1
  54. package/dist/{utils-CriE74ig.cjs → utils-DLXayspX.cjs} +2 -2
  55. package/dist/{utils-CriE74ig.cjs.map → utils-DLXayspX.cjs.map} +1 -1
  56. package/dist/utils.cjs +1 -1
  57. package/dist/utils.js +1 -1
  58. package/docs/AI_IMPROVEMENTS_COMPLETE.md +574 -0
  59. package/docs/AI_USAGE.md +1328 -0
  60. package/docs/API_PATTERNS_COMPARISON.md +418 -0
  61. package/docs/BEST_PRACTICES.md +800 -0
  62. package/docs/COMPLIANCE_CHECKLIST.md +307 -0
  63. package/docs/COMPLIANCE_TESTING.md +436 -0
  64. package/docs/COMPONENTS_CATALOG.md +1076 -0
  65. package/docs/CSS_VARIABLES_AUDIT.md +306 -0
  66. package/docs/CSS_VARIABLES_REFACTORING_PLAN.md +330 -0
  67. package/docs/EXPORTS_MAP.json +875 -0
  68. package/docs/EXPORTS_STRUCTURE.md +729 -0
  69. package/docs/GETTING_STARTED.md +655 -0
  70. package/docs/METRIC_CARD_API_REDESIGN.md +354 -0
  71. package/docs/PRIMITIVE_MAPPING.md +404 -0
  72. package/docs/PUBLISHING.md +593 -0
  73. package/docs/REGISTRY_SUMMARY.md +96 -0
  74. package/docs/REGISTRY_USAGE.md +339 -0
  75. package/docs/SECURITY.md +117 -0
  76. package/docs/THEME_CREATION_GUIDE.md +455 -0
  77. package/docs/TOKEN_ARCHITECTURE.md +365 -0
  78. package/docs/announcements/v1.0.5-devto.md +363 -0
  79. package/docs/announcements/v1.0.5-reddit.md +115 -0
  80. package/docs/announcements/v1.0.5-twitter.md +70 -0
  81. package/docs/api/README.md +725 -0
  82. package/docs/api/functions/ThemeProvider.md +21 -0
  83. package/docs/api/functions/cn.md +27 -0
  84. package/docs/api/functions/getNextTheme.md +21 -0
  85. package/docs/api/functions/getThemeConfig.md +21 -0
  86. package/docs/api/functions/useFocus.md +53 -0
  87. package/docs/api/functions/useHover.md +47 -0
  88. package/docs/api/functions/useResponsive.md +31 -0
  89. package/docs/api/functions/useTheme.md +15 -0
  90. package/docs/api/functions/useWallpaperTint.md +36 -0
  91. package/docs/api/globals.md +139 -0
  92. package/docs/api/interfaces/AlertGlassProps.md +131 -0
  93. package/docs/api/interfaces/AvatarGlassProps.md +114 -0
  94. package/docs/api/interfaces/BadgeGlassProps.md +118 -0
  95. package/docs/api/interfaces/ButtonGlassProps.md +179 -0
  96. package/docs/api/interfaces/CheckboxGlassProps.md +125 -0
  97. package/docs/api/interfaces/DropdownGlassProps.md +123 -0
  98. package/docs/api/interfaces/DropdownItem.md +53 -0
  99. package/docs/api/interfaces/GlassCardProps.md +151 -0
  100. package/docs/api/interfaces/InputGlassProps.md +169 -0
  101. package/docs/api/interfaces/NotificationGlassProps.md +67 -0
  102. package/docs/api/interfaces/ProgressGlassProps.md +49 -0
  103. package/docs/api/interfaces/SkeletonGlassProps.md +41 -0
  104. package/docs/api/interfaces/SliderGlassProps.md +107 -0
  105. package/docs/api/interfaces/TabItem.md +25 -0
  106. package/docs/api/interfaces/ThemeConfig.md +25 -0
  107. package/docs/api/interfaces/ThemeContextValue.md +47 -0
  108. package/docs/api/interfaces/ToggleGlassProps.md +59 -0
  109. package/docs/api/interfaces/TooltipGlassProps.md +119 -0
  110. package/docs/api/type-aliases/AlertType.md +11 -0
  111. package/docs/api/type-aliases/AlertVariant.md +11 -0
  112. package/docs/api/type-aliases/AvatarSize.md +11 -0
  113. package/docs/api/type-aliases/AvatarStatus.md +13 -0
  114. package/docs/api/type-aliases/BadgeSize.md +11 -0
  115. package/docs/api/type-aliases/BadgeVariant.md +11 -0
  116. package/docs/api/type-aliases/ButtonGlassSize.md +11 -0
  117. package/docs/api/type-aliases/ButtonGlassVariant.md +11 -0
  118. package/docs/api/type-aliases/DropdownAlign.md +11 -0
  119. package/docs/api/type-aliases/GlowType.md +11 -0
  120. package/docs/api/type-aliases/InputGlassSize.md +11 -0
  121. package/docs/api/type-aliases/IntensityType.md +11 -0
  122. package/docs/api/type-aliases/ModalSize.md +11 -0
  123. package/docs/api/type-aliases/NotificationType.md +11 -0
  124. package/docs/api/type-aliases/PaddingType.md +11 -0
  125. package/docs/api/type-aliases/ProgressGradient.md +11 -0
  126. package/docs/api/type-aliases/ProgressSize.md +11 -0
  127. package/docs/api/type-aliases/SkeletonVariant.md +11 -0
  128. package/docs/api/type-aliases/Theme.md +11 -0
  129. package/docs/api/type-aliases/ToggleGlassSize.md +11 -0
  130. package/docs/api/type-aliases/TooltipPosition.md +11 -0
  131. package/docs/api/variables/AICardGlass.md +11 -0
  132. package/docs/api/variables/AlertGlass.md +11 -0
  133. package/docs/api/variables/AvatarGlass.md +11 -0
  134. package/docs/api/variables/BadgeGlass.md +11 -0
  135. package/docs/api/variables/BaseProgressGlass.md +11 -0
  136. package/docs/api/variables/ButtonGlass.md +11 -0
  137. package/docs/api/variables/CareerStatsGlass.md +11 -0
  138. package/docs/api/variables/CareerStatsHeaderGlass.md +11 -0
  139. package/docs/api/variables/CheckboxGlass.md +11 -0
  140. package/docs/api/variables/CircularMetricGlass.md +22 -0
  141. package/docs/api/variables/CircularProgressGlass.md +11 -0
  142. package/docs/api/variables/ComboBoxGlass.md +27 -0
  143. package/docs/api/variables/ContributionMetricsGlass.md +11 -0
  144. package/docs/api/variables/DropdownGlass.md +11 -0
  145. package/docs/api/variables/ExpandableHeaderGlass.md +11 -0
  146. package/docs/api/variables/FlagAlertGlass.md +11 -0
  147. package/docs/api/variables/FlagsSectionGlass.md +11 -0
  148. package/docs/api/variables/FormFieldWrapper.md +44 -0
  149. package/docs/api/variables/GlassCard.md +11 -0
  150. package/docs/api/variables/HeaderBrandingGlass.md +11 -0
  151. package/docs/api/variables/HeaderNavGlass.md +11 -0
  152. package/docs/api/variables/IconButtonGlass.md +11 -0
  153. package/docs/api/variables/InputGlass.md +11 -0
  154. package/docs/api/variables/InteractiveCard.md +45 -0
  155. package/docs/api/variables/LanguageBarGlass.md +11 -0
  156. package/docs/api/variables/MetricCardGlass.md +11 -0
  157. package/docs/api/variables/MetricsGridGlass.md +11 -0
  158. package/docs/api/variables/ModalGlass.md +72 -0
  159. package/docs/api/variables/NotificationGlass.md +11 -0
  160. package/docs/api/variables/PopoverGlass.md +11 -0
  161. package/docs/api/variables/ProfileAvatarGlass.md +11 -0
  162. package/docs/api/variables/ProfileHeaderGlass.md +11 -0
  163. package/docs/api/variables/ProgressGlass.md +11 -0
  164. package/docs/api/variables/ProjectsListGlass.md +11 -0
  165. package/docs/api/variables/RainbowProgressGlass.md +11 -0
  166. package/docs/api/variables/RepositoryCardGlass.md +11 -0
  167. package/docs/api/variables/RepositoryHeaderGlass.md +11 -0
  168. package/docs/api/variables/RepositoryMetadataGlass.md +11 -0
  169. package/docs/api/variables/SearchBoxGlass.md +11 -0
  170. package/docs/api/variables/SegmentedControlGlass.md +11 -0
  171. package/docs/api/variables/SkeletonGlass.md +11 -0
  172. package/docs/api/variables/SliderGlass.md +11 -0
  173. package/docs/api/variables/SortDropdownGlass.md +11 -0
  174. package/docs/api/variables/StatItemGlass.md +11 -0
  175. package/docs/api/variables/StatusIndicatorGlass.md +11 -0
  176. package/docs/api/variables/THEMES.md +11 -0
  177. package/docs/api/variables/THEME_CONFIG.md +11 -0
  178. package/docs/api/variables/TabsGlass.md +52 -0
  179. package/docs/api/variables/ThemeToggleGlass.md +11 -0
  180. package/docs/api/variables/ToggleGlass.md +11 -0
  181. package/docs/api/variables/TooltipGlass.md +11 -0
  182. package/docs/api/variables/TouchTarget.md +35 -0
  183. package/docs/api/variables/TrustScoreCardGlass.md +11 -0
  184. package/docs/api/variables/TrustScoreDisplayGlass.md +11 -0
  185. package/docs/api/variables/UserInfoGlass.md +11 -0
  186. package/docs/api/variables/UserStatsLineGlass.md +11 -0
  187. package/docs/api/variables/YearCardGlass.md +11 -0
  188. package/docs/api/variables/alertVariants.md +21 -0
  189. package/docs/api/variables/avatarSizes.md +21 -0
  190. package/docs/api/variables/badgeVariants.md +21 -0
  191. package/docs/api/variables/buttonGlassVariants.md +21 -0
  192. package/docs/api/variables/cardIntensity.md +21 -0
  193. package/docs/api/variables/dropdownAlign.md +21 -0
  194. package/docs/api/variables/inputVariants.md +21 -0
  195. package/docs/api/variables/modalSizes.md +21 -0
  196. package/docs/api/variables/notificationVariants.md +21 -0
  197. package/docs/api/variables/progressSizes.md +21 -0
  198. package/docs/api/variables/shadcnAlertVariants.md +21 -0
  199. package/docs/api/variables/shadcnBadgeVariants.md +21 -0
  200. package/docs/api/variables/shadcnButtonVariants.md +21 -0
  201. package/docs/api/variables/skeletonVariants.md +21 -0
  202. package/docs/api/variables/statusSizes.md +21 -0
  203. package/docs/api/variables/toggleSizes.md +21 -0
  204. package/docs/api/variables/tooltipPositions.md +21 -0
  205. package/docs/design-system/UI_DESIGN.md +628 -0
  206. package/docs/migration/CSS_VARIABLES_MIGRATION_2.0.md +325 -0
  207. package/docs/migration/modal-glass-compound-api.md +458 -0
  208. package/docs/migration/select-to-combobox.md +386 -0
  209. package/docs/migration/tabs-glass-compound-api.md +579 -0
  210. package/docs/technical/DEPENDENCIES.md +291 -0
  211. package/docs/visual-testing-guide.md +324 -0
  212. package/package.json +16 -3
  213. package/dist/trust-score-card-glass-CNcQveNY.cjs +0 -8231
  214. package/dist/trust-score-card-glass-CNcQveNY.cjs.map +0 -1
  215. package/dist/trust-score-card-glass-CowcDyxH.js +0 -7795
  216. package/dist/trust-score-card-glass-CowcDyxH.js.map +0 -1
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "insight-card-glass",
4
+ "type": "registry:component",
5
+ "title": "Insight Card Glass",
6
+ "description": "Insight Card Glass component with glass effects",
7
+ "dependencies": ["class-variance-authority", "lucide-react", "react"],
8
+ "registryDependencies": ["cn", "variants"],
9
+ "files": [
10
+ {
11
+ "path": "components/glass/atomic/insight-card-glass.tsx",
12
+ "type": "registry:component",
13
+ "content": "import { forwardRef, type CSSProperties, type KeyboardEvent } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { ChevronRight } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport {\n insightCardVariants,\n insightVariantConfig,\n type InsightVariant,\n} from '@/lib/variants/insight-card-glass-variants';\nimport '@/glass-theme.css';\n\nexport interface InsightCardGlassProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>,\n VariantProps<typeof insightCardVariants> {\n /** Emoji icon for the insight */\n readonly emoji?: string;\n /** Main insight text */\n readonly text: string;\n /** Additional details */\n readonly detail?: string;\n /** Insight type variant */\n readonly variant?: InsightVariant;\n /** Inline display mode */\n readonly inline?: boolean;\n /** Click handler (makes the insight clickable) */\n readonly onClick?: () => void;\n /** Show arrow indicator */\n readonly showArrow?: boolean;\n /** Fade-in animation */\n readonly animated?: boolean;\n}\n\nexport const InsightCardGlass = forwardRef<HTMLDivElement, InsightCardGlassProps>(\n (\n {\n emoji,\n text,\n detail,\n variant = 'default',\n inline = false,\n onClick,\n showArrow = false,\n animated = false,\n className,\n ...props\n },\n ref\n ) => {\n const config = insightVariantConfig[variant];\n const displayEmoji = emoji ?? config.defaultEmoji;\n const isClickable = !!onClick;\n\n const handleClick = () => onClick?.();\n\n const handleKeyDown = (e: KeyboardEvent<HTMLDivElement>) => {\n if (isClickable && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onClick?.();\n }\n };\n\n const borderStyle: CSSProperties = !inline\n ? {\n borderColor: `var(${config.borderVar})`,\n }\n : {};\n\n const glowStyle: CSSProperties =\n isClickable && config.glowVar\n ? ({\n '--hover-glow': `var(${config.glowVar})`,\n } as CSSProperties)\n : {};\n\n // Inline variant\n if (inline) {\n return (\n <span\n ref={ref as React.Ref<HTMLSpanElement>}\n className={cn(\n 'inline-flex items-center gap-1.5 text-sm text-[var(--text-secondary)]',\n className\n )}\n {...props}\n >\n <span aria-hidden=\"true\">{displayEmoji}</span>\n <span>{text}</span>\n {detail && <span className=\"text-[var(--text-muted)]\">({detail})</span>}\n </span>\n );\n }\n\n // Card variant\n return (\n <div\n ref={ref}\n role={isClickable ? 'button' : undefined}\n tabIndex={isClickable ? 0 : undefined}\n onClick={isClickable ? handleClick : undefined}\n onKeyDown={isClickable ? handleKeyDown : undefined}\n className={cn(\n insightCardVariants({ inline, clickable: isClickable }),\n isClickable && config.glowVar && 'hover:shadow-[0_0_12px_var(--hover-glow)]',\n animated && 'animate-insight-fade-in',\n className\n )}\n style={{ ...borderStyle, ...glowStyle }}\n {...props}\n >\n <div className=\"flex items-start gap-2\">\n <span className=\"text-lg flex-shrink-0\" aria-hidden=\"true\">\n {displayEmoji}\n </span>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm text-[var(--text-primary)]\">{text}</p>\n {detail && <p className=\"text-xs text-[var(--text-muted)] mt-0.5\">{detail}</p>}\n </div>\n {showArrow && (\n <ChevronRight\n className=\"w-4 h-4 text-[var(--text-muted)] flex-shrink-0\"\n aria-hidden=\"true\"\n />\n )}\n </div>\n </div>\n );\n }\n);\n\nInsightCardGlass.displayName = 'InsightCardGlass';\n"
14
+ }
15
+ ],
16
+ "categories": ["atomic"]
17
+ }
@@ -4,21 +4,14 @@
4
4
  "type": "registry:lib",
5
5
  "title": "Interactive Card",
6
6
  "description": "Unified wrapper for card components with hover animations and glass effects.\n * Eliminates hover state duplication in MetricCardGlass, YearCardGlass,\n * AICardGlass, RepositoryCardGlass, and other card components.\n *\n * Features:",
7
- "dependencies": [
8
- "react"
9
- ],
10
- "registryDependencies": [
11
- "cn",
12
- "use-hover"
13
- ],
7
+ "dependencies": ["react"],
8
+ "registryDependencies": ["cn", "use-hover"],
14
9
  "files": [
15
10
  {
16
11
  "path": "components/glass/primitives/interactive-card.tsx",
17
12
  "type": "registry:lib",
18
- "content": "/**\n * InteractiveCard Component\n *\n * Unified wrapper for card components with hover animations and glass effects.\n * Eliminates hover state duplication in MetricCardGlass, YearCardGlass,\n * AICardGlass, RepositoryCardGlass, and other card components.\n *\n * Features:\n * - Hover lift animation (translateY -2px)\n * - Optional glow effects\n * - Glass surface with backdrop blur\n * - Configurable backgrounds and borders\n */\n\nimport { forwardRef, type HTMLAttributes, type CSSProperties } from 'react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\n\n/**\n * Props for the InteractiveCard component\n */\nexport interface InteractiveCardProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Enable hover lift effect (translateY -2px)\n * @default true\n */\n hoverLift?: boolean;\n\n /**\n * CSS variable for hover glow effect\n * @example 'var(--glow-primary)'\n */\n hoverGlow?: string;\n\n /**\n * CSS variable for hover background\n * @example 'var(--card-hover-bg)'\n */\n hoverBg?: string;\n\n /**\n * CSS variable for base background\n * @default 'var(--card-bg)'\n */\n baseBg?: string;\n\n /**\n * CSS variable for border color\n * @default 'var(--card-border)'\n */\n borderColor?: string;\n\n /**\n * CSS variable for hover border color\n */\n hoverBorderColor?: string;\n\n /**\n * Blur level for glass effect\n * @default 'sm'\n */\n blur?: 'sm' | 'md' | 'lg' | 'xl';\n\n /**\n * Disable all hover interactions\n * @default false\n */\n disabled?: boolean;\n\n /**\n * Border radius class\n * @default 'rounded-2xl'\n */\n rounded?: 'rounded-xl' | 'rounded-2xl' | 'rounded-3xl';\n\n /**\n * Transition speed\n * @default 'var(--transition-slow)'\n */\n transition?: string;\n}\n\n/**\n * InteractiveCard component\n *\n * Provides consistent hover animations and glass effects for card components.\n * Replaces ~80 lines of duplicated hover state management across 4+ components.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <InteractiveCard>\n * <h3>Card Title</h3>\n * <p>Card content</p>\n * </InteractiveCard>\n *\n * // With hover effects\n * <InteractiveCard\n * hoverLift\n * hoverGlow=\"var(--glow-primary)\"\n * hoverBg=\"var(--card-hover-bg)\"\n * hoverBorderColor=\"var(--card-hover-border)\"\n * >\n * <MetricContent />\n * </InteractiveCard>\n *\n * // Custom blur and rounding\n * <InteractiveCard\n * blur=\"md\"\n * rounded=\"rounded-3xl\"\n * baseBg=\"var(--metric-emerald-bg)\"\n * >\n * <StatusCard />\n * </InteractiveCard>\n * ```\n */\nexport const InteractiveCard = forwardRef<HTMLDivElement, InteractiveCardProps>(\n (\n {\n hoverLift = true,\n hoverGlow,\n hoverBg,\n baseBg = 'var(--card-bg)',\n borderColor = 'var(--card-border)',\n hoverBorderColor,\n blur = 'sm',\n disabled = false,\n rounded = 'rounded-2xl',\n transition = 'var(--transition-slow)',\n className,\n style,\n children,\n ...props\n },\n ref\n ) => {\n const { isHovered, hoverProps } = useHover({ includeFocus: !disabled });\n\n const cardStyles: CSSProperties = {\n // Background\n background: isHovered && hoverBg ? hoverBg : baseBg,\n\n // Border\n border: `1px solid ${\n isHovered && hoverBorderColor ? hoverBorderColor : borderColor\n }`,\n\n // Glassmorphism\n backdropFilter: `blur(var(--blur-${blur}))`,\n WebkitBackdropFilter: `blur(var(--blur-${blur}))`,\n\n // Hover transform\n transform: hoverLift && isHovered && !disabled ? 'translateY(-2px)' : 'translateY(0)',\n\n // Glow effect\n boxShadow: isHovered && hoverGlow && !disabled ? hoverGlow : 'none',\n\n // Transition\n transition: `all ${transition}`,\n\n // User styles override\n ...style,\n };\n\n return (\n <div\n ref={ref}\n className={cn(rounded, className)}\n style={cardStyles}\n {...(disabled ? {} : hoverProps)}\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nInteractiveCard.displayName = 'InteractiveCard';\n"
13
+ "content": "/**\n * InteractiveCard Component\n *\n * Unified wrapper for card components with hover animations and glass effects.\n * Eliminates hover state duplication in MetricCardGlass, YearCardGlass,\n * AICardGlass, RepositoryCardGlass, and other card components.\n *\n * Features:\n * - Hover lift animation (translateY -2px)\n * - Optional glow effects\n * - Glass surface with backdrop blur\n * - Configurable backgrounds and borders\n */\n\nimport { forwardRef, type HTMLAttributes, type CSSProperties } from 'react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\n\n/**\n * Props for the InteractiveCard component\n */\nexport interface InteractiveCardProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Enable hover lift effect (translateY -2px)\n * @default true\n */\n hoverLift?: boolean;\n\n /**\n * CSS variable for hover glow effect\n * @example 'var(--glow-primary)'\n */\n hoverGlow?: string;\n\n /**\n * CSS variable for hover background\n * @example 'var(--card-hover-bg)'\n */\n hoverBg?: string;\n\n /**\n * CSS variable for base background\n * @default 'var(--card-bg)'\n */\n baseBg?: string;\n\n /**\n * CSS variable for border color\n * @default 'var(--card-border)'\n */\n borderColor?: string;\n\n /**\n * CSS variable for hover border color\n */\n hoverBorderColor?: string;\n\n /**\n * Blur level for glass effect\n * @default 'sm'\n */\n blur?: 'sm' | 'md' | 'lg' | 'xl';\n\n /**\n * Disable all hover interactions\n * @default false\n */\n disabled?: boolean;\n\n /**\n * Border radius class\n * @default 'rounded-2xl'\n */\n rounded?: 'rounded-xl' | 'rounded-2xl' | 'rounded-3xl';\n\n /**\n * Transition speed\n * @default 'var(--transition-slow)'\n */\n transition?: string;\n}\n\n/**\n * InteractiveCard component\n *\n * Provides consistent hover animations and glass effects for card components.\n * Replaces ~80 lines of duplicated hover state management across 4+ components.\n *\n * @example\n * ```tsx\n * // Basic usage\n * <InteractiveCard>\n * <h3>Card Title</h3>\n * <p>Card content</p>\n * </InteractiveCard>\n *\n * // With hover effects\n * <InteractiveCard\n * hoverLift\n * hoverGlow=\"var(--glow-primary)\"\n * hoverBg=\"var(--card-hover-bg)\"\n * hoverBorderColor=\"var(--card-hover-border)\"\n * >\n * <MetricContent />\n * </InteractiveCard>\n *\n * // Custom blur and rounding\n * <InteractiveCard\n * blur=\"md\"\n * rounded=\"rounded-3xl\"\n * baseBg=\"var(--metric-success-bg)\"\n * >\n * <StatusCard />\n * </InteractiveCard>\n * ```\n */\nexport const InteractiveCard = forwardRef<HTMLDivElement, InteractiveCardProps>(\n (\n {\n hoverLift = true,\n hoverGlow,\n hoverBg,\n baseBg = 'var(--card-bg)',\n borderColor = 'var(--card-border)',\n hoverBorderColor,\n blur = 'sm',\n disabled = false,\n rounded = 'rounded-2xl',\n transition = 'var(--transition-slow)',\n className,\n style,\n children,\n ...props\n },\n ref\n ) => {\n const { isHovered, hoverProps } = useHover({ includeFocus: !disabled });\n\n const cardStyles: CSSProperties = {\n // Background\n background: isHovered && hoverBg ? hoverBg : baseBg,\n\n // Border\n border: `1px solid ${isHovered && hoverBorderColor ? hoverBorderColor : borderColor}`,\n\n // Glassmorphism\n backdropFilter: `blur(var(--blur-${blur}))`,\n WebkitBackdropFilter: `blur(var(--blur-${blur}))`,\n\n // Hover transform\n transform: hoverLift && isHovered && !disabled ? 'translateY(-2px)' : 'translateY(0)',\n\n // Glow effect\n boxShadow: isHovered && hoverGlow && !disabled ? hoverGlow : 'none',\n\n // Transition\n transition: `all ${transition}`,\n\n // User styles override\n ...style,\n };\n\n return (\n <div\n ref={ref}\n className={cn(rounded, className)}\n style={cardStyles}\n {...(disabled ? {} : hoverProps)}\n {...props}\n >\n {children}\n </div>\n );\n }\n);\n\nInteractiveCard.displayName = 'InteractiveCard';\n"
19
14
  }
20
15
  ],
21
- "categories": [
22
- "primitives"
23
- ]
24
- }
16
+ "categories": ["primitives"]
17
+ }
@@ -4,20 +4,14 @@
4
4
  "type": "registry:component",
5
5
  "title": "Language Bar Glass",
6
6
  "description": "Language Bar Glass component with glass effects",
7
- "dependencies": [
8
- "react"
9
- ],
10
- "registryDependencies": [
11
- "cn"
12
- ],
7
+ "dependencies": ["react"],
8
+ "registryDependencies": ["cn"],
13
9
  "files": [
14
10
  {
15
11
  "path": "components/glass/specialized/language-bar-glass.tsx",
16
12
  "type": "registry:component",
17
- "content": "// ========================================\n// LANGUAGE BAR GLASS COMPONENT\n// Language/skill proficiency bar with legend\n// ========================================\n\nimport { forwardRef, useState, type CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport \"@/glass-theme.css\";\n\nexport interface LanguageData {\n readonly name: string;\n readonly percent: number;\n readonly color?: string;\n}\n\nexport interface LanguageBarGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly languages: readonly LanguageData[];\n readonly showLegend?: boolean;\n}\n\nconst defaultLangColors: Record<string, string> = {\n TypeScript: \"bg-blue-500\",\n JavaScript: \"bg-yellow-400\",\n Python: \"bg-emerald-500\",\n HTML: \"bg-orange-500\",\n CSS: \"bg-purple-500\",\n Java: \"bg-red-500\",\n Go: \"bg-cyan-500\",\n Rust: \"bg-orange-600\",\n Ruby: \"bg-red-600\",\n PHP: \"bg-indigo-500\",\n};\n\nexport const LanguageBarGlass = forwardRef<HTMLDivElement, LanguageBarGlassProps>(\n ({ languages = [], showLegend = true, className, ...props }, ref) => {\n const [hoveredLang, setHoveredLang] = useState<number | null>(null);\n\n const barStyles: CSSProperties = {\n boxShadow: \"var(--rainbow-glow)\",\n };\n\n // Early return if no languages provided\n if (!languages || languages.length === 0) {\n return null;\n }\n\n return (\n <div ref={ref} className={cn(\"w-full\", className)} {...props}>\n {/* Progress bar */}\n <div\n className=\"flex h-2 md:h-2.5 rounded-full overflow-hidden\"\n style={barStyles}\n role=\"group\"\n aria-label=\"Language distribution\"\n >\n {languages.map((lang, i) => {\n const colorClass = lang.color ?? defaultLangColors[lang.name] ?? \"bg-slate-400\";\n const segmentStyles: CSSProperties = {\n width: `${lang.percent}%`,\n opacity: hoveredLang !== null && hoveredLang !== i ? 0.5 : 1,\n transition: \"all 0.3s\",\n };\n\n return (\n <div\n key={`bar-${lang.name}-${i}`}\n className={cn(colorClass)}\n style={segmentStyles}\n role=\"progressbar\"\n aria-label={`${lang.name}: ${lang.percent}%`}\n aria-valuenow={lang.percent}\n aria-valuemin={0}\n aria-valuemax={100}\n onMouseEnter={() => setHoveredLang(i)}\n onMouseLeave={() => setHoveredLang(null)}\n />\n );\n })}\n </div>\n\n {/* Legend */}\n {showLegend && (\n <div\n className=\"flex items-center gap-3 md:gap-4 mt-1.5 md:mt-2 text-[10px] md:text-xs flex-wrap\"\n style={{ color: \"var(--text-secondary)\" }}\n >\n {languages.map((lang, i) => {\n const colorClass = lang.color ?? defaultLangColors[lang.name] ?? \"bg-slate-400\";\n\n return (\n <span\n key={`legend-${lang.name}-${i}`}\n className=\"flex items-center gap-1 md:gap-1.5 cursor-pointer\"\n onMouseEnter={() => setHoveredLang(i)}\n onMouseLeave={() => setHoveredLang(null)}\n >\n <span className={cn(\"w-2 h-2 md:w-2.5 md:h-2.5 rounded-full\", colorClass)} />\n {lang.name} {lang.percent}%\n </span>\n );\n })}\n </div>\n )}\n </div>\n );\n }\n);\n\nLanguageBarGlass.displayName = \"LanguageBarGlass\";\n"
13
+ "content": "// ========================================\n// LANGUAGE BAR GLASS COMPONENT\n// Language/skill proficiency bar with legend\n// ========================================\n\nimport { forwardRef, useState, type CSSProperties } from 'react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\nexport interface LanguageData {\n readonly name: string;\n readonly percent: number;\n readonly color?: string;\n}\n\nexport interface LanguageBarGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly languages: readonly LanguageData[];\n readonly showLegend?: boolean;\n}\n\nconst defaultLangColors: Record<string, string> = {\n TypeScript: 'bg-blue-500',\n JavaScript: 'bg-yellow-400',\n Python: 'bg-emerald-500',\n HTML: 'bg-orange-500',\n CSS: 'bg-purple-500',\n Java: 'bg-red-500',\n Go: 'bg-cyan-500',\n Rust: 'bg-orange-600',\n Ruby: 'bg-red-600',\n PHP: 'bg-indigo-500',\n};\n\nexport const LanguageBarGlass = forwardRef<HTMLDivElement, LanguageBarGlassProps>(\n ({ languages = [], showLegend = true, className, ...props }, ref) => {\n const [hoveredLang, setHoveredLang] = useState<number | null>(null);\n\n const barStyles: CSSProperties = {\n boxShadow: 'var(--rainbow-glow)',\n };\n\n // Early return if no languages provided\n if (!languages || languages.length === 0) {\n return null;\n }\n\n return (\n <div ref={ref} className={cn('w-full', className)} {...props}>\n {/* Progress bar */}\n <div\n className=\"flex h-2 md:h-2.5 rounded-full overflow-hidden\"\n style={barStyles}\n role=\"group\"\n aria-label=\"Language distribution\"\n >\n {languages.map((lang, i) => {\n const colorClass = lang.color ?? defaultLangColors[lang.name] ?? 'bg-slate-400';\n const segmentStyles: CSSProperties = {\n width: `${lang.percent}%`,\n opacity: hoveredLang !== null && hoveredLang !== i ? 0.5 : 1,\n transition: 'all 0.3s',\n };\n\n return (\n <div\n key={`bar-${lang.name}-${i}`}\n className={cn(colorClass)}\n style={segmentStyles}\n role=\"progressbar\"\n aria-label={`${lang.name}: ${lang.percent}%`}\n aria-valuenow={lang.percent}\n aria-valuemin={0}\n aria-valuemax={100}\n onMouseEnter={() => setHoveredLang(i)}\n onMouseLeave={() => setHoveredLang(null)}\n />\n );\n })}\n </div>\n\n {/* Legend */}\n {showLegend && (\n <div className=\"flex items-center gap-3 md:gap-4 mt-1.5 md:mt-2 text-(length:--font-size-2xs) md:text-xs flex-wrap text-(--text-secondary)\">\n {languages.map((lang, i) => {\n const colorClass = lang.color ?? defaultLangColors[lang.name] ?? 'bg-slate-400';\n\n return (\n <span\n key={`legend-${lang.name}-${i}`}\n className=\"flex items-center gap-1 md:gap-1.5 cursor-pointer\"\n onMouseEnter={() => setHoveredLang(i)}\n onMouseLeave={() => setHoveredLang(null)}\n >\n <span className={cn('w-2 h-2 md:w-2.5 md:h-2.5 rounded-full', colorClass)} />\n {lang.name} {lang.percent}%\n </span>\n );\n })}\n </div>\n )}\n </div>\n );\n }\n);\n\nLanguageBarGlass.displayName = 'LanguageBarGlass';\n"
18
14
  }
19
15
  ],
20
- "categories": [
21
- "specialized"
22
- ]
23
- }
16
+ "categories": ["specialized"]
17
+ }
@@ -3,22 +3,15 @@
3
3
  "name": "metric-card-glass",
4
4
  "type": "registry:block",
5
5
  "title": "Metric Card Glass",
6
- "description": "Metric Card Glass component with glass effects",
7
- "dependencies": [
8
- "react"
9
- ],
10
- "registryDependencies": [
11
- "cn",
12
- "variants"
13
- ],
6
+ "description": "Metric variant system (following AlertGlass, BadgeGlass pattern)",
7
+ "dependencies": ["lucide-react", "react"],
8
+ "registryDependencies": ["cn", "variants"],
14
9
  "files": [
15
10
  {
16
11
  "path": "components/glass/composite/metric-card-glass.tsx",
17
12
  "type": "registry:component",
18
- "content": "// ========================================\n// METRIC CARD GLASS COMPONENT\n// Metric display card with progress\n// ========================================\n\nimport { forwardRef, type CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { ProgressGlass } from \"../specialized/progress-glass\";\nimport { InteractiveCard } from \"../primitives\";\nimport \"@/glass-theme.css\";\n\nimport type { ProgressGradient } from \"@/lib/variants/progress-glass-variants\";\n\nexport type MetricColor = \"emerald\" | \"amber\" | \"blue\" | \"red\";\n\nexport interface MetricCardGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly label: string;\n readonly value: number;\n readonly color?: MetricColor;\n}\n\n// Map MetricColor to ProgressGradient\nconst colorToGradient: Record<MetricColor, ProgressGradient> = {\n emerald: \"emerald\",\n amber: \"amber\",\n blue: \"blue\",\n red: \"rose\",\n};\n\n// CSS variable maps for metric colors\nconst metricVarMap: Record<MetricColor, { bg: string; text: string; border: string; glow: string }> = {\n emerald: {\n bg: \"var(--metric-emerald-bg)\",\n text: \"var(--metric-emerald-text)\",\n border: \"var(--metric-emerald-border)\",\n glow: \"var(--metric-emerald-glow)\",\n },\n amber: {\n bg: \"var(--metric-amber-bg)\",\n text: \"var(--metric-amber-text)\",\n border: \"var(--metric-amber-border)\",\n glow: \"var(--metric-amber-glow)\",\n },\n blue: {\n bg: \"var(--metric-blue-bg)\",\n text: \"var(--metric-blue-text)\",\n border: \"var(--metric-blue-border)\",\n glow: \"var(--metric-blue-glow)\",\n },\n red: {\n bg: \"var(--metric-red-bg)\",\n text: \"var(--metric-red-text)\",\n border: \"var(--metric-red-border)\",\n glow: \"var(--metric-red-glow)\",\n },\n};\n\nexport const MetricCardGlass = forwardRef<HTMLDivElement, MetricCardGlassProps>(\n ({ label, value, color = \"blue\", className, ...props }, ref) => {\n const colorVars = metricVarMap[color];\n\n const valueStyles: CSSProperties = {\n color: colorVars.text,\n textShadow: colorVars.glow,\n };\n\n return (\n <InteractiveCard\n ref={ref}\n baseBg={colorVars.bg}\n borderColor={colorVars.border}\n hoverGlow={colorVars.glow}\n hoverLift\n blur=\"sm\"\n rounded=\"rounded-xl\"\n className={cn(\"p-3 md:p-4\", className)}\n {...props}\n >\n <div className=\"flex flex-col items-center mb-2 md:mb-3 gap-1\">\n <span className=\"font-bold text-sm sm:text-base md:text-lg whitespace-nowrap\" style={valueStyles}>\n {value}%\n </span>\n <span\n className=\"text-[10px] sm:text-xs md:text-sm font-medium truncate\"\n style={{ color: \"var(--text-secondary)\" }}\n >\n {label}\n </span>\n </div>\n <ProgressGlass\n value={value}\n gradient={colorToGradient[color]}\n size=\"sm\"\n />\n </InteractiveCard>\n );\n }\n);\n\nMetricCardGlass.displayName = \"MetricCardGlass\";\n"
13
+ "content": "// ========================================\n// METRIC CARD GLASS COMPONENT\n// Metric display card with progress, sparkline, and trend\n// Domain-specific composite component following shadcn/ui patterns\n// ========================================\n\nimport { forwardRef, type CSSProperties, type ReactNode } from 'react';\nimport { TrendingUp, TrendingDown, Minus } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { ProgressGlass } from '../specialized/progress-glass';\nimport { SparklineGlass } from '../specialized/sparkline-glass';\nimport { InteractiveCard } from '../primitives';\nimport '@/glass-theme.css';\n\nimport type { ProgressGradient } from '@/lib/variants/progress-glass-variants';\n\n// ========================================\n// TYPES\n// ========================================\n\n/**\n * Metric variant system (following AlertGlass, BadgeGlass pattern)\n * - default: Blue (primary metric)\n * - secondary: Gray (neutral metric)\n * - success: Green (positive metric)\n * - warning: Yellow (caution metric)\n * - destructive: Red (negative metric)\n */\nexport type MetricVariant =\n | 'default' // shadcn/ui base (blue)\n | 'secondary' // shadcn/ui base (gray)\n | 'success' // Glass UI extension (green)\n | 'warning' // Glass UI extension (yellow)\n | 'destructive'; // shadcn/ui base (red)\n\n/** @deprecated Use MetricVariant instead */\nexport type MetricColor = 'emerald' | 'amber' | 'blue' | 'red';\n\nexport type TrendDirection = 'up' | 'down' | 'neutral';\n\n/**\n * Detailed change object with trend information\n */\nexport interface MetricChange {\n /** Change value (e.g., 12.5 for +12.5%) */\n readonly value: number;\n /** Trend direction (auto-detected from value if not provided) */\n readonly direction?: TrendDirection;\n /** Optional period label (e.g., \"vs last month\") */\n readonly period?: string;\n}\n\n/** @deprecated Use MetricChange instead */\nexport interface MetricTrend {\n readonly value: number;\n readonly direction: TrendDirection;\n readonly label?: string;\n}\n\n/**\n * MetricCardGlass Props\n *\n * Follows shadcn/ui Card pattern with Glass UI extensions.\n * Compatible with AlertGlass, BadgeGlass, ButtonGlass variant system.\n *\n * @example Simple usage (shadcn/ui style)\n * ```tsx\n * <MetricCardGlass\n * title=\"Total Revenue\"\n * value=\"$45,231\"\n * change=\"+12.5%\"\n * variant=\"success\"\n * icon={<DollarSign />}\n * />\n * ```\n *\n * @example With progress and sparkline (Glass UI extensions)\n * ```tsx\n * <MetricCardGlass\n * title=\"Completion Rate\"\n * value=\"85%\"\n * description=\"Project milestones\"\n * change={{ value: 5.2, direction: 'up', period: 'vs last month' }}\n * variant=\"success\"\n * progress={85}\n * sparklineData={[70, 75, 78, 80, 82, 84, 85]}\n * showProgress\n * showSparkline\n * />\n * ```\n */\nexport interface MetricCardGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n // ========================================\n // CORE PROPS (shadcn/ui compatible)\n // ========================================\n\n /** Metric title (shadcn/ui Card: title) */\n readonly title: string;\n\n /** Display value (shadcn/ui Card: value) */\n readonly value: string | number;\n\n /** Optional description/subtitle (shadcn/ui Card: description) */\n readonly description?: string;\n\n /** Change indicator (shadcn/ui: change). Can be string \"+12.5%\" or detailed object */\n readonly change?: string | number | MetricChange;\n\n /** Semantic variant (follows AlertGlass, BadgeGlass pattern) */\n readonly variant?: MetricVariant;\n\n /** Icon to display */\n readonly icon?: ReactNode;\n\n // ========================================\n // GLASS UI EXTENSIONS\n // ========================================\n\n /** Data for sparkline visualization */\n readonly sparklineData?: readonly number[];\n\n /** Show sparkline chart */\n readonly showSparkline?: boolean;\n\n /** Show progress bar (requires progress prop) */\n readonly showProgress?: boolean;\n\n /** Progress percentage (0-100, separate from display value) */\n readonly progress?: number;\n\n // ========================================\n // DEPRECATED (backward compatibility)\n // ========================================\n\n /** @deprecated Use `title` instead. Will be removed in v2.0 */\n readonly label?: string;\n\n /** @deprecated Use `variant` instead. Mapping: emerald→success, amber→warning, blue→default, red→destructive. Will be removed in v2.0 */\n readonly color?: MetricColor;\n\n /** @deprecated Format value before passing. Use `value` prop directly. Will be removed in v2.0 */\n readonly valueFormatter?: (value: number) => string;\n\n /** @deprecated Use `description` instead. Will be removed in v2.0 */\n readonly valueSuffix?: string;\n\n /** @deprecated Use `change` instead. Will be removed in v2.0 */\n readonly trend?: MetricTrend;\n}\n\n// ========================================\n// VARIANT SYSTEM (following AlertGlass, BadgeGlass pattern)\n// ========================================\n\ntype VariantStyle = { bg: string; text: string; border: string; glow: string };\n\n// New variant-based system (shadcn/ui compatible)\nconst variantStyles: Record<MetricVariant, VariantStyle> = {\n default: {\n bg: 'var(--metric-default-bg)',\n text: 'var(--metric-default-text)',\n border: 'var(--metric-default-border)',\n glow: 'var(--metric-default-glow)',\n },\n secondary: {\n bg: 'var(--metric-secondary-bg)',\n text: 'var(--metric-secondary-text)',\n border: 'var(--metric-secondary-border)',\n glow: 'var(--metric-secondary-glow)',\n },\n success: {\n bg: 'var(--metric-success-bg)',\n text: 'var(--metric-success-text)',\n border: 'var(--metric-success-border)',\n glow: 'var(--metric-success-glow)',\n },\n warning: {\n bg: 'var(--metric-warning-bg)',\n text: 'var(--metric-warning-text)',\n border: 'var(--metric-warning-border)',\n glow: 'var(--metric-warning-glow)',\n },\n destructive: {\n bg: 'var(--metric-destructive-bg)',\n text: 'var(--metric-destructive-text)',\n border: 'var(--metric-destructive-border)',\n glow: 'var(--metric-destructive-glow)',\n },\n};\n\n// Map MetricVariant to ProgressGradient\nconst variantToGradient: Record<MetricVariant, ProgressGradient> = {\n default: 'blue',\n secondary: 'cyan',\n success: 'emerald',\n warning: 'amber',\n destructive: 'rose',\n};\n\n// ========================================\n// DEPRECATED: Old color system (backward compatibility)\n// ========================================\n\n/** @deprecated Use variantStyles instead */\nconst colorToVariant: Record<MetricColor, MetricVariant> = {\n emerald: 'success',\n amber: 'warning',\n blue: 'default',\n red: 'destructive',\n};\n\n// Trend direction colors - using existing alert CSS variables\nconst trendColors: Record<TrendDirection, string> = {\n up: 'text-[var(--alert-success-text)]',\n down: 'text-[var(--alert-destructive-text)]',\n neutral: 'text-[var(--text-muted)]',\n};\n\n// Trend icons\nconst TrendIcons: Record<TrendDirection, typeof TrendingUp> = {\n up: TrendingUp,\n down: TrendingDown,\n neutral: Minus,\n};\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const MetricCardGlass = forwardRef<HTMLDivElement, MetricCardGlassProps>(\n (\n {\n // New API\n title,\n value,\n description,\n change,\n variant,\n progress,\n // Deprecated API (backward compatibility)\n label,\n color,\n valueFormatter,\n valueSuffix,\n trend,\n // Common props\n icon,\n sparklineData,\n showSparkline = true,\n showProgress = true,\n className,\n ...props\n },\n ref\n ) => {\n // ========================================\n // BACKWARD COMPATIBILITY LAYER\n // ========================================\n\n // Support old `label` prop\n const actualTitle = title || label;\n if (!actualTitle) {\n console.warn('[MetricCardGlass] Missing required prop: `title` (or deprecated `label`)');\n }\n if (label && !title) {\n console.warn(\n '[MetricCardGlass] Deprecated prop `label` used. Please use `title` instead. Will be removed in v2.0'\n );\n }\n\n // Support old `color` prop → `variant`\n const actualVariant: MetricVariant = variant || (color ? colorToVariant[color] : 'default');\n if (color && !variant) {\n console.warn(\n `[MetricCardGlass] Deprecated prop \\`color=\"${color}\"\\` used. Please use \\`variant=\"${colorToVariant[color]}\"\\` instead. Will be removed in v2.0`\n );\n }\n\n // Support old `valueSuffix` → `description`\n const actualDescription = description || valueSuffix;\n if (valueSuffix && !description) {\n console.warn(\n '[MetricCardGlass] Deprecated prop `valueSuffix` used. Please use `description` instead. Will be removed in v2.0'\n );\n }\n\n // Support old `trend` → `change`\n const actualChange =\n change ||\n (trend\n ? {\n value: trend.value,\n direction: trend.direction,\n period: trend.label,\n }\n : undefined);\n if (trend && !change) {\n console.warn(\n '[MetricCardGlass] Deprecated prop `trend` used. Please use `change` instead. Will be removed in v2.0'\n );\n }\n\n // Support old `valueFormatter`\n const displayValue =\n typeof value === 'number' && valueFormatter ? valueFormatter(value) : String(value);\n if (valueFormatter) {\n console.warn(\n '[MetricCardGlass] Deprecated prop `valueFormatter` used. Please format value before passing. Will be removed in v2.0'\n );\n }\n\n // Get actual progress value (use prop or infer from value if it's 0-100)\n const actualProgress =\n progress ?? (typeof value === 'number' && value >= 0 && value <= 100 ? value : undefined);\n\n // ========================================\n // COMPONENT LOGIC\n // ========================================\n\n const variantVars = variantStyles[actualVariant];\n const hasSparkline = showSparkline && sparklineData && sparklineData.length > 0;\n\n const valueStyles: CSSProperties = {\n color: variantVars.text,\n textShadow: variantVars.glow,\n };\n\n // Parse and render change indicator\n const renderChange = () => {\n if (!actualChange) return null;\n\n // Handle simple string or number\n if (typeof actualChange === 'string' || typeof actualChange === 'number') {\n const changeStr = String(actualChange);\n const isPositive =\n changeStr.startsWith('+') || (!changeStr.startsWith('-') && parseFloat(changeStr) > 0);\n const isNegative = changeStr.startsWith('-') || parseFloat(changeStr) < 0;\n const direction: TrendDirection = isPositive ? 'up' : isNegative ? 'down' : 'neutral';\n const TrendIcon = TrendIcons[direction];\n\n return (\n <div className={cn('flex items-center gap-1 text-xs', trendColors[direction])}>\n <TrendIcon className=\"w-3 h-3\" aria-hidden=\"true\" />\n <span className=\"font-medium\">{changeStr}</span>\n </div>\n );\n }\n\n // Handle detailed MetricChange object\n const changeValue = actualChange.value;\n const direction =\n actualChange.direction || (changeValue > 0 ? 'up' : changeValue < 0 ? 'down' : 'neutral');\n const TrendIcon = TrendIcons[direction];\n const displayChange = direction === 'down' ? `-${Math.abs(changeValue)}` : `+${changeValue}`;\n\n return (\n <div className={cn('flex items-center gap-1 text-xs', trendColors[direction])}>\n <TrendIcon className=\"w-3 h-3\" aria-hidden=\"true\" />\n <span className=\"font-medium\">{displayChange}%</span>\n {actualChange.period && (\n <span className=\"text-[var(--text-muted)] ml-0.5\">{actualChange.period}</span>\n )}\n </div>\n );\n };\n\n return (\n <InteractiveCard\n ref={ref}\n baseBg={variantVars.bg}\n borderColor={variantVars.border}\n hoverGlow={variantVars.glow}\n hoverLift\n blur=\"sm\"\n rounded=\"rounded-xl\"\n className={cn('p-3 md:p-4', className)}\n {...props}\n >\n {/* Header with icon and change indicator */}\n <div className=\"flex items-start justify-between mb-2\">\n <div className=\"flex items-center gap-2\">\n {icon && (\n <div className=\"text-(--text-muted)\" aria-hidden=\"true\">\n {icon}\n </div>\n )}\n <span className=\"text-(length:--font-size-2xs) sm:text-xs md:text-sm font-medium truncate text-(--text-secondary)\">\n {actualTitle}\n </span>\n </div>\n {renderChange()}\n </div>\n\n {/* Value display */}\n <div className=\"flex flex-col items-center mb-2 md:mb-3 gap-1\">\n <span\n className=\"font-bold text-lg sm:text-xl md:text-2xl whitespace-nowrap\"\n style={valueStyles}\n >\n {displayValue}\n </span>\n {actualDescription && (\n <span className=\"text-(length:--font-size-2xs) text-(--text-muted)\">\n {actualDescription}\n </span>\n )}\n </div>\n\n {/* Progress and Sparkline */}\n {hasSparkline ? (\n <div className=\"space-y-2\">\n {showProgress && actualProgress !== undefined && (\n <ProgressGlass\n value={actualProgress}\n gradient={variantToGradient[actualVariant]}\n size=\"sm\"\n />\n )}\n <SparklineGlass\n data={sparklineData}\n height=\"sm\"\n gap=\"sm\"\n className=\"w-full\"\n barColor={variantVars.text}\n highlightMax\n aria-label={`${actualTitle} trend`}\n />\n </div>\n ) : showProgress && actualProgress !== undefined ? (\n <ProgressGlass\n value={actualProgress}\n gradient={variantToGradient[actualVariant]}\n size=\"sm\"\n />\n ) : null}\n </InteractiveCard>\n );\n }\n);\n\nMetricCardGlass.displayName = 'MetricCardGlass';\n"
19
14
  }
20
15
  ],
21
- "categories": [
22
- "composite"
23
- ]
24
- }
16
+ "categories": ["composite"]
17
+ }
@@ -4,20 +4,14 @@
4
4
  "type": "registry:block",
5
5
  "title": "Metrics Grid Glass",
6
6
  "description": "Metrics Grid Glass component with glass effects",
7
- "dependencies": [
8
- "react"
9
- ],
10
- "registryDependencies": [
11
- "cn"
12
- ],
7
+ "dependencies": ["react"],
8
+ "registryDependencies": ["cn"],
13
9
  "files": [
14
10
  {
15
11
  "path": "components/glass/composite/metrics-grid-glass.tsx",
16
12
  "type": "registry:component",
17
- "content": "// ========================================\n// METRICS GRID GLASS - COMPOSITE COMPONENT\n// Responsive grid of metric cards\n// Level 3: Composite (extracted from TrustScoreCardGlass)\n// ========================================\n\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\nimport { MetricCardGlass, type MetricColor } from './metric-card-glass';\nimport '@/glass-theme.css';\n\nexport interface MetricData {\n readonly label: string;\n readonly value: number;\n readonly color: MetricColor;\n}\n\nexport interface MetricsGridGlassProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of metrics to display */\n readonly metrics: readonly MetricData[];\n /** Number of columns on desktop (1-6) */\n readonly columns?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Gap size */\n readonly gap?: 'sm' | 'md' | 'lg';\n}\n\nexport const MetricsGridGlass = forwardRef<HTMLDivElement, MetricsGridGlassProps>(\n ({ metrics, columns = 4, gap = 'md', className, ...props }, ref) => {\n const gapClasses = {\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n };\n\n const columnClasses = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3',\n 4: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4',\n 5: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-5',\n 6: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-6',\n };\n\n if (metrics.length === 0) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n className={cn('grid', columnClasses[columns], gapClasses[gap], className)}\n {...props}\n >\n {metrics.map((metric) => (\n <MetricCardGlass\n key={metric.label}\n label={metric.label}\n value={metric.value}\n color={metric.color}\n />\n ))}\n </div>\n );\n }\n);\n\nMetricsGridGlass.displayName = 'MetricsGridGlass';\n"
13
+ "content": "// ========================================\n// METRICS GRID GLASS - COMPOSITE COMPONENT\n// Responsive grid of metric cards\n// Level 3: Composite (extracted from TrustScoreCardGlass)\n// ========================================\n\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cn } from '@/lib/utils';\nimport { MetricCardGlass, type MetricVariant } from './metric-card-glass';\nimport '@/glass-theme.css';\n\nexport interface MetricData {\n readonly title: string;\n readonly value: string | number;\n readonly variant: MetricVariant;\n}\n\nexport interface MetricsGridGlassProps extends HTMLAttributes<HTMLDivElement> {\n /** Array of metrics to display */\n readonly metrics: readonly MetricData[];\n /** Number of columns on desktop (1-6) */\n readonly columns?: 1 | 2 | 3 | 4 | 5 | 6;\n /** Gap size */\n readonly gap?: 'sm' | 'md' | 'lg';\n}\n\nexport const MetricsGridGlass = forwardRef<HTMLDivElement, MetricsGridGlassProps>(\n ({ metrics, columns = 4, gap = 'md', className, ...props }, ref) => {\n const gapClasses = {\n sm: 'gap-2',\n md: 'gap-4',\n lg: 'gap-6',\n };\n\n const columnClasses = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-1 sm:grid-cols-2',\n 3: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3',\n 4: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4',\n 5: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-5',\n 6: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-6',\n };\n\n if (metrics.length === 0) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n className={cn('grid', columnClasses[columns], gapClasses[gap], className)}\n {...props}\n >\n {metrics.map((metric) => (\n <MetricCardGlass\n key={metric.title}\n title={metric.title}\n value={metric.value}\n variant={metric.variant}\n />\n ))}\n </div>\n );\n }\n);\n\nMetricsGridGlass.displayName = 'MetricsGridGlass';\n"
18
14
  }
19
15
  ],
20
- "categories": [
21
- "composite"
22
- ]
23
- }
16
+ "categories": ["composite"]
17
+ }
@@ -4,26 +4,16 @@
4
4
  "type": "registry:ui",
5
5
  "title": "Modal Glass",
6
6
  "description": "ModalGlass Component (Compound API only)",
7
- "dependencies": [
8
- "lucide-react"
9
- ],
10
- "registryDependencies": [
11
- "cn",
12
- "primitives",
13
- "use-focus",
14
- "use-hover",
15
- "variants"
16
- ],
7
+ "dependencies": ["lucide-react"],
8
+ "registryDependencies": ["cn", "primitives", "use-focus", "use-hover", "variants"],
17
9
  "files": [
18
10
  {
19
11
  "path": "components/glass/ui/modal-glass.tsx",
20
12
  "type": "registry:component",
21
- "content": "/* eslint-disable react-refresh/only-export-components */\n/**\n * ModalGlass Component (Compound API only)\n *\n * Glass-themed modal with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth open/close animations\n * - Escape key to close\n * - Click outside to close\n * - Body scroll lock\n * - Size variants\n * - Compound component API for advanced composition\n *\n * @example\n * ```tsx\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Confirm</ModalGlass.Title>\n * <ModalGlass.Description>Are you sure?</ModalGlass.Description>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <p>Body content</p>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass onClick={() => setOpen(false)}>Cancel</ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n * ```\n *\n * @since v1.0.0 - Legacy API removed (isOpen/onClose/title props)\n */\n\nimport {\n useState,\n useEffect,\n useCallback,\n useMemo,\n forwardRef,\n createContext,\n useContext,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from 'react';\nimport { X } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport { modalSizes, type ModalSize } from '@/lib/variants/modal-glass-variants';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES & CONSTANTS\n// ========================================\n\nconst MODAL_ANIMATION_DURATION = 200;\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst lockBodyScroll = (): void => {\n if (typeof document === 'undefined') return;\n document.body.style.overflow = 'hidden';\n};\n\nconst unlockBodyScroll = (): void => {\n if (typeof document === 'undefined') return;\n document.body.style.overflow = '';\n};\n\nconst delay = (ms: number): Promise<void> => {\n return new Promise((resolve) => setTimeout(resolve, ms));\n};\n\n// ========================================\n// CONTEXT FOR COMPOUND COMPONENTS\n// ========================================\n\ninterface ModalContextValue {\n isOpen: boolean;\n onClose: () => void;\n size: ModalSize;\n isClosing: boolean;\n}\n\nconst ModalContext = createContext<ModalContextValue | null>(null);\n\nconst useModalContext = () => {\n const context = useContext(ModalContext);\n if (!context) {\n throw new Error('Modal compound components must be used within ModalGlass.Root');\n }\n return context;\n};\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\n/**\n * Props for ModalGlass.Root component\n *\n * Root component that provides context and manages open/close state for the modal.\n * Handles keyboard events, body scroll lock, and accessibility attributes.\n *\n * @accessibility\n * - **Keyboard Navigation:** Escape key closes modal, Tab key traps focus within modal content\n * - **Focus Management:** Focus automatically moved to modal on open, returned to trigger on close (WCAG 2.4.3)\n * - **Screen Readers:** Uses `role=\"dialog\"` and `aria-modal=\"true\"` for proper modal semantics (WCAG 4.1.3)\n * - **Title Association:** Modal title automatically linked via `aria-labelledby=\"modal-title\"`\n * - **Description Association:** Optional description linked via `aria-describedby=\"modal-description\"`\n * - **Body Scroll Lock:** Prevents background scrolling when modal is open (improves UX and focus management)\n * - **Touch Targets:** All interactive elements (close button, action buttons) meet 44x44px minimum (WCAG 2.5.5)\n * - **Color Contrast:** Modal content and overlay meet WCAG AA contrast requirements\n * - **Motion:** Open/close animations respect `prefers-reduced-motion` settings\n *\n * @example\n * ```tsx\n * // Basic modal with title and description\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Confirm Action</ModalGlass.Title>\n * <ModalGlass.Description>\n * This action cannot be undone.\n * </ModalGlass.Description>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <p>Are you sure you want to proceed?</p>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass variant=\"ghost\" onClick={() => setOpen(false)}>\n * Cancel\n * </ButtonGlass>\n * <ButtonGlass variant=\"destructive\" onClick={handleConfirm}>\n * Confirm\n * </ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n *\n * // Different sizes\n * <ModalGlass.Root open={open} onOpenChange={setOpen} size=\"sm\">\n * {// Small modal content}\n * </ModalGlass.Root>\n * <ModalGlass.Root open={open} onOpenChange={setOpen} size=\"lg\">\n * {// Large modal content}\n * </ModalGlass.Root>\n *\n * // Form modal with proper focus management\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Create Account</ModalGlass.Title>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <form id=\"signup-form\" onSubmit={handleSubmit}>\n * <InputGlass label=\"Email\" type=\"email\" required />\n * <InputGlass label=\"Password\" type=\"password\" required />\n * </form>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass variant=\"ghost\" onClick={() => setOpen(false)}>\n * Cancel\n * </ButtonGlass>\n * <ButtonGlass type=\"submit\" form=\"signup-form\">\n * Sign Up\n * </ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n *\n * // Alert modal (no close button)\n * <ModalGlass.Root open={showAlert} onOpenChange={setShowAlert}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Session Expired</ModalGlass.Title>\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * Your session has expired. Please log in again.\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass onClick={handleLogin}>Log In</ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n * ```\n */\ninterface ModalRootProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Open state */\n open: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Size variant */\n size?: ModalSize;\n /** Child components */\n children: ReactNode;\n}\n\nconst ModalRoot: FC<ModalRootProps> = ({\n open,\n onOpenChange,\n size = 'md',\n children,\n ...props\n}) => {\n const [isClosing, setIsClosing] = useState(false);\n\n const handleClose = useCallback(async () => {\n setIsClosing(true);\n await delay(MODAL_ANIMATION_DURATION);\n setIsClosing(false);\n onOpenChange?.(false);\n }, [onOpenChange]);\n\n useEffect(() => {\n if (open) {\n lockBodyScroll();\n } else {\n unlockBodyScroll();\n }\n return () => {\n unlockBodyScroll();\n };\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n\n const handleEscape = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n handleClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [open, handleClose]);\n\n if (!open) return null;\n\n return (\n <ModalContext.Provider value={{ isOpen: open, onClose: handleClose, size, isClosing }}>\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center p-2 sm:p-4\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n {...props}\n >\n {children}\n </div>\n </ModalContext.Provider>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: OVERLAY\n// ========================================\n\ninterface ModalOverlayProps {\n className?: string;\n}\n\nconst ModalOverlay: FC<ModalOverlayProps> = ({ className }) => {\n const { onClose, isClosing } = useModalContext();\n\n const overlayStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-overlay)',\n backdropFilter: 'blur(var(--blur-sm))',\n WebkitBackdropFilter: 'blur(var(--blur-sm))',\n opacity: isClosing ? 0 : 1,\n transition: 'all 0.3s',\n }),\n [isClosing]\n );\n\n return (\n <div\n className={cn('absolute inset-0 transition-all duration-300', className)}\n style={overlayStyles}\n onClick={onClose}\n aria-hidden=\"true\"\n />\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: CONTENT\n// ========================================\n\ninterface ModalContentProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(\n ({ children, className }, ref) => {\n const { size, isClosing } = useModalContext();\n\n const modalStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-bg)',\n border: '1px solid var(--modal-border)',\n boxShadow: 'var(--modal-glow)',\n backdropFilter: 'blur(var(--blur-lg))',\n WebkitBackdropFilter: 'blur(var(--blur-lg))',\n transform: isClosing ? 'scale(0.95) translateY(10px)' : 'scale(1) translateY(0)',\n opacity: isClosing ? 0 : 1,\n animation: !isClosing ? 'modalFadeIn 0.3s ease-out' : 'none',\n }),\n [isClosing]\n );\n\n return (\n <div ref={ref} className={cn(modalSizes({ size }), className)} style={modalStyles}>\n {/* Glow effect */}\n <div\n className=\"absolute inset-0 rounded-3xl pointer-events-none\"\n style={{\n background: 'var(--modal-glow-effect)',\n }}\n />\n {children}\n </div>\n );\n }\n);\n\nModalContent.displayName = 'ModalContent';\n\n// ========================================\n// COMPOUND COMPONENT: HEADER\n// ========================================\n\ninterface ModalHeaderProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalHeader: FC<ModalHeaderProps> = ({ children, className }) => {\n return (\n <div className={cn('relative flex items-start justify-between mb-4 md:mb-5', className)}>\n {children}\n </div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: BODY\n// ========================================\n\ninterface ModalBodyProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalBody: FC<ModalBodyProps> = ({ children, className }) => {\n return (\n <div className={cn('relative', className)} style={{ color: 'var(--text-secondary)' }}>\n {children}\n </div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: FOOTER\n// ========================================\n\ninterface ModalFooterProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalFooter: FC<ModalFooterProps> = ({ children, className }) => {\n return (\n <div className={cn('relative flex gap-3 mt-4 md:mt-5 justify-end', className)}>\n {children}\n </div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: TITLE\n// ========================================\n\ninterface ModalTitleProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalTitle: FC<ModalTitleProps> = ({ children, className }) => {\n return (\n <h3\n id=\"modal-title\"\n className={cn('text-lg md:text-xl font-semibold', className)}\n style={{ color: 'var(--text-primary)' }}\n >\n {children}\n </h3>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: DESCRIPTION\n// ========================================\n\ninterface ModalDescriptionProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalDescription: FC<ModalDescriptionProps> = ({ children, className }) => {\n return (\n <p\n id=\"modal-description\"\n className={cn('text-sm md:text-base mt-1', className)}\n style={{ color: 'var(--text-muted)' }}\n >\n {children}\n </p>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: CLOSE\n// ========================================\n\ninterface ModalCloseProps {\n className?: string;\n}\n\nconst ModalClose: FC<ModalCloseProps> = ({ className }) => {\n const { onClose } = useModalContext();\n const { isHovered, hoverProps } = useHover();\n const { isFocusVisible, focusProps } = useFocus({ focusVisible: true });\n\n const closeButtonStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-close-btn-bg)',\n border: 'var(--modal-close-btn-border)',\n color: 'var(--text-muted)',\n boxShadow: isFocusVisible\n ? 'var(--focus-glow)'\n : isHovered\n ? 'var(--modal-close-btn-hover-glow)'\n : 'none',\n outline: 'none',\n }),\n [isHovered, isFocusVisible]\n );\n\n return (\n <button\n onClick={onClose}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n onFocus={focusProps.onFocus}\n onBlur={focusProps.onBlur}\n className={cn(\n 'p-1.5 md:p-2 rounded-xl transition-all duration-300',\n className\n )}\n style={closeButtonStyles}\n type=\"button\"\n aria-label=\"Close modal\"\n >\n <X className={ICON_SIZES.md} />\n </button>\n );\n};\n\n// ========================================\n// EXPORT COMPOUND COMPONENT (v1.0.0+)\n// ========================================\n\n/**\n * ModalGlass - Compound Component API\n *\n * @example\n * ```tsx\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Confirm</ModalGlass.Title>\n * <ModalGlass.Description>Are you sure?</ModalGlass.Description>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <p>Body content</p>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass>Cancel</ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n * ```\n */\nexport const ModalGlass = {\n Root: ModalRoot,\n Overlay: ModalOverlay,\n Content: ModalContent,\n Header: ModalHeader,\n Body: ModalBody,\n Footer: ModalFooter,\n Title: ModalTitle,\n Description: ModalDescription,\n Close: ModalClose,\n};\n"
13
+ "content": "/* eslint-disable react-refresh/only-export-components */\n/**\n * ModalGlass Component (Compound API only)\n *\n * Glass-themed modal with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth open/close animations\n * - Escape key to close\n * - Click outside to close\n * - Body scroll lock\n * - Size variants\n * - Compound component API for advanced composition\n *\n * @example\n * ```tsx\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Confirm</ModalGlass.Title>\n * <ModalGlass.Description>Are you sure?</ModalGlass.Description>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <p>Body content</p>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass onClick={() => setOpen(false)}>Cancel</ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n * ```\n *\n * @since v1.0.0 - Legacy API removed (isOpen/onClose/title props)\n */\n\nimport React, {\n useState,\n useEffect,\n useCallback,\n useMemo,\n forwardRef,\n createContext,\n useContext,\n type CSSProperties,\n type FC,\n type ReactNode,\n} from 'react';\nimport { X } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport { modalSizes, type ModalSize } from '@/lib/variants/modal-glass-variants';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES & CONSTANTS\n// ========================================\n\nconst MODAL_ANIMATION_DURATION = 200;\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst lockBodyScroll = (): void => {\n if (typeof document === 'undefined') return;\n document.body.style.overflow = 'hidden';\n};\n\nconst unlockBodyScroll = (): void => {\n if (typeof document === 'undefined') return;\n document.body.style.overflow = '';\n};\n\nconst delay = (ms: number): Promise<void> => {\n return new Promise((resolve) => setTimeout(resolve, ms));\n};\n\n// ========================================\n// CONTEXT FOR COMPOUND COMPONENTS\n// ========================================\n\ninterface ModalContextValue {\n isOpen: boolean;\n onClose: () => void;\n size: ModalSize;\n isClosing: boolean;\n}\n\nconst ModalContext = createContext<ModalContextValue | null>(null);\n\nconst useModalContext = () => {\n const context = useContext(ModalContext);\n if (!context) {\n throw new Error('Modal compound components must be used within ModalGlass.Root');\n }\n return context;\n};\n\n// ========================================\n// COMPOUND COMPONENT: ROOT\n// ========================================\n\n/**\n * Props for ModalGlass.Root component\n *\n * Root component that provides context and manages open/close state for the modal.\n * Handles keyboard events, body scroll lock, and accessibility attributes.\n *\n * @accessibility\n * - **Keyboard Navigation:** Escape key closes modal, Tab key traps focus within modal content\n * - **Focus Management:** Focus automatically moved to modal on open, returned to trigger on close (WCAG 2.4.3)\n * - **Screen Readers:** Uses `role=\"dialog\"` and `aria-modal=\"true\"` for proper modal semantics (WCAG 4.1.3)\n * - **Title Association:** Modal title automatically linked via `aria-labelledby=\"modal-title\"`\n * - **Description Association:** Optional description linked via `aria-describedby=\"modal-description\"`\n * - **Body Scroll Lock:** Prevents background scrolling when modal is open (improves UX and focus management)\n * - **Touch Targets:** All interactive elements (close button, action buttons) meet 44x44px minimum (WCAG 2.5.5)\n * - **Color Contrast:** Modal content and overlay meet WCAG AA contrast requirements\n * - **Motion:** Open/close animations respect `prefers-reduced-motion` settings\n *\n * @example\n * ```tsx\n * // Basic modal with title and description\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Confirm Action</ModalGlass.Title>\n * <ModalGlass.Description>\n * This action cannot be undone.\n * </ModalGlass.Description>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <p>Are you sure you want to proceed?</p>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass variant=\"ghost\" onClick={() => setOpen(false)}>\n * Cancel\n * </ButtonGlass>\n * <ButtonGlass variant=\"destructive\" onClick={handleConfirm}>\n * Confirm\n * </ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n *\n * // Different sizes\n * <ModalGlass.Root open={open} onOpenChange={setOpen} size=\"sm\">\n * {// Small modal content}\n * </ModalGlass.Root>\n * <ModalGlass.Root open={open} onOpenChange={setOpen} size=\"lg\">\n * {// Large modal content}\n * </ModalGlass.Root>\n *\n * // Form modal with proper focus management\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Create Account</ModalGlass.Title>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <form id=\"signup-form\" onSubmit={handleSubmit}>\n * <InputGlass label=\"Email\" type=\"email\" required />\n * <InputGlass label=\"Password\" type=\"password\" required />\n * </form>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass variant=\"ghost\" onClick={() => setOpen(false)}>\n * Cancel\n * </ButtonGlass>\n * <ButtonGlass type=\"submit\" form=\"signup-form\">\n * Sign Up\n * </ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n *\n * // Alert modal (no close button)\n * <ModalGlass.Root open={showAlert} onOpenChange={setShowAlert}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Session Expired</ModalGlass.Title>\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * Your session has expired. Please log in again.\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass onClick={handleLogin}>Log In</ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n * ```\n */\ninterface ModalRootProps extends React.HTMLAttributes<HTMLDivElement> {\n /** Open state */\n open: boolean;\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void;\n /** Size variant */\n size?: ModalSize;\n /** Child components */\n children: ReactNode;\n}\n\nconst ModalRoot: FC<ModalRootProps> = ({ open, onOpenChange, size = 'md', children, ...props }) => {\n const [isClosing, setIsClosing] = useState(false);\n\n const handleClose = useCallback(async () => {\n setIsClosing(true);\n await delay(MODAL_ANIMATION_DURATION);\n setIsClosing(false);\n onOpenChange?.(false);\n }, [onOpenChange]);\n\n useEffect(() => {\n if (open) {\n lockBodyScroll();\n } else {\n unlockBodyScroll();\n }\n return () => {\n unlockBodyScroll();\n };\n }, [open]);\n\n useEffect(() => {\n if (!open) return;\n\n const handleEscape = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n handleClose();\n }\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => {\n document.removeEventListener('keydown', handleEscape);\n };\n }, [open, handleClose]);\n\n if (!open) return null;\n\n return (\n <ModalContext.Provider value={{ isOpen: open, onClose: handleClose, size, isClosing }}>\n <div\n className=\"fixed inset-0 z-50 flex items-center justify-center p-2 sm:p-4\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby=\"modal-title\"\n aria-describedby=\"modal-description\"\n {...props}\n >\n {children}\n </div>\n </ModalContext.Provider>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: OVERLAY\n// ========================================\n\ninterface ModalOverlayProps {\n className?: string;\n}\n\nconst ModalOverlay: FC<ModalOverlayProps> = ({ className }) => {\n const { onClose, isClosing } = useModalContext();\n\n const overlayStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-overlay)',\n backdropFilter: 'blur(var(--blur-sm))',\n WebkitBackdropFilter: 'blur(var(--blur-sm))',\n opacity: isClosing ? 0 : 1,\n transition: 'all 0.3s',\n }),\n [isClosing]\n );\n\n return (\n <div\n className={cn('absolute inset-0 transition-all duration-300', className)}\n style={overlayStyles}\n onClick={onClose}\n aria-hidden=\"true\"\n />\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: CONTENT\n// ========================================\n\ninterface ModalContentProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalContent = forwardRef<HTMLDivElement, ModalContentProps>(\n ({ children, className }, ref) => {\n const { size, isClosing } = useModalContext();\n\n const modalStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-bg)',\n border: '1px solid var(--modal-border)',\n boxShadow: 'var(--modal-glow)',\n backdropFilter: 'blur(var(--blur-lg))',\n WebkitBackdropFilter: 'blur(var(--blur-lg))',\n transform: isClosing ? 'scale(0.95) translateY(10px)' : 'scale(1) translateY(0)',\n opacity: isClosing ? 0 : 1,\n animation: !isClosing ? 'modalFadeIn 0.3s ease-out' : 'none',\n }),\n [isClosing]\n );\n\n return (\n <div ref={ref} className={cn(modalSizes({ size }), className)} style={modalStyles}>\n {/* Glow effect */}\n <div\n className=\"absolute inset-0 rounded-3xl pointer-events-none\"\n style={{\n background: 'var(--modal-glow-effect)',\n }}\n />\n {children}\n </div>\n );\n }\n);\n\nModalContent.displayName = 'ModalContent';\n\n// ========================================\n// COMPOUND COMPONENT: HEADER\n// ========================================\n\ninterface ModalHeaderProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalHeader: FC<ModalHeaderProps> = ({ children, className }) => {\n return (\n <div className={cn('relative flex items-start justify-between mb-4 md:mb-5', className)}>\n {children}\n </div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: BODY\n// ========================================\n\ninterface ModalBodyProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalBody: FC<ModalBodyProps> = ({ children, className }) => {\n return (\n <div className={cn('relative', className)} style={{ color: 'var(--text-secondary)' }}>\n {children}\n </div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: FOOTER\n// ========================================\n\ninterface ModalFooterProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalFooter: FC<ModalFooterProps> = ({ children, className }) => {\n return (\n <div className={cn('relative flex gap-3 mt-4 md:mt-5 justify-end', className)}>{children}</div>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: TITLE\n// ========================================\n\ninterface ModalTitleProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalTitle: FC<ModalTitleProps> = ({ children, className }) => {\n return (\n <h3\n id=\"modal-title\"\n className={cn('text-lg md:text-xl font-semibold', className)}\n style={{ color: 'var(--text-primary)' }}\n >\n {children}\n </h3>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: DESCRIPTION\n// ========================================\n\ninterface ModalDescriptionProps {\n children: ReactNode;\n className?: string;\n}\n\nconst ModalDescription: FC<ModalDescriptionProps> = ({ children, className }) => {\n return (\n <p\n id=\"modal-description\"\n className={cn('text-sm md:text-base mt-1', className)}\n style={{ color: 'var(--text-muted)' }}\n >\n {children}\n </p>\n );\n};\n\n// ========================================\n// COMPOUND COMPONENT: CLOSE\n// ========================================\n\ninterface ModalCloseProps {\n className?: string;\n}\n\nconst ModalClose: FC<ModalCloseProps> = ({ className }) => {\n const { onClose } = useModalContext();\n const { isHovered, hoverProps } = useHover();\n const { isFocusVisible, focusProps } = useFocus({ focusVisible: true });\n\n const closeButtonStyles: CSSProperties = useMemo(\n () => ({\n background: 'var(--modal-close-btn-bg)',\n border: 'var(--modal-close-btn-border)',\n color: 'var(--text-muted)',\n boxShadow: isFocusVisible\n ? 'var(--focus-glow)'\n : isHovered\n ? 'var(--modal-close-btn-hover-glow)'\n : 'none',\n outline: 'none',\n }),\n [isHovered, isFocusVisible]\n );\n\n return (\n <button\n onClick={onClose}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n onFocus={focusProps.onFocus}\n onBlur={focusProps.onBlur}\n className={cn('p-1.5 md:p-2 rounded-xl transition-all duration-300', className)}\n style={closeButtonStyles}\n type=\"button\"\n aria-label=\"Close modal\"\n >\n <X className={ICON_SIZES.md} />\n </button>\n );\n};\n\n// ========================================\n// EXPORT COMPOUND COMPONENT (v1.0.0+)\n// ========================================\n\n/**\n * ModalGlass - Compound Component API\n *\n * @example\n * ```tsx\n * <ModalGlass.Root open={open} onOpenChange={setOpen}>\n * <ModalGlass.Overlay />\n * <ModalGlass.Content>\n * <ModalGlass.Header>\n * <ModalGlass.Title>Confirm</ModalGlass.Title>\n * <ModalGlass.Description>Are you sure?</ModalGlass.Description>\n * <ModalGlass.Close />\n * </ModalGlass.Header>\n * <ModalGlass.Body>\n * <p>Body content</p>\n * </ModalGlass.Body>\n * <ModalGlass.Footer>\n * <ButtonGlass>Cancel</ButtonGlass>\n * </ModalGlass.Footer>\n * </ModalGlass.Content>\n * </ModalGlass.Root>\n * ```\n */\nexport const ModalGlass = {\n Root: ModalRoot,\n Overlay: ModalOverlay,\n Content: ModalContent,\n Header: ModalHeader,\n Body: ModalBody,\n Footer: ModalFooter,\n Title: ModalTitle,\n Description: ModalDescription,\n Close: ModalClose,\n};\n"
22
14
  }
23
15
  ],
24
- "categories": [
25
- "ui"
26
- ],
16
+ "categories": ["ui"],
27
17
  "cssVars": {
28
18
  "light": {
29
19
  "--glass-bg": "rgba(255, 255, 255, 0.1)",
@@ -40,4 +30,4 @@
40
30
  "--blur-lg": "24px"
41
31
  }
42
32
  }
43
- }
33
+ }
@@ -15,7 +15,7 @@
15
15
  {
16
16
  "path": "components/glass/ui/popover-glass.tsx",
17
17
  "type": "registry:component",
18
- "content": "/**\n * PopoverGlass Component\n *\n * Floating glass-themed container for tooltips, dropdowns, and overlays with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth animations with fade-in effect\n * - Arrow pointer with glass styling\n * - ESC key and click-outside to close\n * - Focus trap for accessibility\n * - All position/alignment options (top/right/bottom/left × start/center/end)\n *\n * @example\n * ```tsx\n * <PopoverGlass\n * trigger={<ButtonGlass>Open</ButtonGlass>}\n * side=\"top\"\n * align=\"center\"\n * >\n * <div className=\"p-4\">Popover content</div>\n * </PopoverGlass>\n * ```\n */\n\nimport * as React from 'react';\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface PopoverGlassProps {\n /** The trigger element that opens the popover */\n readonly trigger: React.ReactNode;\n /** The content to display inside the popover */\n readonly children: React.ReactNode;\n /** The preferred side of the trigger to render against */\n readonly side?: 'top' | 'right' | 'bottom' | 'left';\n /** The preferred alignment against the trigger */\n readonly align?: 'start' | 'center' | 'end';\n /** The distance in pixels from the trigger */\n readonly sideOffset?: number;\n /** Controlled open state */\n readonly open?: boolean;\n /** Callback when open state changes */\n readonly onOpenChange?: (open: boolean) => void;\n /** Whether to show the arrow pointer */\n readonly showArrow?: boolean;\n /** Additional class name for the content wrapper */\n readonly className?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const PopoverGlass = React.forwardRef<\n HTMLDivElement,\n PopoverGlassProps\n>(\n (\n {\n trigger,\n children,\n side = 'bottom',\n align = 'center',\n sideOffset = 8,\n open,\n onOpenChange,\n showArrow = true,\n className,\n },\n ref\n ) => {\n // Popover content styles with CSS variables\n const popoverStyles: React.CSSProperties = {\n background: 'var(--popover-bg)',\n border: '1px solid var(--popover-border)',\n boxShadow: 'var(--popover-shadow)',\n backdropFilter: 'blur(var(--blur-md))', // 16px - standard popover blur\n WebkitBackdropFilter: 'blur(var(--blur-md))',\n };\n\n // Arrow styles\n const arrowStyles: React.CSSProperties = {\n fill: 'var(--popover-arrow-bg)',\n };\n\n return (\n <PopoverPrimitive.Root open={open} onOpenChange={onOpenChange}>\n <PopoverPrimitive.Trigger asChild>{trigger}</PopoverPrimitive.Trigger>\n\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n side={side}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n 'z-[50003] rounded-2xl p-4',\n 'animate-in fade-in-0 zoom-in-95 duration-200',\n 'data-[side=bottom]:slide-in-from-top-2',\n 'data-[side=top]:slide-in-from-bottom-2',\n 'data-[side=right]:slide-in-from-left-2',\n 'data-[side=left]:slide-in-from-right-2',\n 'outline-none',\n className\n )}\n style={popoverStyles}\n role=\"dialog\"\n aria-modal=\"false\"\n >\n {children}\n\n {showArrow && (\n <PopoverPrimitive.Arrow\n className=\"fill-current\"\n style={arrowStyles}\n width={16}\n height={8}\n />\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n );\n }\n);\n\nPopoverGlass.displayName = 'PopoverGlass';\n"
18
+ "content": "/**\n * PopoverGlass Component\n *\n * Floating glass-themed container for tooltips, dropdowns, and overlays with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth animations with fade-in effect\n * - Arrow pointer with glass styling\n * - ESC key and click-outside to close\n * - Focus trap for accessibility\n * - All position/alignment options (top/right/bottom/left × start/center/end)\n *\n * @example\n * ```tsx\n * <PopoverGlass\n * trigger={<ButtonGlass>Open</ButtonGlass>}\n * side=\"top\"\n * align=\"center\"\n * >\n * <div className=\"p-4\">\n * <h3 style={{ color: 'var(--text-primary)' }}>Title</h3>\n * <p style={{ color: 'var(--text-secondary)' }}>Content</p>\n * </div>\n * </PopoverGlass>\n * ```\n */\n\nimport * as React from 'react';\nimport * as PopoverPrimitive from '@radix-ui/react-popover';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface PopoverGlassProps {\n /** The trigger element that opens the popover */\n readonly trigger: React.ReactNode;\n /** The content to display inside the popover */\n readonly children: React.ReactNode;\n /** The preferred side of the trigger to render against */\n readonly side?: 'top' | 'right' | 'bottom' | 'left';\n /** The preferred alignment against the trigger */\n readonly align?: 'start' | 'center' | 'end';\n /** The distance in pixels from the trigger */\n readonly sideOffset?: number;\n /** Controlled open state */\n readonly open?: boolean;\n /** Callback when open state changes */\n readonly onOpenChange?: (open: boolean) => void;\n /** Whether to show the arrow pointer */\n readonly showArrow?: boolean;\n /** Additional class name for the content wrapper */\n readonly className?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const PopoverGlass = React.forwardRef<\n HTMLDivElement,\n PopoverGlassProps\n>(\n (\n {\n trigger,\n children,\n side = 'bottom',\n align = 'center',\n sideOffset = 8,\n open,\n onOpenChange,\n showArrow = true,\n className,\n },\n ref\n ) => {\n // Popover content styles with CSS variables\n const popoverStyles: React.CSSProperties = {\n background: 'var(--popover-bg)',\n border: '1px solid var(--popover-border)',\n boxShadow: 'var(--popover-shadow)',\n backdropFilter: 'blur(var(--blur-md))', // 16px - standard popover blur\n WebkitBackdropFilter: 'blur(var(--blur-md))',\n };\n\n // Arrow styles\n const arrowStyles: React.CSSProperties = {\n fill: 'var(--popover-arrow-bg)',\n };\n\n return (\n <PopoverPrimitive.Root open={open} onOpenChange={onOpenChange}>\n <PopoverPrimitive.Trigger asChild>{trigger}</PopoverPrimitive.Trigger>\n\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n ref={ref}\n side={side}\n align={align}\n sideOffset={sideOffset}\n className={cn(\n 'z-[50003] rounded-2xl',\n 'animate-in fade-in-0 zoom-in-95 duration-200',\n 'data-[side=bottom]:slide-in-from-top-2',\n 'data-[side=top]:slide-in-from-bottom-2',\n 'data-[side=right]:slide-in-from-left-2',\n 'data-[side=left]:slide-in-from-right-2',\n 'outline-none',\n className\n )}\n style={popoverStyles}\n role=\"dialog\"\n aria-modal=\"false\"\n >\n {children}\n\n {showArrow && (\n <PopoverPrimitive.Arrow\n className=\"fill-current\"\n style={arrowStyles}\n width={16}\n height={8}\n />\n )}\n </PopoverPrimitive.Content>\n </PopoverPrimitive.Portal>\n </PopoverPrimitive.Root>\n );\n }\n);\n\nPopoverGlass.displayName = 'PopoverGlass';\n"
19
19
  }
20
20
  ],
21
21
  "categories": [
@@ -4,22 +4,14 @@
4
4
  "type": "registry:component",
5
5
  "title": "Progress Glass",
6
6
  "description": "Glass-themed progress bar with:",
7
- "dependencies": [
8
- "class-variance-authority",
9
- "react"
10
- ],
11
- "registryDependencies": [
12
- "cn",
13
- "variants"
14
- ],
7
+ "dependencies": ["class-variance-authority", "react"],
8
+ "registryDependencies": ["cn", "variants"],
15
9
  "files": [
16
10
  {
17
11
  "path": "components/glass/specialized/progress-glass.tsx",
18
12
  "type": "registry:component",
19
- "content": "/**\n * ProgressGlass Component\n *\n * Glass-themed progress bar with:\n * - Theme-aware styling (glass/light/aurora)\n * - Gradient fill with glow\n * - Size variants\n * - Optional label\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { progressSizes, type ProgressGradient } from '@/lib/variants/progress-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface ProgressGlassProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>,\n VariantProps<typeof progressSizes> {\n readonly value: number;\n readonly gradient?: ProgressGradient;\n readonly showLabel?: boolean;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// Gradient colors for the fill - CSS variable based\nconst getGradientColors = (gradient: ProgressGradient): { from: string; to: string; glowVar: string } => {\n const gradients: Record<ProgressGradient, { from: string; to: string; glowVar: string }> = {\n violet: { from: '#8b5cf6', to: '#a855f7', glowVar: '--progress-glow-violet' },\n blue: { from: '#3b82f6', to: '#60a5fa', glowVar: '--progress-glow-blue' },\n cyan: { from: '#06b6d4', to: '#22d3ee', glowVar: '--progress-glow-cyan' },\n amber: { from: '#f59e0b', to: '#fbbf24', glowVar: '--progress-glow-amber' },\n emerald: { from: '#10b981', to: '#34d399', glowVar: '--progress-glow-emerald' },\n rose: { from: '#f43f5e', to: '#fb7185', glowVar: '--progress-glow-rose' },\n };\n return gradients[gradient];\n};\n\nexport const ProgressGlass = forwardRef<HTMLDivElement, ProgressGlassProps>(\n (\n {\n className,\n size = 'md',\n value = 0,\n gradient = 'violet',\n showLabel,\n ...props\n },\n ref\n ) => {\n const clampedValue = Math.min(100, Math.max(0, value));\n const gradientColors = getGradientColors(gradient ?? 'violet');\n\n const trackStyles: CSSProperties = {\n background: 'var(--progress-bg)',\n };\n\n const fillStyles: CSSProperties = {\n width: `${clampedValue}%`,\n background: `linear-gradient(90deg, ${gradientColors.from}, ${gradientColors.to})`,\n boxShadow: `var(${gradientColors.glowVar})`,\n };\n\n return (\n <div ref={ref} className={cn('w-full', className)} {...props}>\n {showLabel && (\n <div className=\"flex justify-between mb-1 md:mb-1.5\">\n <span className=\"text-[10px] md:text-xs\" style={{ color: 'var(--text-muted)' }}>\n Progress\n </span>\n <span className=\"text-[10px] md:text-xs font-medium\" style={{ color: 'var(--text-secondary)' }}>\n {clampedValue}%\n </span>\n </div>\n )}\n <div className={cn(progressSizes({ size }))} style={trackStyles}>\n <div\n className=\"h-full rounded-full transition-all duration-700 ease-out\"\n style={fillStyles}\n role=\"progressbar\"\n aria-valuenow={clampedValue}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={`Progress: ${clampedValue}%`}\n />\n </div>\n </div>\n );\n }\n);\n\nProgressGlass.displayName = 'ProgressGlass';\n"
13
+ "content": "/**\n * ProgressGlass Component\n *\n * Glass-themed progress bar with:\n * - Theme-aware styling (glass/light/aurora)\n * - Gradient fill with glow\n * - Size variants\n * - Optional label\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { progressSizes, type ProgressGradient } from '@/lib/variants/progress-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface ProgressGlassProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>, VariantProps<typeof progressSizes> {\n readonly value: number;\n readonly gradient?: ProgressGradient;\n readonly showLabel?: boolean;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// Map gradient to existing metric color variables\nconst getGradientColor = (\n gradient: ProgressGradient = 'violet'\n): { colorVar: string; glowVar: string } => {\n const gradients: Record<ProgressGradient, { colorVar: string; glowVar: string }> = {\n violet: { colorVar: '--metric-default-text', glowVar: '--progress-glow-violet' }, // Uses blue metric color\n blue: { colorVar: '--metric-default-text', glowVar: '--progress-glow-blue' },\n cyan: { colorVar: '--metric-secondary-text', glowVar: '--progress-glow-cyan' },\n amber: { colorVar: '--metric-warning-text', glowVar: '--progress-glow-amber' },\n emerald: { colorVar: '--metric-success-text', glowVar: '--progress-glow-emerald' },\n rose: { colorVar: '--metric-destructive-text', glowVar: '--progress-glow-rose' },\n };\n return gradients[gradient] || gradients.violet;\n};\n\nexport const ProgressGlass = forwardRef<HTMLDivElement, ProgressGlassProps>(\n ({ className, size = 'md', value = 0, gradient = 'violet', showLabel, ...props }, ref) => {\n const clampedValue = Math.min(100, Math.max(0, value));\n const { colorVar, glowVar } = getGradientColor(gradient);\n\n const trackStyles: CSSProperties = {\n background: 'var(--progress-bg)',\n };\n\n const fillStyles: CSSProperties = {\n width: `${clampedValue}%`,\n background: `var(${colorVar})`,\n boxShadow: `var(${glowVar})`,\n };\n\n return (\n <div ref={ref} className={cn('w-full', className)} {...props}>\n {showLabel && (\n <div className=\"flex justify-between mb-1 md:mb-1.5\">\n <span className=\"text-(length:--font-size-2xs) md:text-xs text-(--text-muted)\">\n Progress\n </span>\n <span className=\"text-(length:--font-size-2xs) md:text-xs font-medium text-(--text-secondary)\">\n {clampedValue}%\n </span>\n </div>\n )}\n <div className={cn(progressSizes({ size }))} style={trackStyles}>\n <div\n className=\"h-full rounded-full transition-all duration-700 ease-out\"\n style={fillStyles}\n role=\"progressbar\"\n aria-valuenow={clampedValue}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={`Progress: ${clampedValue}%`}\n />\n </div>\n </div>\n );\n }\n);\n\nProgressGlass.displayName = 'ProgressGlass';\n"
20
14
  }
21
15
  ],
22
- "categories": [
23
- "specialized"
24
- ]
25
- }
16
+ "categories": ["specialized"]
17
+ }
@@ -21,6 +21,12 @@
21
21
  "title": "Tabs Glass",
22
22
  "description": "TabsGlass Component (Compound API only)"
23
23
  },
24
+ {
25
+ "name": "stepper-glass",
26
+ "type": "registry:ui",
27
+ "title": "Stepper Glass",
28
+ "description": "StepperGlass Component (Compound API)"
29
+ },
24
30
  {
25
31
  "name": "slider-glass",
26
32
  "type": "registry:ui",
@@ -117,6 +123,12 @@
117
123
  "title": "Status Indicator Glass",
118
124
  "description": "Status Indicator Glass component with glass effects"
119
125
  },
126
+ {
127
+ "name": "sparkline-glass",
128
+ "type": "registry:component",
129
+ "title": "Sparkline Glass",
130
+ "description": "Sparkline Glass component with glass effects"
131
+ },
120
132
  {
121
133
  "name": "segmented-control-glass",
122
134
  "type": "registry:component",
@@ -271,7 +283,7 @@
271
283
  "name": "metric-card-glass",
272
284
  "type": "registry:block",
273
285
  "title": "Metric Card Glass",
274
- "description": "Metric Card Glass component with glass effects"
286
+ "description": "Metric variant system (following AlertGlass, BadgeGlass pattern)"
275
287
  },
276
288
  {
277
289
  "name": "contribution-metrics-glass",
@@ -321,6 +333,12 @@
321
333
  "title": "Search Box Glass",
322
334
  "description": "Search Box Glass component with glass effects"
323
335
  },
336
+ {
337
+ "name": "insight-card-glass",
338
+ "type": "registry:component",
339
+ "title": "Insight Card Glass",
340
+ "description": "Insight Card Glass component with glass effects"
341
+ },
324
342
  {
325
343
  "name": "icon-button-glass",
326
344
  "type": "registry:component",
@@ -334,4 +352,4 @@
334
352
  "description": "Expandable Header Glass component with glass effects"
335
353
  }
336
354
  ]
337
- }
355
+ }
@@ -4,20 +4,14 @@
4
4
  "type": "registry:component",
5
5
  "title": "Segmented Control Glass",
6
6
  "description": "Segmented Control Glass component with glass effects",
7
- "dependencies": [
8
- "react"
9
- ],
10
- "registryDependencies": [
11
- "cn"
12
- ],
7
+ "dependencies": ["react"],
8
+ "registryDependencies": ["cn"],
13
9
  "files": [
14
10
  {
15
11
  "path": "components/glass/specialized/segmented-control-glass.tsx",
16
12
  "type": "registry:component",
17
- "content": "// ========================================\n// SEGMENTED CONTROL GLASS COMPONENT\n// ========================================\n\nimport { forwardRef, type CSSProperties } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport \"@/glass-theme.css\";\n\nexport interface SegmentOption {\n readonly value: string;\n readonly label: string;\n}\n\nexport interface SegmentedControlGlassProps extends Omit<React.HTMLAttributes<HTMLDivElement>, \"onChange\"> {\n readonly options: readonly SegmentOption[];\n readonly value: string;\n readonly onChange?: (value: string) => void;\n}\n\nexport const SegmentedControlGlass = forwardRef<HTMLDivElement, SegmentedControlGlassProps>(\n ({ options = [], value, onChange, className, ...props }, ref) => {\n const containerStyles: CSSProperties = {\n border: \"1px solid var(--segmented-container-border)\",\n background: \"var(--segmented-container-bg)\",\n };\n\n // Early return if no options provided\n if (!options || options.length === 0) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n className={cn(\"flex rounded-xl overflow-hidden\", className)}\n style={containerStyles}\n role=\"tablist\"\n {...props}\n >\n {options.map((opt) => {\n const isActive = value === opt.value;\n const buttonStyles: CSSProperties = {\n background: isActive ? \"var(--segmented-active-bg)\" : \"transparent\",\n color: isActive ? \"var(--segmented-active-text)\" : \"var(--segmented-inactive-text)\",\n };\n\n return (\n <button\n key={opt.value}\n onClick={() => onChange?.(opt.value)}\n className=\"px-3 py-1.5 md:px-4 md:py-2 text-xs md:text-sm font-medium transition-all duration-300\"\n style={buttonStyles}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n >\n {opt.label}\n </button>\n );\n })}\n </div>\n );\n }\n);\n\nSegmentedControlGlass.displayName = \"SegmentedControlGlass\";\n"
13
+ "content": "// ========================================\n// SEGMENTED CONTROL GLASS COMPONENT\n// ========================================\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\nexport interface SegmentOption {\n readonly value: string;\n readonly label: string;\n}\n\nexport interface SegmentedControlGlassProps extends Omit<\n React.HTMLAttributes<HTMLDivElement>,\n 'onChange'\n> {\n readonly options: readonly SegmentOption[];\n readonly value: string;\n readonly onChange?: (value: string) => void;\n}\n\nexport const SegmentedControlGlass = forwardRef<HTMLDivElement, SegmentedControlGlassProps>(\n ({ options = [], value, onChange, className, ...props }, ref) => {\n const containerStyles: CSSProperties = {\n border: '1px solid var(--segmented-container-border)',\n background: 'var(--segmented-container-bg)',\n };\n\n // Early return if no options provided\n if (!options || options.length === 0) {\n return null;\n }\n\n return (\n <div\n ref={ref}\n className={cn('inline-flex rounded-xl overflow-hidden', className)}\n style={containerStyles}\n role=\"tablist\"\n {...props}\n >\n {options.map((opt) => {\n const isActive = value === opt.value;\n const buttonStyles: CSSProperties = {\n background: isActive ? 'var(--segmented-active-bg)' : 'transparent',\n color: isActive ? 'var(--segmented-active-text)' : 'var(--segmented-inactive-text)',\n };\n\n return (\n <button\n key={opt.value}\n onClick={() => onChange?.(opt.value)}\n className=\"px-3 py-1.5 md:px-4 md:py-2 text-xs md:text-sm font-medium transition-all duration-300\"\n style={buttonStyles}\n type=\"button\"\n role=\"tab\"\n aria-selected={isActive}\n >\n {opt.label}\n </button>\n );\n })}\n </div>\n );\n }\n);\n\nSegmentedControlGlass.displayName = 'SegmentedControlGlass';\n"
18
14
  }
19
15
  ],
20
- "categories": [
21
- "specialized"
22
- ]
23
- }
16
+ "categories": ["specialized"]
17
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "sparkline-glass",
4
+ "type": "registry:component",
5
+ "title": "Sparkline Glass",
6
+ "description": "Sparkline Glass component with glass effects",
7
+ "dependencies": ["class-variance-authority", "react", "recharts"],
8
+ "registryDependencies": ["cn", "variants"],
9
+ "files": [
10
+ {
11
+ "path": "components/glass/specialized/sparkline-glass.tsx",
12
+ "type": "registry:component",
13
+ "content": "// ========================================\n// SPARKLINE GLASS COMPONENT\n// Recharts-based sparkline following shadcn/ui Charts pattern\n// ========================================\n\nimport { forwardRef, useMemo } from 'react';\nimport { Bar, BarChart, Cell, ResponsiveContainer, Tooltip } from 'recharts';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { sparklineContainerVariants } from '@/lib/variants/sparkline-glass-variants';\nimport { type ChartConfig } from '@/components/ui/chart';\nimport '@/glass-theme.css';\n\n// ========================================\n// SPARKLINE CONFIG TYPE\n// ========================================\n\nexport type SparklineConfig = ChartConfig;\n\n// ========================================\n// DEFAULT CONFIG\n// ========================================\n\nconst defaultSparklineConfig = {\n value: {\n label: 'Value',\n // Use existing chart-1 color with fallback to primary accent\n color: 'var(--sparkline-bar-fill, hsl(var(--chart-1-base)))',\n },\n max: {\n label: 'Maximum',\n // Use existing success color with fallback\n color: 'var(--sparkline-bar-max, var(--alert-success-text))',\n },\n} satisfies SparklineConfig;\n\n// ========================================\n// SPARKLINE PROPS\n// ========================================\n\nexport interface SparklineGlassProps\n extends\n Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>,\n VariantProps<typeof sparklineContainerVariants> {\n /** Array of numeric values to display */\n readonly data: readonly number[];\n /** Labels for each bar (shown in tooltip) */\n readonly labels?: readonly string[];\n /** Show labels below bars */\n readonly showLabels?: boolean;\n /** Highlight the maximum value with different color */\n readonly highlightMax?: boolean;\n /** Color for regular bars (CSS variable or color) */\n readonly barColor?: string;\n /** Color for maximum bar */\n readonly maxBarColor?: string;\n /** Tooltips for each bar */\n readonly tooltips?: readonly string[];\n /** Enable animation */\n readonly animated?: boolean;\n /** Minimum bar height as percentage */\n readonly minBarHeightPercent?: number;\n /** Chart configuration (shadcn/ui pattern) */\n readonly config?: SparklineConfig;\n /** Value formatter for tooltip */\n readonly valueFormatter?: (value: number, index: number) => string;\n /** Callback when bar is clicked */\n readonly onBarClick?: (value: number, index: number) => void;\n /** Show tooltip on hover */\n readonly showTooltip?: boolean;\n}\n\n// ========================================\n// HEIGHT MAP\n// ========================================\n\nconst heightMap = {\n sm: 16,\n md: 24,\n lg: 32,\n} as const;\n\n// ========================================\n// CUSTOM TOOLTIP\n// ========================================\n\ninterface SparklineTooltipProps {\n active?: boolean;\n payload?: Array<{\n value: number;\n payload: { index: number; label?: string; value: number };\n }>;\n valueFormatter?: (value: number, index: number) => string;\n}\n\nconst SparklineTooltip = ({ active, payload, valueFormatter }: SparklineTooltipProps) => {\n if (!active || !payload?.length) {\n return null;\n }\n\n const data = payload[0];\n const value = data.value;\n const index = data.payload.index;\n const label = data.payload.label;\n\n const displayValue = valueFormatter ? valueFormatter(value, index) : value.toLocaleString();\n\n return (\n <div className=\"rounded-md border border-[var(--glass-border)] bg-[var(--glass-bg)] px-2 py-1 text-xs shadow-lg backdrop-blur-md\">\n {label && <div className=\"font-medium text-[var(--text-primary)]\">{label}</div>}\n <div className=\"text-[var(--text-secondary)]\">{displayValue}</div>\n </div>\n );\n};\n\n// ========================================\n// SPARKLINE COMPONENT\n// ========================================\n\nexport const SparklineGlass = forwardRef<HTMLDivElement, SparklineGlassProps>(\n (\n {\n data,\n labels,\n showLabels = false,\n highlightMax = false,\n barColor,\n maxBarColor,\n tooltips,\n height = 'md',\n gap = 'sm',\n animated = false,\n minBarHeightPercent = 4,\n config = defaultSparklineConfig,\n valueFormatter,\n onBarClick,\n showTooltip = true,\n className,\n ...props\n },\n ref\n ) => {\n // Transform data for Recharts\n const chartData = useMemo(() => {\n const maxValue = Math.max(...data, 0);\n const maxIndex = data.indexOf(maxValue);\n\n return data.map((value, index) => ({\n index,\n value,\n label: labels?.[index] || tooltips?.[index],\n isMax: highlightMax && index === maxIndex && value > 0,\n // Ensure minimum bar height\n displayValue:\n maxValue > 0\n ? Math.max(value, maxValue * (minBarHeightPercent / 100))\n : minBarHeightPercent,\n }));\n }, [data, labels, tooltips, highlightMax, minBarHeightPercent]);\n\n const { maxValue, maxIndex } = useMemo(() => {\n const max = Math.max(...data, 0);\n return { maxValue: max, maxIndex: data.indexOf(max) };\n }, [data]);\n\n const ariaLabel = useMemo(() => {\n if (data.length === 0) return 'Empty sparkline chart';\n return `Sparkline chart with ${data.length} data points, maximum value ${maxValue}${\n labels ? ` at ${labels[maxIndex] || `position ${maxIndex + 1}`}` : ''\n }`;\n }, [data.length, maxValue, maxIndex, labels]);\n\n // Resolve colors\n const resolvedBarColor = barColor || config.value?.color || 'var(--sparkline-bar-fill)';\n const resolvedMaxColor = maxBarColor || config.max?.color || 'var(--sparkline-bar-max)';\n\n // Gap map for bar spacing\n const gapMap = { none: 0, sm: 1, md: 2 };\n const barGap = gapMap[gap || 'sm'];\n\n const chartHeight = heightMap[height || 'md'];\n\n const handleClick = (dataPoint: (typeof chartData)[0]) => {\n if (onBarClick) {\n onBarClick(dataPoint.value, dataPoint.index);\n }\n };\n\n return (\n <div\n ref={ref}\n role=\"img\"\n aria-label={ariaLabel}\n className={cn('flex flex-col', className)}\n {...props}\n >\n {/* Chart Container - following shadcn/ui pattern */}\n <div\n className={cn(sparklineContainerVariants({ height, gap }))}\n data-chart=\"sparkline\"\n style={\n {\n '--color-value': resolvedBarColor,\n '--color-max': resolvedMaxColor,\n } as React.CSSProperties\n }\n >\n <ResponsiveContainer width=\"100%\" height={chartHeight}>\n <BarChart\n data={chartData}\n margin={{ top: 0, right: 0, bottom: 0, left: 0 }}\n barGap={barGap}\n >\n {showTooltip && (\n <Tooltip\n content={<SparklineTooltip valueFormatter={valueFormatter} />}\n cursor={false}\n isAnimationActive={false}\n />\n )}\n <Bar\n dataKey=\"displayValue\"\n radius={[2, 2, 0, 0]}\n isAnimationActive={animated}\n animationDuration={500}\n animationEasing=\"ease-out\"\n onClick={(_, index) => handleClick(chartData[index])}\n style={{ cursor: onBarClick ? 'pointer' : 'default' }}\n >\n {chartData.map((entry, index) => (\n <Cell\n key={`cell-${index}`}\n fill={entry.isMax ? resolvedMaxColor : resolvedBarColor}\n className=\"transition-colors duration-200 hover:opacity-80\"\n />\n ))}\n </Bar>\n </BarChart>\n </ResponsiveContainer>\n </div>\n\n {/* Labels */}\n {showLabels && labels && labels.length > 0 && (\n <div className=\"flex mt-1\" aria-hidden=\"true\">\n {labels.map((label, index) => (\n <span\n key={index}\n className={cn(\n 'flex-1 text-center leading-none truncate text-xs',\n 'text-(--text-muted)'\n )}\n >\n {label}\n </span>\n ))}\n </div>\n )}\n </div>\n );\n }\n);\n\nSparklineGlass.displayName = 'SparklineGlass';\n"
14
+ }
15
+ ],
16
+ "categories": ["specialized"]
17
+ }