@syscore/ui-library 1.5.2 → 1.6.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.
@@ -1,1022 +1,672 @@
1
+ "use client";
2
+
1
3
  import * as React from "react";
4
+ import { Slot } from "@radix-ui/react-slot";
2
5
  import { AnimatePresence, motion } from "motion/react";
3
6
  import { cn } from "@/lib/utils";
4
7
  import { NavLogo } from "../icons/NavLogo";
5
8
  import { NavAccount } from "../icons/NavAccount";
6
9
  import { NavBullet } from "../icons/NavBullet";
7
- import { Button } from "@/components/ui/button";
8
10
  import { UtilityClearRegular } from "../icons/UtilityClearRegular";
9
- import { StandardLogo } from "../icons/StandardLogo";
10
- export interface NavItem {
11
- label: string;
12
- active?: boolean;
11
+ import { buttonVariants } from "./button";
12
+
13
+ // =============================================================================
14
+ // CONTEXT
15
+ // =============================================================================
16
+
17
+ interface NavigationContextValue {
18
+ light: boolean;
19
+ isOpen: boolean;
20
+ activeIndex: number | null;
21
+ setActiveIndex: (index: number | null) => void;
22
+ setIsOpen: (open: boolean) => void;
23
+ registerTrays: (trays: React.ReactNode[]) => void;
24
+ }
25
+
26
+ const NavigationContext = React.createContext<NavigationContextValue | null>(
27
+ null,
28
+ );
29
+
30
+ function useNavigation() {
31
+ const ctx = React.useContext(NavigationContext);
32
+ if (!ctx)
33
+ throw new Error("Navigation components must be used within <Navigation>");
34
+ return ctx;
13
35
  }
14
36
 
15
- // Define a Link component type that matches common routing patterns
16
- export type LinkComponent = React.ComponentType<{
17
- href: string;
37
+ // =============================================================================
38
+ // NAVIGATION (Root)
39
+ // =============================================================================
40
+
41
+ interface NavigationProps extends React.ComponentPropsWithoutRef<"nav"> {
42
+ light?: boolean;
43
+ }
44
+
45
+ const Navigation = React.forwardRef<HTMLElement, NavigationProps>(
46
+ ({ className, light = false, children, ...props }, ref) => {
47
+ const [isOpen, setIsOpen] = React.useState(false);
48
+ const [activeIndex, setActiveIndex] = React.useState<number | null>(null);
49
+ const [trays, setTrays] = React.useState<React.ReactNode[]>([]);
50
+
51
+ const handleMouseLeave = () => {
52
+ setIsOpen(false);
53
+ setActiveIndex(null);
54
+ };
55
+
56
+ const registerTrays = React.useCallback((newTrays: React.ReactNode[]) => {
57
+ setTrays(newTrays);
58
+ }, []);
59
+
60
+ const contextValue = React.useMemo(
61
+ () => ({
62
+ light,
63
+ isOpen,
64
+ activeIndex,
65
+ setActiveIndex,
66
+ setIsOpen,
67
+ registerTrays,
68
+ }),
69
+ [light, isOpen, activeIndex, registerTrays],
70
+ );
71
+
72
+ return (
73
+ <NavigationContext.Provider value={contextValue}>
74
+ <nav
75
+ ref={ref}
76
+ className={cn("navigation", light && "navigation--light", className)}
77
+ onMouseLeave={handleMouseLeave}
78
+ {...props}
79
+ >
80
+ {/* Animated backdrop blur overlay */}
81
+ {!light && (
82
+ <motion.div
83
+ className="navigation__backdrop"
84
+ initial={false}
85
+ animate={{
86
+ height: isOpen ? 340 : 64,
87
+ backgroundColor: isOpen
88
+ ? "rgba(62, 64, 73, 0.90)"
89
+ : "rgba(0, 0, 0, 0.16)",
90
+ }}
91
+ transition={{ duration: 0.25, ease: "easeOut" }}
92
+ style={{ willChange: "transform" }}
93
+ />
94
+ )}
95
+
96
+ {/* Nav bar content */}
97
+ <div className="navigation__content">{children}</div>
98
+
99
+ {/* Separator line */}
100
+ {isOpen && !light && <div className="navigation__separator" />}
101
+
102
+ {/* Tray dropdown area */}
103
+ <div className="navigation__tray-container">
104
+ <AnimatePresence mode="wait">
105
+ {isOpen && activeIndex !== null && (
106
+ <motion.div
107
+ key={activeIndex}
108
+ initial={{ y: -10, opacity: 0 }}
109
+ animate={{ y: 0, opacity: 1 }}
110
+ exit={{ y: -10, opacity: 0 }}
111
+ transition={{ duration: 0.2 }}
112
+ style={{ willChange: "transform, opacity" }}
113
+ >
114
+ {trays[activeIndex - 1]}
115
+ </motion.div>
116
+ )}
117
+ </AnimatePresence>
118
+ </div>
119
+ </nav>
120
+ </NavigationContext.Provider>
121
+ );
122
+ },
123
+ );
124
+ Navigation.displayName = "Navigation";
125
+
126
+ // =============================================================================
127
+ // NAVIGATION BRAND (Left container)
128
+ // =============================================================================
129
+
130
+ interface NavigationBrandProps {
18
131
  className?: string;
19
- "aria-label"?: string;
20
- children: React.ReactNode;
21
- onClick?: (e: React.MouseEvent) => void;
22
- }>;
23
-
24
- interface NavLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
25
- href: string;
26
- LinkComponent?: LinkComponent;
27
- onLinkClick?: (href: string) => void;
132
+ children?: React.ReactNode;
133
+ }
134
+
135
+ const NavigationBrand = React.forwardRef<HTMLDivElement, NavigationBrandProps>(
136
+ ({ className, children }, ref) => {
137
+ return (
138
+ <motion.div
139
+ ref={ref}
140
+ className={cn("navigation-brand", className)}
141
+ layout
142
+ transition={{ duration: 0.2, ease: "easeOut" }}
143
+ >
144
+ {children}
145
+ </motion.div>
146
+ );
147
+ },
148
+ );
149
+ NavigationBrand.displayName = "NavigationBrand";
150
+
151
+ // =============================================================================
152
+ // NAVIGATION BACK
153
+ // =============================================================================
154
+
155
+ interface NavigationBackProps extends React.ComponentPropsWithoutRef<"a"> {
156
+ show?: boolean;
157
+ asChild?: boolean;
28
158
  }
29
159
 
30
- const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
31
- ({ href, LinkComponent, onLinkClick, children, ...props }, ref) => {
32
- if (LinkComponent) {
160
+ const NavigationBack = React.forwardRef<HTMLAnchorElement, NavigationBackProps>(
161
+ ({ className, show, asChild, children, ...props }, ref) => {
162
+ const { light } = useNavigation();
163
+
164
+ const linkClassName = cn(
165
+ buttonVariants({ size: "icon" }),
166
+ "navigation-back",
167
+ light ? "navigation-back--light" : "navigation-back--dark",
168
+ className,
169
+ );
170
+
171
+ const defaultIcon = (
172
+ <UtilityClearRegular
173
+ className={
174
+ light ? "navigation-back__icon--light" : "navigation-back__icon--dark"
175
+ }
176
+ />
177
+ );
178
+
179
+ return (
180
+ <AnimatePresence mode="popLayout">
181
+ {show && (
182
+ <motion.div
183
+ layout
184
+ initial={{ opacity: 0, scale: 0.8 }}
185
+ animate={{ opacity: 1, scale: 1 }}
186
+ exit={{ opacity: 0, scale: 0.8 }}
187
+ transition={{ duration: 0.2, ease: "easeOut" }}
188
+ style={{ willChange: "transform, opacity" }}
189
+ >
190
+ {asChild && React.isValidElement(children) ? (
191
+ (() => {
192
+ const childElement = children as React.ReactElement<{
193
+ children?: React.ReactNode;
194
+ }>;
195
+ const hasChildContent = childElement.props.children != null;
196
+ return (
197
+ <Slot
198
+ ref={ref}
199
+ className={linkClassName}
200
+ aria-label="Back"
201
+ {...props}
202
+ >
203
+ {hasChildContent
204
+ ? children
205
+ : React.cloneElement(childElement, {}, defaultIcon)}
206
+ </Slot>
207
+ );
208
+ })()
209
+ ) : (
210
+ <a
211
+ ref={ref}
212
+ className={linkClassName}
213
+ aria-label="Back"
214
+ href="/"
215
+ {...props}
216
+ >
217
+ {children || defaultIcon}
218
+ </a>
219
+ )}
220
+ </motion.div>
221
+ )}
222
+ </AnimatePresence>
223
+ );
224
+ },
225
+ );
226
+ NavigationBack.displayName = "NavigationBack";
227
+
228
+ // =============================================================================
229
+ // NAVIGATION LOGO
230
+ // =============================================================================
231
+
232
+ interface NavigationLogoProps extends React.ComponentPropsWithoutRef<"a"> {
233
+ asChild?: boolean;
234
+ }
235
+
236
+ const NavigationLogo = React.forwardRef<HTMLAnchorElement, NavigationLogoProps>(
237
+ ({ className, asChild, children, ...props }, ref) => {
238
+ const { light } = useNavigation();
239
+
240
+ const defaultLogo = <NavLogo dark={light} />;
241
+
242
+ const linkClassName = cn("navigation-logo", className);
243
+
244
+ // When asChild, render the child with merged props
245
+ // If child has its own children, use those as content; otherwise inject default logo
246
+ if (asChild && React.isValidElement(children)) {
247
+ const childElement = children as React.ReactElement<{
248
+ children?: React.ReactNode;
249
+ }>;
250
+ const hasChildContent = childElement.props.children != null;
251
+
33
252
  return (
34
- <LinkComponent
35
- href={href}
36
- className={props.className}
37
- aria-label={props["aria-label"]}
253
+ <motion.div
254
+ layout
255
+ transition={{ duration: 0.2, ease: "easeOut" }}
256
+ style={{ willChange: "transform" }}
38
257
  >
39
- {children}
40
- </LinkComponent>
258
+ <Slot
259
+ ref={ref}
260
+ className={linkClassName}
261
+ aria-label="Home"
262
+ {...props}
263
+ >
264
+ {hasChildContent
265
+ ? children
266
+ : React.cloneElement(childElement, {}, defaultLogo)}
267
+ </Slot>
268
+ </motion.div>
41
269
  );
42
270
  }
43
271
 
44
- if (onLinkClick) {
45
- return (
272
+ return (
273
+ <motion.div
274
+ layout
275
+ transition={{ duration: 0.2, ease: "easeOut" }}
276
+ style={{ willChange: "transform" }}
277
+ >
46
278
  <a
47
279
  ref={ref}
48
- href={href}
280
+ className={linkClassName}
281
+ aria-label="Home"
282
+ href="/"
49
283
  {...props}
50
- onClick={(e) => {
51
- e.preventDefault();
52
- onLinkClick(href);
53
- }}
54
284
  >
285
+ {defaultLogo}
55
286
  {children}
56
287
  </a>
57
- );
58
- }
288
+ </motion.div>
289
+ );
290
+ },
291
+ );
292
+ NavigationLogo.displayName = "NavigationLogo";
293
+
294
+ // =============================================================================
295
+ // NAVIGATION MENU (Center container)
296
+ // =============================================================================
297
+
298
+ interface NavigationMenuProps extends React.ComponentPropsWithoutRef<"div"> {}
299
+
300
+ const NavigationMenu = React.forwardRef<HTMLDivElement, NavigationMenuProps>(
301
+ ({ className, children, ...props }, ref) => {
302
+ const { registerTrays } = useNavigation();
303
+
304
+ // Register trays with Navigation on mount/update
305
+ React.useEffect(() => {
306
+ const trayElements = React.Children.toArray(children)
307
+ .filter(
308
+ (child) =>
309
+ React.isValidElement(child) && child.type === NavigationTray,
310
+ )
311
+ .map((child, index) => {
312
+ if (React.isValidElement(child)) {
313
+ return React.cloneElement(
314
+ child as React.ReactElement<NavigationTrayProps>,
315
+ {
316
+ _index: index + 1,
317
+ _renderTriggerOnly: false,
318
+ },
319
+ );
320
+ }
321
+ return child;
322
+ });
323
+ registerTrays(trayElements);
324
+ }, [children, registerTrays]);
59
325
 
60
326
  return (
61
- <a ref={ref} href={href} {...props}>
62
- {children}
63
- </a>
327
+ <div ref={ref} className={cn("navigation-menu", className)} {...props}>
328
+ <ul className="navigation-menu__list">
329
+ {React.Children.map(children, (child, index) => {
330
+ if (React.isValidElement(child) && child.type === NavigationTray) {
331
+ return React.cloneElement(
332
+ child as React.ReactElement<NavigationTrayProps>,
333
+ {
334
+ _index: index + 1,
335
+ _renderTriggerOnly: true,
336
+ },
337
+ );
338
+ }
339
+ return child;
340
+ })}
341
+ </ul>
342
+ </div>
64
343
  );
65
- }
344
+ },
66
345
  );
67
- NavLink.displayName = "NavLink";
68
-
69
- interface NavigationProps {
70
- navItems?: NavItem[];
71
- isStrategy?: boolean;
72
- Link?: LinkComponent; // Optional Link component from routing library
73
- onLinkClick?: (href: string) => void; // Fallback callback for navigation
74
- onClose?: () => void;
75
- pathname: string;
346
+ NavigationMenu.displayName = "NavigationMenu";
347
+
348
+ // =============================================================================
349
+ // NAVIGATION TRAY
350
+ // =============================================================================
351
+
352
+ interface NavigationTrayProps extends React.ComponentPropsWithoutRef<"div"> {
353
+ trigger: string;
354
+ title: string;
355
+ /** @internal */
356
+ _index?: number;
357
+ /** @internal */
358
+ _renderTriggerOnly?: boolean;
76
359
  }
77
360
 
78
- const ALPHA_PATH =
79
- "M5.3387 0.0229882C5.37971 0.0229882 5.45295 0.0199228 5.5584 0.0137925C5.66386 0.00459714 5.74442 0 5.80007 0C6.72867 0 7.49908 0.269732 8.11131 0.809196C8.72354 1.34559 9.14097 2.09195 9.3636 3.04828C9.44855 3.47433 9.49835 3.96628 9.513 4.52414V5.16782C10.2014 4.07663 10.7287 2.85517 11.0948 1.50345C11.1505 1.29808 11.1959 1.17701 11.2311 1.14023C11.2662 1.10345 11.3761 1.08506 11.5606 1.08506C11.8535 1.08506 12 1.13563 12 1.23678C12 1.25211 11.9722 1.37778 11.9165 1.61379C11.5093 3.24751 10.7931 4.77701 9.76785 6.2023L9.53497 6.51034L9.55694 7.04368C9.59795 8.23295 9.72391 8.9318 9.93482 9.14023C9.97583 9.16782 10.0388 9.18161 10.1238 9.18161C10.32 9.15402 10.5031 9.06973 10.673 8.92874C10.8429 8.78774 10.963 8.62222 11.0333 8.43218C11.0597 8.32797 11.0948 8.26513 11.1388 8.24368C11.1798 8.22222 11.2852 8.21149 11.4551 8.21149C11.7364 8.21149 11.877 8.27739 11.877 8.40919C11.877 8.49808 11.8345 8.63142 11.7495 8.8092C11.5943 9.13103 11.3687 9.4069 11.0729 9.63678C10.777 9.8636 10.4475 9.97701 10.0842 9.97701H9.93482C8.97986 9.97701 8.3398 9.44674 8.01465 8.38621L7.95313 8.23448C7.20615 8.74942 6.79165 9.02835 6.70963 9.07126C5.66679 9.69042 4.61809 10 3.56353 10C2.56756 10 1.76346 9.70575 1.15123 9.11724C0.538997 8.52874 0.162578 7.75632 0.02197 6.8C0.0073233 6.71111 0 6.54866 0 6.31264C0 5.9295 0.02197 5.62146 0.0659099 5.38851C0.276822 4.06437 0.887587 2.8751 1.89821 1.82069C2.91175 0.769348 4.05859 0.170115 5.3387 0.0229882ZM1.83669 7.10805C1.83669 7.73946 1.9978 8.24368 2.32003 8.62069C2.64518 8.99464 3.09484 9.18161 3.66899 9.18161C4.03515 9.18161 4.44379 9.12337 4.89491 9.0069C5.6829 8.80153 6.44892 8.4046 7.19297 7.81609C7.29257 7.74253 7.38777 7.66437 7.47858 7.58161C7.56939 7.50192 7.64262 7.43295 7.69828 7.37471L7.78176 7.28276L7.76419 7.08506C7.76419 6.95326 7.75979 6.75862 7.75101 6.50115C7.74515 6.24368 7.74222 5.98927 7.74222 5.73793C7.72757 5.18008 7.71732 4.74636 7.71146 4.43678C7.70267 4.1272 7.67338 3.74866 7.62358 3.30115C7.57671 2.85364 7.5108 2.50728 7.42585 2.26207C7.3409 2.01992 7.22519 1.77471 7.07873 1.52644C6.92933 1.2751 6.73892 1.09425 6.50751 0.983908C6.27609 0.873563 6.00513 0.81839 5.69462 0.81839C4.72501 0.81839 3.87404 1.35479 3.14171 2.42759C2.79019 2.97318 2.49579 3.71647 2.25851 4.65747C1.9773 5.73333 1.83669 6.55019 1.83669 7.10805Z";
80
-
81
- const PATH_LENGTH = 60;
361
+ const NavigationTray = React.forwardRef<HTMLDivElement, NavigationTrayProps>(
362
+ (
363
+ {
364
+ className,
365
+ trigger,
366
+ title,
367
+ children,
368
+ _index,
369
+ _renderTriggerOnly,
370
+ ...props
371
+ },
372
+ ref,
373
+ ) => {
374
+ const { light, activeIndex, setActiveIndex, setIsOpen } = useNavigation();
375
+ const isActive = activeIndex === _index;
376
+
377
+ const handleMouseEnter = () => {
378
+ setIsOpen(true);
379
+ setActiveIndex(_index ?? null);
380
+ };
381
+
382
+ // Render just the trigger in the menu bar
383
+ if (_renderTriggerOnly) {
384
+ return (
385
+ <li className="navigation-tray-trigger" onMouseEnter={handleMouseEnter}>
386
+ <span
387
+ className={cn(
388
+ "body-small navigation-tray-trigger__text",
389
+ light
390
+ ? "navigation-tray-trigger__text--light"
391
+ : "navigation-tray-trigger__text--dark",
392
+ )}
393
+ >
394
+ {trigger}
395
+ </span>
396
+
397
+ {/* Animated underline indicator */}
398
+ {isActive && (
399
+ <motion.div
400
+ className="navigation-tray-trigger__indicator"
401
+ layoutId="nav-underline"
402
+ transition={{ duration: 0.2, ease: "easeOut" }}
403
+ >
404
+ <div className="navigation-tray-trigger__indicator-shape" />
405
+ </motion.div>
406
+ )}
407
+
408
+ {/* Static indicator for first item */}
409
+ {_index === 1 && !light && (
410
+ <div className="navigation-tray-trigger__static-indicator">
411
+ <div className="navigation-tray-trigger__static-indicator-shape" />
412
+ </div>
413
+ )}
414
+ </li>
415
+ );
416
+ }
82
417
 
83
- const AlphaIcon = ({ dark }: { dark?: boolean }) => {
84
- if (dark) {
418
+ // Render the tray content
85
419
  return (
86
- <svg
87
- xmlns="http://www.w3.org/2000/svg"
88
- width="12"
89
- height="10"
90
- viewBox="0 0 12 10"
91
- fill="none"
92
- className="mt-1.5"
93
- style={{ overflow: "visible" }}
94
- >
95
- <path d={ALPHA_PATH} fill="currentColor" />
96
- {/* Animated stroke trace */}
97
- {/* <motion.path
98
- d={ALPHA_PATH}
99
- fill="none"
100
- stroke="#282A31"
101
- strokeWidth="0.4"
102
- strokeLinecap="round"
103
- strokeLinejoin="round"
104
- initial={{ strokeDashoffset: PATH_LENGTH }}
105
- animate={{ strokeDashoffset: -PATH_LENGTH }}
106
- style={{ strokeDasharray: `8 ${PATH_LENGTH - 8}` }}
107
- transition={{
108
- duration: 4,
109
- repeat: Infinity,
110
- ease: "linear",
420
+ <div ref={ref} className={cn("navigation-tray", className)} {...props}>
421
+ <h2
422
+ className="heading-xsmall navigation-tray__title"
423
+ style={{
424
+ background:
425
+ "linear-gradient(90deg, #41D5F6 0%, #3EECD1 25%, #66FCD9 50%, #66FCD9 75%, #3EECD1 100%)",
426
+ WebkitBackgroundClip: "text",
427
+ WebkitTextFillColor: "transparent",
428
+ backgroundClip: "text",
111
429
  }}
112
- /> */}
113
- </svg>
430
+ >
431
+ {title}
432
+ </h2>
433
+ <div className="navigation-tray__content">{children}</div>
434
+ </div>
114
435
  );
115
- }
436
+ },
437
+ );
438
+ NavigationTray.displayName = "NavigationTray";
439
+
440
+ // =============================================================================
441
+ // NAVIGATION COLUMN
442
+ // =============================================================================
116
443
 
444
+ interface NavigationColumnProps extends React.ComponentPropsWithoutRef<"div"> {
445
+ title: string;
446
+ }
447
+
448
+ const NavigationColumn = React.forwardRef<
449
+ HTMLDivElement,
450
+ NavigationColumnProps
451
+ >(({ className, title, children, ...props }, ref) => {
452
+ const { light } = useNavigation();
117
453
  return (
118
- <svg
119
- xmlns="http://www.w3.org/2000/svg"
120
- width="12"
121
- height="10"
122
- viewBox="0 0 12 10"
123
- fill="none"
124
- className="mt-1.5"
125
- style={{ overflow: "visible" }}
126
- >
127
- {/* Base fill with gradient */}
128
- <path
129
- d={ALPHA_PATH}
130
- fill="url(#paint0_linear_10081_30848)"
131
- opacity="0.4"
132
- />
133
- {/* Animated stroke trace with gradient */}
134
- <motion.path
135
- d={ALPHA_PATH}
136
- fill="none"
137
- stroke="url(#stroke_gradient)"
138
- strokeWidth="0.4"
139
- strokeLinecap="round"
140
- strokeLinejoin="round"
141
- initial={{ strokeDashoffset: PATH_LENGTH }}
142
- animate={{ strokeDashoffset: -PATH_LENGTH }}
143
- style={{ strokeDasharray: `8 ${PATH_LENGTH - 8}` }}
144
- transition={{
145
- duration: 2,
146
- repeat: Infinity,
147
- repeatDelay: 8,
148
- ease: "linear",
149
- }}
150
- />
151
- <defs>
152
- <linearGradient
153
- id="paint0_linear_10081_30848"
154
- x1="-3.27254"
155
- y1="4.99923"
156
- x2="6.36738"
157
- y2="-2.36962"
158
- gradientUnits="userSpaceOnUse"
159
- >
160
- <stop stopColor="#8AEFDB" />
161
- <stop offset="0.5" stopColor="#D4BACE" />
162
- <stop offset="1" stopColor="#CBE0F1" />
163
- </linearGradient>
164
- <linearGradient
165
- id="stroke_gradient"
166
- x1="0"
167
- y1="0"
168
- x2="12"
169
- y2="10"
170
- gradientUnits="userSpaceOnUse"
171
- >
172
- <stop stopColor="#8AEFDB" />
173
- <stop offset="0.5" stopColor="#D4BACE" />
174
- <stop offset="1" stopColor="#CBE0F1" />
175
- </linearGradient>
176
- </defs>
177
- </svg>
454
+ <div ref={ref} className={cn("navigation-column", className)} {...props}>
455
+ <h3
456
+ className={cn(
457
+ "overline-medium navigation-column__title",
458
+ light
459
+ ? "navigation-column__title--light"
460
+ : "navigation-column__title--dark",
461
+ )}
462
+ >
463
+ {title}
464
+ </h3>
465
+ <ul className="navigation-column__list">{children}</ul>
466
+ </div>
178
467
  );
179
- };
468
+ });
469
+ NavigationColumn.displayName = "NavigationColumn";
470
+
471
+ // =============================================================================
472
+ // NAVIGATION LINK
473
+ // =============================================================================
474
+
475
+ /**
476
+ * Navigation link component that supports custom routing via the asChild pattern.
477
+ *
478
+ * @example Basic usage
479
+ * ```tsx
480
+ * <NavigationLink href="/about">About</NavigationLink>
481
+ * ```
482
+ *
483
+ * @example With Next.js Link (asChild pattern)
484
+ * ```tsx
485
+ * <NavigationLink asChild>
486
+ * <Link href="/about">About</Link>
487
+ * </NavigationLink>
488
+ * ```
489
+ *
490
+ * @example Sub-item with bullet
491
+ * ```tsx
492
+ * <NavigationLink sub bullet="#0F748A">
493
+ * Performance Rating
494
+ * </NavigationLink>
495
+ * ```
496
+ */
497
+ interface NavigationLinkProps extends React.ComponentPropsWithoutRef<"a"> {
498
+ /** Use child element instead of anchor tag (composition pattern using Radix UI Slot) */
499
+ asChild?: boolean;
500
+ /** Render as sub-item with smaller text and bullet point */
501
+ sub?: boolean;
502
+ /** Hex color for bullet point (implies sub-item styling) */
503
+ bullet?: string;
504
+ /** Badge text displayed next to link (e.g., "PILOT") */
505
+ badge?: string;
506
+ }
507
+
508
+ const NavigationLink = React.forwardRef<HTMLAnchorElement, NavigationLinkProps>(
509
+ ({ className, asChild, sub, bullet, badge, children, ...props }, ref) => {
510
+ const { light } = useNavigation();
511
+ const Comp = asChild ? Slot : "a";
512
+
513
+ const isSubItem = sub || bullet;
514
+ const baseClassName = isSubItem
515
+ ? cn(
516
+ "body-small navigation-link--sub",
517
+ light ? "navigation-link--sub-light" : "navigation-link--sub-dark",
518
+ )
519
+ : cn(
520
+ "body-base navigation-link",
521
+ light ? "navigation-link--light" : "navigation-link--dark",
522
+ );
523
+
524
+ // Sub-item with bullet
525
+ if (isSubItem) {
526
+ const bulletElement = bullet ? (
527
+ <NavBullet color={bullet} />
528
+ ) : (
529
+ <div className="navigation-link__bullet" />
530
+ );
180
531
 
181
- export const Navigation: React.FC<NavigationProps> = ({
182
- navItems,
183
- isStrategy = false,
184
- Link: LinkComponent,
185
- onLinkClick,
186
- onClose,
187
- pathname,
188
- }) => {
189
- const [isOpen, setIsOpen] = React.useState(false);
190
- const [activeItem, setActiveItem] = React.useState<number | null>(null);
191
-
192
- const containerRef = React.useRef<HTMLElement>(null);
193
- const menuRef = React.useRef<HTMLDivElement>(null);
194
- const menuItemsRef = React.useRef<HTMLDivElement>(null);
195
- const overlayRef = React.useRef<HTMLDivElement>(null);
196
-
197
- const isHomepage = pathname === "/";
198
-
199
- const handleMouseLeave = () => {
200
- setIsOpen(false);
201
- setActiveItem(null);
202
- };
203
-
204
- const handleNavItemMouseEnter = (index: number) => {
205
- setIsOpen(true);
206
- setActiveItem(index + 1);
207
- };
208
-
209
- // Internal NavLink component that handles different routing scenarios
210
- const NavLink: React.FC<{
211
- href: string;
212
- className?: string;
213
- "aria-label"?: string;
214
- children: React.ReactNode;
215
- }> = ({ href, className, "aria-label": ariaLabel, children }) => {
216
- // If Link component is provided, use it
217
- if (LinkComponent) {
218
532
  return (
219
- <LinkComponent href={href} className={className} aria-label={ariaLabel}>
220
- {children}
221
- </LinkComponent>
533
+ <li>
534
+ <div className="navigation-link__container">
535
+ {bulletElement}
536
+ <Comp ref={ref} className={cn(baseClassName, className)} {...props}>
537
+ {children}
538
+ </Comp>
539
+ </div>
540
+ </li>
222
541
  );
223
542
  }
224
543
 
225
- // If onLinkClick callback is provided, use anchor with click handler
226
- if (onLinkClick) {
544
+ // Regular link (possibly with badge)
545
+ const linkClassName = cn(
546
+ baseClassName,
547
+ badge && "navigation-link__badge-wrapper",
548
+ className,
549
+ );
550
+
551
+ // When using asChild with a badge, we need to wrap both in a container
552
+ // because Slot expects a single child
553
+ if (badge) {
227
554
  return (
228
- <a
229
- href={href}
230
- className={className}
231
- aria-label={ariaLabel}
232
- onClick={(e) => {
233
- e.preventDefault();
234
- onLinkClick(href);
235
- }}
236
- >
237
- {children}
238
- </a>
555
+ <li>
556
+ <span className="navigation-link__badge-wrapper">
557
+ <Comp ref={ref} className={cn(baseClassName, className)} {...props}>
558
+ {children}
559
+ </Comp>
560
+ <span className="overline-small navigation-link__badge">
561
+ {badge}
562
+ </span>
563
+ </span>
564
+ </li>
239
565
  );
240
566
  }
241
567
 
242
- // Default: plain anchor tag (browser navigation)
243
568
  return (
244
- <a href={href} className={className} aria-label={ariaLabel}>
245
- {children}
246
- </a>
569
+ <li>
570
+ <Comp ref={ref} className={linkClassName} {...props}>
571
+ {children}
572
+ </Comp>
573
+ </li>
247
574
  );
248
- };
575
+ },
576
+ );
577
+ NavigationLink.displayName = "NavigationLink";
578
+
579
+ // =============================================================================
580
+ // NAVIGATION ACTIONS (Right container)
581
+ // =============================================================================
249
582
 
583
+ const NavigationActions = React.forwardRef<
584
+ HTMLDivElement,
585
+ React.ComponentPropsWithoutRef<"div">
586
+ >(({ className, children, ...props }, ref) => {
250
587
  return (
251
- <nav
252
- ref={containerRef}
253
- className={cn("navigation", isStrategy && "navigation--strategy")}
254
- data-strategy={isStrategy}
255
- onMouseLeave={handleMouseLeave}
256
- >
257
- {!isStrategy && (
258
- <div
259
- ref={overlayRef}
260
- className="navigation-overlay"
261
- data-open={isOpen}
262
- />
263
- )}
264
-
265
- <div className="navigation-container">
266
- <div className="navigation-logo-container">
267
- <AnimatePresence mode="popLayout">
268
- {!isHomepage && (
269
- <motion.div
270
- layout
271
- initial={{ opacity: 0, scale: 0.8 }}
272
- animate={{ opacity: 1, scale: 1 }}
273
- exit={{ opacity: 0, scale: 0.8 }}
274
- transition={{ duration: 0.2, ease: "easeOut" }}
275
- style={{ willChange: "transform, opacity" }}
276
- >
277
- <Button
278
- size="icon"
279
- className=" mr-6"
280
- onClick={() => onClose?.()}
281
- >
282
- <UtilityClearRegular />
283
- </Button>
284
- </motion.div>
285
- )}
286
- </AnimatePresence>
588
+ <div ref={ref} className={cn("navigation-actions", className)} {...props}>
589
+ {children}
590
+ </div>
591
+ );
592
+ });
593
+ NavigationActions.displayName = "NavigationActions";
287
594
 
288
- <motion.div
289
- layout
290
- transition={{ duration: 0.2, ease: "easeOut" }}
291
- >
292
- <NavLink
293
- href="/"
294
- className={cn("navigation-logo-link")}
295
- data-strategy={isStrategy}
296
- aria-label="Home"
297
- >
298
- <NavLogo dark={isStrategy} />
595
+ // =============================================================================
596
+ // NAVIGATION ACCOUNT
597
+ // =============================================================================
299
598
 
300
- <StandardLogo className={cn(!isStrategy ? "text-white" : "text-black")} />
301
- <AlphaIcon dark={isStrategy} />
302
- </NavLink>
303
- </motion.div>
304
- </div>
305
-
306
- {!isStrategy ? (
307
- <div className="navigation-nav-container">
308
- <ul className="navigation-nav-list">
309
- {navItems
310
- ? navItems.map((item, index) => (
311
- <li
312
- key={index + 1}
313
- className="navigation-nav-item"
314
- onMouseEnter={() => handleNavItemMouseEnter(index)}
315
- >
316
- <span className="navigation-nav-link">{item.label}</span>
317
-
318
- {/* Underline */}
319
- {activeItem === index + 1 && (
320
- <motion.div
321
- id="underline"
322
- className="navigation-underline"
323
- layoutId="underline"
324
- transition={{ duration: 0.2, ease: "easeOut" }}
325
- >
326
- <div
327
- className="navigation-underline-circle navigation-underline-circle--gradient"
328
- style={{
329
- width: "10px",
330
- height: "5px",
331
- borderBottomLeftRadius: "100px",
332
- borderBottomRightRadius: "100px",
333
- borderBottom: "0",
334
- boxSizing: "border-box",
335
- }}
336
- />
337
- </motion.div>
338
- )}
339
- {index === 0 && (
340
- <div
341
- className="navigation-underline navigation-underline--static"
342
- style={{ transition: "all 0.2s ease-out" }}
343
- >
344
- <div
345
- className="navigation-underline-circle navigation-underline-circle--white"
346
- style={{
347
- width: "10px",
348
- height: "5px",
349
- borderTopLeftRadius: "100px",
350
- borderTopRightRadius: "100px",
351
- borderBottom: "0",
352
- boxSizing: "border-box",
353
- }}
354
- />
355
- </div>
356
- )}
357
- </li>
358
- ))
359
- : null}
360
- </ul>
361
- </div>
362
- ) : null}
363
-
364
- <div className="navigation-actions">
365
- <NavLink
366
- href="/"
367
- aria-label="Account link"
368
- className={cn("navigation-account-link", isStrategy && "navigation-account-link--strategy")}
369
- data-open={isOpen}
370
- data-strategy={isStrategy}
371
- >
372
- <NavAccount
373
- className={cn(
374
- "navigation-account-icon",
375
- isStrategy
376
- ? "navigation-account-icon--strategy"
377
- : "navigation-account-icon--default",
378
- )}
379
- />
380
- </NavLink>
381
- </div>
382
- </div>
599
+ interface NavigationAccountProps extends React.ComponentPropsWithoutRef<"a"> {
600
+ asChild?: boolean;
601
+ }
383
602
 
384
- {isOpen && <div className="navigation-divider" />}
603
+ const NavigationAccount = React.forwardRef<
604
+ HTMLAnchorElement,
605
+ NavigationAccountProps
606
+ >(({ className, asChild, children, ...props }, ref) => {
607
+ const { light } = useNavigation();
608
+
609
+ const linkClassName = cn(
610
+ buttonVariants({ size: "icon" }),
611
+ "navigation-account",
612
+ light && "navigation-account--light",
613
+ className,
614
+ );
385
615
 
386
- {/* Tray */}
387
- <div ref={menuRef} className="navigation-tray" data-open={isOpen}>
388
- <div className="navigation-tray-content">
389
- <div ref={menuItemsRef} className="navigation-tray-grid">
390
- <AnimatePresence mode="wait">
391
- <motion.div
392
- key={activeItem ? activeItem + 1 : "empty"}
393
- initial={{ y: 10, opacity: 0 }}
394
- animate={{ y: 0, opacity: 1 }}
395
- exit={{ y: -10, opacity: 0 }}
396
- transition={{ duration: 0.2 }}
397
- >
398
- {/* WELL Tray */}
399
- {activeItem === 1 && (
400
- <div className="navigation-tray-section">
401
- <h2
402
- className="navigation-tray-heading"
403
- style={{
404
- background:
405
- "linear-gradient(90deg, #41D5F6 0%, #3EECD1 25%, #66FCD9 50%, #66FCD9 75%, #3EECD1 100%)",
406
- WebkitBackgroundClip: "text",
407
- WebkitTextFillColor: "transparent",
408
- backgroundClip: "text",
409
- }}
410
- >
411
- WELL sets the standard for people-first places
412
- </h2>
413
-
414
- <div className="navigation-tray-columns">
415
- <div className="navigation-tray-column">
416
- <h3 className="navigation-tray-column-title">
417
- Everything
418
- </h3>
419
- <ul className="navigation-tray-column-list navigation-tray-column-list--spacing-5">
420
- <li>
421
- <NavLink href="/" className="navigation-tray-link">
422
- Explore WELL
423
- </NavLink>
424
- </li>
425
- <li>
426
- <NavLink href="/" className="navigation-tray-link">
427
- Enroll in WELL
428
- </NavLink>
429
- </li>
430
- <li>
431
- <NavLink href="/" className="navigation-tray-link">
432
- What's new
433
- </NavLink>
434
- </li>
435
- </ul>
436
- </div>
437
- <div className="navigation-tray-column">
438
- <h3 className="navigation-tray-column-title">
439
- WHY WELL
440
- </h3>
441
- <ul className="navigation-tray-column-list navigation-tray-column-list--spacing-5">
442
- <li>
443
- <NavLink href="/" className="navigation-tray-link">
444
- Performance
445
- </NavLink>
446
- </li>
447
- <li>
448
- <NavLink href="/" className="navigation-tray-link">
449
- ROI
450
- </NavLink>
451
- </li>
452
- <li>
453
- <NavLink href="/" className="navigation-tray-link">
454
- Impact
455
- </NavLink>
456
- </li>
457
- </ul>
458
- </div>
459
- <div className="navigation-tray-column">
460
- <h3 className="navigation-tray-column-title">
461
- Standard
462
- </h3>
463
- <ul className="navigation-tray-column-list navigation-tray-column-list--spacing-5">
464
- <li>
465
- <NavLink href="/" className="navigation-tray-link">
466
- Strategies
467
- </NavLink>
468
- </li>
469
- <li>
470
- <NavLink href="/" className="navigation-tray-link">
471
- Themes
472
- </NavLink>
473
- </li>
474
- <li>
475
- <NavLink href="/" className="navigation-tray-link">
476
- Milestones
477
- </NavLink>
478
- </li>
479
- </ul>
480
- </div>
481
- <div className="navigation-tray-column">
482
- <h3 className="navigation-tray-column-title">
483
- Network
484
- </h3>
485
- <ul className="navigation-tray-column-list navigation-tray-column-list--spacing-5">
486
- <li>
487
- <NavLink href="/" className="navigation-tray-link">
488
- Organizations
489
- </NavLink>
490
- </li>
491
- <li>
492
- <NavLink href="/" className="navigation-tray-link">
493
- People
494
- </NavLink>
495
- </li>
496
- </ul>
497
- </div>
498
- <div className="navigation-tray-column">
499
- <h3 className="navigation-tray-column-title">
500
- Solutions
501
- </h3>
502
- <ul className="navigation-tray-column-list navigation-tray-column-list--spacing-5">
503
- <li>
504
- <NavLink href="/" className="navigation-tray-link">
505
- Products
506
- </NavLink>
507
- </li>
508
- <li>
509
- <NavLink href="/" className="navigation-tray-link">
510
- Services
511
- </NavLink>
512
- </li>
513
- </ul>
514
- </div>
515
- <div className="navigation-tray-column">
516
- <h3 className="navigation-tray-column-title">Places</h3>
517
- <ul className="navigation-tray-column-list navigation-tray-column-list--spacing-5">
518
- <li>
519
- <NavLink href="/" className="navigation-tray-link">
520
- Locations
521
- </NavLink>
522
- </li>
523
- <li>
524
- <NavLink href="/" className="navigation-tray-link">
525
- Portfolios
526
- </NavLink>
527
- </li>
528
- </ul>
529
- </div>
530
- </div>
531
- </div>
532
- )}
533
-
534
- {/* Pursuits Tray */}
535
- {activeItem === 2 && (
536
- <div className="navigation-tray-section">
537
- <h2
538
- className="navigation-tray-heading"
539
- style={{
540
- background:
541
- "linear-gradient(99deg, #41D5F6 3.39%, #3EECD1 57.86%, #66FCD9 112.32%)",
542
- backgroundClip: "text",
543
- WebkitBackgroundClip: "text",
544
- WebkitTextFillColor: "transparent",
545
- }}
546
- >
547
- WELL works everywhere, at any scale
548
- </h2>
549
- <div className="navigation-tray-columns">
550
- <div className="navigation-tray-column">
551
- <h3 className="navigation-tray-column-title">
552
- SUBSCRIBE
553
- </h3>
554
- <ul className="navigation-tray-column-list">
555
- <li>
556
- <NavLink href="/" className="navigation-tray-link">
557
- WELL at scale
558
- </NavLink>
559
- </li>
560
- <li>
561
- <div className="navigation-tray-bullet-item">
562
- <div className="navigation-tray-bullet" />
563
- <NavLink href="/">
564
- <span className="navigation-tray-link-small">
565
- Pricing
566
- </span>
567
- </NavLink>
568
- </div>
569
- </li>
570
- <li>
571
- <div className="navigation-tray-bullet-item">
572
- <div className="navigation-tray-bullet" />
573
- <NavLink href="/">
574
- <span className="navigation-tray-link-small">
575
- Leaderboard
576
- </span>
577
- </NavLink>
578
- </div>
579
- </li>
580
- </ul>
581
- </div>
582
- <div className="navigation-tray-column">
583
- <h3 className="navigation-tray-column-title">
584
- Certify
585
- </h3>
586
- <ul className="navigation-tray-column-list">
587
- <li>
588
- <NavLink href="/" className="navigation-tray-link">
589
- WELL Certification
590
- </NavLink>
591
- </li>
592
- <li>
593
- <NavLink
594
- href="/"
595
- className="navigation-tray-link navigation-tray-link--with-badge"
596
- >
597
- WELL Residence
598
- <span className="navigation-tray-badge">
599
- PILOT
600
- </span>
601
- </NavLink>
602
- </li>
603
- </ul>
604
- </div>
605
- <div className="navigation-tray-column">
606
- <h3 className="navigation-tray-column-title">
607
- Get rated
608
- </h3>
609
- <ul className="navigation-tray-column-list">
610
- <li>
611
- <NavLink href="/" className="navigation-tray-link">
612
- WELL Ratings
613
- </NavLink>
614
- </li>
615
- <li>
616
- <div className="navigation-tray-bullet-item">
617
- <NavBullet color="#F3E7D8" />
618
- <NavLink
619
- href="/"
620
- className="navigation-tray-link-small"
621
- >
622
- Health-Safety Rating
623
- </NavLink>
624
- </div>
625
- </li>
626
- <li>
627
- <div className="navigation-tray-bullet-item">
628
- <NavBullet color="#0F748A" />
629
- <NavLink
630
- href="/"
631
- className="navigation-tray-link-small"
632
- >
633
- Performance Rating
634
- </NavLink>
635
- </div>
636
- </li>
637
- <li>
638
- <div className="navigation-tray-bullet-item">
639
- <NavBullet color="#17AA8D" />
640
- <NavLink
641
- href="/"
642
- className="navigation-tray-link-small"
643
- >
644
- Equity Rating
645
- </NavLink>
646
- </div>
647
- </li>
648
- </ul>
649
- </div>
650
- </div>
651
- </div>
652
- )}
653
-
654
- {/* Network Tray */}
655
- {activeItem === 3 && (
656
- <div className="navigation-tray-section">
657
- <h2
658
- className="navigation-tray-heading"
659
- style={{
660
- background:
661
- "linear-gradient(90deg, #41D5F6 0%, #3EECD1 25%, #66FCD9 50%, #66FCD9 75%, #3EECD1 100%)",
662
- WebkitBackgroundClip: "text",
663
- WebkitTextFillColor: "transparent",
664
- }}
665
- >
666
- Our network drives our movement
667
- </h2>
668
-
669
- <div className="navigation-tray-columns">
670
- <div className="navigation-tray-column">
671
- <h3 className="navigation-tray-column-title">JOIN</h3>
672
- <ul className="navigation-tray-column-list">
673
- <li>
674
- <NavLink href="/" className="navigation-tray-link">
675
- Membership
676
- </NavLink>
677
- </li>
678
- <li>
679
- <NavLink href="/" className="navigation-tray-link">
680
- Works with WELL
681
- </NavLink>
682
- </li>
683
- <li>
684
- <div className="navigation-tray-bullet-item">
685
- <NavBullet color="#2E74AD" />
686
- <NavLink
687
- href="/"
688
- className="navigation-tray-link-small"
689
- >
690
- Enterprise Provider
691
- </NavLink>
692
- </div>
693
- </li>
694
- <li>
695
- <div className="navigation-tray-bullet-item">
696
- <NavBullet color="#149EBD" />
697
- <NavLink
698
- href="/"
699
- className="navigation-tray-link-small"
700
- >
701
- Product Provider
702
- </NavLink>
703
- </div>
704
- </li>
705
- <li>
706
- <div className="navigation-tray-bullet-item">
707
- <NavBullet color="#ED896F" />
708
- <NavLink
709
- href="/"
710
- className="navigation-tray-link-small"
711
- >
712
- Performance Testing Provider
713
- </NavLink>
714
- </div>
715
- </li>
716
- </ul>
717
- </div>
718
-
719
- <div className="navigation-tray-column">
720
- <h3 className="navigation-tray-column-title">EARN</h3>
721
- <ul className="navigation-tray-column-list">
722
- <li>
723
- <NavLink href="/" className="navigation-tray-link">
724
- WELL AP
725
- </NavLink>
726
- </li>
727
- <li>
728
- <NavLink href="/" className="navigation-tray-link">
729
- WELL Faculty
730
- </NavLink>
731
- </li>
732
- </ul>
733
- </div>
734
- </div>
735
- </div>
736
- )}
737
-
738
- {/* Knowledge Tray */}
739
- {activeItem === 4 && (
740
- <div className="navigation-tray-section">
741
- <h2
742
- className="navigation-tray-heading"
743
- style={{
744
- background:
745
- "linear-gradient(90deg, #41D5F6 0%, #3EECD1 25%, #66FCD9 50%, #66FCD9 75%, #3EECD1 100%)",
746
- WebkitBackgroundClip: "text",
747
- WebkitTextFillColor: "transparent",
748
- }}
749
- >
750
- Learn from the experts on health
751
- </h2>
752
-
753
- <div className="navigation-tray-columns">
754
- <div className="navigation-tray-column">
755
- <h3 className="navigation-tray-column-title">LEARN</h3>
756
- <ul className="navigation-tray-column-list">
757
- <li>
758
- <NavLink href="/" className="navigation-tray-link">
759
- WELL Forum
760
- </NavLink>
761
- </li>
762
- <li>
763
- <div className="navigation-tray-bullet-item">
764
- <div className="navigation-tray-bullet" />
765
- <NavLink
766
- href="/"
767
- className="navigation-tray-link-small"
768
- >
769
- Threads
770
- </NavLink>
771
- </div>
772
- </li>
773
- <li>
774
- <div className="navigation-tray-bullet-item">
775
- <div className="navigation-tray-bullet" />
776
- <NavLink
777
- href="/"
778
- className="navigation-tray-link-small"
779
- >
780
- Webcasts
781
- </NavLink>
782
- </div>
783
- </li>
784
- <li>
785
- <div className="navigation-tray-bullet-item">
786
- <div className="navigation-tray-bullet" />
787
- <NavLink
788
- href="/"
789
- className="navigation-tray-link-small"
790
- >
791
- Trainings
792
- </NavLink>
793
- </div>
794
- </li>
795
- </ul>
796
- </div>
797
- <div className="navigation-tray-column">
798
- <h3 className="navigation-tray-column-title">ATTEND</h3>
799
- <ul className="navigation-tray-column-list">
800
- <li>
801
- <NavLink href="/" className="navigation-tray-link">
802
- WELL 2025
803
- </NavLink>
804
- </li>
805
- <li>
806
- <div className="navigation-tray-bullet-item">
807
- <div className="navigation-tray-bullet" />
808
- <NavLink
809
- href="/"
810
- className="navigation-tray-link-small"
811
- >
812
- Flagship events
813
- </NavLink>
814
- </div>
815
- </li>
816
- <li>
817
- <div className="navigation-tray-bullet-item">
818
- <div className="navigation-tray-bullet" />
819
- <NavLink
820
- href="/"
821
- className="navigation-tray-link-small"
822
- >
823
- Thematic summits
824
- </NavLink>
825
- </div>
826
- </li>
827
- <li>
828
- <div className="navigation-tray-bullet-item">
829
- <div className="navigation-tray-bullet" />
830
- <NavLink
831
- href="/"
832
- className="navigation-tray-link-small"
833
- >
834
- Regional summits
835
- </NavLink>
836
- </div>
837
- </li>
838
- </ul>
839
- </div>
840
- <div className="navigation-tray-column">
841
- <h3 className="navigation-tray-column-title">
842
- GUIDANCE
843
- </h3>
844
- <ul className="navigation-tray-column-list">
845
- <li>
846
- <NavLink href="/" className="navigation-tray-link">
847
- Knowledge base
848
- </NavLink>
849
- </li>
850
- <li>
851
- <div className="navigation-tray-bullet-item">
852
- <div className="navigation-tray-bullet" />
853
- <NavLink
854
- href="/"
855
- className="navigation-tray-link-small"
856
- >
857
- Tutorials
858
- </NavLink>
859
- </div>
860
- </li>
861
- <li>
862
- <div className="navigation-tray-bullet-item">
863
- <div className="navigation-tray-bullet" />
864
- <NavLink
865
- href="/"
866
- className="navigation-tray-link-small"
867
- >
868
- Guides
869
- </NavLink>
870
- </div>
871
- </li>
872
- <li>
873
- <div className="navigation-tray-bullet-item">
874
- <div className="navigation-tray-bullet" />
875
- <NavLink
876
- href="/"
877
- className="navigation-tray-link-small"
878
- >
879
- FAQs
880
- </NavLink>
881
- </div>
882
- </li>
883
- </ul>
884
- </div>
885
- <div className="navigation-tray-column">
886
- <h3 className="navigation-tray-column-title">
887
- Resources
888
- </h3>
889
- <ul className="navigation-tray-column-list">
890
- <li>
891
- <NavLink href="/" className="navigation-tray-link">
892
- Resource library
893
- </NavLink>
894
- </li>
895
- <li>
896
- <div className="navigation-tray-bullet-item">
897
- <div className="navigation-tray-bullet" />
898
- <NavLink
899
- href="/"
900
- className="navigation-tray-link-small"
901
- >
902
- Technical tools
903
- </NavLink>
904
- </div>
905
- </li>
906
- <li>
907
- <div className="navigation-tray-bullet-item">
908
- <div className="navigation-tray-bullet" />
909
- <NavLink
910
- href="/"
911
- className="navigation-tray-link-small"
912
- >
913
- Sales tools
914
- </NavLink>
915
- </div>
916
- </li>
917
- <li>
918
- <div className="navigation-tray-bullet-item">
919
- <div className="navigation-tray-bullet" />
920
- <NavLink
921
- href="/"
922
- className="navigation-tray-link-small"
923
- >
924
- Media
925
- </NavLink>
926
- </div>
927
- </li>
928
- </ul>
929
- </div>
930
- </div>
931
- </div>
932
- )}
933
-
934
- {/* IWBI Tray */}
935
- {activeItem === 5 && (
936
- <div className="navigation-tray-section">
937
- <h2
938
- className="navigation-tray-heading"
939
- style={{
940
- background:
941
- "linear-gradient(90deg, #41D5F6 0%, #3EECD1 25%, #66FCD9 50%, #66FCD9 75%, #3EECD1 100%)",
942
- WebkitBackgroundClip: "text",
943
- WebkitTextFillColor: "transparent",
944
- }}
945
- >
946
- The International WELL Building Institute (IWBI)
947
- </h2>
948
-
949
- <div className="navigation-tray-columns">
950
- <div className="navigation-tray-column">
951
- <h3 className="navigation-tray-column-title">About</h3>
952
- <ul className="navigation-tray-column-list">
953
- <li>
954
- <NavLink href="/" className="navigation-tray-link">
955
- IWBI
956
- </NavLink>
957
- </li>
958
- <li>
959
- <NavLink href="/" className="navigation-tray-link">
960
- Research
961
- </NavLink>
962
- </li>
963
- <li>
964
- <NavLink href="/" className="navigation-tray-link">
965
- Advocacy
966
- </NavLink>
967
- </li>
968
- </ul>
969
- </div>
970
- <div className="navigation-tray-column">
971
- <h3 className="navigation-tray-column-title">Meet</h3>
972
- <ul className="navigation-tray-column-list">
973
- <li>
974
- <NavLink href="/" className="navigation-tray-link">
975
- Team
976
- </NavLink>
977
- </li>
978
- <li>
979
- <NavLink href="/" className="navigation-tray-link">
980
- Advisories
981
- </NavLink>
982
- </li>
983
- <li>
984
- <NavLink href="/" className="navigation-tray-link">
985
- Governance Council
986
- </NavLink>
987
- </li>
988
- </ul>
989
- </div>
990
- <div className="navigation-tray-column">
991
- <h3 className="navigation-tray-column-title">
992
- Explore
993
- </h3>
994
- <ul className="navigation-tray-column-list">
995
- <li>
996
- <NavLink href="/" className="navigation-tray-link">
997
- Newsroom
998
- </NavLink>
999
- </li>
1000
- <li>
1001
- <NavLink href="/" className="navigation-tray-link">
1002
- Jobs
1003
- </NavLink>
1004
- </li>
1005
- <li>
1006
- <NavLink href="/" className="navigation-tray-link">
1007
- Blog
1008
- </NavLink>
1009
- </li>
1010
- </ul>
1011
- </div>
1012
- </div>
1013
- </div>
1014
- )}
1015
- </motion.div>
1016
- </AnimatePresence>
1017
- </div>
1018
- </div>
1019
- </div>
1020
- </nav>
616
+ const defaultIcon = (
617
+ <NavAccount
618
+ className={
619
+ light
620
+ ? "navigation-account__icon--light"
621
+ : "navigation-account__icon--dark"
622
+ }
623
+ />
624
+ );
625
+
626
+ // When asChild, render the child with merged props
627
+ // If child has its own children, use those as icon; otherwise inject default icon
628
+ if (asChild && React.isValidElement(children)) {
629
+ const childElement = children as React.ReactElement<{
630
+ children?: React.ReactNode;
631
+ }>;
632
+ const hasChildContent = childElement.props.children != null;
633
+
634
+ return (
635
+ <Slot ref={ref} className={linkClassName} aria-label="Account" {...props}>
636
+ {hasChildContent
637
+ ? children
638
+ : React.cloneElement(childElement, {}, defaultIcon)}
639
+ </Slot>
640
+ );
641
+ }
642
+
643
+ return (
644
+ <a
645
+ ref={ref}
646
+ className={linkClassName}
647
+ aria-label="Account"
648
+ href="/account"
649
+ {...props}
650
+ >
651
+ {children || defaultIcon}
652
+ </a>
1021
653
  );
654
+ });
655
+ NavigationAccount.displayName = "NavigationAccount";
656
+
657
+ // =============================================================================
658
+ // EXPORTS
659
+ // =============================================================================
660
+
661
+ export {
662
+ Navigation,
663
+ NavigationBrand,
664
+ NavigationBack,
665
+ NavigationLogo,
666
+ NavigationMenu,
667
+ NavigationTray,
668
+ NavigationColumn,
669
+ NavigationLink,
670
+ NavigationActions,
671
+ NavigationAccount,
1022
672
  };