shadcn-glass-ui 1.0.8 → 1.0.10

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 (36) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/components.cjs +4 -4
  3. package/dist/components.js +1 -1
  4. package/dist/hooks.cjs +2 -2
  5. package/dist/index.cjs +5 -5
  6. package/dist/index.js +1 -1
  7. package/dist/r/alert-glass.json +1 -1
  8. package/dist/r/badge-glass.json +1 -1
  9. package/dist/r/button-glass.json +1 -1
  10. package/dist/r/checkbox-glass.json +1 -1
  11. package/dist/r/dropdown-glass.json +1 -1
  12. package/dist/r/glass-card.json +1 -1
  13. package/dist/r/input-glass.json +1 -1
  14. package/dist/r/language-bar-glass.json +1 -1
  15. package/dist/r/modal-glass.json +1 -1
  16. package/dist/r/progress-glass.json +1 -1
  17. package/dist/r/registry.json +0 -2
  18. package/dist/r/segmented-control-glass.json +1 -1
  19. package/dist/r/tabs-glass.json +1 -1
  20. package/dist/r/tooltip-glass.json +1 -1
  21. package/dist/shadcn-glass-ui.css +1 -1
  22. package/dist/{theme-context-BcTQdqsj.cjs → theme-context-XtasSxRT.cjs} +2 -2
  23. package/dist/{theme-context-BcTQdqsj.cjs.map → theme-context-XtasSxRT.cjs.map} +1 -1
  24. package/dist/themes.cjs +1 -1
  25. package/dist/{trust-score-card-glass-Dq28n8en.cjs → trust-score-card-glass-CNcQveNY.cjs} +19 -15
  26. package/dist/{trust-score-card-glass-Dq28n8en.cjs.map → trust-score-card-glass-CNcQveNY.cjs.map} +1 -1
  27. package/dist/{trust-score-card-glass-CHzWGuko.js → trust-score-card-glass-CowcDyxH.js} +16 -12
  28. package/dist/{trust-score-card-glass-CHzWGuko.js.map → trust-score-card-glass-CowcDyxH.js.map} +1 -1
  29. package/dist/{use-focus-CH8KNgcY.cjs → use-focus-BbpE2qGq.cjs} +2 -2
  30. package/dist/{use-focus-CH8KNgcY.cjs.map → use-focus-BbpE2qGq.cjs.map} +1 -1
  31. package/dist/{use-wallpaper-tint-DNecAf46.cjs → use-wallpaper-tint-CIqtoETa.cjs} +2 -2
  32. package/dist/{use-wallpaper-tint-DNecAf46.cjs.map → use-wallpaper-tint-CIqtoETa.cjs.map} +1 -1
  33. package/dist/{utils-3cDWhVvH.cjs → utils-CriE74ig.cjs} +2 -2
  34. package/dist/{utils-3cDWhVvH.cjs.map → utils-CriE74ig.cjs.map} +1 -1
  35. package/dist/utils.cjs +1 -1
  36. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.9] - 2025-12-06
9
+
10
+ ### 🐛 Bug Fixes
11
+
12
+ **CSS Import Order:**
13
+ - Fixed PostCSS warnings by moving all `@import` directives before CSS rules
14
+ - All imports now precede style declarations (per CSS spec requirement)
15
+ - Build process now runs completely clean without warnings
16
+
17
+ **Result:** Zero PostCSS warnings in production build
18
+
8
19
  ## [1.0.8] - 2025-12-06
9
20
 
10
21
  ### 📚 Documentation
@@ -1,7 +1,7 @@
1
- const require_trust_score_card_glass = require("./trust-score-card-glass-Dq28n8en.cjs");
2
- require("./utils-3cDWhVvH.cjs");
3
- require("./use-focus-CH8KNgcY.cjs");
4
- require("./theme-context-BcTQdqsj.cjs");
1
+ const require_trust_score_card_glass = require("./trust-score-card-glass-CNcQveNY.cjs");
2
+ require("./utils-CriE74ig.cjs");
3
+ require("./use-focus-BbpE2qGq.cjs");
4
+ require("./theme-context-XtasSxRT.cjs");
5
5
  exports.AICardGlass = require_trust_score_card_glass.AICardGlass;
6
6
  exports.AlertGlass = require_trust_score_card_glass.AlertGlass;
7
7
  exports.AvatarGlass = require_trust_score_card_glass.AvatarGlass;
@@ -1,5 +1,5 @@
1
1
  import "./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, M as SortDropdownGlass, N as SearchBoxGlass, O as StatusIndicatorGlass, P as IconButtonGlass, Q as DropdownGlass, R as ToggleGlass, S as RainbowProgressGlass, T as ProfileAvatarGlass, V as SliderGlass, W as PopoverGlass, X as GlassCard, Y as InputGlass, _ as ContributionMetricsGlass, a as HeaderBrandingGlass, at as BadgeGlass, b as AICardGlass, c as YearCardGlass, d as TrustScoreDisplayGlass, dt as InteractiveCard, f as RepositoryMetadataGlass, ft as FormFieldWrapper, g as MetricCardGlass, h as MetricsGridGlass, i as HeaderNavGlass, j as StatItemGlass, k as BaseProgressGlass, l as UserStatsLineGlass, m as RepositoryCardGlass, n as ProjectsListGlass, nt as CheckboxGlass, o as FlagsSectionGlass, 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, x as SegmentedControlGlass, y as CareerStatsHeaderGlass } from "./trust-score-card-glass-CHzWGuko.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, M as SortDropdownGlass, N as SearchBoxGlass, O as StatusIndicatorGlass, P as IconButtonGlass, Q as DropdownGlass, R as ToggleGlass, S as RainbowProgressGlass, T as ProfileAvatarGlass, V as SliderGlass, W as PopoverGlass, X as GlassCard, Y as InputGlass, _ as ContributionMetricsGlass, a as HeaderBrandingGlass, at as BadgeGlass, b as AICardGlass, c as YearCardGlass, d as TrustScoreDisplayGlass, dt as InteractiveCard, f as RepositoryMetadataGlass, ft as FormFieldWrapper, g as MetricCardGlass, h as MetricsGridGlass, i as HeaderNavGlass, j as StatItemGlass, k as BaseProgressGlass, l as UserStatsLineGlass, m as RepositoryCardGlass, n as ProjectsListGlass, nt as CheckboxGlass, o as FlagsSectionGlass, 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, x as SegmentedControlGlass, y as CareerStatsHeaderGlass } from "./trust-score-card-glass-CowcDyxH.js";
3
3
  import "./use-focus-CX0TJJIj.js";
4
4
  import "./theme-context-BZoCplcU.js";
5
5
  export { AICardGlass, AlertGlass, AvatarGlass, BadgeGlass, BaseProgressGlass, ButtonGlass, CareerStatsGlass, CareerStatsHeaderGlass, CheckboxGlass, CircularMetricGlass, CircularProgressGlass, ComboBoxGlass, ContributionMetricsGlass, DropdownGlass, ExpandableHeaderGlass, FlagAlertGlass, FlagsSectionGlass, FormFieldWrapper, GlassCard, HeaderBrandingGlass, HeaderNavGlass, IconButtonGlass, InputGlass, InteractiveCard, LanguageBarGlass, MetricCardGlass, MetricsGridGlass, ModalGlass, NotificationGlass, PopoverGlass, ProfileAvatarGlass, ProfileHeaderGlass, ProgressGlass, ProjectsListGlass, RainbowProgressGlass, RepositoryCardGlass, RepositoryHeaderGlass, RepositoryMetadataGlass, SearchBoxGlass, SegmentedControlGlass, SkeletonGlass, SliderGlass, SortDropdownGlass, StatItemGlass, StatusIndicatorGlass, TabsGlass, ThemeToggleGlass, ToggleGlass, TooltipGlass, TouchTarget, TrustScoreCardGlass, TrustScoreDisplayGlass, UserInfoGlass, UserStatsLineGlass, YearCardGlass };
