@olympusoss/canvas 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olympusoss/canvas",
3
- "version": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -16,6 +16,13 @@ export interface NavBarProps extends React.HTMLAttributes<HTMLElement> {
16
16
  logo?: React.ReactNode;
17
17
  links?: NavLink[];
18
18
  actions?: React.ReactNode;
19
+ /**
20
+ * When `true` (default) the bar uses `position: sticky` + `top: 0` so it
21
+ * stays anchored to the top of its scroll container while page content
22
+ * scrolls underneath. The translucent background + `backdrop-blur` creates
23
+ * a frosted-glass effect through which the scrolling content reads.
24
+ * Set `false` to render inline (e.g. for examples or non-sticky layouts).
25
+ */
19
26
  sticky?: boolean;
20
27
  /** Custom link component (e.g. Next.js Link) for client-side navigation */
21
28
  linkComponent?: React.ElementType;
@@ -27,72 +34,68 @@ const NavBar = React.forwardRef<HTMLElement, NavBarProps>(
27
34
  const LinkEl = linkComponent || "a";
28
35
 
29
36
  return (
30
- <>
31
- <nav
32
- ref={ref}
33
- className={cn(
34
- "left-0 right-0 top-0 z-50 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60",
35
- sticky && "fixed",
36
- className,
37
+ <nav
38
+ ref={ref}
39
+ className={cn(
40
+ "z-50 border-b border-border bg-background/80 backdrop-blur",
41
+ sticky && "sticky top-0",
42
+ className,
43
+ )}
44
+ {...props}
45
+ >
46
+ <div className="mx-auto flex h-14 max-w-6xl items-center justify-between px-4 sm:px-6">
47
+ {/* Logo */}
48
+ {logo && <div className="shrink-0">{logo}</div>}
49
+
50
+ {/* Desktop links */}
51
+ <div className="hidden items-center gap-6 md:flex">
52
+ {links.map((link) => (
53
+ <LinkEl
54
+ key={link.href}
55
+ href={link.href}
56
+ className="text-sm font-medium text-muted-foreground no-underline transition-colors hover:text-brand"
57
+ {...(link.external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
58
+ >
59
+ {link.label}
60
+ </LinkEl>
61
+ ))}
62
+ {actions}
63
+ </div>
64
+
65
+ {/* Mobile hamburger */}
66
+ {(links.length > 0 || actions) && (
67
+ <Button
68
+ variant="ghost"
69
+ size="icon"
70
+ className="md:hidden"
71
+ onClick={() => setMobileOpen((v) => !v)}
72
+ aria-label={mobileOpen ? "Close menu" : "Open menu"}
73
+ >
74
+ {mobileOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
75
+ </Button>
37
76
  )}
38
- {...props}
39
- >
40
- <div className="mx-auto flex h-14 max-w-6xl items-center justify-between px-4 sm:px-6">
41
- {/* Logo */}
42
- {logo && <div className="shrink-0">{logo}</div>}
77
+ </div>
43
78
 
44
- {/* Desktop links */}
45
- <div className="hidden items-center gap-6 md:flex">
79
+ {/* Mobile dropdown */}
80
+ {mobileOpen && (
81
+ <div className="border-t md:hidden">
82
+ <div className="flex flex-col gap-1 px-4 py-3 sm:px-6">
46
83
  {links.map((link) => (
47
84
  <LinkEl
48
85
  key={link.href}
49
86
  href={link.href}
50
- className="text-sm font-medium text-muted-foreground no-underline transition-colors hover:text-brand"
87
+ onClick={() => setMobileOpen(false)}
88
+ className="rounded-md px-3 py-2.5 text-sm font-medium text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-brand"
51
89
  {...(link.external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
52
90
  >
53
91
  {link.label}
54
92
  </LinkEl>
55
93
  ))}
56
- {actions}
94
+ {actions && <div className="px-3 py-2.5">{actions}</div>}
57
95
  </div>
58
-
59
- {/* Mobile hamburger */}
60
- {(links.length > 0 || actions) && (
61
- <Button
62
- variant="ghost"
63
- size="icon"
64
- className="md:hidden"
65
- onClick={() => setMobileOpen((v) => !v)}
66
- aria-label={mobileOpen ? "Close menu" : "Open menu"}
67
- >
68
- {mobileOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
69
- </Button>
70
- )}
71
96
  </div>
72
-
73
- {/* Mobile dropdown */}
74
- {mobileOpen && (
75
- <div className="border-t md:hidden">
76
- <div className="flex flex-col gap-1 px-4 py-3 sm:px-6">
77
- {links.map((link) => (
78
- <LinkEl
79
- key={link.href}
80
- href={link.href}
81
- onClick={() => setMobileOpen(false)}
82
- className="rounded-md px-3 py-2.5 text-sm font-medium text-muted-foreground no-underline transition-colors hover:bg-accent hover:text-brand"
83
- {...(link.external ? { target: "_blank", rel: "noopener noreferrer" } : {})}
84
- >
85
- {link.label}
86
- </LinkEl>
87
- ))}
88
- {actions && <div className="px-3 py-2.5">{actions}</div>}
89
- </div>
90
- </div>
91
- )}
92
- </nav>
93
- {/* Spacer when sticky to prevent content from hiding behind navbar */}
94
- {sticky && <div className="h-14" />}
95
- </>
97
+ )}
98
+ </nav>
96
99
  );
97
100
  },
98
101
  );