shadcn-glass-ui 1.0.0 → 1.0.7

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 (81) hide show
  1. package/CHANGELOG.md +168 -0
  2. package/README.md +128 -138
  3. package/dist/components.cjs +4 -4
  4. package/dist/components.js +1 -1
  5. package/dist/hooks.cjs +2 -2
  6. package/dist/index.cjs +5 -5
  7. package/dist/index.js +1 -1
  8. package/dist/r/ai-card-glass.json +24 -0
  9. package/dist/r/alert-glass.json +42 -0
  10. package/dist/r/avatar-glass.json +43 -0
  11. package/dist/r/badge-glass.json +41 -0
  12. package/dist/r/base-progress-glass.json +23 -0
  13. package/dist/r/button-glass.json +45 -0
  14. package/dist/r/career-stats-glass.json +24 -0
  15. package/dist/r/career-stats-header-glass.json +24 -0
  16. package/dist/r/checkbox-glass.json +42 -0
  17. package/dist/r/circular-metric-glass.json +23 -0
  18. package/dist/r/circular-progress-glass.json +40 -0
  19. package/dist/r/combobox-glass.json +41 -0
  20. package/dist/r/contribution-metrics-glass.json +23 -0
  21. package/dist/r/dropdown-glass.json +43 -0
  22. package/dist/r/expandable-header-glass.json +23 -0
  23. package/dist/r/flag-alert-glass.json +23 -0
  24. package/dist/r/flags-section-glass.json +24 -0
  25. package/dist/r/form-field-wrapper.json +23 -0
  26. package/dist/r/glass-card.json +42 -0
  27. package/dist/r/header-branding-glass.json +24 -0
  28. package/dist/r/header-nav-glass.json +25 -0
  29. package/dist/r/icon-button-glass.json +25 -0
  30. package/dist/r/input-glass.json +43 -0
  31. package/dist/r/interactive-card.json +24 -0
  32. package/dist/r/language-bar-glass.json +23 -0
  33. package/dist/r/metric-card-glass.json +24 -0
  34. package/dist/r/metrics-grid-glass.json +23 -0
  35. package/dist/r/modal-glass.json +43 -0
  36. package/dist/r/notification-glass.json +44 -0
  37. package/dist/r/popover-glass.json +40 -0
  38. package/dist/r/profile-avatar-glass.json +23 -0
  39. package/dist/r/profile-header-glass.json +24 -0
  40. package/dist/r/progress-glass.json +25 -0
  41. package/dist/r/projects-list-glass.json +24 -0
  42. package/dist/r/rainbow-progress-glass.json +23 -0
  43. package/dist/r/registry.json +337 -0
  44. package/dist/r/repository-card-glass.json +23 -0
  45. package/dist/r/repository-header-glass.json +24 -0
  46. package/dist/r/repository-metadata-glass.json +23 -0
  47. package/dist/r/search-box-glass.json +23 -0
  48. package/dist/r/segmented-control-glass.json +23 -0
  49. package/dist/r/skeleton-glass.json +41 -0
  50. package/dist/r/slider-glass.json +42 -0
  51. package/dist/r/sort-dropdown-glass.json +25 -0
  52. package/dist/r/stat-item-glass.json +25 -0
  53. package/dist/r/status-indicator-glass.json +23 -0
  54. package/dist/r/tabs-glass.json +38 -0
  55. package/dist/r/theme-toggle-glass.json +25 -0
  56. package/dist/r/toggle-glass.json +42 -0
  57. package/dist/r/tooltip-glass.json +42 -0
  58. package/dist/r/touch-target.json +23 -0
  59. package/dist/r/trust-score-card-glass.json +24 -0
  60. package/dist/r/trust-score-display-glass.json +24 -0
  61. package/dist/r/user-info-glass.json +24 -0
  62. package/dist/r/user-stats-line-glass.json +24 -0
  63. package/dist/r/year-card-glass.json +25 -0
  64. package/dist/shadcn-glass-ui.css +1 -1
  65. package/dist/{theme-context-DrLak65e.cjs → theme-context-e3yxC7A6.cjs} +2 -2
  66. package/dist/{theme-context-DrLak65e.cjs.map → theme-context-e3yxC7A6.cjs.map} +1 -1
  67. package/dist/themes.cjs +1 -1
  68. package/dist/{trust-score-card-glass-DqaCKo1w.cjs → trust-score-card-glass-CZeCRkHL.cjs} +19 -17
  69. package/dist/trust-score-card-glass-CZeCRkHL.cjs.map +1 -0
  70. package/dist/{trust-score-card-glass-tJnNNzeS.js → trust-score-card-glass-DWrcNoI2.js} +16 -14
  71. package/dist/trust-score-card-glass-DWrcNoI2.js.map +1 -0
  72. package/dist/{use-focus-6xqfE5s6.cjs → use-focus-CUkhhBRX.cjs} +2 -2
  73. package/dist/{use-focus-6xqfE5s6.cjs.map → use-focus-CUkhhBRX.cjs.map} +1 -1
  74. package/dist/{use-wallpaper-tint-D1f3UGGs.cjs → use-wallpaper-tint-b9KAZtoy.cjs} +2 -2
  75. package/dist/{use-wallpaper-tint-D1f3UGGs.cjs.map → use-wallpaper-tint-b9KAZtoy.cjs.map} +1 -1
  76. package/dist/{utils-BNzkwPwE.cjs → utils-Ba5INf7M.cjs} +2 -2
  77. package/dist/{utils-BNzkwPwE.cjs.map → utils-Ba5INf7M.cjs.map} +1 -1
  78. package/dist/utils.cjs +1 -1
  79. package/package.json +5 -2
  80. package/dist/trust-score-card-glass-DqaCKo1w.cjs.map +0 -1
  81. package/dist/trust-score-card-glass-tJnNNzeS.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as cn } from "./utils-CGCOTvxT.js";
2
- import { $ as ComboBoxGlass, A as ThemeToggleGlass, B as TabsGlass, C as ProgressGlass, D as FlagAlertGlass, E as LanguageBarGlass, F as ExpandableHeaderGlass, G as NotificationGlass, H as SkeletonGlass, I as TooltipGlass, J as modalSizes, K as notificationVariants, L as tooltipPositions, M as SortDropdownGlass, N as SearchBoxGlass, O as StatusIndicatorGlass, P as IconButtonGlass, Q as DropdownGlass, R as ToggleGlass, S as RainbowProgressGlass, T as ProfileAvatarGlass, U as skeletonVariants, V as SliderGlass, W as PopoverGlass, X as GlassCard, Y as InputGlass, Z as cardIntensity, _ as ContributionMetricsGlass, a as HeaderBrandingGlass, at as BadgeGlass, b as AICardGlass, c as YearCardGlass, ct as avatarSizes, d as TrustScoreDisplayGlass, dt as InteractiveCard, et as inputVariants, f as RepositoryMetadataGlass, ft as FormFieldWrapper, g as MetricCardGlass, h as MetricsGridGlass, i as HeaderNavGlass, it as buttonGlassVariants, j as StatItemGlass, k as BaseProgressGlass, l as UserStatsLineGlass, lt as statusSizes, m as RepositoryCardGlass, mt as alertVariants, n as ProjectsListGlass, nt as CheckboxGlass, o as FlagsSectionGlass, ot as badgeVariants, p as RepositoryHeaderGlass, pt as TouchTarget, q as ModalGlass, r as ProfileHeaderGlass, rt as ButtonGlass, s as CareerStatsGlass, st as AvatarGlass, t as TrustScoreCardGlass, tt as CircularProgressGlass, u as UserInfoGlass, ut as AlertGlass, v as CircularMetricGlass, w as progressSizes, x as SegmentedControlGlass, y as CareerStatsHeaderGlass, z as toggleSizes } from "./trust-score-card-glass-tJnNNzeS.js";
2
+ import { $ as ComboBoxGlass, A as ThemeToggleGlass, B as TabsGlass, C as ProgressGlass, D as FlagAlertGlass, E as LanguageBarGlass, F as ExpandableHeaderGlass, G as NotificationGlass, H as SkeletonGlass, I as TooltipGlass, J as modalSizes, K as notificationVariants, L as tooltipPositions, M as SortDropdownGlass, N as SearchBoxGlass, O as StatusIndicatorGlass, P as IconButtonGlass, Q as DropdownGlass, R as ToggleGlass, S as RainbowProgressGlass, T as ProfileAvatarGlass, U as skeletonVariants, V as SliderGlass, W as PopoverGlass, X as GlassCard, Y as InputGlass, Z as cardIntensity, _ as ContributionMetricsGlass, a as HeaderBrandingGlass, at as BadgeGlass, b as AICardGlass, c as YearCardGlass, ct as avatarSizes, d as TrustScoreDisplayGlass, dt as InteractiveCard, et as inputVariants, f as RepositoryMetadataGlass, ft as FormFieldWrapper, g as MetricCardGlass, h as MetricsGridGlass, i as HeaderNavGlass, it as buttonGlassVariants, j as StatItemGlass, k as BaseProgressGlass, l as UserStatsLineGlass, lt as statusSizes, m as RepositoryCardGlass, mt as alertVariants, n as ProjectsListGlass, nt as CheckboxGlass, o as FlagsSectionGlass, ot as badgeVariants, p as RepositoryHeaderGlass, pt as TouchTarget, q as ModalGlass, r as ProfileHeaderGlass, rt as ButtonGlass, s as CareerStatsGlass, st as AvatarGlass, t as TrustScoreCardGlass, tt as CircularProgressGlass, u as UserInfoGlass, ut as AlertGlass, v as CircularMetricGlass, w as progressSizes, x as SegmentedControlGlass, y as CareerStatsHeaderGlass, z as toggleSizes } from "./trust-score-card-glass-DWrcNoI2.js";
3
3
  import { n as useHover, t as useFocus } from "./use-focus-CX0TJJIj.js";