package/dist/hooks.cjs CHANGED
@@ -1,5 +1,5 @@
1
- const require_use_focus = require("./use-focus-CH8KNgcY.cjs");
2
- const require_use_wallpaper_tint = require("./use-wallpaper-tint-DNecAf46.cjs");
1
+ const require_use_focus = require("./use-focus-BbpE2qGq.cjs");
2
+ const require_use_wallpaper_tint = require("./use-wallpaper-tint-CIqtoETa.cjs");
3
3
  exports.useFocus = require_use_focus.useFocus;
4
4
  exports.useHover = require_use_focus.useHover;
5
5
  exports.useResponsive = require_use_wallpaper_tint.useResponsive;
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
- const require_trust_score_card_glass = require("./trust-score-card-glass-Dq28n8en.cjs");
2
- const require_utils = require("./utils-3cDWhVvH.cjs");
3
- const require_use_focus = require("./use-focus-CH8KNgcY.cjs");
4
- const require_theme_context = require("./theme-context-BcTQdqsj.cjs");
5
- const require_use_wallpaper_tint = require("./use-wallpaper-tint-DNecAf46.cjs");
1
+ const require_trust_score_card_glass = require("./trust-score-card-glass-CNcQveNY.cjs");
2
+ const require_utils = require("./utils-CriE74ig.cjs");
3
+ const require_use_focus = require("./use-focus-BbpE2qGq.cjs");
4
+ const require_theme_context = require("./theme-context-XtasSxRT.cjs");
5
+ const require_use_wallpaper_tint = require("./use-wallpaper-tint-CIqtoETa.cjs");
6
6
  let class_variance_authority = require("class-variance-authority");
