@syscore/ui-library 1.3.7 → 1.5.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,5 +1,6 @@
1
1
  import React from "react";
2
2
  import { cn } from "../../lib/utils";
3
+ import { conceptColors } from "../../lib/concept-colors";
3
4
 
4
5
 
5
6
  export interface ConceptColor {
@@ -10,79 +11,6 @@ export interface ConceptColor {
10
11
  contrast?: string;
11
12
  }
12
13
 
13
- // Define the concept colors matching Figma design
14
- export const conceptColors = {
15
- mind: {
16
- solid: "#0a5161",
17
- light: "rgba(10,81,97,0.08)",
18
- border: "rgba(10,81,97,0.16)",
19
- prefix: "M",
20
- },
21
- community: {
22
- solid: "#0f748a",
23
- light: "rgba(15,116,138,0.12)",
24
- border: "rgba(15,116,138,0.24)",
25
- prefix: "C",
26
- },
27
- movement: {
28
- solid: "#149ebd",
29
- light: "rgba(20,158,189,0.12)",
30
- border: "rgba(20,158,189,0.24)",
31
- prefix: "V",
32
- },
33
- water: {
34
- solid: "#39c9ea",
35
- light: "rgba(57,201,234,0.12)",
36
- border: "rgba(57,201,234,0.24)",
37
- prefix: "W",
38
- },
39
- air: {
40
- solid: "#87dff2",
41
- light: "rgba(135,223,242,0.12)",
42
- border: "rgba(135,223,242,0.24)",
43
- prefix: "A",
44
- contrast: "#7CCDDF",
45
- },
46
- light: {
47
- solid: "#8aefdb",
48
- light: "rgba(138,239,219,0.12)",
49
- border: "rgba(138,239,219,0.24)",
50
- prefix: "L",
51
- contrast: "#7FDCC9",
52
- },
53
- thermalComfort: {
54
- solid: "#3eddbf",
55
- light: "rgba(62,221,191,0.12)",
56
- border: "rgba(62,221,191,0.24)",
57
- prefix: "T",
58
- contrast: "#39CBB0",
59
- },
60
- nourishment: {
61
- solid: "#17aa8d",
62
- light: "rgba(23,170,141,0.12)",
63
- border: "rgba(23,170,141,0.24)",
64
- prefix: "N",
65
- },
66
- sound: {
67
- solid: "#0c705c",
68
- light: "rgba(12,112,92,0.12)",
69
- border: "rgba(12,112,92,0.24)",
70
- prefix: "S",
71
- },
72
- materials: {
73
- solid: "#0a4f41",
74
- light: "rgba(10,79,65,0.08)",
75
- border: "rgba(10,79,65,0.16)",
76
- prefix: "X",
77
- },
78
- innovation: {
79
- solid: "#52545D",
80
- light: "rgba(82,84,93,0.08)",
81
- border: "rgba(82,84,93,0.16)",
82
- prefix: "I",
83
- },
84
- } as const;
85
-
86
14
 
87
15
  const DEFAULT_FILL = "#EFF5FB";
88
16
  const DEFAULT_STROKE = "#2E74AD";
@@ -476,7 +404,7 @@ export const IconConceptThermalComfort: React.FC<ConceptIconProps> = ({
476
404
  outlined = false,
477
405
  }) => {
478
406
  const conceptColor =
479
- conceptColors.thermalComfort?.contrast || conceptColors.thermalComfort.solid;
407
+ conceptColors["thermal-comfort"].contrast || conceptColors["thermal-comfort"].solid;
480
408
  const bgFill = outlined ? "white" : active ? conceptColor : DEFAULT_FILL;
481
409
  const strokeColor = outlined ? conceptColor : active ? "white" : DEFAULT_STROKE;
482
410
 
@@ -0,0 +1,45 @@
1
+ import { Button } from "./button"
2
+ import { motion } from "motion/react"
3
+
4
+ interface HeroSectionProps {
5
+ backgroundSlot?: React.ReactNode;
6
+ contentSlot?: React.ReactNode;
7
+ }
8
+
9
+ export const HeroSection = ({ backgroundSlot, contentSlot }: HeroSectionProps) => {
10
+ return (
11
+ <section className="relative flex flex-col sm:pt-[169px] pt-24 items-center min-h-[454px] bg-cyan-900 overflow-hidden">
12
+ {backgroundSlot}
13
+
14
+ {/* Hero Content */}
15
+ <header className="relative z-10 flex flex-col items-center text-center overflow-visible">
16
+ <motion.h2
17
+ className="heading-small mb-6"
18
+ initial={{ opacity: 0, y: 20 }}
19
+ animate={{ opacity: 1, y: 0 }}
20
+ transition={{ duration: 0.6, delay: 0.3 }}
21
+ >
22
+ <span
23
+ style={{
24
+ background: "linear-gradient(90deg, #084654 0.08%, #2D718A 50.08%, #084654 100.08%)",
25
+ backgroundClip: "text",
26
+ WebkitBackgroundClip: "text",
27
+ WebkitTextFillColor: "transparent",
28
+ }}
29
+ >
30
+ One unified, harmonized standard
31
+ </span>
32
+ </motion.h2>
33
+ <motion.h1
34
+ className="text-white heading-large mb-[70px]"
35
+ initial={{ opacity: 0, y: 20 }}
36
+ animate={{ opacity: 1, y: 0 }}
37
+ transition={{ duration: 0.6, delay: 0.4 }}
38
+ >
39
+ One WELL
40
+ </motion.h1>
41
+ {contentSlot}
42
+ </header>
43
+ </section>
44
+ )
45
+ }
@@ -1,12 +1,12 @@
1
1
  import * as React from "react";
2
2
  import { AnimatePresence, motion } from "motion/react";
3
3
  import { cn } from "@/lib/utils";
4
- import { UtilityClose } from "../icons/UtilityClose";
5
4
  import { NavLogo } from "../icons/NavLogo";
6
5
  import { NavAccount } from "../icons/NavAccount";
7
6
  import { NavBullet } from "../icons/NavBullet";
8
7
  import { Button } from "@/components/ui/button";
9
-
8
+ import { UtilityClearRegular } from "../icons/UtilityClearRegular";
9
+ import { StandardLogo } from "../icons/StandardLogo";
10
10
  export interface NavItem {
11
11
  label: string;
12
12
  active?: boolean;
@@ -21,20 +21,170 @@ export type LinkComponent = React.ComponentType<{
21
21
  onClick?: (e: React.MouseEvent) => void;
22
22
  }>;
23
23
 
24
+ interface NavLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
25
+ href: string;
26
+ LinkComponent?: LinkComponent;
27
+ onLinkClick?: (href: string) => void;
28
+ }
29
+
30
+ const NavLink = React.forwardRef<HTMLAnchorElement, NavLinkProps>(
31
+ ({ href, LinkComponent, onLinkClick, children, ...props }, ref) => {
32
+ if (LinkComponent) {
33
+ return (
34
+ <LinkComponent
35
+ href={href}
36
+ className={props.className}
37
+ aria-label={props["aria-label"]}
38
+ >
39
+ {children}
40
+ </LinkComponent>
41
+ );
42
+ }
43
+
44
+ if (onLinkClick) {
45
+ return (
46
+ <a
47
+ ref={ref}
48
+ href={href}
49
+ {...props}
50
+ onClick={(e) => {
51
+ e.preventDefault();
52
+ onLinkClick(href);
53
+ }}
54
+ >
55
+ {children}
56
+ </a>
57
+ );
58
+ }
59
+
60
+ return (
61
+ <a ref={ref} href={href} {...props}>
62
+ {children}
63
+ </a>
64
+ );
65
+ }
66
+ );
67
+ NavLink.displayName = "NavLink";
68
+
24
69
  interface NavigationProps {
25
70
  navItems?: NavItem[];
26
71
  isStrategy?: boolean;
27
72
  Link?: LinkComponent; // Optional Link component from routing library
28
73
  onLinkClick?: (href: string) => void; // Fallback callback for navigation
29
74
  onClose?: () => void;
75
+ pathname: string;
30
76
  }
31
77
 
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;
82
+
83
+ const AlphaIcon = ({ dark }: { dark?: boolean }) => {
84
+ if (dark) {
85
+ 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",
111
+ }}
112
+ /> */}
113
+ </svg>
114
+ );
115
+ }
116
+
117
+ 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>
178
+ );
179
+ };
180
+
32
181
  export const Navigation: React.FC<NavigationProps> = ({
33
182
  navItems,
34
183
  isStrategy = false,
35
184
  Link: LinkComponent,
36
185
  onLinkClick,
37
186
  onClose,
187
+ pathname,
38
188
  }) => {
39
189
  const [isOpen, setIsOpen] = React.useState(false);
40
190
  const [activeItem, setActiveItem] = React.useState<number | null>(null);
@@ -44,6 +194,8 @@ export const Navigation: React.FC<NavigationProps> = ({
44
194
  const menuItemsRef = React.useRef<HTMLDivElement>(null);
45
195
  const overlayRef = React.useRef<HTMLDivElement>(null);
46
196
 
197
+ const isHomepage = pathname === "/";
198
+
47
199
  const handleMouseLeave = () => {
48
200
  setIsOpen(false);
49
201
  setActiveItem(null);
@@ -112,34 +264,43 @@ export const Navigation: React.FC<NavigationProps> = ({
112
264
 
113
265
  <div className="navigation-container">
114
266
  <div className="navigation-logo-container">
115
- {isStrategy ? (
116
- <Button
117
- className="navigation-close-button"
118
- size="icon"
119
- onClick={(e) => {
120
- e.preventDefault();
121
- if (onClose) {
122
- onClose();
123
- } else if (onLinkClick) {
124
- onLinkClick("/");
125
- }
126
- }}
127
- >
128
- <UtilityClose className="navigation-close-icon" />
129
- </Button>
130
- ) : null}
131
-
132
- <NavLink
133
- href="/"
134
- className={cn(
135
- "navigation-logo-link",
136
- isStrategy && "navigation-logo-link--strategy",
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>
137
285
  )}
138
- data-strategy={isStrategy}
139
- aria-label="Home"
286
+ </AnimatePresence>
287
+
288
+ <motion.div
289
+ layout
290
+ transition={{ duration: 0.2, ease: "easeOut" }}
140
291
  >
141
- <NavLogo dark={isStrategy} />
142
- </NavLink>
292
+ <NavLink
293
+ href="/"
294
+ className={cn("navigation-logo-link")}
295
+ data-strategy={isStrategy}
296
+ aria-label="Home"
297
+ >
298
+ <NavLogo dark={isStrategy} />
299
+
300
+ <StandardLogo className={cn(!isStrategy ? "text-white" : "text-black")} />
301
+ <AlphaIcon dark={isStrategy} />
302
+ </NavLink>
303
+ </motion.div>
143
304
  </div>
144
305
 
145
306
  {!isStrategy ? (
@@ -147,54 +308,54 @@ export const Navigation: React.FC<NavigationProps> = ({
147
308
  <ul className="navigation-nav-list">
148
309
  {navItems
149
310
  ? navItems.map((item, index) => (
150
- <li
151
- key={index + 1}
152
- className="navigation-nav-item"
153
- onMouseEnter={() => handleNavItemMouseEnter(index)}
154
- >
155
- <span className="navigation-nav-link">{item.label}</span>
156
-
157
- {/* Underline */}
158
- {activeItem === index + 1 && (
159
- <motion.div
160
- id="underline"
161
- className="navigation-underline"
162
- layoutId="underline"
163
- transition={{ duration: 0.2, ease: "easeOut" }}
164
- >
165
- <div
166
- className="navigation-underline-circle navigation-underline-circle--gradient"
167
- style={{
168
- width: "10px",
169
- height: "5px",
170
- borderBottomLeftRadius: "100px",
171
- borderBottomRightRadius: "100px",
172
- borderBottom: "0",
173
- boxSizing: "border-box",
174
- }}
175
- />
176
- </motion.div>
177
- )}
178
- {index === 0 && (
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
+ >
179
344
  <div
180
- className="navigation-underline navigation-underline--static"
181
- style={{ transition: "all 0.2s ease-out" }}
182
- >
183
- <div
184
- className="navigation-underline-circle navigation-underline-circle--white"
185
- style={{
186
- width: "10px",
187
- height: "5px",
188
- borderTopLeftRadius: "100px",
189
- borderTopRightRadius: "100px",
190
- borderBottom: "0",
191
- boxSizing: "border-box",
192
- }}
193
- />
194
- </div>
195
- )}
196
- </li>
197
- ))
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
+ ))
198
359
  : null}
199
360
  </ul>
200
361
  </div>
@@ -204,7 +365,7 @@ export const Navigation: React.FC<NavigationProps> = ({
204
365
  <NavLink
205
366
  href="/"
206
367
  aria-label="Account link"
207
- className="navigation-account-link"
368
+ className={cn("navigation-account-link", isStrategy && "navigation-account-link--strategy")}
208
369
  data-open={isOpen}
209
370
  data-strategy={isStrategy}
210
371
  >
@@ -0,0 +1,145 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { motion, AnimatePresence } from "motion/react";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ // Root Component
8
+ interface PageHeaderProps extends React.ComponentPropsWithoutRef<"header"> {}
9
+
10
+ const PageHeader = React.forwardRef<HTMLElement, PageHeaderProps>(
11
+ ({ children, className, ...props }, ref) => {
12
+ return (
13
+ <header ref={ref} className={cn("page-header", className)} {...props}>
14
+ {children}
15
+ </header>
16
+ );
17
+ }
18
+ );
19
+ PageHeader.displayName = "PageHeader";
20
+
21
+ // TopSection - flex container for badges/actions
22
+ interface PageHeaderTopSectionProps
23
+ extends React.ComponentPropsWithoutRef<"div"> {}
24
+
25
+ const PageHeaderTopSection = React.forwardRef<
26
+ HTMLDivElement,
27
+ PageHeaderTopSectionProps
28
+ >(({ children, className, ...props }, ref) => {
29
+ return (
30
+ <div
31
+ ref={ref}
32
+ className={cn("page-header-top-section", className)}
33
+ {...props}
34
+ >
35
+ {children}
36
+ </div>
37
+ );
38
+ });
39
+ PageHeaderTopSection.displayName = "PageHeaderTopSection";
40
+
41
+ // LeftContent - groups left-side elements
42
+ interface PageHeaderLeftContentProps
43
+ extends React.ComponentPropsWithoutRef<"div"> {}
44
+
45
+ const PageHeaderLeftContent = React.forwardRef<
46
+ HTMLDivElement,
47
+ PageHeaderLeftContentProps
48
+ >(({ children, className, ...props }, ref) => {
49
+ return (
50
+ <div
51
+ ref={ref}
52
+ className={cn("page-header-left-content", className)}
53
+ {...props}
54
+ >
55
+ {children}
56
+ </div>
57
+ );
58
+ });
59
+ PageHeaderLeftContent.displayName = "PageHeaderLeftContent";
60
+
61
+ // Badge - with optional animation
62
+ interface PageHeaderBadgeProps extends React.ComponentPropsWithoutRef<"div"> {
63
+ /** Whether to animate the badge appearance */
64
+ animated?: boolean;
65
+ /** Show badge conditionally */
66
+ show?: boolean;
67
+ }
68
+
69
+ const PageHeaderBadge = React.forwardRef<HTMLDivElement, PageHeaderBadgeProps>(
70
+ ({ children, animated = false, show = true, className, style, ...props }, ref) => {
71
+ if (animated) {
72
+ return (
73
+ <AnimatePresence>
74
+ {show && (
75
+ <motion.div
76
+ initial={{ opacity: 0, scale: 0.8 }}
77
+ animate={{ opacity: 1, scale: 1 }}
78
+ exit={{ opacity: 0, scale: 0.8 }}
79
+ transition={{ duration: 0.2 }}
80
+ className={className}
81
+ style={style}
82
+ >
83
+ {children}
84
+ </motion.div>
85
+ )}
86
+ </AnimatePresence>
87
+ );
88
+ }
89
+
90
+ if (!show) return null;
91
+
92
+ return (
93
+ <div ref={ref} className={className} style={style} {...props}>
94
+ {children}
95
+ </div>
96
+ );
97
+ }
98
+ );
99
+ PageHeaderBadge.displayName = "PageHeaderBadge";
100
+
101
+ // Title
102
+ interface PageHeaderTitleProps
103
+ extends React.ComponentPropsWithoutRef<"h1"> {}
104
+
105
+ const PageHeaderTitle = React.forwardRef<
106
+ HTMLHeadingElement,
107
+ PageHeaderTitleProps
108
+ >(({ children, className, ...props }, ref) => {
109
+ return (
110
+ <h1 ref={ref} className={cn("page-header-title", className)} {...props}>
111
+ {children}
112
+ </h1>
113
+ );
114
+ });
115
+ PageHeaderTitle.displayName = "PageHeaderTitle";
116
+
117
+ // Description
118
+ interface PageHeaderDescriptionProps
119
+ extends React.ComponentPropsWithoutRef<"p"> {}
120
+
121
+ const PageHeaderDescription = React.forwardRef<
122
+ HTMLParagraphElement,
123
+ PageHeaderDescriptionProps
124
+ >(({ children, className, ...props }, ref) => {
125
+ return (
126
+ <p
127
+ ref={ref}
128
+ className={cn("page-header-description", className)}
129
+ {...props}
130
+ >
131
+ {children}
132
+ </p>
133
+ );
134
+ });
135
+ PageHeaderDescription.displayName = "PageHeaderDescription";
136
+
137
+ // Compound exports
138
+ export {
139
+ PageHeader,
140
+ PageHeaderTopSection,
141
+ PageHeaderLeftContent,
142
+ PageHeaderBadge,
143
+ PageHeaderTitle,
144
+ PageHeaderDescription,
145
+ };