@nationaldesignstudio/react 0.0.17 → 0.1.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/dist/component-registry.md +181 -29
- package/dist/components/atoms/accordion/accordion.d.ts +2 -2
- package/dist/components/atoms/background/background.d.ts +158 -0
- package/dist/components/atoms/button/button.d.ts +64 -82
- package/dist/components/atoms/button/icon-button.d.ts +128 -66
- package/dist/components/organisms/card/card.d.ts +130 -4
- package/dist/components/organisms/us-gov-banner/us-gov-banner.d.ts +120 -2
- package/dist/components/sections/hero/hero.d.ts +166 -150
- package/dist/components/sections/quote-block/quote-block.d.ts +152 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +4068 -6052
- package/dist/index.js.map +1 -1
- package/dist/lib/utils.d.ts +1 -2
- package/dist/tokens.css +207 -16
- package/package.json +2 -4
- package/src/components/atoms/accordion/accordion.test.tsx +233 -0
- package/src/components/atoms/accordion/accordion.tsx +8 -8
- package/src/components/atoms/background/background.test.tsx +213 -0
- package/src/components/atoms/background/background.tsx +435 -0
- package/src/components/atoms/background/index.ts +22 -0
- package/src/components/atoms/button/button.stories.tsx +81 -32
- package/src/components/atoms/button/button.tsx +101 -49
- package/src/components/atoms/button/icon-button.stories.tsx +179 -28
- package/src/components/atoms/button/icon-button.test.tsx +254 -0
- package/src/components/atoms/button/icon-button.tsx +178 -59
- package/src/components/atoms/pager-control/pager-control.tsx +32 -3
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +2 -0
- package/src/components/organisms/card/card.tsx +82 -24
- package/src/components/organisms/card/index.ts +7 -0
- package/src/components/organisms/navbar/navbar.tsx +2 -0
- package/src/components/organisms/us-gov-banner/index.ts +5 -1
- package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +72 -16
- package/src/components/sections/hero/hero.stories.tsx +124 -1
- package/src/components/sections/hero/hero.test.tsx +21 -18
- package/src/components/sections/hero/hero.tsx +188 -301
- package/src/components/sections/hero/index.ts +13 -0
- package/src/components/sections/quote-block/index.ts +5 -0
- package/src/components/sections/quote-block/quote-block.tsx +216 -0
- package/src/index.ts +40 -0
- package/src/lib/utils.ts +1 -6
- package/src/stories/ThemeProvider.stories.tsx +11 -5
|
@@ -1,49 +1,69 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { tv, type VariantProps } from "tailwind-variants";
|
|
3
|
+
import { BackgroundOverlay } from "@/components/atoms/background";
|
|
3
4
|
import { type ComponentTheme, themeToStyleVars } from "@/lib/theme";
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
// Re-export Background components for convenience as HeroBackground
|
|
7
|
+
export {
|
|
8
|
+
Background as HeroBackground,
|
|
9
|
+
BackgroundGradient as HeroGradient,
|
|
10
|
+
type BackgroundGradientProps as HeroGradientProps,
|
|
11
|
+
BackgroundImage as HeroBackgroundImage,
|
|
12
|
+
type BackgroundImageProps as HeroBackgroundImageProps,
|
|
13
|
+
BackgroundOverlay as HeroOverlay,
|
|
14
|
+
type BackgroundOverlayProps as HeroOverlayProps,
|
|
15
|
+
BackgroundStream as HeroBackgroundStream,
|
|
16
|
+
type BackgroundStreamProps as HeroBackgroundStreamProps,
|
|
17
|
+
BackgroundVideo as HeroBackgroundVideo,
|
|
18
|
+
type BackgroundVideoProps as HeroBackgroundVideoProps,
|
|
19
|
+
} from "@/components/atoms/background";
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// Hero Variants
|
|
23
|
+
// =============================================================================
|
|
5
24
|
|
|
6
25
|
/**
|
|
7
26
|
* Hero variants based on Figma BaseKit / Heros
|
|
8
27
|
*
|
|
28
|
+
* Slots:
|
|
29
|
+
* - root: The outer container
|
|
30
|
+
* - top: Full-width slot at top for banners/nav (no padding)
|
|
31
|
+
* - content: Padded content area with alignment
|
|
32
|
+
*
|
|
9
33
|
* Variants:
|
|
10
34
|
* - A1: Content aligned at bottom (default)
|
|
11
35
|
* - A2: Content aligned at top
|
|
12
|
-
* - A3: Content
|
|
13
|
-
*
|
|
14
|
-
* Each variant is responsive across breakpoints:
|
|
15
|
-
* - sm (Mobile): 500px height, 20px padding
|
|
16
|
-
* - md (Tablet): 650px height, 56px padding
|
|
17
|
-
* - lg (Desktop): 700-850px height, 64-72px padding
|
|
36
|
+
* - A3: Content centered
|
|
18
37
|
*/
|
|
19
38
|
const heroVariants = tv({
|
|
20
|
-
|
|
21
|
-
"flex w-full",
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
slots: {
|
|
40
|
+
root: "relative flex w-full flex-col overflow-hidden",
|
|
41
|
+
top: "relative z-10 w-full",
|
|
42
|
+
content: [
|
|
43
|
+
"relative z-10 mx-auto flex w-full max-w-screen-xl flex-1 flex-col",
|
|
44
|
+
// Mobile padding
|
|
45
|
+
"p-spacing-20",
|
|
46
|
+
// Tablet padding
|
|
47
|
+
"md:p-spacing-56",
|
|
48
|
+
],
|
|
49
|
+
},
|
|
27
50
|
variants: {
|
|
28
51
|
variant: {
|
|
29
52
|
// A1: Content at bottom
|
|
30
|
-
A1:
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
],
|
|
53
|
+
A1: {
|
|
54
|
+
root: "min-h-[80vh]",
|
|
55
|
+
content: ["justify-end", "lg:p-spacing-72"],
|
|
56
|
+
},
|
|
35
57
|
// A2: Content at top
|
|
36
|
-
A2:
|
|
37
|
-
"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
],
|
|
58
|
+
A2: {
|
|
59
|
+
root: "min-h-[80vh]",
|
|
60
|
+
content: ["justify-start", "lg:p-spacing-64"],
|
|
61
|
+
},
|
|
41
62
|
// A3: Content centered
|
|
42
|
-
A3:
|
|
43
|
-
"
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
],
|
|
63
|
+
A3: {
|
|
64
|
+
root: "min-h-[80vh]",
|
|
65
|
+
content: ["items-center justify-center", "lg:p-spacing-64"],
|
|
66
|
+
},
|
|
47
67
|
},
|
|
48
68
|
},
|
|
49
69
|
defaultVariants: {
|
|
@@ -52,253 +72,80 @@ const heroVariants = tv({
|
|
|
52
72
|
});
|
|
53
73
|
|
|
54
74
|
// =============================================================================
|
|
55
|
-
//
|
|
75
|
+
// Standalone variants for sub-components (for external use)
|
|
56
76
|
// =============================================================================
|
|
57
77
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
* URL for the background image
|
|
62
|
-
*/
|
|
63
|
-
src: string;
|
|
64
|
-
/**
|
|
65
|
-
* CSS background-position value (default: "center")
|
|
66
|
-
*/
|
|
67
|
-
position?: string;
|
|
68
|
-
/**
|
|
69
|
-
* Alt text for accessibility (used in aria-label)
|
|
70
|
-
*/
|
|
71
|
-
alt?: string;
|
|
72
|
-
}
|
|
78
|
+
const heroHeaderVariants = tv({
|
|
79
|
+
base: "relative z-10 w-full",
|
|
80
|
+
});
|
|
73
81
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
ref={ref}
|
|
83
|
-
role="img"
|
|
84
|
-
aria-label={alt}
|
|
85
|
-
aria-hidden={!alt}
|
|
86
|
-
className={cn("absolute inset-0 bg-cover", className)}
|
|
87
|
-
style={{
|
|
88
|
-
backgroundImage: `url(${src})`,
|
|
89
|
-
backgroundPosition: position,
|
|
90
|
-
}}
|
|
91
|
-
{...props}
|
|
92
|
-
/>
|
|
93
|
-
));
|
|
94
|
-
HeroBackgroundImage.displayName = "HeroBackground.Image";
|
|
82
|
+
const heroContentVariants = tv({
|
|
83
|
+
base: [
|
|
84
|
+
"relative z-10 mx-auto flex w-full max-w-screen-xl flex-1 flex-col",
|
|
85
|
+
"p-spacing-20",
|
|
86
|
+
"md:p-spacing-56",
|
|
87
|
+
"lg:p-spacing-72",
|
|
88
|
+
],
|
|
89
|
+
});
|
|
95
90
|
|
|
96
|
-
export interface
|
|
97
|
-
extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, "children"> {
|
|
98
|
-
/**
|
|
99
|
-
* URL for the video source
|
|
100
|
-
*/
|
|
101
|
-
src: string;
|
|
102
|
-
/**
|
|
103
|
-
* Video MIME type (default: auto-detected from src)
|
|
104
|
-
*/
|
|
105
|
-
type?: string;
|
|
106
|
-
/**
|
|
107
|
-
* Poster image URL shown before video loads
|
|
108
|
-
*/
|
|
109
|
-
poster?: string;
|
|
110
|
-
}
|
|
91
|
+
export interface HeroHeaderProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
111
92
|
|
|
112
93
|
/**
|
|
113
|
-
*
|
|
94
|
+
* Header slot for Hero - used for navigation, gov banners, etc.
|
|
95
|
+
* Full-width with no padding, sits at the top of the hero.
|
|
114
96
|
*/
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
>(
|
|
119
|
-
(
|
|
120
|
-
{
|
|
121
|
-
className,
|
|
122
|
-
src,
|
|
123
|
-
type,
|
|
124
|
-
poster,
|
|
125
|
-
autoPlay = true,
|
|
126
|
-
loop = true,
|
|
127
|
-
muted = true,
|
|
128
|
-
playsInline = true,
|
|
129
|
-
...props
|
|
130
|
-
},
|
|
131
|
-
ref,
|
|
132
|
-
) => (
|
|
133
|
-
<video
|
|
97
|
+
const HeroHeader = React.forwardRef<HTMLDivElement, HeroHeaderProps>(
|
|
98
|
+
({ className, ...props }, ref) => (
|
|
99
|
+
<div
|
|
134
100
|
ref={ref}
|
|
135
|
-
|
|
136
|
-
loop={loop}
|
|
137
|
-
muted={muted}
|
|
138
|
-
playsInline={playsInline}
|
|
139
|
-
poster={poster}
|
|
140
|
-
className={cn("absolute inset-0 h-full w-full object-cover", className)}
|
|
101
|
+
className={heroHeaderVariants({ class: className })}
|
|
141
102
|
{...props}
|
|
142
|
-
|
|
143
|
-
<source src={src} type={type} />
|
|
144
|
-
</video>
|
|
103
|
+
/>
|
|
145
104
|
),
|
|
146
105
|
);
|
|
147
|
-
|
|
106
|
+
HeroHeader.displayName = "Hero.Header";
|
|
148
107
|
|
|
149
|
-
export interface
|
|
150
|
-
extends React.
|
|
151
|
-
/**
|
|
152
|
-
* Cloudflare Stream video ID
|
|
153
|
-
*/
|
|
154
|
-
videoId: string;
|
|
155
|
-
/**
|
|
156
|
-
* Poster image URL (Cloudflare Stream thumbnail or custom)
|
|
157
|
-
*/
|
|
158
|
-
poster?: string;
|
|
159
|
-
/**
|
|
160
|
-
* Whether the video should autoplay (default: true)
|
|
161
|
-
*/
|
|
162
|
-
autoplay?: boolean;
|
|
163
|
-
/**
|
|
164
|
-
* Whether the video should loop (default: true)
|
|
165
|
-
*/
|
|
166
|
-
loop?: boolean;
|
|
167
|
-
/**
|
|
168
|
-
* Whether the video should be muted (default: true)
|
|
169
|
-
*/
|
|
170
|
-
muted?: boolean;
|
|
171
|
-
/**
|
|
172
|
-
* Whether to show playback controls (default: false)
|
|
173
|
-
*/
|
|
174
|
-
controls?: boolean;
|
|
175
|
-
/**
|
|
176
|
-
* Custom Cloudflare customer subdomain (if using custom domains)
|
|
177
|
-
*/
|
|
178
|
-
customerSubdomain?: string;
|
|
179
|
-
}
|
|
108
|
+
export interface HeroContentProps
|
|
109
|
+
extends React.HTMLAttributes<HTMLDivElement> {}
|
|
180
110
|
|
|
181
111
|
/**
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
* @example
|
|
185
|
-
* ```tsx
|
|
186
|
-
* <HeroBackground.Stream videoId="5d5bc37ffcf54c9b82e996823bffbb81" />
|
|
187
|
-
*
|
|
188
|
-
* // With custom subdomain
|
|
189
|
-
* <HeroBackground.Stream
|
|
190
|
-
* videoId="5d5bc37ffcf54c9b82e996823bffbb81"
|
|
191
|
-
* customerSubdomain="customer-abc123"
|
|
192
|
-
* />
|
|
193
|
-
* ```
|
|
112
|
+
* Content container for Hero - use for main content.
|
|
113
|
+
* Has padding and sits above backgrounds with z-10.
|
|
194
114
|
*/
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
poster,
|
|
204
|
-
autoplay = true,
|
|
205
|
-
loop = true,
|
|
206
|
-
muted = true,
|
|
207
|
-
controls = false,
|
|
208
|
-
customerSubdomain,
|
|
209
|
-
title = "Background video",
|
|
210
|
-
...props
|
|
211
|
-
},
|
|
212
|
-
ref,
|
|
213
|
-
) => {
|
|
214
|
-
// Build Cloudflare Stream embed URL
|
|
215
|
-
const baseUrl = customerSubdomain
|
|
216
|
-
? `https://${customerSubdomain}.cloudflarestream.com`
|
|
217
|
-
: "https://iframe.videodelivery.net";
|
|
218
|
-
|
|
219
|
-
const params = new URLSearchParams();
|
|
220
|
-
if (autoplay) params.set("autoplay", "true");
|
|
221
|
-
if (loop) params.set("loop", "true");
|
|
222
|
-
if (muted) params.set("muted", "true");
|
|
223
|
-
if (!controls) params.set("controls", "false");
|
|
224
|
-
if (poster) params.set("poster", poster);
|
|
225
|
-
// Preload for better performance
|
|
226
|
-
params.set("preload", "auto");
|
|
227
|
-
|
|
228
|
-
const streamUrl = `${baseUrl}/${videoId}?${params.toString()}`;
|
|
229
|
-
|
|
230
|
-
return (
|
|
231
|
-
<iframe
|
|
232
|
-
ref={ref}
|
|
233
|
-
src={streamUrl}
|
|
234
|
-
title={title}
|
|
235
|
-
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
|
|
236
|
-
allowFullScreen
|
|
237
|
-
className={cn(
|
|
238
|
-
"absolute inset-0 h-full w-full border-0",
|
|
239
|
-
// Scale up to hide letterboxing if video aspect doesn't match
|
|
240
|
-
"scale-[1.5] object-cover",
|
|
241
|
-
className,
|
|
242
|
-
)}
|
|
243
|
-
{...props}
|
|
244
|
-
/>
|
|
245
|
-
);
|
|
246
|
-
},
|
|
115
|
+
const HeroContent = React.forwardRef<HTMLDivElement, HeroContentProps>(
|
|
116
|
+
({ className, ...props }, ref) => (
|
|
117
|
+
<div
|
|
118
|
+
ref={ref}
|
|
119
|
+
className={heroContentVariants({ class: className })}
|
|
120
|
+
{...props}
|
|
121
|
+
/>
|
|
122
|
+
),
|
|
247
123
|
);
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Compound component for Hero backgrounds
|
|
252
|
-
*
|
|
253
|
-
* Provides sub-components for different background types:
|
|
254
|
-
* - `HeroBackground.Image` - Static image backgrounds
|
|
255
|
-
* - `HeroBackground.Video` - HTML5 video backgrounds
|
|
256
|
-
* - `HeroBackground.Stream` - Cloudflare Stream video backgrounds
|
|
257
|
-
*
|
|
258
|
-
* @example
|
|
259
|
-
* ```tsx
|
|
260
|
-
* // Image background
|
|
261
|
-
* <Hero
|
|
262
|
-
* title="Welcome"
|
|
263
|
-
* background={<HeroBackground.Image src="/hero.jpg" />}
|
|
264
|
-
* />
|
|
265
|
-
*
|
|
266
|
-
* // Video background
|
|
267
|
-
* <Hero
|
|
268
|
-
* title="Welcome"
|
|
269
|
-
* background={<HeroBackground.Video src="/hero.mp4" />}
|
|
270
|
-
* />
|
|
271
|
-
*
|
|
272
|
-
* // Cloudflare Stream background
|
|
273
|
-
* <Hero
|
|
274
|
-
* title="Welcome"
|
|
275
|
-
* background={<HeroBackground.Stream videoId="abc123" />}
|
|
276
|
-
* />
|
|
277
|
-
* ```
|
|
278
|
-
*/
|
|
279
|
-
const HeroBackground = {
|
|
280
|
-
Image: HeroBackgroundImage,
|
|
281
|
-
Video: HeroBackgroundVideo,
|
|
282
|
-
Stream: HeroBackgroundStream,
|
|
283
|
-
};
|
|
124
|
+
HeroContent.displayName = "Hero.Content";
|
|
284
125
|
|
|
285
126
|
// =============================================================================
|
|
286
127
|
// Hero Component
|
|
287
128
|
// =============================================================================
|
|
288
129
|
|
|
289
130
|
export interface HeroProps
|
|
290
|
-
extends React.HTMLAttributes<HTMLElement>,
|
|
131
|
+
extends Omit<React.HTMLAttributes<HTMLElement>, "title">,
|
|
291
132
|
VariantProps<typeof heroVariants> {
|
|
292
133
|
/**
|
|
293
|
-
* The title text displayed in the hero
|
|
134
|
+
* The title text displayed in the hero.
|
|
135
|
+
* If provided, renders an h1 with default typography.
|
|
136
|
+
* Omit to use children for custom content composition.
|
|
294
137
|
*/
|
|
295
|
-
title
|
|
138
|
+
title?: string;
|
|
296
139
|
/**
|
|
297
140
|
* Custom typography class for the title using primitive tokens.
|
|
298
|
-
*
|
|
299
|
-
* Default: "text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 xl:text-192 xl:leading-192 xl:tracking-192"
|
|
141
|
+
* Default: "text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 lg:text-192 lg:leading-192 lg:tracking-192"
|
|
300
142
|
*/
|
|
301
143
|
titleClassName?: string;
|
|
144
|
+
/**
|
|
145
|
+
* Content for the top slot (full-width, no padding).
|
|
146
|
+
* Use for USGovBanner, Navigation, etc.
|
|
147
|
+
*/
|
|
148
|
+
top?: React.ReactNode;
|
|
302
149
|
/**
|
|
303
150
|
* Background for the hero. Can be:
|
|
304
151
|
* - A color string (hex, rgb, etc.) for solid backgrounds
|
|
@@ -314,12 +161,24 @@ export interface HeroProps
|
|
|
314
161
|
* Color of the overlay (default: "black")
|
|
315
162
|
*/
|
|
316
163
|
overlayColor?: string;
|
|
164
|
+
/**
|
|
165
|
+
* Border radius for the hero container
|
|
166
|
+
* Useful for designs with rounded bottom corners
|
|
167
|
+
*/
|
|
168
|
+
borderRadius?: string;
|
|
317
169
|
/**
|
|
318
170
|
* Theme overrides for component styling via CSS custom properties
|
|
319
171
|
*/
|
|
320
172
|
theme?: ComponentTheme;
|
|
321
173
|
}
|
|
322
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Default responsive typography for hero title using primitive tokens
|
|
177
|
+
* Mobile: 64px, Tablet: 128px, Desktop: 192px
|
|
178
|
+
*/
|
|
179
|
+
const DEFAULT_TITLE_TYPOGRAPHY =
|
|
180
|
+
"text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 lg:text-192 lg:leading-192 lg:tracking-192 font-medium";
|
|
181
|
+
|
|
323
182
|
/**
|
|
324
183
|
* Checks if the background prop is a color string
|
|
325
184
|
*/
|
|
@@ -331,7 +190,6 @@ function isColorString(
|
|
|
331
190
|
(background.startsWith("#") ||
|
|
332
191
|
background.startsWith("rgb") ||
|
|
333
192
|
background.startsWith("hsl") ||
|
|
334
|
-
// Named colors or CSS variables
|
|
335
193
|
/^(var\(|[a-z]+$)/i.test(background))
|
|
336
194
|
);
|
|
337
195
|
}
|
|
@@ -339,80 +197,80 @@ function isColorString(
|
|
|
339
197
|
/**
|
|
340
198
|
* Hero component for page headers with large display typography.
|
|
341
199
|
*
|
|
342
|
-
*
|
|
200
|
+
* Slots:
|
|
201
|
+
* - `top`: Full-width slot at top for USGovBanner, Navigation (no padding)
|
|
202
|
+
* - `children`: Main content slot with padding and alignment
|
|
203
|
+
*
|
|
204
|
+
* The `title` prop is a convenience for simple heroes - it renders an h1 with
|
|
205
|
+
* responsive typography. Children are always rendered, so you can use both
|
|
206
|
+
* or just children for full control.
|
|
207
|
+
*
|
|
208
|
+
* Variants:
|
|
343
209
|
* - A1: Content at bottom (default)
|
|
344
210
|
* - A2: Content at top
|
|
345
211
|
* - A3: Content centered
|
|
346
212
|
*
|
|
347
|
-
* Each variant responds to breakpoints:
|
|
348
|
-
* - Mobile: 500px height, 20px padding, 64px typography
|
|
349
|
-
* - Tablet (768px+): 650px height, 56px padding, 128-148px typography
|
|
350
|
-
* - Desktop (1440px+): 700-800px height, 64-72px padding, 148-192px typography
|
|
351
|
-
*
|
|
352
213
|
* @example
|
|
353
214
|
* ```tsx
|
|
354
|
-
* // Simple
|
|
355
|
-
* <Hero title="Welcome"
|
|
215
|
+
* // Simple - just title and background
|
|
216
|
+
* <Hero title="Welcome" background="#1a1a1a" />
|
|
356
217
|
*
|
|
357
|
-
* // With
|
|
218
|
+
* // With top slot for banner/nav
|
|
358
219
|
* <Hero
|
|
359
|
-
* title="Welcome"
|
|
360
220
|
* variant="A1"
|
|
361
221
|
* background={<HeroBackground.Image src="/hero.jpg" />}
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
222
|
+
* top={
|
|
223
|
+
* <>
|
|
224
|
+
* <USGovBanner variant="inverted" />
|
|
225
|
+
* <Navigation />
|
|
226
|
+
* </>
|
|
227
|
+
* }
|
|
228
|
+
* >
|
|
229
|
+
* <h1>Board of Peace</h1>
|
|
230
|
+
* <p>Subtitle goes here</p>
|
|
231
|
+
* </Hero>
|
|
372
232
|
* ```
|
|
373
233
|
*/
|
|
374
|
-
/**
|
|
375
|
-
* Default responsive typography for hero title using primitive tokens
|
|
376
|
-
* Mobile: 64px, Tablet: 128px, Desktop: 192px
|
|
377
|
-
*/
|
|
378
|
-
const DEFAULT_TITLE_TYPOGRAPHY =
|
|
379
|
-
"text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 xl:text-192 xl:leading-192 xl:tracking-192 font-medium";
|
|
380
|
-
|
|
381
234
|
const Hero = React.forwardRef<HTMLElement, HeroProps>(
|
|
382
235
|
(
|
|
383
236
|
{
|
|
384
237
|
className,
|
|
238
|
+
children,
|
|
385
239
|
title,
|
|
386
240
|
titleClassName,
|
|
241
|
+
top,
|
|
387
242
|
variant,
|
|
388
243
|
background,
|
|
389
244
|
overlayOpacity = 0,
|
|
390
245
|
overlayColor = "black",
|
|
246
|
+
borderRadius,
|
|
391
247
|
theme,
|
|
392
248
|
style,
|
|
393
249
|
...props
|
|
394
250
|
},
|
|
395
251
|
ref,
|
|
396
252
|
) => {
|
|
253
|
+
const styles = heroVariants({ variant });
|
|
397
254
|
const isColor = isColorString(background);
|
|
398
255
|
const hasMediaBackground = background && !isColor;
|
|
399
256
|
const themeStyles = themeToStyleVars(theme);
|
|
400
257
|
const combinedStyles = {
|
|
401
258
|
...themeStyles,
|
|
402
259
|
...(isColor ? { backgroundColor: background } : {}),
|
|
260
|
+
...(borderRadius ? { borderRadius } : {}),
|
|
403
261
|
...style,
|
|
404
262
|
};
|
|
405
263
|
|
|
406
264
|
return (
|
|
407
265
|
<section
|
|
408
266
|
ref={ref}
|
|
409
|
-
className={
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
)}
|
|
267
|
+
className={styles.root({
|
|
268
|
+
class: [
|
|
269
|
+
// Default background color when no background is provided
|
|
270
|
+
!background && "bg-bg-overlay",
|
|
271
|
+
className,
|
|
272
|
+
],
|
|
273
|
+
})}
|
|
416
274
|
style={
|
|
417
275
|
Object.keys(combinedStyles).length > 0 ? combinedStyles : undefined
|
|
418
276
|
}
|
|
@@ -423,31 +281,60 @@ const Hero = React.forwardRef<HTMLElement, HeroProps>(
|
|
|
423
281
|
|
|
424
282
|
{/* Overlay */}
|
|
425
283
|
{hasMediaBackground && overlayOpacity > 0 && (
|
|
426
|
-
<
|
|
427
|
-
|
|
428
|
-
className="
|
|
429
|
-
style={
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
284
|
+
<BackgroundOverlay
|
|
285
|
+
opacity={overlayOpacity}
|
|
286
|
+
className={overlayColor !== "black" ? undefined : "bg-black"}
|
|
287
|
+
style={
|
|
288
|
+
overlayColor !== "black"
|
|
289
|
+
? { backgroundColor: overlayColor }
|
|
290
|
+
: undefined
|
|
291
|
+
}
|
|
433
292
|
/>
|
|
434
293
|
)}
|
|
435
294
|
|
|
436
|
-
{/*
|
|
437
|
-
<
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
295
|
+
{/* Top slot - full width, no padding (for banners, nav) */}
|
|
296
|
+
{top && <div className={styles.top()}>{top}</div>}
|
|
297
|
+
|
|
298
|
+
{/* Content slot - padded area with alignment */}
|
|
299
|
+
<div className={styles.content()}>
|
|
300
|
+
{/* Title - convenience prop, renders h1 with default typography */}
|
|
301
|
+
{title && (
|
|
302
|
+
<h1
|
|
303
|
+
className={[
|
|
304
|
+
titleClassName || DEFAULT_TITLE_TYPOGRAPHY,
|
|
305
|
+
"text-text-inverted",
|
|
306
|
+
]
|
|
307
|
+
.filter(Boolean)
|
|
308
|
+
.join(" ")}
|
|
309
|
+
>
|
|
310
|
+
{title}
|
|
311
|
+
</h1>
|
|
443
312
|
)}
|
|
444
|
-
|
|
445
|
-
{
|
|
446
|
-
|
|
313
|
+
|
|
314
|
+
{/* Children - always render if provided */}
|
|
315
|
+
{children}
|
|
316
|
+
</div>
|
|
447
317
|
</section>
|
|
448
318
|
);
|
|
449
319
|
},
|
|
450
|
-
)
|
|
320
|
+
) as React.ForwardRefExoticComponent<
|
|
321
|
+
HeroProps & React.RefAttributes<HTMLElement>
|
|
322
|
+
> & {
|
|
323
|
+
Header: typeof HeroHeader;
|
|
324
|
+
Content: typeof HeroContent;
|
|
325
|
+
};
|
|
451
326
|
Hero.displayName = "Hero";
|
|
452
327
|
|
|
453
|
-
|
|
328
|
+
// Attach sub-components
|
|
329
|
+
Hero.Header = HeroHeader;
|
|
330
|
+
Hero.Content = HeroContent;
|
|
331
|
+
|
|
332
|
+
export {
|
|
333
|
+
Hero,
|
|
334
|
+
heroVariants,
|
|
335
|
+
heroContentVariants,
|
|
336
|
+
heroHeaderVariants,
|
|
337
|
+
DEFAULT_TITLE_TYPOGRAPHY,
|
|
338
|
+
HeroHeader,
|
|
339
|
+
HeroContent,
|
|
340
|
+
};
|
|
@@ -2,9 +2,22 @@ export {
|
|
|
2
2
|
DEFAULT_TITLE_TYPOGRAPHY,
|
|
3
3
|
Hero,
|
|
4
4
|
HeroBackground,
|
|
5
|
+
HeroBackgroundImage,
|
|
5
6
|
type HeroBackgroundImageProps,
|
|
7
|
+
HeroBackgroundStream,
|
|
6
8
|
type HeroBackgroundStreamProps,
|
|
9
|
+
HeroBackgroundVideo,
|
|
7
10
|
type HeroBackgroundVideoProps,
|
|
11
|
+
HeroContent,
|
|
12
|
+
type HeroContentProps,
|
|
13
|
+
HeroGradient,
|
|
14
|
+
type HeroGradientProps,
|
|
15
|
+
HeroHeader,
|
|
16
|
+
type HeroHeaderProps,
|
|
17
|
+
HeroOverlay,
|
|
18
|
+
type HeroOverlayProps,
|
|
8
19
|
type HeroProps,
|
|
20
|
+
heroContentVariants,
|
|
21
|
+
heroHeaderVariants,
|
|
9
22
|
heroVariants,
|
|
10
23
|
} from "./hero";
|