7
7
  const dropdownAlign = (0, class_variance_authority.cva)("absolute mt-2 min-w-[160px] md:min-w-[200px] rounded-2xl py-1.5 md:py-2", {
8
8
  variants: { align: {
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-CHzWGuko.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-CowcDyxH.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";
@@ -17,7 +17,7 @@
17
17
  {
18
18
  "path": "components/glass/ui/alert-glass.tsx",
19
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"
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\n/**\n * Props for the AlertGlass component\n *\n * A glass-themed alert with semantic variants, dismissible option, and automatic icon selection.\n * Features theme-aware styling and WCAG-compliant role attributes.\n *\n * @accessibility\n * - **Keyboard Navigation:** Dismissible alerts include a keyboard-accessible close button (Tab + Enter/Space)\n * - **Focus Management:** Close button receives visible focus ring (WCAG 2.4.7)\n * - **Screen Readers:** Uses `role=\"alert\"` for immediate announcement to screen readers (WCAG 4.1.3)\n * - **Icon Semantics:** Icons are decorative and hidden from screen readers with `aria-hidden=\"true\"`\n * - **Variant Semantics:** Each variant uses distinct colors and icons for multi-modal communication (color + icon)\n * - **Touch Targets:** Dismiss button meets minimum 44x44px touch target (WCAG 2.5.5)\n * - **Color Contrast:** All variant text and backgrounds meet WCAG AA contrast ratio 4.5:1\n * - **Motion:** Transitions respect `prefers-reduced-motion` settings\n *\n * @example\n * ```tsx\n * // Basic alert (info/default variant)\n * <AlertGlass title=\"Information\" variant=\"default\">\n * This is an informational message\n * </AlertGlass>\n *\n * // Error alert with aria-live for dynamic updates\n * <AlertGlass variant=\"destructive\" title=\"Error\" aria-live=\"assertive\">\n * Your session has expired. Please log in again.\n * </AlertGlass>\n *\n * // Success alert\n * <AlertGlass variant=\"success\" title=\"Success\">\n * Your changes have been saved successfully.\n * </AlertGlass>\n *\n * // Warning alert\n * <AlertGlass variant=\"warning\" title=\"Warning\">\n * Your subscription expires in 3 days.\n * </AlertGlass>\n *\n * // Dismissible alert with accessible close button\n * <AlertGlass\n * variant=\"default\"\n * title=\"Welcome\"\n * dismissible\n * onDismiss={() => setShowAlert(false)}\n * >\n * Check out our new features!\n * </AlertGlass>\n *\n * // Alert without title\n * <AlertGlass variant=\"destructive\">\n * Quick error message without title\n * </AlertGlass>\n *\n * // Form validation alert\n * <form onSubmit={handleSubmit}>\n * {formError && (\n * <AlertGlass variant=\"destructive\" title=\"Validation Error\" role=\"alert\">\n * {formError}\n * </AlertGlass>\n * )}\n * <InputGlass label=\"Email\" />\n * <ButtonGlass type=\"submit\">Submit</ButtonGlass>\n * </form>\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
21
  }
22
22
  ],
23
23
  "categories": [
@@ -16,7 +16,7 @@
16
16
  {
17
17
  "path": "components/glass/ui/badge-glass.tsx",
18
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"
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\n/**\n * Props for the BadgeGlass component\n *\n * A glass-themed badge with semantic variants and optional animated status dot.\n * Features shadcn/ui compatible variants plus extended Glass UI variants.\n *\n * @accessibility\n * - **Keyboard Navigation:** Badges are non-interactive by default (display-only)\n * - **Focus Management:** N/A - badges do not receive focus unless wrapped in interactive elements\n * - **Screen Readers:** Semantic `<span>` element, content announced naturally\n * - **Status Indicators:** Use `aria-label` to provide context for status badges (e.g., \"Status: Active\")\n * - **Animated Dot:** Pulse animation respects `prefers-reduced-motion` settings\n * - **Touch Targets:** N/A for display badges, ensure 44x44px if wrapping in button/link (WCAG 2.5.5)\n * - **Color Contrast:** All variant text meets WCAG AA contrast ratio 4.5:1 against badge background\n * - **Motion:** Dot pulse animation can be disabled for users with motion sensitivity\n *\n * @example\n * ```tsx\n * // Basic badge with variant\n * <BadgeGlass variant=\"default\">New</BadgeGlass>\n *\n * // Status badge with aria-label for screen readers\n * <BadgeGlass variant=\"success\" aria-label=\"Status: Active\">\n * Active\n * </BadgeGlass>\n *\n * // Different variants (shadcn/ui compatible)\n * <BadgeGlass variant=\"default\">Default</BadgeGlass>\n * <BadgeGlass variant=\"secondary\">Secondary</BadgeGlass>\n * <BadgeGlass variant=\"destructive\">Error</BadgeGlass>\n * <BadgeGlass variant=\"outline\">Outline</BadgeGlass>\n *\n * // Extended Glass UI variants\n * <BadgeGlass variant=\"success\">Success</BadgeGlass>\n * <BadgeGlass variant=\"warning\">Warning</BadgeGlass>\n * <BadgeGlass variant=\"info\">Info</BadgeGlass>\n *\n * // With animated status dot\n * <BadgeGlass variant=\"success\" dot aria-label=\"Status: Online\">\n * Online\n * </BadgeGlass>\n * <BadgeGlass variant=\"destructive\" dot aria-label=\"Status: Offline\">\n * Offline\n * </BadgeGlass>\n *\n * // Size variants\n * <BadgeGlass size=\"sm\">Small</BadgeGlass>\n * <BadgeGlass size=\"md\">Medium</BadgeGlass>\n * <BadgeGlass size=\"lg\">Large</BadgeGlass>\n *\n * // Inside interactive elements (ensure accessible labels)\n * <button aria-label=\"Filter by active status\">\n * Filter: <BadgeGlass variant=\"success\">Active</BadgeGlass>\n * </button>\n *\n * // Count badge with semantic meaning\n * <div>\n * <span>Notifications</span>\n * <BadgeGlass variant=\"destructive\" aria-label=\"3 unread notifications\">\n * 3\n * </BadgeGlass>\n * </div>\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
20
  }
21
21
  ],
22
22
  "categories": [
@@ -20,7 +20,7 @@
20
20
  {
21
21
  "path": "components/glass/ui/button-glass.tsx",
22
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"
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 * @accessibility\n * - **Keyboard Navigation:** Fully keyboard accessible with native `<button>` element\n * - **Focus Management:** Visible focus ring using `--focus-glow` CSS variable (WCAG 2.4.7)\n * - **Screen Readers:** Semantic `<button>` element, disabled state announced automatically\n * - **Loading State:** When loading=true, button is disabled and loading spinner is visible\n * - **Touch Targets:** Minimum 44x44px touch target (WCAG 2.5.5) via size variants\n * - **Color Contrast:** All variants meet WCAG AA contrast ratio 4.5:1 minimum\n * - **Motion:** Respects `prefers-reduced-motion` for ripple/shine animations\n *\n * @example\n * ```tsx\n * // Basic button\n * <ButtonGlass variant=\"primary\">Click me</ButtonGlass>\n *\n * // With icon and aria-label for icon-only buttons\n * <ButtonGlass icon={Check} iconPosition=\"left\">Save</ButtonGlass>\n * <ButtonGlass icon={X} size=\"icon\" aria-label=\"Close dialog\" />\n *\n * // Loading state (automatically disables and shows spinner)\n * <ButtonGlass loading aria-live=\"polite\">Processing...</ButtonGlass>\n *\n * // Different variants\n * <ButtonGlass variant=\"ghost\">Cancel</ButtonGlass>\n * <ButtonGlass variant=\"success\">Confirm</ButtonGlass>\n * <ButtonGlass variant=\"destructive\">Delete</ButtonGlass>\n *\n * // As a link (asChild pattern) - maintains semantic HTML\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 * // Form submit button\n * <ButtonGlass type=\"submit\" variant=\"primary\">\n * Submit Form\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
24
  }
25
25
  ],
26
26
  "categories": [
@@ -17,7 +17,7 @@
17
17
  {
18
18
  "path": "components/glass/ui/checkbox-glass.tsx",
19
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"
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\n/**\n * Props for the CheckboxGlass component\n *\n * A glass-themed checkbox with accessible keyboard navigation, focus management,\n * and touch-friendly targets. Features glow effects and theme-aware styling.\n *\n * @accessibility\n * - **Keyboard Navigation:** Full keyboard support with Enter/Space to toggle, Tab to focus (WCAG 2.1.1)\n * - **Focus Management:** Visible focus ring using `--focus-glow` CSS variable (WCAG 2.4.7)\n * - **Screen Readers:** Dual implementation with native `<input type=\"checkbox\">` (hidden) + visual `<div role=\"checkbox\">`\n * - **ARIA Attributes:** Uses `role=\"checkbox\"` and `aria-checked` for proper state announcement\n * - **Label Association:** Visual label automatically associated with checkbox via `<label>` wrapper\n * - **Touch Targets:** 44x44px minimum touch area per Apple HIG (WCAG 2.5.5 compliance)\n * - **Color Contrast:** Check mark and backgrounds meet WCAG AA contrast ratio 4.5:1\n * - **Motion:** Transitions respect `prefers-reduced-motion` settings\n *\n * @example\n * ```tsx\n * // Basic checkbox with label\n * <CheckboxGlass checked={agreed} onChange={setAgreed} label=\"I agree to terms\" />\n *\n * // Checkbox with accessible name (no visual label)\n * <CheckboxGlass\n * checked={checked}\n * onChange={setChecked}\n * aria-label=\"Select all items\"\n * />\n *\n * // Form integration with validation\n * <form onSubmit={handleSubmit}>\n * <CheckboxGlass\n * checked={newsletter}\n * onChange={setNewsletter}\n * label=\"Subscribe to newsletter\"\n * aria-describedby=\"newsletter-help\"\n * />\n * <p id=\"newsletter-help\">Receive weekly updates</p>\n * <CheckboxGlass\n * checked={terms}\n * onChange={setTerms}\n * label=\"Accept terms and conditions\"\n * required\n * aria-invalid={submitted && !terms}\n * />\n * {submitted && !terms && (\n * <span role=\"alert\">You must accept the terms</span>\n * )}\n * </form>\n *\n * // Disabled checkbox (state announced to screen readers)\n * <CheckboxGlass\n * checked={true}\n * onChange={() => {}}\n * label=\"This option is locked\"\n * disabled\n * />\n *\n * // Checkbox group with fieldset\n * <fieldset>\n * <legend>Select your interests</legend>\n * <CheckboxGlass\n * checked={interests.tech}\n * onChange={(checked) => setInterests({ ...interests, tech: checked })}\n * label=\"Technology\"\n * />\n * <CheckboxGlass\n * checked={interests.design}\n * onChange={(checked) => setInterests({ ...interests, design: checked })}\n * label=\"Design\"\n * />\n * </fieldset>\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
21
  }
22
22
  ],
23
23
  "categories": [
@@ -18,7 +18,7 @@
18
18
  {
19
19
  "path": "components/glass/ui/dropdown-glass.tsx",
20
20
  "type": "registry:component",
21
- "content": "/**\n * DropdownGlass Component\n *\n * Glass-themed dropdown menu based on Radix UI with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth animations\n * - Proper positioning and accessibility\n * - Optional item icons and dividers\n *\n * @example\n * Simple API (recommended for basic dropdowns):\n * ```tsx\n * import { DropdownGlass } from '@/components/glass/ui/dropdown-glass';\n * import { MoreVertical, Edit, Trash } from 'lucide-react';\n *\n * <DropdownGlass\n * trigger={\n * <button>\n * <MoreVertical />\n * </button>\n * }\n * items={[\n * { label: 'Edit', icon: Edit, onClick: () => handleEdit() },\n * { divider: true },\n * { label: 'Delete', icon: Trash, onClick: () => handleDelete(), danger: true }\n * ]}\n * />\n * ```\n *\n * @example\n * Advanced: Using Radix UI primitives directly (for complex dropdowns):\n * ```tsx\n * import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\n * import { getDropdownContentStyles, dropdownContentClasses } from '@/lib/variants/dropdown-content-styles';\n *\n * <DropdownMenuPrimitive.Root>\n * <DropdownMenuPrimitive.Trigger asChild>\n * <button>Open Menu</button>\n * </DropdownMenuPrimitive.Trigger>\n *\n * <DropdownMenuPrimitive.Portal>\n * <DropdownMenuPrimitive.Content\n * className={dropdownContentClasses}\n * style={getDropdownContentStyles()}\n * align=\"start\"\n * sideOffset={8}\n * >\n * <DropdownMenuPrimitive.Label className=\"px-3 py-1.5 text-xs font-medium\">\n * Actions\n * </DropdownMenuPrimitive.Label>\n *\n * <DropdownMenuPrimitive.Item\n * className=\"px-3 py-2 cursor-pointer hover:bg-[var(--dropdown-item-hover)]\"\n * onSelect={() => handleAction()}\n * >\n * Action Item\n * </DropdownMenuPrimitive.Item>\n *\n * <DropdownMenuPrimitive.Separator className=\"h-px my-1 bg-[var(--dropdown-border)]\" />\n *\n * <DropdownMenuPrimitive.CheckboxItem\n * checked={isChecked}\n * onCheckedChange={setIsChecked}\n * >\n * <DropdownMenuPrimitive.ItemIndicator>\n * <Check className=\"w-4 h-4\" />\n * </DropdownMenuPrimitive.ItemIndicator>\n * Checkbox Item\n * </DropdownMenuPrimitive.CheckboxItem>\n *\n * <DropdownMenuPrimitive.Sub>\n * <DropdownMenuPrimitive.SubTrigger>\n * More Options\n * </DropdownMenuPrimitive.SubTrigger>\n * <DropdownMenuPrimitive.SubContent>\n * <DropdownMenuPrimitive.Item>Sub Item 1</DropdownMenuPrimitive.Item>\n * <DropdownMenuPrimitive.Item>Sub Item 2</DropdownMenuPrimitive.Item>\n * </DropdownMenuPrimitive.SubContent>\n * </DropdownMenuPrimitive.Sub>\n * </DropdownMenuPrimitive.Content>\n * </DropdownMenuPrimitive.Portal>\n * </DropdownMenuPrimitive.Root>\n * ```\n *\n * @see {@link https://www.radix-ui.com/primitives/docs/components/dropdown-menu Radix UI Dropdown Menu Documentation}\n *\n * Available Radix primitives:\n * - `DropdownMenuPrimitive.Root` - Root component\n * - `DropdownMenuPrimitive.Trigger` - Trigger button (use `asChild` for custom triggers)\n * - `DropdownMenuPrimitive.Content` - Dropdown content container\n * - `DropdownMenuPrimitive.Item` - Menu item\n * - `DropdownMenuPrimitive.CheckboxItem` - Checkbox menu item\n * - `DropdownMenuPrimitive.RadioGroup` + `RadioItem` - Radio group\n * - `DropdownMenuPrimitive.Label` - Section label\n * - `DropdownMenuPrimitive.Separator` - Visual separator\n * - `DropdownMenuPrimitive.Sub` + `SubTrigger` + `SubContent` - Nested menus\n * - `DropdownMenuPrimitive.Portal` - Portal for dropdown content\n *\n * Use `getDropdownContentStyles()` and `dropdownContentClasses` from\n * `@/lib/variants/dropdown-content-styles` for consistent glass styling.\n */\n\nimport * as React from 'react';\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport type { LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport { getDropdownContentStyles, dropdownContentClasses } from '@/lib/variants/dropdown-content-styles';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport interface DropdownItem {\n readonly label?: string;\n readonly icon?: LucideIcon;\n readonly onClick?: () => void;\n readonly danger?: boolean;\n readonly divider?: boolean;\n}\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\nexport interface DropdownGlassProps {\n readonly trigger: React.ReactNode;\n readonly items: readonly DropdownItem[];\n readonly align?: 'left' | 'right';\n readonly className?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const DropdownGlass = React.forwardRef<\n HTMLDivElement,\n DropdownGlassProps\n>(({ trigger, items, align = 'left', className }, ref) => {\n return (\n <div ref={ref} className={cn('relative inline-block', className)}>\n <DropdownMenuPrimitive.Root>\n <DropdownMenuPrimitive.Trigger asChild>\n {trigger}\n </DropdownMenuPrimitive.Trigger>\n\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n align={align === 'left' ? 'start' : 'end'}\n sideOffset={8}\n className={dropdownContentClasses}\n style={getDropdownContentStyles()}\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {items.map((item, idx) =>\n item.divider ? (\n <DropdownMenuPrimitive.Separator\n key={`divider-${idx}`}\n className=\"my-2 mx-3 h-px\"\n style={{\n borderTop: '1px solid var(--dropdown-divider)',\n }}\n role=\"separator\"\n />\n ) : (\n <DropdownMenuPrimitive.Item\n key={`item-${idx}`}\n onClick={item.onClick}\n className={cn(\n 'group w-full px-3 py-2 md:px-4 md:py-2.5 text-xs md:text-sm text-left flex items-center gap-2 md:gap-3',\n 'cursor-default select-none',\n 'transition-colors duration-200 ease-out',\n 'focus-visible:outline-none focus-visible:shadow-(--focus-glow)',\n 'data-[highlighted]:bg-[var(--dropdown-item-hover)]',\n item.danger\n ? 'text-[var(--alert-danger-text)] data-[highlighted]:text-[var(--alert-danger-text)]'\n : 'text-[var(--dropdown-item-text)]'\n )}\n role=\"menuitem\"\n >\n {item.icon && (\n <item.icon\n className={cn(\n ICON_SIZES.md,\n 'transition-colors duration-200 ease-out shrink-0',\n item.danger\n ? 'text-[var(--alert-danger-text)]'\n : 'text-[var(--dropdown-icon)] group-data-[highlighted]:text-[var(--dropdown-icon-hover)]'\n )}\n />\n )}\n <span className=\"font-medium\">{item.label}</span>\n </DropdownMenuPrimitive.Item>\n )\n )}\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n </DropdownMenuPrimitive.Root>\n </div>\n );\n});\n\nDropdownGlass.displayName = 'DropdownGlass';\n"
21
+ "content": "/**\n * DropdownGlass Component\n *\n * Glass-themed dropdown menu based on Radix UI with:\n * - Theme-aware styling (glass/light/aurora)\n * - Smooth animations\n * - Proper positioning and accessibility\n * - Optional item icons and dividers\n *\n * @example\n * Simple API (recommended for basic dropdowns):\n * ```tsx\n * import { DropdownGlass } from '@/components/glass/ui/dropdown-glass';\n * import { MoreVertical, Edit, Trash } from 'lucide-react';\n *\n * <DropdownGlass\n * trigger={\n * <button>\n * <MoreVertical />\n * </button>\n * }\n * items={[\n * { label: 'Edit', icon: Edit, onClick: () => handleEdit() },\n * { divider: true },\n * { label: 'Delete', icon: Trash, onClick: () => handleDelete(), danger: true }\n * ]}\n * />\n * ```\n *\n * @example\n * Advanced: Using Radix UI primitives directly (for complex dropdowns):\n * ```tsx\n * import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\n * import { getDropdownContentStyles, dropdownContentClasses } from '@/lib/variants/dropdown-content-styles';\n *\n * <DropdownMenuPrimitive.Root>\n * <DropdownMenuPrimitive.Trigger asChild>\n * <button>Open Menu</button>\n * </DropdownMenuPrimitive.Trigger>\n *\n * <DropdownMenuPrimitive.Portal>\n * <DropdownMenuPrimitive.Content\n * className={dropdownContentClasses}\n * style={getDropdownContentStyles()}\n * align=\"start\"\n * sideOffset={8}\n * >\n * <DropdownMenuPrimitive.Label className=\"px-3 py-1.5 text-xs font-medium\">\n * Actions\n * </DropdownMenuPrimitive.Label>\n *\n * <DropdownMenuPrimitive.Item\n * className=\"px-3 py-2 cursor-pointer hover:bg-[var(--dropdown-item-hover)]\"\n * onSelect={() => handleAction()}\n * >\n * Action Item\n * </DropdownMenuPrimitive.Item>\n *\n * <DropdownMenuPrimitive.Separator className=\"h-px my-1 bg-[var(--dropdown-border)]\" />\n *\n * <DropdownMenuPrimitive.CheckboxItem\n * checked={isChecked}\n * onCheckedChange={setIsChecked}\n * >\n * <DropdownMenuPrimitive.ItemIndicator>\n * <Check className=\"w-4 h-4\" />\n * </DropdownMenuPrimitive.ItemIndicator>\n * Checkbox Item\n * </DropdownMenuPrimitive.CheckboxItem>\n *\n * <DropdownMenuPrimitive.Sub>\n * <DropdownMenuPrimitive.SubTrigger>\n * More Options\n * </DropdownMenuPrimitive.SubTrigger>\n * <DropdownMenuPrimitive.SubContent>\n * <DropdownMenuPrimitive.Item>Sub Item 1</DropdownMenuPrimitive.Item>\n * <DropdownMenuPrimitive.Item>Sub Item 2</DropdownMenuPrimitive.Item>\n * </DropdownMenuPrimitive.SubContent>\n * </DropdownMenuPrimitive.Sub>\n * </DropdownMenuPrimitive.Content>\n * </DropdownMenuPrimitive.Portal>\n * </DropdownMenuPrimitive.Root>\n * ```\n *\n * @see {@link https://www.radix-ui.com/primitives/docs/components/dropdown-menu Radix UI Dropdown Menu Documentation}\n *\n * Available Radix primitives:\n * - `DropdownMenuPrimitive.Root` - Root component\n * - `DropdownMenuPrimitive.Trigger` - Trigger button (use `asChild` for custom triggers)\n * - `DropdownMenuPrimitive.Content` - Dropdown content container\n * - `DropdownMenuPrimitive.Item` - Menu item\n * - `DropdownMenuPrimitive.CheckboxItem` - Checkbox menu item\n * - `DropdownMenuPrimitive.RadioGroup` + `RadioItem` - Radio group\n * - `DropdownMenuPrimitive.Label` - Section label\n * - `DropdownMenuPrimitive.Separator` - Visual separator\n * - `DropdownMenuPrimitive.Sub` + `SubTrigger` + `SubContent` - Nested menus\n * - `DropdownMenuPrimitive.Portal` - Portal for dropdown content\n *\n * Use `getDropdownContentStyles()` and `dropdownContentClasses` from\n * `@/lib/variants/dropdown-content-styles` for consistent glass styling.\n */\n\nimport * as React from 'react';\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';\nimport type { LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { ICON_SIZES } from '@/components/glass/primitives';\nimport { getDropdownContentStyles, dropdownContentClasses } from '@/lib/variants/dropdown-content-styles';\nimport '@/glass-theme.css';\n\n// ========================================\n// TYPES\n// ========================================\n\nexport interface DropdownItem {\n readonly label?: string;\n readonly icon?: LucideIcon;\n readonly onClick?: () => void;\n readonly danger?: boolean;\n readonly divider?: boolean;\n}\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the DropdownGlass component\n *\n * A glass-themed dropdown menu with accessible keyboard navigation, based on Radix UI primitives.\n * Features theme-aware styling, smooth animations, and WCAG-compliant interactions.\n *\n * @accessibility\n * - **Keyboard Navigation:** Arrow keys navigate menu items, Enter/Space activates, Escape closes (WCAG 2.1.1)\n * - **Focus Management:** Focus trapped within menu when open, returned to trigger on close (WCAG 2.4.3)\n * - **Screen Readers:** Uses `role=\"menu\"` and `role=\"menuitem\"` for proper menu semantics (WCAG 4.1.3)\n * - **ARIA Attributes:** Items marked with `data-highlighted` state for screen reader announcement\n * - **Trigger Association:** Menu automatically associated with trigger button via Radix UI primitives\n * - **Touch Targets:** All menu items meet minimum 44x44px touch target (WCAG 2.5.5)\n * - **Color Contrast:** Menu text and backgrounds meet WCAG AA contrast ratio 4.5:1\n * - **Motion:** Open/close animations respect `prefers-reduced-motion` settings\n *\n * @example\n * ```tsx\n * // Basic dropdown with icon items\n * <DropdownGlass\n * trigger={\n * <button aria-label=\"Open menu\">\n * <MoreVertical className=\"w-5 h-5\" />\n * </button>\n * }\n * items={[\n * { label: 'Edit', icon: Edit, onClick: handleEdit },\n * { label: 'Delete', icon: Trash, onClick: handleDelete, danger: true }\n * ]}\n * />\n *\n * // Dropdown with dividers and alignment\n * <DropdownGlass\n * trigger={<ButtonGlass>Actions</ButtonGlass>}\n * align=\"right\"\n * items={[\n * { label: 'View', icon: Eye, onClick: () => navigate('/view') },\n * { label: 'Edit', icon: Edit, onClick: () => setEditMode(true) },\n * { divider: true },\n * { label: 'Archive', icon: Archive, onClick: handleArchive },\n * { divider: true },\n * { label: 'Delete', icon: Trash, onClick: handleDelete, danger: true }\n * ]}\n * />\n *\n * // Dropdown in table row (ensure trigger has accessible label)\n * <DropdownGlass\n * trigger={\n * <button aria-label={`Actions for ${user.name}`}>\n * <MoreHorizontal />\n * </button>\n * }\n * items={[\n * { label: 'View Profile', onClick: () => viewProfile(user.id) },\n * { label: 'Send Message', onClick: () => sendMessage(user.id) }\n * ]}\n * />\n *\n * // Dropdown with keyboard-friendly trigger\n * <DropdownGlass\n * trigger={\n * <ButtonGlass\n * icon={Settings}\n * variant=\"ghost\"\n * aria-haspopup=\"menu\"\n * aria-label=\"Settings menu\"\n * >\n * Settings\n * </ButtonGlass>\n * }\n * items={[\n * { label: 'Preferences', icon: Sliders, onClick: () => navigate('/settings/preferences') },\n * { label: 'Security', icon: Lock, onClick: () => navigate('/settings/security') },\n * { divider: true },\n * { label: 'Log Out', icon: LogOut, onClick: handleLogout }\n * ]}\n * />\n *\n * // Advanced: Using Radix primitives directly (see component JSDoc for examples)\n * ```\n */\nexport interface DropdownGlassProps {\n readonly trigger: React.ReactNode;\n readonly items: readonly DropdownItem[];\n readonly align?: 'left' | 'right';\n readonly className?: string;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const DropdownGlass = React.forwardRef<\n HTMLDivElement,\n DropdownGlassProps\n>(({ trigger, items, align = 'left', className }, ref) => {\n return (\n <div ref={ref} className={cn('relative inline-block', className)}>\n <DropdownMenuPrimitive.Root>\n <DropdownMenuPrimitive.Trigger asChild>\n {trigger}\n </DropdownMenuPrimitive.Trigger>\n\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n align={align === 'left' ? 'start' : 'end'}\n sideOffset={8}\n className={dropdownContentClasses}\n style={getDropdownContentStyles()}\n role=\"menu\"\n aria-orientation=\"vertical\"\n >\n {items.map((item, idx) =>\n item.divider ? (\n <DropdownMenuPrimitive.Separator\n key={`divider-${idx}`}\n className=\"my-2 mx-3 h-px\"\n style={{\n borderTop: '1px solid var(--dropdown-divider)',\n }}\n role=\"separator\"\n />\n ) : (\n <DropdownMenuPrimitive.Item\n key={`item-${idx}`}\n onClick={item.onClick}\n className={cn(\n 'group w-full px-3 py-2 md:px-4 md:py-2.5 text-xs md:text-sm text-left flex items-center gap-2 md:gap-3',\n 'cursor-default select-none',\n 'transition-colors duration-200 ease-out',\n 'focus-visible:outline-none focus-visible:shadow-(--focus-glow)',\n 'data-[highlighted]:bg-[var(--dropdown-item-hover)]',\n item.danger\n ? 'text-[var(--alert-danger-text)] data-[highlighted]:text-[var(--alert-danger-text)]'\n : 'text-[var(--dropdown-item-text)]'\n )}\n role=\"menuitem\"\n >\n {item.icon && (\n <item.icon\n className={cn(\n ICON_SIZES.md,\n 'transition-colors duration-200 ease-out shrink-0',\n item.danger\n ? 'text-[var(--alert-danger-text)]'\n : 'text-[var(--dropdown-icon)] group-data-[highlighted]:text-[var(--dropdown-icon-hover)]'\n )}\n />\n )}\n <span className=\"font-medium\">{item.label}</span>\n </DropdownMenuPrimitive.Item>\n )\n )}\n </DropdownMenuPrimitive.Content>\n </DropdownMenuPrimitive.Portal>\n </DropdownMenuPrimitive.Root>\n </div>\n );\n});\n\nDropdownGlass.displayName = 'DropdownGlass';\n"
22
22
  }
23
23
  ],
24
24
  "categories": [
@@ -17,7 +17,7 @@
17
17
  {
18
18
  "path": "components/glass/ui/glass-card.tsx",
19
19
  "type": "registry:component",
20
- "content": "/**\n * GlassCard Component\n *\n * Glass-themed container with:\n * - Theme-aware styling (glass/light/aurora)\n * - Configurable blur intensity\n * - Optional glow effects\n * - Hover animations\n */\n\nimport {\n forwardRef,\n type ReactNode,\n type CSSProperties,\n} 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 { cardIntensity } from '@/lib/variants/glass-card-variants';\nimport '@/glass-theme.css';\n\nimport type { GlowType, IntensityType, PaddingType } from '@/lib/variants/glass-card-variants';\n\n// ========================================\n// BLUR MAP\n// ========================================\n// Per UI_DESIGN.md design tokens:\n// - subtle: 8px (--glass-blur-sm) - light glass effect\n// - medium: 16px (--glass-blur-md) - standard cards (was 12px - breaking change)\n// - strong: 24px (--glass-blur-lg) - featured cards (was 16px - breaking change)\n\nconst blurMap: Record<IntensityType, string> = {\n subtle: 'var(--blur-sm)', // 8px\n medium: 'var(--blur-md)', // 16px (BREAKING: was 12px)\n strong: 'var(--blur-lg)', // 24px (BREAKING: was 16px)\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the GlassCard component\n *\n * @example\n * ```tsx\n * // Basic card\n * <GlassCard intensity=\"medium\">Content</GlassCard>\n *\n * // As a clickable link\n * <GlassCard asChild intensity=\"medium\">\n * <a href=\"/details\">View Details</a>\n * </GlassCard>\n * ```\n */\nexport interface GlassCardProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>,\n VariantProps<typeof cardIntensity> {\n /**\n * Render as child element instead of div (polymorphic rendering).\n * Useful for making cards clickable links or custom interactive elements.\n * @default false\n * @example\n * ```tsx\n * <GlassCard asChild>\n * <a href=\"/article\">Article Content</a>\n * </GlassCard>\n * ```\n */\n readonly asChild?: boolean;\n\n readonly children: ReactNode;\n readonly glow?: GlowType;\n readonly padding?: PaddingType;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// CSS variable maps for intensity\nconst bgVarMap: Record<IntensityType, string> = {\n subtle: 'var(--card-subtle-bg)',\n medium: 'var(--card-medium-bg)',\n strong: 'var(--card-strong-bg)',\n};\n\nconst borderVarMap: Record<IntensityType, string> = {\n subtle: 'var(--card-subtle-border)',\n medium: 'var(--card-medium-border)',\n strong: 'var(--card-strong-border)',\n};\n\nconst glowVarMap: Record<string, string> = {\n blue: 'var(--glow-blue)',\n violet: 'var(--glow-violet)',\n purple: 'var(--glow-violet)',\n cyan: 'var(--glow-cyan)',\n};\n\nexport const GlassCard = forwardRef<HTMLDivElement, GlassCardProps>(\n (\n {\n asChild = false,\n children,\n className,\n intensity = 'medium',\n glow = null,\n hover = true,\n padding = 'default',\n ...props\n },\n ref\n ) => {\n const { isHovered, hoverProps } = useHover();\n const intensityVal = intensity ?? 'medium';\n\n const cardStyles: CSSProperties = {\n background: isHovered && hover ? 'var(--card-hover-bg)' : bgVarMap[intensityVal],\n borderColor: isHovered && hover ? 'var(--card-hover-border)' : borderVarMap[intensityVal],\n backdropFilter: `blur(${blurMap[intensityVal]})`,\n WebkitBackdropFilter: `blur(${blurMap[intensityVal]})`,\n boxShadow: glow\n ? glowVarMap[glow]\n : isHovered && hover\n ? 'var(--card-hover-glow)'\n : 'var(--glow-neutral)',\n };\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(cardIntensity({ intensity, hover, padding }), className)}\n style={cardStyles}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n {...props}\n >\n {children}\n </Comp>\n );\n }\n);\n\nGlassCard.displayName = 'GlassCard';\n"
20
+ "content": "/**\n * GlassCard Component\n *\n * Glass-themed container with:\n * - Theme-aware styling (glass/light/aurora)\n * - Configurable blur intensity\n * - Optional glow effects\n * - Hover animations\n */\n\nimport {\n forwardRef,\n type ReactNode,\n type CSSProperties,\n} 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 { cardIntensity } from '@/lib/variants/glass-card-variants';\nimport '@/glass-theme.css';\n\nimport type { GlowType, IntensityType, PaddingType } from '@/lib/variants/glass-card-variants';\n\n// ========================================\n// BLUR MAP\n// ========================================\n// Per UI_DESIGN.md design tokens:\n// - subtle: 8px (--glass-blur-sm) - light glass effect\n// - medium: 16px (--glass-blur-md) - standard cards (was 12px - breaking change)\n// - strong: 24px (--glass-blur-lg) - featured cards (was 16px - breaking change)\n\nconst blurMap: Record<IntensityType, string> = {\n subtle: 'var(--blur-sm)', // 8px\n medium: 'var(--blur-md)', // 16px (BREAKING: was 12px)\n strong: 'var(--blur-lg)', // 24px (BREAKING: was 16px)\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the GlassCard component\n *\n * A glass-themed container with configurable blur, glow effects, and hover animations.\n * Features polymorphic rendering via `asChild` for semantic HTML flexibility.\n *\n * @accessibility\n * - **Keyboard Navigation:** When used with `asChild` as a link/button, inherits native keyboard support (Enter/Space activation)\n * - **Focus Management:** Focus ring applied to child element when using `asChild` pattern with interactive elements\n * - **Screen Readers:** Semantic HTML preserved via `asChild` - use appropriate elements (`<a>`, `<button>`, `<article>`)\n * - **Hover State:** Hover effects are purely visual and do not affect functionality (progressive enhancement)\n * - **Touch Targets:** When interactive, ensure child element meets minimum 44x44px touch target (WCAG 2.5.5)\n * - **Color Contrast:** Card border and background meet WCAG AA contrast requirements, content contrast is consumer's responsibility\n * - **Motion:** Hover scale animation respects `prefers-reduced-motion` settings via CSS transitions\n *\n * @example\n * ```tsx\n * // Basic card\n * <GlassCard intensity=\"medium\">Content</GlassCard>\n *\n * // As a clickable link with accessible name\n * <GlassCard asChild intensity=\"medium\">\n * <a href=\"/details\" aria-label=\"View product details\">\n * <h3>Product Title</h3>\n * <p>Description</p>\n * </a>\n * </GlassCard>\n *\n * // Different intensity levels\n * <GlassCard intensity=\"subtle\">Subtle blur</GlassCard>\n * <GlassCard intensity=\"medium\">Standard blur</GlassCard>\n * <GlassCard intensity=\"strong\">Heavy blur</GlassCard>\n *\n * // With glow effects\n * <GlassCard glow=\"blue\">Blue glow card</GlassCard>\n * <GlassCard glow=\"violet\">Violet glow card</GlassCard>\n * <GlassCard glow=\"cyan\">Cyan glow card</GlassCard>\n *\n * // As a button (interactive) with role\n * <GlassCard asChild hover intensity=\"medium\">\n * <button onClick={handleClick} aria-label=\"Open settings\">\n * <Settings className=\"w-6 h-6\" />\n * <span>Settings</span>\n * </button>\n * </GlassCard>\n *\n * // Article card with semantic HTML\n * <GlassCard asChild intensity=\"medium\" padding=\"lg\">\n * <article>\n * <header><h2>Article Title</h2></header>\n * <p>Article content...</p>\n * <footer>Published: Jan 1, 2025</footer>\n * </article>\n * </GlassCard>\n * ```\n */\nexport interface GlassCardProps\n extends Omit<React.HTMLAttributes<HTMLDivElement>, 'style'>,\n VariantProps<typeof cardIntensity> {\n /**\n * Render as child element instead of div (polymorphic rendering).\n * Useful for making cards clickable links or custom interactive elements.\n * @default false\n * @example\n * ```tsx\n * <GlassCard asChild>\n * <a href=\"/article\">Article Content</a>\n * </GlassCard>\n * ```\n */\n readonly asChild?: boolean;\n\n readonly children: ReactNode;\n readonly glow?: GlowType;\n readonly padding?: PaddingType;\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\n// CSS variable maps for intensity\nconst bgVarMap: Record<IntensityType, string> = {\n subtle: 'var(--card-subtle-bg)',\n medium: 'var(--card-medium-bg)',\n strong: 'var(--card-strong-bg)',\n};\n\nconst borderVarMap: Record<IntensityType, string> = {\n subtle: 'var(--card-subtle-border)',\n medium: 'var(--card-medium-border)',\n strong: 'var(--card-strong-border)',\n};\n\nconst glowVarMap: Record<string, string> = {\n blue: 'var(--glow-blue)',\n violet: 'var(--glow-violet)',\n purple: 'var(--glow-violet)',\n cyan: 'var(--glow-cyan)',\n};\n\nexport const GlassCard = forwardRef<HTMLDivElement, GlassCardProps>(\n (\n {\n asChild = false,\n children,\n className,\n intensity = 'medium',\n glow = null,\n hover = true,\n padding = 'default',\n ...props\n },\n ref\n ) => {\n const { isHovered, hoverProps } = useHover();\n const intensityVal = intensity ?? 'medium';\n\n const cardStyles: CSSProperties = {\n background: isHovered && hover ? 'var(--card-hover-bg)' : bgVarMap[intensityVal],\n borderColor: isHovered && hover ? 'var(--card-hover-border)' : borderVarMap[intensityVal],\n backdropFilter: `blur(${blurMap[intensityVal]})`,\n WebkitBackdropFilter: `blur(${blurMap[intensityVal]})`,\n boxShadow: glow\n ? glowVarMap[glow]\n : isHovered && hover\n ? 'var(--card-hover-glow)'\n : 'var(--glow-neutral)',\n };\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(cardIntensity({ intensity, hover, padding }), className)}\n style={cardStyles}\n onMouseEnter={hoverProps.onMouseEnter}\n onMouseLeave={hoverProps.onMouseLeave}\n {...props}\n >\n {children}\n </Comp>\n );\n }\n);\n\nGlassCard.displayName = 'GlassCard';\n"
21
21
  }
22
22
  ],
23
23
  "categories": [
@@ -18,7 +18,7 @@
18
18
  {
19
19
  "path": "components/glass/ui/input-glass.tsx",
20
20
  "type": "registry:component",
21
- "content": "/**\n * InputGlass Component\n *\n * Glass-themed input with:\n * - Theme-aware styling via CSS variables (glass/light/aurora)\n * - Focus glow effects\n * - Error/success states\n * - Icon support (left/right position)\n * - Backdrop blur effect\n */\n\nimport {\n forwardRef,\n useCallback,\n type InputHTMLAttributes,\n type CSSProperties,\n type FocusEvent,\n} from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { type LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport { inputVariants } from '@/lib/variants/input-glass-variants';\nimport { ICON_SIZES, FormFieldWrapper } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// CSS VARIABLE HELPERS\n// ========================================\n\nconst getInputStyles = (\n isFocused: boolean,\n error?: string,\n success?: string\n): CSSProperties => {\n // Determine border color based on state\n let borderColor = 'var(--input-border)';\n if (error) {\n borderColor = 'var(--alert-danger-text)';\n } else if (success) {\n borderColor = 'var(--alert-success-text)';\n } else if (isFocused) {\n borderColor = 'var(--input-focus-border)';\n }\n\n return {\n background: 'var(--input-bg)',\n border: `1px solid ${borderColor}`,\n color: 'var(--input-text)',\n boxShadow: isFocused ? 'var(--focus-glow)' : 'none',\n };\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the InputGlass component\n *\n * A glass-themed input field with labels, validation states, and icon support.\n * Features focus glow effects and theme-aware styling.\n *\n * @example\n * ```tsx\n * // Basic input with label\n * <InputGlass label=\"Email\" placeholder=\"you@example.com\" />\n *\n * // With validation states\n * <InputGlass label=\"Username\" error=\"Username is required\" />\n * <InputGlass label=\"Password\" success=\"Strong password\" type=\"password\" />\n *\n * // With icon\n * <InputGlass icon={Search} placeholder=\"Search...\" />\n * <InputGlass icon={Mail} iconPosition=\"right\" />\n * ```\n */\nexport interface InputGlassProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>,\n VariantProps<typeof inputVariants> {\n /**\n * Label text displayed above the input field\n */\n readonly label?: string;\n\n /**\n * Error message to display below the input (red styling)\n */\n readonly error?: string;\n\n /**\n * Success message to display below the input (green styling)\n */\n readonly success?: string;\n\n /**\n * Icon component from lucide-react to display\n * @example icon={Search}\n */\n readonly icon?: LucideIcon;\n\n /**\n * Position of the icon relative to input text\n * @default \"left\"\n */\n readonly iconPosition?: 'left' | 'right';\n\n /**\n * @deprecated Use `size` prop instead. Will be removed in v4.0\n * @default \"md\"\n */\n readonly inputSize?: 'sm' | 'md' | 'lg';\n\n // Note: size prop comes from VariantProps<typeof inputVariants>\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const InputGlass = forwardRef<HTMLInputElement, InputGlassProps>(\n (\n {\n className,\n size,\n inputSize,\n label,\n error,\n success,\n icon: Icon,\n iconPosition = 'left',\n disabled,\n onFocus,\n onBlur,\n ...props\n },\n ref\n ) => {\n // Determine size value with fallback to deprecated inputSize prop\n const sizeValue = size ?? inputSize ?? 'md';\n\n // Deprecation warning in development mode\n if (process.env.NODE_ENV !== 'production' && inputSize !== undefined) {\n console.warn(\n '[InputGlass] The `inputSize` prop is deprecated and will be removed in v4.0. Use `size` instead.'\n );\n }\n\n const { isFocused, focusProps } = useFocus();\n\n // Wrap focus handlers to call both internal and external callbacks\n const handleFocus = useCallback(\n (e: FocusEvent<HTMLInputElement>) => {\n focusProps.onFocus(e);\n onFocus?.(e);\n },\n [focusProps, onFocus]\n );\n\n const handleBlur = useCallback(\n (e: FocusEvent<HTMLInputElement>) => {\n focusProps.onBlur(e);\n onBlur?.(e);\n },\n [focusProps, onBlur]\n );\n\n const hasIcon = Boolean(Icon);\n const paddingLeft = hasIcon && iconPosition === 'left' ? 'pl-10' : '';\n const paddingRight = hasIcon && iconPosition === 'right' ? 'pr-10' : '';\n\n return (\n <FormFieldWrapper\n label={label}\n error={error}\n success={success}\n htmlFor={props.id}\n className={className}\n >\n <div className=\"relative\">\n {Icon && iconPosition === 'left' && (\n <Icon\n className={cn(\n 'absolute left-2.5 md:left-3 top-1/2 -translate-y-1/2 transition-colors duration-300 z-10',\n ICON_SIZES.md\n )}\n style={{\n color: isFocused ? 'var(--text-accent)' : 'var(--text-muted)',\n }}\n />\n )}\n <input\n ref={ref}\n className={cn(\n inputVariants({ size: sizeValue }),\n paddingLeft,\n paddingRight\n )}\n style={getInputStyles(isFocused, error, success)}\n disabled={disabled}\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n />\n {Icon && iconPosition === 'right' && (\n <Icon\n className={cn(\n 'absolute right-2.5 md:right-3 top-1/2 -translate-y-1/2 transition-colors duration-300 z-10',\n ICON_SIZES.md\n )}\n style={{\n color: isFocused ? 'var(--text-accent)' : 'var(--text-muted)',\n }}\n />\n )}\n </div>\n </FormFieldWrapper>\n );\n }\n);\n\nInputGlass.displayName = 'InputGlass';\n"
21
+ "content": "/**\n * InputGlass Component\n *\n * Glass-themed input with:\n * - Theme-aware styling via CSS variables (glass/light/aurora)\n * - Focus glow effects\n * - Error/success states\n * - Icon support (left/right position)\n * - Backdrop blur effect\n */\n\nimport {\n forwardRef,\n useCallback,\n type InputHTMLAttributes,\n type CSSProperties,\n type FocusEvent,\n} from 'react';\nimport { type VariantProps } from 'class-variance-authority';\nimport { type LucideIcon } from 'lucide-react';\nimport { cn } from '@/lib/utils';\nimport { useFocus } from '@/lib/hooks/use-focus';\nimport { inputVariants } from '@/lib/variants/input-glass-variants';\nimport { ICON_SIZES, FormFieldWrapper } from '@/components/glass/primitives';\nimport '@/glass-theme.css';\n\n// ========================================\n// CSS VARIABLE HELPERS\n// ========================================\n\nconst getInputStyles = (\n isFocused: boolean,\n error?: string,\n success?: string\n): CSSProperties => {\n // Determine border color based on state\n let borderColor = 'var(--input-border)';\n if (error) {\n borderColor = 'var(--alert-danger-text)';\n } else if (success) {\n borderColor = 'var(--alert-success-text)';\n } else if (isFocused) {\n borderColor = 'var(--input-focus-border)';\n }\n\n return {\n background: 'var(--input-bg)',\n border: `1px solid ${borderColor}`,\n color: 'var(--input-text)',\n boxShadow: isFocused ? 'var(--focus-glow)' : 'none',\n };\n};\n\n// ========================================\n// PROPS INTERFACE\n// ========================================\n\n/**\n * Props for the InputGlass component\n *\n * A glass-themed input field with labels, validation states, and icon support.\n * Features focus glow effects and theme-aware styling.\n *\n * @accessibility\n * - **Keyboard Navigation:** Full keyboard support with native `<input>` element, standard tab navigation\n * - **Focus Management:** Visible focus ring using `--focus-glow` CSS variable (WCAG 2.4.7)\n * - **Screen Readers:** Semantic `<label>` elements properly associated via `htmlFor`, error/success messages announced with `aria-describedby`\n * - **Error State:** Red border and error message displayed below input, programmatically associated for screen readers\n * - **Success State:** Green border and success message displayed below input, programmatically associated for screen readers\n * - **Touch Targets:** Minimum 44x44px touch target on mobile devices (WCAG 2.5.5)\n * - **Color Contrast:** All text meets WCAG AA contrast ratio 4.5:1 minimum against backgrounds\n * - **Motion:** Icon color transitions respect `prefers-reduced-motion` settings\n *\n * @example\n * ```tsx\n * // Basic input with label\n * <InputGlass label=\"Email\" placeholder=\"you@example.com\" />\n *\n * // With aria-describedby for additional context\n * <InputGlass\n * label=\"Username\"\n * placeholder=\"Enter username\"\n * aria-describedby=\"username-help\"\n * />\n * <span id=\"username-help\">Must be 3-20 characters</span>\n *\n * // With validation states (automatically announced to screen readers)\n * <InputGlass label=\"Username\" error=\"Username is required\" />\n * <InputGlass label=\"Password\" success=\"Strong password\" type=\"password\" />\n *\n * // With icon and accessible label\n * <InputGlass\n * icon={Search}\n * placeholder=\"Search...\"\n * aria-label=\"Search products\"\n * />\n * <InputGlass icon={Mail} iconPosition=\"right\" type=\"email\" />\n *\n * // Disabled state (automatically announced)\n * <InputGlass label=\"Email\" disabled value=\"locked@example.com\" />\n *\n * // Form integration with accessible error handling\n * <form onSubmit={handleSubmit}>\n * <InputGlass\n * label=\"Email\"\n * type=\"email\"\n * required\n * error={errors.email}\n * aria-invalid={!!errors.email}\n * />\n * <InputGlass\n * label=\"Password\"\n * type=\"password\"\n * icon={Lock}\n * error={errors.password}\n * />\n * <ButtonGlass type=\"submit\">Sign In</ButtonGlass>\n * </form>\n * ```\n */\nexport interface InputGlassProps\n extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>,\n VariantProps<typeof inputVariants> {\n /**\n * Label text displayed above the input field\n */\n readonly label?: string;\n\n /**\n * Error message to display below the input (red styling)\n */\n readonly error?: string;\n\n /**\n * Success message to display below the input (green styling)\n */\n readonly success?: string;\n\n /**\n * Icon component from lucide-react to display\n * @example icon={Search}\n */\n readonly icon?: LucideIcon;\n\n /**\n * Position of the icon relative to input text\n * @default \"left\"\n */\n readonly iconPosition?: 'left' | 'right';\n\n /**\n * @deprecated Use `size` prop instead. Will be removed in v4.0\n * @default \"md\"\n */\n readonly inputSize?: 'sm' | 'md' | 'lg';\n\n // Note: size prop comes from VariantProps<typeof inputVariants>\n}\n\n// ========================================\n// COMPONENT\n// ========================================\n\nexport const InputGlass = forwardRef<HTMLInputElement, InputGlassProps>(\n (\n {\n className,\n size,\n inputSize,\n label,\n error,\n success,\n icon: Icon,\n iconPosition = 'left',\n disabled,\n onFocus,\n onBlur,\n ...props\n },\n ref\n ) => {\n // Determine size value with fallback to deprecated inputSize prop\n const sizeValue = size ?? inputSize ?? 'md';\n\n // Deprecation warning in development mode\n if (process.env.NODE_ENV !== 'production' && inputSize !== undefined) {\n console.warn(\n '[InputGlass] The `inputSize` prop is deprecated and will be removed in v4.0. Use `size` instead.'\n );\n }\n\n const { isFocused, focusProps } = useFocus();\n\n // Wrap focus handlers to call both internal and external callbacks\n const handleFocus = useCallback(\n (e: FocusEvent<HTMLInputElement>) => {\n focusProps.onFocus(e);\n onFocus?.(e);\n },\n [focusProps, onFocus]\n );\n\n const handleBlur = useCallback(\n (e: FocusEvent<HTMLInputElement>) => {\n focusProps.onBlur(e);\n onBlur?.(e);\n },\n [focusProps, onBlur]\n );\n\n const hasIcon = Boolean(Icon);\n const paddingLeft = hasIcon && iconPosition === 'left' ? 'pl-10' : '';\n const paddingRight = hasIcon && iconPosition === 'right' ? 'pr-10' : '';\n\n return (\n <FormFieldWrapper\n label={label}\n error={error}\n success={success}\n htmlFor={props.id}\n className={className}\n >\n <div className=\"relative\">\n {Icon && iconPosition === 'left' && (\n <Icon\n className={cn(\n 'absolute left-2.5 md:left-3 top-1/2 -translate-y-1/2 transition-colors duration-300 z-10',\n ICON_SIZES.md\n )}\n style={{\n color: isFocused ? 'var(--text-accent)' : 'var(--text-muted)',\n }}\n />\n )}\n <input\n ref={ref}\n className={cn(\n inputVariants({ size: sizeValue }),\n paddingLeft,\n paddingRight\n )}\n style={getInputStyles(isFocused, error, success)}\n disabled={disabled}\n onFocus={handleFocus}\n onBlur={handleBlur}\n {...props}\n />\n {Icon && iconPosition === 'right' && (\n <Icon\n className={cn(\n 'absolute right-2.5 md:right-3 top-1/2 -translate-y-1/2 transition-colors duration-300 z-10',\n ICON_SIZES.md\n )}\n style={{\n color: isFocused ? 'var(--text-accent)' : 'var(--text-muted)',\n }}\n />\n )}\n </div>\n </FormFieldWrapper>\n );\n }\n);\n\nInputGlass.displayName = 'InputGlass';\n"
22
22
  }
23
23
  ],
24
24
  "categories": [
@@ -14,7 +14,7 @@
14
14
  {
15
15
  "path": "components/glass/specialized/language-bar-glass.tsx",
16
16
  "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 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"
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"
18
18
  }
19
19
  ],
20
20
  "categories": [
@@ -18,7 +18,7 @@
18
18
  {
19
19
  "path": "components/glass/ui/modal-glass.tsx",
20
20
  "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\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"
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"
22
22
  }
23
23
  ],
24
24
  "categories": [
@@ -16,7 +16,7 @@
16
16
  {
17
17
  "path": "components/glass/specialized/progress-glass.tsx",
18
18
  "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,\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);\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"
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"
20
20
  }
21
21
  ],
22
22
  "categories": [
@@ -1,8 +1,6 @@
1
1
  {
2
2
  "$schema": "https://ui.shadcn.com/schema/registry.json",
3
3
  "name": "shadcn-glass-ui",
4
- "description": "AI-friendly glassmorphism UI component library with 55+ components, strict TypeScript, and WCAG 2.1 AA compliance",
5
- "url": "https://raw.githubusercontent.com/Yhooi2/shadcn-glass-ui-library/main/public/r",
6
4
  "homepage": "https://yhooi2.github.io/shadcn-glass-ui-library/",
7
5
  "items": [
8
6
  {
@@ -14,7 +14,7 @@
14
14
  {
15
15
  "path": "components/glass/specialized/segmented-control-glass.tsx",
16
16
  "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 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"
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"
18
18
  }
19
19
  ],
20
20
  "categories": [