4
4
  import { a as getThemeConfig, i as getNextTheme, n as THEME_CONFIG, o as useTheme, r as ThemeProvider, t as THEMES } from "./theme-context-BZoCplcU.js";
5
5
  import { n as useResponsive, t as useWallpaperTint } from "./use-wallpaper-tint-DUgmytlY.js";
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "ai-card-glass",
4
+ "type": "registry:block",
5
+ "title": "Ai Card Glass",
6
+ "description": "Ai Card Glass component with glass effects",
7
+ "dependencies": [
8
+ "lucide-react",
9
+ "react"
10
+ ],
11
+ "registryDependencies": [
12
+ "cn"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "components/glass/composite/ai-card-glass.tsx",
17
+ "type": "registry:component",
18
+ "content": "// ========================================\n// AI CARD GLASS COMPONENT\n// AI summary card with feature list\n// ========================================\n\nimport { forwardRef } from \"react\";\nimport { Sparkles, Check, Zap, Clock } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { ButtonGlass } from \"../ui/button-glass\";\nimport { InteractiveCard } from \"../primitives\";\nimport \"@/glass-theme.css\";\n\nexport interface AICardGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly onGenerate?: () => void;\n readonly features?: readonly string[];\n readonly estimatedTime?: string;\n}\n\nconst defaultFeatures: readonly string[] = [\n \"Code quality assessment\",\n \"Architecture patterns\",\n \"Best practices\",\n];\n\nexport const AICardGlass = forwardRef<HTMLDivElement, AICardGlassProps>(\n (\n {\n onGenerate,\n features = defaultFeatures,\n estimatedTime = \"~30 seconds\",\n className,\n ...props\n },\n ref\n ) => {\n return (\n <InteractiveCard\n ref={ref}\n baseBg=\"var(--ai-card-bg)\"\n borderColor=\"var(--ai-card-border)\"\n hoverGlow=\"var(--ai-card-hover-glow)\"\n hoverLift\n blur=\"sm\"\n rounded=\"rounded-xl\"\n className={cn(\"w-full sm:w-56 md:w-64 p-3 md:p-4\", className)}\n {...props}\n >\n <div\n className=\"flex items-center gap-1.5 md:gap-2 font-semibold text-xs md:text-sm mb-1.5 md:mb-2\"\n style={{ color: \"var(--text-accent)\" }}\n >\n <Sparkles className=\"w-3.5 h-3.5 md:w-4 md:h-4\" />\n AI Summary\n </div>\n <p\n className=\"text-[10px] md:text-xs mb-1.5 md:mb-2\"\n style={{ color: \"var(--text-secondary)\" }}\n >\n Get comprehensive analysis:\n </p>\n <ul className=\"text-[10px] md:text-xs space-y-0.5 md:space-y-1 mb-2 md:mb-3\">\n {features.map((feature, i) => (\n <li\n key={`feature-${i}`}\n className=\"flex items-center gap-1\"\n style={{ color: \"var(--text-muted)\" }}\n >\n <Check\n className=\"w-2.5 h-2.5 md:w-3 md:h-3\"\n style={{ color: \"var(--status-online)\" }}\n />\n {feature}\n </li>\n ))}\n </ul>\n <ButtonGlass\n variant=\"primary\"\n size=\"sm\"\n icon={Zap}\n onClick={onGenerate}\n className=\"w-full\"\n >\n Generate Report\n </ButtonGlass>\n <p\n className=\"text-[10px] md:text-xs mt-1.5 md:mt-2 text-center flex items-center justify-center gap-1\"\n style={{ color: \"var(--text-muted)\" }}\n >\n <Clock className=\"w-2.5 h-2.5 md:w-3 md:h-3\" />\n {estimatedTime}\n </p>\n </InteractiveCard>\n );\n }\n);\n\nAICardGlass.displayName = \"AICardGlass\";\n"
19
+ }
20
+ ],
21
+ "categories": [
22
+ "composite"
23
+ ]
24
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "alert-glass",
4
+ "type": "registry:ui",
5
+ "title": "Alert Glass",
6
+ "description": "Glass-themed alert with:",
7
+ "dependencies": [
8
+ "class-variance-authority",
9
+ "react"
10
+ ],
11
+ "registryDependencies": [
12
+ "cn",
13
+ "primitives",
14
+ "variants"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "components/glass/ui/alert-glass.tsx",
19
+ "type": "registry:component",
20
+ "content": "/**\n * AlertGlass Component\n *\n * Glass-themed alert with:\n * - Theme-aware styling via CSS variables (glass/light/aurora)\n * - shadcn/ui compatible variants (default, destructive)\n * - Extended Glass UI variants (success, warning)\n * - Optional title\n * - Dismissible option\n * - Backdrop blur effect\n */\n\nimport { forwardRef, type ReactNode, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport {\n Info,\n CheckCircle,\n AlertTriangle,\n AlertCircle,\n X,\n} from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { alertVariants } from '@/lib/variants/alert-glass-variants';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\nimport type { AlertVariant } from '@/lib/variants/alert-glass-variants';\n\n// ========================================\n// ICON MAP\n// ========================================\n\nconst iconMap: Record<AlertVariant, typeof Info> = {\n default: Info,\n destructive: AlertCircle,\n success: CheckCircle,\n warning: AlertTriangle,\n // Aliases\n info: Info,\n error: AlertCircle,\n};\n\n// ========================================\n// CSS VARIABLE HELPERS\n// ========================================\n\ntype AlertStyleVars = { bg: string; border: string; text: string };\n\nconst variantStyles: Record<AlertVariant, AlertStyleVars> = {\n // shadcn/ui compatible variants\n default: {\n bg: 'var(--alert-default-bg)',\n border: 'var(--alert-default-border)',\n text: 'var(--alert-default-text)',\n },\n destructive: {\n bg: 'var(--alert-destructive-bg)',\n border: 'var(--alert-destructive-border)',\n text: 'var(--alert-destructive-text)',\n },\n // Glass UI extended variants\n success: {\n bg: 'var(--alert-success-bg)',\n border: 'var(--alert-success-border)',\n text: 'var(--alert-success-text)',\n },\n warning: {\n bg: 'var(--alert-warning-bg)',\n border: 'var(--alert-warning-border)',\n text: 'var(--alert-warning-text)',\n },\n // Backward compatibility aliases\n info: {\n bg: 'var(--alert-default-bg)',\n border: 'var(--alert-default-border)',\n text: 'var(--alert-default-text)',\n },\n error: {\n bg: 'var(--alert-destructive-bg)',\n border: 'var(--alert-destructive-border)',\n text: 'var(--alert-destructive-text)',\n },\n};\n\nconst getAlertStyles = (variant: AlertVariant): CSSProperties => {\n const config = variantStyles[variant];\n return {\n background: config.bg,\n border: `1px solid ${config.border}`,\n };\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface AlertGlassProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style' | 'title'>,\n VariantProps<typeof alertVariants> {\n readonly title?: string;\n readonly children: ReactNode;\n readonly dismissible?: boolean;\n readonly onDismiss?: () => void;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const AlertGlass = forwardRef<HTMLDivElement, AlertGlassProps>(\n (\n {\n className,\n variant = 'default',\n title,\n children,\n dismissible,\n onDismiss,\n ...props\n },\n ref\n ) => {\n // Ensure variant is never null/undefined for type safety\n const effectiveVariant: AlertVariant = variant ?? 'default';\n\n const config = variantStyles[effectiveVariant];\n const Icon = iconMap[effectiveVariant];\n\n return (\n <div\n ref={ref}\n className={cn(alertVariants({ variant: effectiveVariant }), className)}\n style={getAlertStyles(effectiveVariant)}\n role=\"alert\"\n {...props}\n >\n <Icon\n className=\"w-4 h-4 md:w-5 md:h-5 flex-shrink-0 mt-0.5\"\n style={{ color: config.text }}\n />\n <div className=\"flex-1\">\n {title && (\n <p\n className=\"font-medium text-xs md:text-sm mb-0.5 md:mb-1\"\n style={{ color: config.text }}\n >\n {title}\n </p>\n )}\n <p className=\"text-xs md:text-sm opacity-80\" style={{ color: config.text }}>\n {children}\n </p>\n </div>\n {dismissible && (\n <button\n onClick={onDismiss}\n className=\"p-0.5 md:p-1 rounded transition-colors duration-200 hover:bg-black/5 flex-shrink-0\"\n aria-label=\"Dismiss alert\"\n >\n <X className={ICON_SIZES.md} style={{ color: config.text }} />\n </button>\n )}\n </div>\n );\n }\n);\n\nAlertGlass.displayName = 'AlertGlass';\n"
21
+ }
22
+ ],
23
+ "categories": [
24
+ "ui"
25
+ ],
26
+ "cssVars": {
27
+ "light": {
28
+ "--glass-bg": "rgba(255, 255, 255, 0.1)",
29
+ "--glass-border": "rgba(255, 255, 255, 0.2)",
30
+ "--blur-sm": "8px",
31
+ "--blur-md": "16px",
32
+ "--blur-lg": "24px"
33
+ },
34
+ "dark": {
35
+ "--glass-bg": "rgba(255, 255, 255, 0.05)",
36
+ "--glass-border": "rgba(255, 255, 255, 0.1)",
37
+ "--blur-sm": "8px",
38
+ "--blur-md": "16px",
39
+ "--blur-lg": "24px"
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "avatar-glass",
4
+ "type": "registry:ui",
5
+ "title": "Avatar Glass",
6
+ "description": "Glass-themed avatar with:",
7
+ "dependencies": [
8
+ "@radix-ui/react-slot",
9
+ "class-variance-authority",
10
+ "react"
11
+ ],
12
+ "registryDependencies": [
13
+ "cn",
14
+ "use-hover",
15
+ "variants"
16
+ ],
17
+ "files": [
18
+ {
19
+ "path": "components/glass/ui/avatar-glass.tsx",
20
+ "type": "registry:component",
21
+ "content": "/**\n * AvatarGlass Component\n *\n * Glass-themed avatar with:\n * - Theme-aware styling (glass/light/aurora)\n * - Glow effect on hover\n * - Status indicator with glow\n * - Size variants\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { avatarSizes, statusSizes } from '@/lib/variants/avatar-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\n/**\n * Avatar status indicator type\n */\nexport type AvatarStatus = 'online' | 'offline' | 'busy' | 'away';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the AvatarGlass component\n *\n * A glass-themed avatar component with status indicators and size variants.\n * Displays user initials with theme-aware styling and optional status badge.\n *\n * @example\n * ```tsx\n * // Basic avatar\n * <AvatarGlass name=\"John Doe\" />\n *\n * // With status indicator\n * <AvatarGlass name=\"Jane Smith\" status=\"online\" size=\"lg\" />\n *\n * // Different sizes\n * <AvatarGlass name=\"Alex\" size=\"sm\" />\n * <AvatarGlass name=\"Sam\" size=\"xl\" />\n *\n * // As a link (asChild pattern)\n * <AvatarGlass asChild name=\"Sarah Connor\" status=\"online\">\n * <a href=\"/profile/sarah\">View Profile</a>\n * </AvatarGlass>\n * ```\n */\nexport interface AvatarGlassProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>,\n VariantProps<typeof avatarSizes> {\n /**\n * Render as child element instead of div (polymorphic rendering).\n * Useful for making avatars clickable links.\n * @default false\n * @example\n * ```tsx\n * <AvatarGlass asChild name=\"John\">\n * <a href=\"/profile\">View Profile</a>\n * </AvatarGlass>\n * ```\n */\n readonly asChild?: boolean;\n\n /**\n * Full name of the user. Automatically generates initials (first 2 letters).\n * @example \"John Doe\" → \"JD\"\n */\n readonly name: string;\n\n /**\n * Optional status indicator with glow effect\n * @default undefined\n */\n readonly status?: AvatarStatus;\n\n /**\n * Size variant of the avatar\n * @default \"md\"\n */\n readonly size?: 'sm' | 'md' | 'lg' | 'xl';\n}\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst getInitials = (name: string): string => {\n if (!name || name.trim().length === 0) return '?';\n return name\n .split(' ')\n .map((part) => part[0])\n .join('')\n .toUpperCase()\n .slice(0, 2);\n};\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// Status colors mapping to CSS variables\nconst getStatusVars = (statusType: AvatarStatus): { bg: string; glow: string } => {\n const statusVars: Record<AvatarStatus, { bg: string; glow: string }> = {\n online: { bg: 'var(--status-online)', glow: 'var(--status-online-glow)' },\n offline: { bg: 'var(--status-offline)', glow: 'none' },\n busy: { bg: 'var(--status-busy)', glow: 'var(--status-busy-glow)' },\n away: { bg: 'var(--status-away)', glow: 'var(--status-away-glow)' },\n };\n return statusVars[statusType];\n};\n\nexport const AvatarGlass = forwardRef<HTMLDivElement, AvatarGlassProps>(\n ({ asChild = false, name, size = 'md', status, className, ...props }, ref) => {\n const { isHovered, hoverProps } = useHover();\n\n const avatarStyles: CSSProperties = {\n background: 'var(--avatar-bg)',\n border: '2px solid var(--avatar-border)',\n boxShadow: isHovered ? 'var(--avatar-hover-glow)' : 'var(--avatar-shadow)',\n color: '#ffffff',\n };\n\n const initials = getInitials(name);\n\n // Polymorphic component - render as Slot when asChild is true\n const Comp = asChild ? Slot : 'div';\n\n return (\n <Comp\n ref={ref}\n className={cn('relative inline-flex', className)}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n {...props}\n >\n {/* Avatar circle */}\n <div\n className={cn(avatarSizes({ size }))}\n style={avatarStyles}\n role=\"img\"\n aria-label={`Avatar for ${name}`}\n >\n {initials}\n </div>\n\n {/* Status indicator */}\n {status && (\n <span\n className={cn(statusSizes({ size }))}\n style={{\n background: getStatusVars(status).bg,\n boxShadow: getStatusVars(status).glow,\n }}\n role=\"status\"\n aria-label={`Status: ${status}`}\n />\n )}\n </Comp>\n );\n }\n);\n\nAvatarGlass.displayName = 'AvatarGlass';\n"
22
+ }
23
+ ],
24
+ "categories": [
25
+ "ui"
26
+ ],
27
+ "cssVars": {
28
+ "light": {
29
+ "--glass-bg": "rgba(255, 255, 255, 0.1)",
30
+ "--glass-border": "rgba(255, 255, 255, 0.2)",
31
+ "--blur-sm": "8px",
32
+ "--blur-md": "16px",
33
+ "--blur-lg": "24px"
34
+ },
35
+ "dark": {
36
+ "--glass-bg": "rgba(255, 255, 255, 0.05)",
37
+ "--glass-border": "rgba(255, 255, 255, 0.1)",
38
+ "--blur-sm": "8px",
39
+ "--blur-md": "16px",
40
+ "--blur-lg": "24px"
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "badge-glass",
4
+ "type": "registry:ui",
5
+ "title": "Badge Glass",
6
+ "description": "Glass-themed badge with:",
7
+ "dependencies": [
8
+ "class-variance-authority",
9
+ "react"
10
+ ],
11
+ "registryDependencies": [
12
+ "cn",
13
+ "variants"
14
+ ],
15
+ "files": [
16
+ {
17
+ "path": "components/glass/ui/badge-glass.tsx",
18
+ "type": "registry:component",
19
+ "content": "/**\n * BadgeGlass Component\n *\n * Glass-themed badge with:\n * - Theme-aware styling via CSS variables (glass/light/aurora)\n * - shadcn/ui compatible variants (default, secondary, destructive, outline)\n * - Extended Glass UI variants (success, warning, info)\n * - Size options\n * - Optional animated dot\n */\n\nimport { forwardRef, type ReactNode, type CSSProperties } from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport { badgeVariants, type BadgeVariant } from '@/lib/variants/badge-glass-variants';\nimport '@/glass-theme.css';\n\n// ========================================\n// CSS VARIABLE HELPERS\n// ========================================\n\ntype BadgeStyleVars = { bg: string; text: string; border: string };\n\nconst variantStyles: Record<BadgeVariant, BadgeStyleVars> = {\n // shadcn/ui compatible variants\n default: {\n bg: 'var(--badge-default-bg)',\n text: 'var(--badge-default-text)',\n border: 'var(--badge-default-border)',\n },\n secondary: {\n bg: 'var(--badge-secondary-bg)',\n text: 'var(--badge-secondary-text)',\n border: 'var(--badge-secondary-border)',\n },\n destructive: {\n bg: 'var(--badge-destructive-bg)',\n text: 'var(--badge-destructive-text)',\n border: 'var(--badge-destructive-border)',\n },\n outline: {\n bg: 'var(--badge-outline-bg)',\n text: 'var(--badge-outline-text)',\n border: 'var(--badge-outline-border)',\n },\n // Glass UI extended variants\n success: {\n bg: 'var(--badge-success-bg)',\n text: 'var(--badge-success-text)',\n border: 'var(--badge-success-border)',\n },\n warning: {\n bg: 'var(--badge-warning-bg)',\n text: 'var(--badge-warning-text)',\n border: 'var(--badge-warning-border)',\n },\n info: {\n bg: 'var(--badge-info-bg)',\n text: 'var(--badge-info-text)',\n border: 'var(--badge-info-border)',\n },\n};\n\nconst getBadgeStyles = (variant: BadgeVariant): CSSProperties => {\n const v = variantStyles[variant] || variantStyles.default;\n return {\n background: v.bg,\n color: v.text,\n border: `1px solid ${v.border}`,\n };\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface BadgeGlassProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, 'style'>,\n VariantProps<typeof badgeVariants> {\n readonly children: ReactNode;\n readonly variant?: BadgeVariant;\n readonly dot?: boolean;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const BadgeGlass = forwardRef<HTMLSpanElement, BadgeGlassProps>(\n (\n { children, className, variant = 'default', size = 'md', dot, ...props },\n ref\n ) => {\n const v = variantStyles[variant];\n\n return (\n <span\n ref={ref}\n className={cn(badgeVariants({ size }), className)}\n style={getBadgeStyles(variant)}\n {...props}\n >\n {dot && (\n <span\n className=\"w-1 h-1 md:w-1.5 md:h-1.5 rounded-full animate-pulse\"\n style={{ background: v.text }}\n />\n )}\n {children}\n </span>\n );\n }\n);\n\nBadgeGlass.displayName = 'BadgeGlass';\n"
20
+ }
21
+ ],
22
+ "categories": [
23
+ "ui"
24
+ ],
25
+ "cssVars": {
26
+ "light": {
27
+ "--glass-bg": "rgba(255, 255, 255, 0.1)",
28
+ "--glass-border": "rgba(255, 255, 255, 0.2)",
29
+ "--blur-sm": "8px",
30
+ "--blur-md": "16px",
31
+ "--blur-lg": "24px"
32
+ },
33
+ "dark": {
34
+ "--glass-bg": "rgba(255, 255, 255, 0.05)",
35
+ "--glass-border": "rgba(255, 255, 255, 0.1)",
36
+ "--blur-sm": "8px",
37
+ "--blur-md": "16px",
38
+ "--blur-lg": "24px"
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "base-progress-glass",
4
+ "type": "registry:component",
5
+ "title": "Base Progress Glass",
6
+ "description": "Base progress bar component used as foundation for:",
7
+ "dependencies": [
8
+ "react"
9
+ ],
10
+ "registryDependencies": [
11
+ "cn"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "components/glass/specialized/base-progress-glass.tsx",
16
+ "type": "registry:component",
17
+ "content": "/**\n * BaseProgressGlass Component\n *\n * Base progress bar component used as foundation for:\n * - ProgressGlass (standard progress bar)\n * - RainbowProgressGlass (gradient progress bar)\n *\n * Features:\n * - Theme-aware styling\n * - Percentage calculation\n * - Accessible (ARIA progressbar)\n * - Customizable through children render prop\n */\n\nimport { forwardRef, type ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface BaseProgressGlassProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {\n readonly value?: number;\n readonly max?: number;\n readonly children?: ReactNode | ((percentage: number) => ReactNode);\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const BaseProgressGlass = forwardRef<HTMLDivElement, BaseProgressGlassProps>(\n ({ value = 0, max = 100, className, children, ...props }, ref) => {\n const percentage = Math.min(100, Math.max(0, (value / max) * 100));\n\n return (\n <div\n ref={ref}\n className={cn(\n 'relative h-2 w-full overflow-hidden rounded-full',\n 'bg-white/5 backdrop-blur-sm',\n className\n )}\n role=\"progressbar\"\n aria-valuenow={value}\n aria-valuemin={0}\n aria-valuemax={max}\n aria-valuetext={`${Math.round(percentage)}%`}\n {...props}\n >\n {typeof children === 'function'\n ? children(percentage)\n : children || (\n <div\n className=\"h-full transition-all duration-300 ease-out\"\n style={{\n width: `${percentage}%`,\n background: 'var(--progress-bar-bg, linear-gradient(90deg, #a855f7, #ec4899))',\n }}\n />\n )}\n </div>\n );\n }\n);\n\nBaseProgressGlass.displayName = 'BaseProgressGlass';\n"
18
+ }
19
+ ],
20
+ "categories": [
21
+ "specialized"
22
+ ]
23
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "button-glass",
4
+ "type": "registry:ui",
5
+ "title": "Button Glass",
6
+ "description": "Glass-themed button with:",
7
+ "dependencies": [
8
+ "@radix-ui/react-slot",
9
+ "class-variance-authority",
10
+ "lucide-react"
11
+ ],
12
+ "registryDependencies": [
13
+ "cn",
14
+ "primitives",
15
+ "use-focus",
16
+ "use-hover",
17
+ "variants"
18
+ ],
19
+ "files": [
20
+ {
21
+ "path": "components/glass/ui/button-glass.tsx",
22
+ "type": "registry:component",
23
+ "content": "/**\n * ButtonGlass Component\n *\n * Glass-themed button with:\n * - Theme-aware styling via CSS variables (glass/light/aurora)\n * - Glow effects on hover\n * - Ripple effect on click (JS)\n * - Shine animation for primary variant (JS)\n * - Loading state with spinner\n * - Icon support (left/right position)\n */\n\nimport {\n forwardRef,\n useState,\n useCallback,\n useEffect,\n useRef,\n type MouseEvent,\n type CSSProperties,\n} from 'react';\nimport { Slot } from '@radix-ui/react-slot';\nimport { type VariantProps } from 'class-variance-authority';\nimport { RefreshCw, type LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport { buttonGlassVariants, type ButtonGlassVariant } from '@/lib/variants/button-glass-variants';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// CSS VARIABLE STYLE MAPS\n// ========================================\n\nconst getVariantStyles = (\n variant: ButtonGlassVariant,\n isHovered: boolean,\n isFocusVisible: boolean\n): CSSProperties => {\n const baseStyles: Record<ButtonGlassVariant, CSSProperties> = {\n primary: {\n background: isHovered\n ? 'var(--btn-primary-hover-bg)'\n : 'var(--btn-primary-bg)',\n color: 'var(--btn-primary-text)',\n border: 'none',\n boxShadow: isFocusVisible\n ? 'var(--focus-glow)'\n : isHovered\n ? 'var(--btn-primary-glow)'\n : '0 4px 15px oklch(48.5% 0.26 283 / 0.25)',\n },\n secondary: {\n background: isHovered\n ? 'var(--btn-secondary-hover-bg)'\n : 'var(--btn-secondary-bg)',\n color: 'var(--btn-secondary-text)',\n border: '1px solid var(--btn-secondary-border)',\n boxShadow: isFocusVisible\n ? 'var(--focus-glow)'\n : isHovered\n ? 'var(--btn-secondary-glow)'\n : 'none',\n },\n ghost: {\n background: isHovered\n ? 'var(--btn-ghost-hover-bg)'\n : 'var(--btn-ghost-bg)',\n color: 'var(--btn-ghost-text)',\n border: 'none',\n boxShadow: isFocusVisible ? 'var(--focus-glow)' : 'none',\n },\n destructive: {\n background: 'var(--btn-destructive-bg)',\n color: 'var(--btn-destructive-text)',\n border: 'none',\n boxShadow: isFocusVisible\n ? 'var(--focus-glow)'\n : isHovered\n ? 'var(--btn-destructive-glow)'\n : '0 4px 15px oklch(62.8% 0.225 29 / 0.25)',\n },\n success: {\n background: 'var(--btn-success-bg)',\n color: 'var(--btn-success-text)',\n border: 'none',\n boxShadow: isFocusVisible\n ? 'var(--focus-glow)'\n : isHovered\n ? 'var(--btn-success-glow)'\n : '0 4px 15px oklch(70.7% 0.143 167 / 0.25)',\n },\n text: {\n background: 'transparent',\n color: 'var(--text-secondary)',\n border: 'none',\n boxShadow: isFocusVisible ? 'var(--focus-glow)' : 'none',\n },\n };\n\n return baseStyles[variant];\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the ButtonGlass component\n *\n * A glass-themed button with ripple effects, loading states, and icon support.\n * Features theme-aware styling and hover animations.\n *\n * @example\n * ```tsx\n * // Basic button\n * <ButtonGlass variant=\"primary\">Click me</ButtonGlass>\n *\n * // With icon\n * <ButtonGlass icon={Check} iconPosition=\"left\">Save</ButtonGlass>\n *\n * // Loading state\n * <ButtonGlass loading>Processing...</ButtonGlass>\n *\n * // Different variants\n * <ButtonGlass variant=\"ghost\">Cancel</ButtonGlass>\n * <ButtonGlass variant=\"success\">Confirm</ButtonGlass>\n *\n * // As a link (asChild pattern)\n * <ButtonGlass asChild variant=\"primary\">\n * <a href=\"/dashboard\">Go to Dashboard</a>\n * </ButtonGlass>\n *\n * // With Next.js Link\n * <ButtonGlass asChild variant=\"ghost\">\n * <Link href=\"/settings\">Settings</Link>\n * </ButtonGlass>\n * ```\n */\nexport interface ButtonGlassProps\n extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'style'>,\n VariantProps<typeof buttonGlassVariants> {\n /**\n * Render as child element instead of button (polymorphic rendering).\n * Useful for rendering buttons as links or other interactive elements.\n *\n * **Note:** When using `asChild`, decorative effects (ripple, shine, glow)\n * are disabled to maintain compatibility with Radix UI Slot.\n * Only styles and event handlers are passed to the child element.\n *\n * @default false\n * @example\n * ```tsx\n * <ButtonGlass asChild>\n * <a href=\"/about\">About Us</a>\n * </ButtonGlass>\n * ```\n */\n readonly asChild?: boolean;\n\n /**\n * Visual style variant of the button\n * @default \"primary\"\n */\n readonly variant?: ButtonGlassVariant;\n\n /**\n * Show loading spinner and disable interactions\n * @default false\n */\n readonly loading?: boolean;\n\n /**\n * Icon component from lucide-react to display\n * @example icon={Check}\n */\n readonly icon?: LucideIcon;\n\n /**\n * Position of the icon relative to button text\n * @default \"left\"\n */\n readonly iconPosition?: 'left' | 'right';\n\n /**\n * Size variant of the button\n * @default \"md\"\n */\n readonly size?: 'sm' | 'md' | 'lg' | 'icon';\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const ButtonGlass = forwardRef<HTMLButtonElement, ButtonGlassProps>(\n (\n {\n asChild = false,\n className,\n variant = 'primary',\n size = 'md',\n children,\n loading = false,\n disabled,\n icon: Icon,\n iconPosition = 'left',\n onClick,\n ...props\n },\n ref\n ) => {\n const { isHovered, hoverProps } = useHover();\n const { isFocusVisible, focusProps } = useFocus({ focusVisible: true });\n const [ripple, setRipple] = useState<{ x: number; y: number } | null>(null);\n\n const isDisabled = disabled || loading;\n const rippleTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n\n // Cleanup ripple timeout on unmount\n useEffect(() => {\n return () => {\n if (rippleTimeoutRef.current) {\n clearTimeout(rippleTimeoutRef.current);\n }\n };\n }, []);\n\n // Ripple effect handler\n const handleClick = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n if (isDisabled) return;\n\n // Create ripple effect\n const rect = e.currentTarget.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n setRipple({ x, y });\n\n // Clear previous timeout if exists\n if (rippleTimeoutRef.current) {\n clearTimeout(rippleTimeoutRef.current);\n }\n\n rippleTimeoutRef.current = setTimeout(() => {\n setRipple(null);\n rippleTimeoutRef.current = null;\n }, 600);\n\n onClick?.(e);\n },\n [isDisabled, onClick]\n );\n\n // Polymorphic component - render as Slot when asChild is true\n const Comp = asChild ? Slot : 'button';\n\n return (\n <Comp\n ref={ref}\n className={cn(\n buttonGlassVariants({ variant, size }),\n isHovered && !isDisabled && 'scale-[1.02]',\n className\n )}\n style={{\n ...getVariantStyles(variant, isHovered && !isDisabled, isFocusVisible && !isDisabled),\n outline: 'none',\n }}\n type={asChild ? undefined : 'button'}\n disabled={isDisabled}\n onClick={handleClick}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n onFocus={focusProps.onFocus}\n onBlur={focusProps.onBlur}\n {...props}\n >\n {/* When asChild is true, only render children (Slot expects a single child) */}\n {asChild ? (\n children\n ) : (\n <>\n {/* Shine effect on hover for primary */}\n {isHovered && variant === 'primary' && !isDisabled && (\n <div\n className=\"absolute inset-0 overflow-hidden pointer-events-none\"\n style={{ borderRadius: 'inherit' }}\n >\n <div\n className=\"absolute top-0 h-full w-1/3 bg-linear-to-r from-transparent via-white/20 to-transparent\"\n style={{ animation: 'btn-shine 1.5s ease-in-out infinite' }}\n />\n </div>\n )}\n\n {/* Ripple effect */}\n {ripple && (\n <span\n className=\"absolute rounded-full bg-white/30 pointer-events-none\"\n style={{\n left: ripple.x,\n top: ripple.y,\n width: 10,\n height: 10,\n transform: 'translate(-50%, -50%)',\n animation: 'ripple 0.6s ease-out',\n }}\n />\n )}\n\n {/* Pulsing glow on hover */}\n {isHovered && variant === 'primary' && !isDisabled && (\n <div\n className=\"absolute inset-0 rounded-xl animate-glow-pulse pointer-events-none\"\n style={{\n background:\n 'radial-gradient(circle, oklch(100% 0 0 / 0.1) 0%, transparent 70%)',\n }}\n />\n )}\n\n {/* Loading spinner */}\n {loading && <RefreshCw className={cn(ICON_SIZES.md, 'animate-spin')} />}\n\n {/* Icon left */}\n {!loading && Icon && iconPosition === 'left' && (\n <Icon className={ICON_SIZES.md} />\n )}\n\n {/* Content */}\n {!loading && children}\n\n {/* Icon right */}\n {!loading && Icon && iconPosition === 'right' && (\n <Icon className={ICON_SIZES.md} />\n )}\n </>\n )}\n </Comp>\n );\n }\n);\n\nButtonGlass.displayName = 'ButtonGlass';\n"
24
+ }
25
+ ],
26
+ "categories": [
27
+ "ui"
28
+ ],
29
+ "cssVars": {
30
+ "light": {
31
+ "--glass-bg": "rgba(255, 255, 255, 0.1)",
32
+ "--glass-border": "rgba(255, 255, 255, 0.2)",
33
+ "--blur-sm": "8px",
34
+ "--blur-md": "16px",
35
+ "--blur-lg": "24px"
36
+ },
37
+ "dark": {
38
+ "--glass-bg": "rgba(255, 255, 255, 0.05)",
39
+ "--glass-border": "rgba(255, 255, 255, 0.1)",
40
+ "--blur-sm": "8px",
41
+ "--blur-md": "16px",
42
+ "--blur-lg": "24px"
43
+ }
44
+ }
45
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "career-stats-glass",
4
+ "type": "registry:block",
5
+ "title": "Career Stats Glass",
6
+ "description": "Career Stats Glass component with glass effects",
7
+ "dependencies": [
8
+ "lucide-react",
9
+ "react"
10
+ ],
11
+ "registryDependencies": [
12
+ "cn"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "components/glass/sections/career-stats-glass.tsx",
17
+ "type": "registry:component",
18
+ "content": "// ========================================\n// CAREER STATS GLASS COMPONENT\n// Career statistics with expandable year cards\n// ========================================\n\nimport { forwardRef, useState } from \"react\";\nimport { TrendingUp, Code, GitPullRequest, FolderGit2 } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { GlassCard } from \"../ui/glass-card\";\nimport { YearCardGlass } from \"../composite/year-card-glass\";\nimport \"@/glass-theme.css\";\n\nexport interface YearData {\n readonly year: string | number;\n readonly emoji: string;\n readonly label: string;\n readonly commits: string;\n readonly progress: number;\n readonly prs?: number;\n readonly repos?: number;\n}\n\nexport interface CareerStatsGlassProps extends React.HTMLAttributes<HTMLDivElement> {\n readonly totalCommits?: number;\n readonly totalPRs?: number;\n readonly totalRepos?: number;\n readonly years?: readonly YearData[];\n}\n\nexport const CareerStatsGlass = forwardRef<HTMLDivElement, CareerStatsGlassProps>(\n (\n {\n totalCommits = 2242,\n totalPRs = 47,\n totalRepos = 11,\n years = [],\n className,\n ...props\n },\n ref\n ) => {\n const [expandedYear, setExpandedYear] = useState<string | number | null>(null);\n\n const handleYearClick = (year: string | number): void => {\n setExpandedYear(expandedYear === year ? null : year);\n };\n\n return (\n <GlassCard\n ref={ref}\n className={cn(\"p-4 md:p-5 lg:p-6\", className)}\n intensity=\"medium\"\n hover={false}\n {...props}\n >\n <h3\n className=\"font-semibold flex items-center gap-2 md:gap-2.5 lg:gap-3 mb-1 text-base md:text-lg lg:text-xl\"\n style={{ color: \"var(--text-primary)\" }}\n >\n <TrendingUp className=\"w-4 h-4 md:w-5 md:h-5 lg:w-6 lg:h-6\" style={{ color: \"var(--text-accent)\" }} />\n Career Stats\n </h3>\n <p\n className=\"text-xs md:text-sm lg:text-base mb-3 md:mb-4 flex items-center gap-2 md:gap-2.5 lg:gap-3 flex-wrap\"\n style={{ color: \"var(--text-secondary)\" }}\n >\n <span className=\"flex items-center gap-1 md:gap-1.5\">\n <Code className=\"w-3.5 h-3.5 md:w-4 md:h-4 lg:w-5 lg:h-5\" />\n {totalCommits.toLocaleString()} commits\n </span>\n <span>·</span>\n <span className=\"flex items-center gap-1 md:gap-1.5\">\n <GitPullRequest className=\"w-3.5 h-3.5 md:w-4 md:h-4 lg:w-5 lg:h-5\" />\n {totalPRs} PRs\n </span>\n <span>·</span>\n <span className=\"flex items-center gap-1 md:gap-1.5\">\n <FolderGit2 className=\"w-3.5 h-3.5 md:w-4 md:h-4 lg:w-5 lg:h-5\" />\n {totalRepos} repos\n </span>\n </p>\n <div className=\"space-y-2 md:space-y-2.5 lg:space-y-3\">\n {years.map((y) => (\n <YearCardGlass\n key={y.year}\n year={y.year}\n emoji={y.emoji}\n label={y.label}\n commits={y.commits}\n progress={y.progress}\n prs={y.prs}\n repos={y.repos}\n isExpanded={expandedYear === y.year}\n onClick={() => handleYearClick(y.year)}\n onShowYear={() => {\n // This can be used to filter repos by year\n // Example: onYearFilter?.(y.year)\n }}\n />\n ))}\n </div>\n </GlassCard>\n );\n }\n);\n\nCareerStatsGlass.displayName = \"CareerStatsGlass\";\n"
19
+ }
20
+ ],
21
+ "categories": [
22
+ "sections"
23
+ ]
24
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "career-stats-header-glass",
4
+ "type": "registry:block",
5
+ "title": "Career Stats Header Glass",
6
+ "description": "Career Stats Header Glass component with glass effects",
7
+ "dependencies": [
8
+ "lucide-react",
9
+ "react"
10
+ ],
11
+ "registryDependencies": [
12
+ "cn"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "components/glass/composite/career-stats-header-glass.tsx",
17
+ "type": "registry:component",
18
+ "content": "// ========================================\n// CAREER STATS HEADER GLASS - COMPOSITE COMPONENT\n// Career statistics header with total counts\n// Level 3: Composite (extracted from CareerStatsGlass)\n// ========================================\n\nimport { forwardRef, type HTMLAttributes, type CSSProperties } from 'react';\nimport { TrendingUp, Code, GitPullRequest, FolderGit2 } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\nexport interface CareerStatsHeaderGlassProps extends HTMLAttributes<HTMLDivElement> {\n /** Total commits count */\n readonly totalCommits: number;\n /** Total pull requests count */\n readonly totalPRs: number;\n /** Total repositories count */\n readonly totalRepos: number;\n /** Header title */\n readonly title?: string;\n /** Stats wrap on mobile */\n readonly wrapStats?: boolean;\n}\n\nexport const CareerStatsHeaderGlass = forwardRef<\n HTMLDivElement,\n CareerStatsHeaderGlassProps\n>(\n (\n {\n totalCommits,\n totalPRs,\n totalRepos,\n title = 'Career Stats',\n wrapStats = true,\n className,\n ...props\n },\n ref\n ) => {\n const titleStyles: CSSProperties = {\n color: 'var(--text-primary)',\n };\n\n const accentStyles: CSSProperties = {\n color: 'var(--text-accent)',\n };\n\n const statsStyles: CSSProperties = {\n color: 'var(--text-secondary)',\n };\n\n return (\n <div ref={ref} className={cn('mb-4', className)} {...props}>\n <h3\n className=\"font-semibold flex items-center gap-2 mb-1\"\n style={titleStyles}\n >\n <TrendingUp className=\"w-5 h-5\" style={accentStyles} />\n {title}\n </h3>\n <p\n className={cn(\n 'text-sm flex items-center gap-2',\n wrapStats && 'flex-wrap'\n )}\n style={statsStyles}\n >\n <span className=\"flex items-center gap-1\">\n <Code className=\"w-4 h-4\" />\n {totalCommits.toLocaleString()} commits\n </span>\n <span>·</span>\n <span className=\"flex items-center gap-1\">\n <GitPullRequest className=\"w-4 h-4\" />\n {totalPRs} PRs\n </span>\n <span>·</span>\n <span className=\"flex items-center gap-1\">\n <FolderGit2 className=\"w-4 h-4\" />\n {totalRepos} repos\n </span>\n </p>\n </div>\n );\n }\n);\n\nCareerStatsHeaderGlass.displayName = 'CareerStatsHeaderGlass';\n"
19
+ }
20
+ ],
21
+ "categories": [
22
+ "composite"
23
+ ]
24
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "checkbox-glass",
4
+ "type": "registry:ui",
5
+ "title": "Checkbox Glass",
6
+ "description": "Glass-themed checkbox with:",
7
+ "dependencies": [
8
+ "lucide-react",
9
+ "react"
10
+ ],
11
+ "registryDependencies": [
12
+ "cn",
13
+ "use-focus",
14
+ "use-hover"
15
+ ],
16
+ "files": [
17
+ {
18
+ "path": "components/glass/ui/checkbox-glass.tsx",
19
+ "type": "registry:component",
20
+ "content": "/**\n * CheckboxGlass Component\n *\n * Glass-themed checkbox with:\n * - Theme-aware styling (glass/light/aurora)\n * - Glow effect on hover\n * - Optional label\n */\n\nimport { forwardRef, type CSSProperties } from 'react';\nimport { Check } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useHover } from '@/lib/hooks/use-hover';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport '@/glass-theme.css';\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface CheckboxGlassProps\n extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'> {\n readonly checked: boolean;\n readonly onChange?: (checked: boolean) => void;\n readonly label?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const CheckboxGlass = forwardRef<HTMLInputElement, CheckboxGlassProps>(\n (\n {\n className,\n checked,\n onChange,\n label,\n disabled,\n ...props\n },\n ref\n ) => {\n const { isHovered, hoverProps } = useHover();\n const { isFocusVisible, focusProps } = useFocus({ focusVisible: true });\n\n const checkboxStyles: CSSProperties = {\n background: checked ? 'var(--checkbox-checked-bg)' : 'var(--checkbox-bg)',\n border: `2px solid ${checked ? 'var(--checkbox-checked-bg)' : 'var(--checkbox-border)'}`,\n boxShadow: isFocusVisible && !disabled\n ? 'var(--focus-glow)'\n : isHovered && !disabled\n ? 'var(--checkbox-glow)'\n : 'none',\n };\n\n return (\n <label\n className={cn(\n 'inline-flex items-center gap-2 md:gap-2.5',\n disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer',\n className\n )}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n >\n <input\n ref={ref}\n type=\"checkbox\"\n checked={checked}\n onChange={(e) => !disabled && onChange?.(e.target.checked)}\n disabled={disabled}\n className=\"sr-only\"\n {...props}\n />\n {/* Touch area wrapper - 44px minimum for Apple HIG compliance */}\n <span className=\"inline-flex items-center justify-center min-w-11 min-h-11\">\n {/* Visual checkbox - smaller but within 44px touch area */}\n <div\n onClick={() => !disabled && onChange?.(!checked)}\n onFocus={focusProps.onFocus}\n onBlur={focusProps.onBlur}\n className=\"relative w-6 h-6 md:w-5 md:h-5 rounded-md flex items-center justify-center transition-all duration-300\"\n style={checkboxStyles}\n role=\"checkbox\"\n aria-checked={checked}\n aria-label={label || 'Checkbox'}\n tabIndex={disabled ? -1 : 0}\n onKeyDown={(e) => {\n if (!disabled && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onChange?.(!checked);\n }\n }}\n >\n {checked && (\n <Check className=\"w-3.5 h-3.5 md:w-3 md:h-3\" style={{ color: 'var(--text-inverse)' }} />\n )}\n </div>\n </span>\n {label && (\n <span className=\"text-xs md:text-sm\" style={{ color: 'var(--text-secondary)' }}>\n {label}\n </span>\n )}\n </label>\n );\n }\n);\n\nCheckboxGlass.displayName = 'CheckboxGlass';\n"
21
+ }
22
+ ],
23
+ "categories": [
24
+ "ui"
25
+ ],
26
+ "cssVars": {
27
+ "light": {
28
+ "--glass-bg": "rgba(255, 255, 255, 0.1)",
29
+ "--glass-border": "rgba(255, 255, 255, 0.2)",
30
+ "--blur-sm": "8px",
31
+ "--blur-md": "16px",
32
+ "--blur-lg": "24px"
33
+ },
34
+ "dark": {
35
+ "--glass-bg": "rgba(255, 255, 255, 0.05)",
36
+ "--glass-border": "rgba(255, 255, 255, 0.1)",
37
+ "--blur-sm": "8px",
38
+ "--blur-md": "16px",
39
+ "--blur-lg": "24px"
40
+ }
41
+ }
42
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "circular-metric-glass",
4
+ "type": "registry:block",
5
+ "title": "Circular Metric Glass",
6
+ "description": "CircularMetricGlass - Compact circular progress metric display",
7
+ "dependencies": [
8
+ "react"
9
+ ],
10
+ "registryDependencies": [
11
+ "cn"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "components/glass/composite/circular-metric-glass.tsx",
16
+ "type": "registry:component",
17
+ "content": "// ========================================\n// CIRCULAR METRIC GLASS COMPONENT\n// Compact circular metric display for mobile\n// ========================================\n\nimport { forwardRef } from 'react';\nimport { cn } from '@/lib/utils';\nimport {\n CircularProgressGlass,\n type CircularProgressGradient,\n} from '../ui/circular-progress-glass';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport type CircularMetricColor = 'emerald' | 'amber' | 'blue' | 'red';\n\nexport interface CircularMetricGlassProps\n extends React.HTMLAttributes<HTMLDivElement> {\n /** Metric label (e.g., \"Reg\", \"Imp\") */\n readonly label: string;\n /** Metric value (0-100) */\n readonly value: number;\n /** Metric color */\n readonly color?: CircularMetricColor;\n /** Size variant */\n readonly size?: 'sm' | 'md';\n}\n\n// ========================================\n// HELPERS\n// ========================================\n\n// Map CircularMetricColor to CircularProgressGlass gradient and CSS variable\nconst colorMap: Record<\n CircularMetricColor,\n { gradient: CircularProgressGradient; textVar: string }\n> = {\n emerald: { gradient: 'emerald', textVar: 'var(--metric-emerald-text)' },\n amber: { gradient: 'amber', textVar: 'var(--metric-amber-text)' },\n blue: { gradient: 'blue', textVar: 'var(--metric-blue-text)' },\n red: { gradient: 'rose', textVar: 'var(--metric-red-text)' },\n};\n\n// ========================================\n// COMPONENT\n// ========================================\n\n/**\n * CircularMetricGlass - Compact circular progress metric display\n *\n * Designed for mobile layouts where rectangular MetricCardGlass is too wide.\n * Shows a circular progress indicator with percentage inside and label below.\n *\n * @example\n * ```tsx\n * <CircularMetricGlass label=\"Reg\" value={84} color=\"emerald\" />\n * ```\n */\nexport const CircularMetricGlass = forwardRef<\n HTMLDivElement,\n CircularMetricGlassProps\n>(({ label, value, color = 'blue', size = 'sm', className, ...props }, ref) => {\n const { gradient, textVar } = colorMap[color];\n\n return (\n <div\n ref={ref}\n className={cn('flex flex-col items-center gap-1', className)}\n {...props}\n >\n <CircularProgressGlass\n value={value}\n size={size}\n color={gradient}\n labelColor={textVar}\n thickness={size === 'sm' ? 6 : 8}\n showGlow\n glowIntensity=\"medium\"\n />\n <span\n className=\"text-xs font-medium\"\n style={{ color: textVar }}\n >\n {label}\n </span>\n </div>\n );\n});\n\nCircularMetricGlass.displayName = 'CircularMetricGlass';\n"
18
+ }
19
+ ],
20
+ "categories": [
21
+ "composite"
22
+ ]
23
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "circular-progress-glass",
4
+ "type": "registry:ui",
5
+ "title": "Circular Progress Glass",
6
+ "description": "SVG-based circular progress indicator with:",
7
+ "dependencies": [
8
+ "class-variance-authority",
9
+ "react"
10
+ ],
11
+ "registryDependencies": [
12
+ "cn"
13
+ ],
14
+ "files": [
15
+ {
16
+ "path": "components/glass/ui/circular-progress-glass.tsx",
17
+ "type": "registry:component",
18
+ "content": "/**\n * CircularProgressGlass Component\n *\n * SVG-based circular progress indicator with:\n * - Determinate and indeterminate variants\n * - Configurable size and thickness\n * - Glow effect with SVG filters\n * - Theme-aware styling\n * - Optional label in center\n * - Gradient colors support\n */\n\nimport { forwardRef, useMemo, useId } from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// VARIANTS\n// ========================================\n\nconst circularProgressVariants = cva('relative inline-flex items-center justify-center', {\n variants: {\n size: {\n sm: 'w-16 h-16',\n md: 'w-24 h-24',\n lg: 'w-32 h-32',\n xl: 'w-40 h-40',\n },\n },\n defaultVariants: {\n size: 'md',\n },\n});\n\n// ========================================\n// TYPES\n// ========================================\n\nexport type CircularProgressGradient = 'violet' | 'blue' | 'cyan' | 'amber' | 'emerald' | 'rose';\n\nexport interface CircularProgressGlassProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'>,\n VariantProps<typeof circularProgressVariants> {\n /** Progress value (0-100) for determinate variant */\n readonly value?: number;\n /** Variant type */\n readonly variant?: 'determinate' | 'indeterminate';\n /** Stroke width in pixels */\n readonly thickness?: number;\n /** Background track width in pixels */\n readonly trackWidth?: number;\n /** Progress color gradient */\n readonly color?: CircularProgressGradient;\n /** Track color (background circle) */\n readonly trackColor?: string;\n /** Show percentage label in center */\n readonly showLabel?: boolean;\n /** Custom label text (overrides percentage) */\n readonly label?: string;\n /** Custom color for the center label text */\n readonly labelColor?: string;\n /** Show glow effect */\n readonly showGlow?: boolean;\n /** Glow intensity */\n readonly glowIntensity?: 'low' | 'medium' | 'high';\n /** Stroke linecap style */\n readonly strokeLinecap?: 'round' | 'butt' | 'square';\n /** Animation duration in seconds */\n readonly animationDuration?: number;\n}\n\n// ========================================\n// HELPERS\n// ========================================\n\nconst getGradientColors = (gradient: CircularProgressGradient) => {\n const gradients: Record<CircularProgressGradient, { 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\nconst getGlowStdDeviation = (intensity: 'low' | 'medium' | 'high'): number => {\n const intensities = { low: 2, medium: 4, high: 6 };\n return intensities[intensity];\n};\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const CircularProgressGlass = forwardRef<HTMLDivElement, CircularProgressGlassProps>(\n (\n {\n className,\n size = 'md',\n value = 0,\n variant = 'determinate',\n thickness = 8,\n trackWidth = 8,\n color = 'violet',\n trackColor = 'oklch(100% 0 0 / 0.1)',\n showLabel = true,\n label,\n labelColor,\n showGlow = true,\n glowIntensity = 'medium',\n strokeLinecap = 'round',\n animationDuration = 1,\n ...props\n },\n ref\n ) => {\n const clampedValue = Math.min(100, Math.max(0, value));\n const gradientColors = getGradientColors(color);\n\n // SVG dimensions\n const sizeMap = { sm: 64, md: 96, lg: 128, xl: 160 };\n const svgSize = sizeMap[size || 'md'];\n const radius = (svgSize - Math.max(thickness, trackWidth)) / 2;\n const circumference = 2 * Math.PI * radius;\n const center = svgSize / 2;\n\n // Calculate stroke dash offset for determinate progress\n const dashOffset = useMemo(() => {\n if (variant === 'indeterminate') return circumference * 0.75;\n return circumference * ((100 - clampedValue) / 100);\n }, [variant, clampedValue, circumference]);\n\n // Generate unique IDs for SVG elements (using useId for stable IDs)\n const uniqueId = useId();\n const gradientId = `circular-gradient-${uniqueId}`;\n const glowId = `circular-glow-${uniqueId}`;\n\n return (\n <div ref={ref} className={cn(circularProgressVariants({ size }), className)} {...props}>\n <svg\n width={svgSize}\n height={svgSize}\n className=\"transform -rotate-90\"\n aria-hidden=\"true\"\n >\n <defs>\n {/* Gradient definition */}\n <linearGradient id={gradientId} x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor={gradientColors.from} />\n <stop offset=\"100%\" stopColor={gradientColors.to} />\n </linearGradient>\n\n {/* Glow filter */}\n {showGlow && (\n <filter id={glowId}>\n <feGaussianBlur stdDeviation={getGlowStdDeviation(glowIntensity)} result=\"coloredBlur\" />\n <feMerge>\n <feMergeNode in=\"coloredBlur\" />\n <feMergeNode in=\"SourceGraphic\" />\n </feMerge>\n </filter>\n )}\n </defs>\n\n {/* Background track */}\n <circle\n cx={center}\n cy={center}\n r={radius}\n fill=\"none\"\n stroke={trackColor}\n strokeWidth={trackWidth}\n />\n\n {/* Progress circle */}\n <circle\n cx={center}\n cy={center}\n r={radius}\n fill=\"none\"\n stroke={`url(#${gradientId})`}\n strokeWidth={thickness}\n strokeLinecap={strokeLinecap}\n strokeDasharray={circumference}\n strokeDashoffset={dashOffset}\n filter={showGlow ? `url(#${glowId})` : undefined}\n className={cn(\n 'transition-all',\n variant === 'indeterminate' && 'animate-circular-progress-spin'\n )}\n style={{\n transitionDuration: `${animationDuration}s`,\n transitionTimingFunction: 'cubic-bezier(0.4, 0, 0.2, 1)',\n }}\n />\n </svg>\n\n {/* Center label */}\n {showLabel && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span\n className=\"text-sm font-semibold tabular-nums\"\n style={{ color: labelColor || 'var(--text-primary)' }}\n >\n {label || (variant === 'determinate' ? `${clampedValue}%` : '')}\n </span>\n </div>\n )}\n\n {/* Accessibility */}\n <div\n role=\"progressbar\"\n aria-valuenow={variant === 'determinate' ? clampedValue : undefined}\n aria-valuemin={0}\n aria-valuemax={100}\n aria-label={label || (variant === 'determinate' ? `Progress: ${clampedValue}%` : 'Loading progress')}\n aria-valuetext={label || (variant === 'determinate' ? `${clampedValue}%` : 'Loading...')}\n className=\"sr-only\"\n >\n {label || (variant === 'determinate' ? `${clampedValue}%` : 'Loading...')}\n </div>\n </div>\n );\n }\n);\n\nCircularProgressGlass.displayName = 'CircularProgressGlass';\n"
19
+ }
20
+ ],
21
+ "categories": [
22
+ "ui"
23
+ ],
24
+ "cssVars": {
25
+ "light": {
26
+ "--glass-bg": "rgba(255, 255, 255, 0.1)",
27
+ "--glass-border": "rgba(255, 255, 255, 0.2)",
28
+ "--blur-sm": "8px",
29
+ "--blur-md": "16px",
30
+ "--blur-lg": "24px"
31
+ },
32
+ "dark": {
33
+ "--glass-bg": "rgba(255, 255, 255, 0.05)",
34
+ "--glass-border": "rgba(255, 255, 255, 0.1)",
35
+ "--blur-sm": "8px",
36
+ "--blur-md": "16px",
37
+ "--blur-lg": "24px"
38
+ }
39
+ }
40
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "combobox-glass",
4
+ "type": "registry:ui",
5
+ "title": "Combobox Glass",
6
+ "description": "Glass-themed combobox (searchable select) with:",
7
+ "dependencies": [
8
+ "lucide-react",
9
+ "react"
10
+ ],
11
+ "registryDependencies": [
12
+ "cn",
13
+ "variants"
14
+ ],
15
+ "files": [
16
+ {
17
+ "path": "components/glass/ui/combobox-glass.tsx",
18
+ "type": "registry:component",
19
+ "content": "/**\n * ComboBoxGlass Component\n *\n * Glass-themed combobox (searchable select) with:\n * - Keyboard navigation support\n * - Search/filter functionality\n * - Custom rendering options\n * - Async data loading support\n * - Glass styling with theme support\n * - Accessibility features\n * - Form field wrapper support (label, error, success)\n * - Size variants (sm, md, lg, xl)\n * - Optional searchable mode\n * - Icon support for trigger and options\n */\n\n'use client';\n\nimport { useState, useMemo, useCallback, forwardRef, useId } from 'react';\nimport { CheckIcon, ChevronsUpDownIcon, type LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { ButtonGlass } from './button-glass';\nimport { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';\nimport {\n Command,\n CommandEmpty,\n CommandGroup,\n CommandInput,\n CommandItem,\n CommandList,\n} from '@/components/ui/command';\nimport { FormFieldWrapper } from '../primitives/form-field-wrapper';\nimport { inputVariants, type InputGlassSize } from '@/lib/variants/input-glass-variants';\nimport { getDropdownContentStyles } from '@/lib/variants/dropdown-content-styles';\nimport { ICON_SIZES } from '../primitives/style-utils';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport type GlassVariant = 'glass' | 'frosted' | 'fluted' | 'crystal';\n\nexport interface ComboBoxOption<T = string> {\n readonly value: T;\n readonly label: string;\n readonly disabled?: boolean;\n /** Optional icon component for the option */\n readonly icon?: LucideIcon;\n}\n\nexport interface ComboBoxGlassProps<T = string> {\n /** Available options */\n readonly options: readonly ComboBoxOption<T>[];\n /** Currently selected value */\n readonly value?: T;\n /** Callback when value changes */\n readonly onChange?: (value: T | undefined) => void;\n /** Placeholder text for trigger button */\n readonly placeholder?: string;\n /** Text shown when no results found */\n readonly emptyText?: string;\n /** Placeholder for search input */\n readonly searchPlaceholder?: string;\n /** Glass variant style */\n readonly glassVariant?: GlassVariant;\n /** Disabled state */\n readonly disabled?: boolean;\n /** Custom className for container */\n readonly className?: string;\n /** Custom className for popover content */\n readonly popoverClassName?: string;\n /** Allow clearing selection */\n readonly clearable?: boolean;\n /** Popover side */\n readonly side?: 'top' | 'right' | 'bottom' | 'left';\n /** Popover alignment */\n readonly align?: 'start' | 'center' | 'end';\n\n // ========================================\n // NEW PROPS (Week 3 Enhancement)\n // ========================================\n\n /** Label text displayed above the field */\n readonly label?: string;\n /** Error message - displays in red below the field */\n readonly error?: string;\n /** Success message - displays in green if no error */\n readonly success?: string;\n /** Shows required asterisk (*) next to label */\n readonly required?: boolean;\n /** Size variant (affects trigger button height and padding) */\n readonly size?: InputGlassSize;\n /** Enable/disable search functionality */\n readonly searchable?: boolean;\n /** Optional icon for trigger button (displayed before text) */\n readonly icon?: LucideIcon;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nfunction ComboBoxGlassInner<T = string>(\n {\n options,\n value,\n onChange,\n placeholder = 'Select option...',\n emptyText = 'No results found.',\n searchPlaceholder = 'Search...',\n glassVariant = 'glass',\n disabled = false,\n className,\n popoverClassName,\n clearable = false,\n side = 'bottom',\n align = 'start',\n // NEW PROPS\n label,\n error,\n success,\n required = false,\n size = 'md',\n searchable = true,\n icon: TriggerIcon,\n }: ComboBoxGlassProps<T>,\n ref: React.ForwardedRef<HTMLButtonElement>\n) {\n const [open, setOpen] = useState(false);\n const [search, setSearch] = useState('');\n const fieldId = useId();\n\n // Find selected option\n const selectedOption = useMemo(\n () => options.find((opt) => opt.value === value),\n [options, value]\n );\n\n // Filter options based on search\n const filteredOptions = useMemo(() => {\n if (!search) return options;\n const searchLower = search.toLowerCase();\n return options.filter((opt) =>\n opt.label.toLowerCase().includes(searchLower)\n );\n }, [options, search]);\n\n // Handle option selection\n const handleSelect = useCallback(\n (optionValue: T) => {\n if (clearable && value === optionValue) {\n onChange?.(undefined);\n } else {\n onChange?.(optionValue);\n }\n setOpen(false);\n setSearch('');\n },\n [value, onChange, clearable]\n );\n\n // Get glass variant class\n const getGlassClass = () => {\n const variants: Record<GlassVariant, string> = {\n glass: 'glass',\n frosted: 'frosted',\n fluted: 'fluted',\n crystal: 'crystal',\n };\n return variants[glassVariant];\n };\n\n // Render content\n const comboboxContent = (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <ButtonGlass\n ref={ref}\n variant=\"secondary\"\n role=\"combobox\"\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-label={selectedOption?.label || placeholder}\n aria-describedby={error ? `${fieldId}-error` : success ? `${fieldId}-success` : undefined}\n disabled={disabled}\n className={cn(\n 'w-full justify-between',\n // Apply size variant via inputVariants\n inputVariants({ size }),\n !selectedOption && 'text-muted-foreground',\n className\n )}\n >\n <span className=\"flex items-center gap-2 truncate\">\n {TriggerIcon && <TriggerIcon className={ICON_SIZES.md} />}\n {selectedOption ? selectedOption.label : placeholder}\n </span>\n <ChevronsUpDownIcon className={cn(ICON_SIZES.md, 'shrink-0 opacity-50')} />\n </ButtonGlass>\n </PopoverTrigger>\n <PopoverContent\n side={side}\n align={align}\n className={cn(\n 'w-[--radix-popover-trigger-width] p-0 rounded-2xl overflow-hidden border-0',\n getGlassClass(),\n popoverClassName\n )}\n style={getDropdownContentStyles()}\n >\n <Command\n shouldFilter={false}\n className={cn(\n 'bg-transparent',\n // Hide default border, use subtle divider instead\n '[&_[data-slot=command-input-wrapper]]:border-b-0',\n // Fix search icon - use glass theme color with better visibility\n '[&_[data-slot=command-input-wrapper]_svg]:text-[var(--text-muted)]',\n '[&_[data-slot=command-input-wrapper]_svg]:opacity-80'\n )}\n >\n {searchable && (\n <CommandInput\n placeholder={searchPlaceholder}\n value={search}\n onValueChange={setSearch}\n className=\"text-[var(--text-primary)] placeholder:text-[var(--text-muted)] h-10 font-medium\"\n />\n )}\n <CommandList className=\"p-1.5\">\n <CommandEmpty className=\"text-[var(--text-muted)] py-4\">{emptyText}</CommandEmpty>\n <CommandGroup className=\"text-[var(--text-primary)] p-0\">\n {filteredOptions.map((option) => {\n const OptionIcon = option.icon;\n const isSelected = value === option.value;\n\n return (\n <CommandItem\n key={String(option.value)}\n value={String(option.value)}\n disabled={option.disabled}\n onSelect={() => handleSelect(option.value)}\n className={cn(\n 'w-full px-3 py-2.5 text-sm flex items-center gap-2 rounded-none',\n 'cursor-pointer transition-colors duration-150',\n 'text-[var(--dropdown-item-text)]',\n 'data-[selected=true]:bg-[var(--dropdown-item-hover)]',\n isSelected && 'bg-[var(--select-item-selected-bg)] text-[var(--select-item-selected-text)]',\n option.disabled && 'cursor-not-allowed opacity-50'\n )}\n >\n <CheckIcon\n className={cn(\n ICON_SIZES.md,\n 'shrink-0',\n isSelected ? 'opacity-100 text-[var(--text-accent)]' : 'opacity-0'\n )}\n />\n {OptionIcon && <OptionIcon className={cn(ICON_SIZES.md, 'shrink-0 text-[var(--dropdown-icon)]')} />}\n <span className=\"truncate\">{option.label}</span>\n </CommandItem>\n );\n })}\n </CommandGroup>\n </CommandList>\n </Command>\n </PopoverContent>\n </Popover>\n );\n\n // Wrap with FormFieldWrapper if label/error/success provided\n if (label || error || success) {\n return (\n <FormFieldWrapper\n label={label}\n error={error}\n success={success}\n required={required}\n htmlFor={fieldId}\n >\n {comboboxContent}\n </FormFieldWrapper>\n );\n }\n\n return comboboxContent;\n}\n\n// Export with generic type support\nexport const ComboBoxGlass = forwardRef(ComboBoxGlassInner) as <T = string>(\n props: ComboBoxGlassProps<T> & { ref?: React.ForwardedRef<HTMLButtonElement> }\n) => ReturnType<typeof ComboBoxGlassInner>;\n\n// Add display name for debugging\nComboBoxGlassInner.displayName = 'ComboBoxGlass';\n"
20
+ }
21
+ ],
22
+ "categories": [
23
+ "ui"
24
+ ],
25
+ "cssVars": {
26
+ "light": {
27
+ "--glass-bg": "rgba(255, 255, 255, 0.1)",
28
+ "--glass-border": "rgba(255, 255, 255, 0.2)",
29
+ "--blur-sm": "8px",
30
+ "--blur-md": "16px",
31
+ "--blur-lg": "24px"
32
+ },
33
+ "dark": {
34
+ "--glass-bg": "rgba(255, 255, 255, 0.05)",
35
+ "--glass-border": "rgba(255, 255, 255, 0.1)",
36
+ "--blur-sm": "8px",
37
+ "--blur-md": "16px",
38
+ "--blur-lg": "24px"
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://ui.shadcn.com/schema/registry-item.json",
3
+ "name": "contribution-metrics-glass",
4
+ "type": "registry:block",
5
+ "title": "Contribution Metrics Glass",
6
+ "description": "Contribution Metrics Glass component with glass effects",
7
+ "dependencies": [
8
+ "react"
9
+ ],
10
+ "registryDependencies": [
11
+ "cn"
12
+ ],
13
+ "files": [
14
+ {
15
+ "path": "components/glass/composite/contribution-metrics-glass.tsx",
16
+ "type": "registry:component",
17
+ "content": "// ========================================\n// CONTRIBUTION METRICS GLASS - COMPOSITE COMPONENT\n// Repository contribution metrics grid\n// Level 3: Composite (extracted from RepositoryCardGlass)\n// ========================================\n\nimport { forwardRef, type HTMLAttributes, type CSSProperties } from 'react';\nimport { cn } from '@/lib/utils';\nimport '@/glass-theme.css';\n\nexport interface ContributionMetricsGlassProps extends HTMLAttributes<HTMLDivElement> {\n /** User's commit count */\n readonly userCommits: number;\n /** User's contribution percentage */\n readonly userContribution: number;\n /** Total project commits (calculated if not provided) */\n readonly totalProjectCommits?: number;\n /** Estimated lines of code */\n readonly estimatedLines?: number;\n /** Grid layout (1 or 2 columns) */\n readonly columns?: 1 | 2;\n}\n\nexport const ContributionMetricsGlass = forwardRef<\n HTMLDivElement,\n ContributionMetricsGlassProps\n>(\n (\n {\n userCommits,\n userContribution,\n totalProjectCommits: providedTotal,\n estimatedLines: providedLines,\n columns = 2,\n className,\n ...props\n },\n ref\n ) => {\n // Calculate total project commits from contribution percentage\n const totalProjectCommits =\n providedTotal ??\n (userContribution > 0\n ? Math.round(userCommits / (userContribution / 100))\n : userCommits);\n\n const estimatedLines = providedLines ?? Math.round(userCommits * 12);\n\n const cardStyles: CSSProperties = {\n background: 'var(--card-bg)',\n borderColor: 'var(--card-border)',\n };\n\n const mutedStyles: CSSProperties = {\n color: 'var(--text-muted)',\n };\n\n const primaryStyles: CSSProperties = {\n color: 'var(--text-primary)',\n };\n\n const columnClasses = {\n 1: 'grid-cols-1',\n 2: 'grid-cols-1 sm:grid-cols-2',\n };\n\n return (\n <div\n ref={ref}\n className={cn('grid gap-2', columnClasses[columns], className)}\n {...props}\n >\n <div className=\"p-2.5 rounded-lg border\" style={cardStyles}>\n <div className=\"text-xs\" style={mutedStyles}>\n Your Contribution\n </div>\n <div className=\"font-semibold\" style={primaryStyles}>\n {userCommits.toLocaleString()} commits\n </div>\n <div className=\"text-xs\" style={mutedStyles}>\n {userContribution}%\n </div>\n </div>\n <div className=\"p-2.5 rounded-lg border\" style={cardStyles}>\n <div className=\"text-xs\" style={mutedStyles}>\n Full Project\n </div>\n <div className=\"font-semibold\" style={primaryStyles}>\n {totalProjectCommits.toLocaleString()} commits\n </div>\n <div className=\"text-xs\" style={mutedStyles}>\n ~{estimatedLines.toLocaleString()} lines\n </div>\n </div>\n </div>\n );\n }\n);\n\nContributionMetricsGlass.displayName = 'ContributionMetricsGlass';\n"
18
+ }
19
+ ],
20
+ "categories": [
21
+ "composite"
22
+ ]
23
+ }