@nationaldesignstudio/react 0.0.16 → 0.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/component-registry.md +181 -29
  2. package/dist/components/atoms/background/background.d.ts +135 -0
  3. package/dist/components/atoms/button/button.d.ts +64 -82
  4. package/dist/components/atoms/button/icon-button.d.ts +100 -66
  5. package/dist/components/organisms/card/card.d.ts +130 -4
  6. package/dist/components/organisms/us-gov-banner/us-gov-banner.d.ts +120 -2
  7. package/dist/components/sections/hero/hero.d.ts +166 -150
  8. package/dist/components/sections/quote-block/quote-block.d.ts +152 -0
  9. package/dist/index.d.ts +6 -2
  10. package/dist/index.js +3868 -5978
  11. package/dist/index.js.map +1 -1
  12. package/dist/lib/utils.d.ts +1 -2
  13. package/dist/tokens.css +336 -145
  14. package/package.json +2 -4
  15. package/src/components/atoms/background/background.tsx +377 -0
  16. package/src/components/atoms/background/index.ts +22 -0
  17. package/src/components/atoms/button/button.stories.tsx +81 -32
  18. package/src/components/atoms/button/button.tsx +91 -49
  19. package/src/components/atoms/button/icon-button.stories.tsx +179 -28
  20. package/src/components/atoms/button/icon-button.tsx +111 -49
  21. package/src/components/organisms/card/card.tsx +82 -24
  22. package/src/components/organisms/card/index.ts +7 -0
  23. package/src/components/organisms/us-gov-banner/index.ts +5 -1
  24. package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +72 -16
  25. package/src/components/sections/hero/hero.stories.tsx +124 -1
  26. package/src/components/sections/hero/hero.test.tsx +21 -18
  27. package/src/components/sections/hero/hero.tsx +188 -301
  28. package/src/components/sections/hero/index.ts +13 -0
  29. package/src/components/sections/quote-block/index.ts +5 -0
  30. package/src/components/sections/quote-block/quote-block.tsx +216 -0
  31. package/src/index.ts +40 -0
  32. package/src/lib/utils.ts +1 -6
  33. package/src/stories/ThemeProvider.stories.tsx +5 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nationaldesignstudio/react",
3
- "version": "0.0.16",
3
+ "version": "0.0.19",
4
4
  "type": "module",
5
5
  "sideEffects": [
6
6
  "*.css"
@@ -47,9 +47,7 @@
47
47
  "dependencies": {
48
48
  "@base-ui-components/react": "^1.0.0-rc.0",
49
49
  "@radix-ui/react-slot": "^1.2.4",
50
- "tailwind-variants": "^0.3.1",
51
- "clsx": "^2.1.1",
52
- "tailwind-merge": "^3.4.0"
50
+ "tailwind-variants": "^0.3.1"
53
51
  },
54
52
  "devDependencies": {
55
53
  "@chromatic-com/storybook": "catalog:",
@@ -0,0 +1,377 @@
1
+ import * as React from "react";
2
+ import { tv } from "tailwind-variants";
3
+
4
+ // =============================================================================
5
+ // Background Atomic Component
6
+ // =============================================================================
7
+
8
+ const backgroundVariants = tv({
9
+ base: "absolute inset-0",
10
+ });
11
+
12
+ /**
13
+ * Base container for background composition.
14
+ * Use as a wrapper to compose multiple background layers (image, video, overlay, gradient).
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <Background>
19
+ * <Background.Image src="/hero.jpg" />
20
+ * <Background.Overlay opacity={0.4} />
21
+ * <Background.Gradient direction="to-t" from="black" to="transparent" />
22
+ * </Background>
23
+ * ```
24
+ */
25
+ export interface BackgroundProps extends React.HTMLAttributes<HTMLDivElement> {}
26
+
27
+ const Background = React.forwardRef<HTMLDivElement, BackgroundProps>(
28
+ ({ className, children, ...props }, ref) => (
29
+ <div
30
+ ref={ref}
31
+ aria-hidden="true"
32
+ className={backgroundVariants({ class: className })}
33
+ {...props}
34
+ >
35
+ {children}
36
+ </div>
37
+ ),
38
+ );
39
+ Background.displayName = "Background";
40
+
41
+ // =============================================================================
42
+ // Background.Image
43
+ // =============================================================================
44
+
45
+ const backgroundImageVariants = tv({
46
+ base: "absolute inset-0 size-full object-cover",
47
+ });
48
+
49
+ export interface BackgroundImageProps
50
+ extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src"> {
51
+ /**
52
+ * URL for the background image
53
+ */
54
+ src: string;
55
+ /**
56
+ * Object position (default: "center")
57
+ */
58
+ position?: string;
59
+ }
60
+
61
+ /**
62
+ * Background image layer using an actual img element with object-cover.
63
+ * Supports native lazy loading, srcset, and better accessibility.
64
+ */
65
+ const BackgroundImage = React.forwardRef<
66
+ HTMLImageElement,
67
+ BackgroundImageProps
68
+ >(({ className, src, position = "center", alt = "", style, ...props }, ref) => (
69
+ <img
70
+ ref={ref}
71
+ src={src}
72
+ alt={alt}
73
+ className={backgroundImageVariants({ class: className })}
74
+ style={{
75
+ objectPosition: position,
76
+ ...style,
77
+ }}
78
+ {...props}
79
+ />
80
+ ));
81
+ BackgroundImage.displayName = "Background.Image";
82
+
83
+ // =============================================================================
84
+ // Background.Video
85
+ // =============================================================================
86
+
87
+ const backgroundVideoVariants = tv({
88
+ base: "absolute inset-0 size-full object-cover",
89
+ });
90
+
91
+ export interface BackgroundVideoProps
92
+ extends Omit<React.VideoHTMLAttributes<HTMLVideoElement>, "children"> {
93
+ /**
94
+ * URL for the video source
95
+ */
96
+ src: string;
97
+ /**
98
+ * Video MIME type (default: auto-detected from src)
99
+ */
100
+ type?: string;
101
+ /**
102
+ * Poster image URL shown before video loads
103
+ */
104
+ poster?: string;
105
+ }
106
+
107
+ /**
108
+ * Background video layer using HTML5 video element.
109
+ */
110
+ const BackgroundVideo = React.forwardRef<
111
+ HTMLVideoElement,
112
+ BackgroundVideoProps
113
+ >(
114
+ (
115
+ {
116
+ className,
117
+ src,
118
+ type,
119
+ poster,
120
+ autoPlay = true,
121
+ loop = true,
122
+ muted = true,
123
+ playsInline = true,
124
+ ...props
125
+ },
126
+ ref,
127
+ ) => (
128
+ <video
129
+ ref={ref}
130
+ autoPlay={autoPlay}
131
+ loop={loop}
132
+ muted={muted}
133
+ playsInline={playsInline}
134
+ poster={poster}
135
+ className={backgroundVideoVariants({ class: className })}
136
+ {...props}
137
+ >
138
+ <source src={src} type={type} />
139
+ </video>
140
+ ),
141
+ );
142
+ BackgroundVideo.displayName = "Background.Video";
143
+
144
+ // =============================================================================
145
+ // Background.Stream
146
+ // =============================================================================
147
+
148
+ const backgroundStreamVariants = tv({
149
+ base: "absolute inset-0 size-full border-0 scale-[1.5] object-cover",
150
+ });
151
+
152
+ export interface BackgroundStreamProps
153
+ extends React.IframeHTMLAttributes<HTMLIFrameElement> {
154
+ /**
155
+ * Cloudflare Stream video ID
156
+ */
157
+ videoId: string;
158
+ /**
159
+ * Poster image URL (Cloudflare Stream thumbnail or custom)
160
+ */
161
+ poster?: string;
162
+ /**
163
+ * Whether the video should autoplay (default: true)
164
+ */
165
+ autoplay?: boolean;
166
+ /**
167
+ * Whether the video should loop (default: true)
168
+ */
169
+ loop?: boolean;
170
+ /**
171
+ * Whether the video should be muted (default: true)
172
+ */
173
+ muted?: boolean;
174
+ /**
175
+ * Whether to show playback controls (default: false)
176
+ */
177
+ controls?: boolean;
178
+ /**
179
+ * Custom Cloudflare customer subdomain (if using custom domains)
180
+ */
181
+ customerSubdomain?: string;
182
+ }
183
+
184
+ /**
185
+ * Background video layer using Cloudflare Stream.
186
+ */
187
+ const BackgroundStream = React.forwardRef<
188
+ HTMLIFrameElement,
189
+ BackgroundStreamProps
190
+ >(
191
+ (
192
+ {
193
+ className,
194
+ videoId,
195
+ poster,
196
+ autoplay = true,
197
+ loop = true,
198
+ muted = true,
199
+ controls = false,
200
+ customerSubdomain,
201
+ title = "Background video",
202
+ ...props
203
+ },
204
+ ref,
205
+ ) => {
206
+ const baseUrl = customerSubdomain
207
+ ? `https://${customerSubdomain}.cloudflarestream.com`
208
+ : "https://iframe.videodelivery.net";
209
+
210
+ const params = new URLSearchParams();
211
+ if (autoplay) params.set("autoplay", "true");
212
+ if (loop) params.set("loop", "true");
213
+ if (muted) params.set("muted", "true");
214
+ if (!controls) params.set("controls", "false");
215
+ if (poster) params.set("poster", poster);
216
+ params.set("preload", "auto");
217
+
218
+ const streamUrl = `${baseUrl}/${videoId}?${params.toString()}`;
219
+
220
+ return (
221
+ <iframe
222
+ ref={ref}
223
+ src={streamUrl}
224
+ title={title}
225
+ allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
226
+ allowFullScreen
227
+ className={backgroundStreamVariants({ class: className })}
228
+ {...props}
229
+ />
230
+ );
231
+ },
232
+ );
233
+ BackgroundStream.displayName = "Background.Stream";
234
+
235
+ // =============================================================================
236
+ // Background.Overlay
237
+ // =============================================================================
238
+
239
+ const backgroundOverlayVariants = tv({
240
+ base: "absolute inset-0 bg-bg-overlay",
241
+ });
242
+
243
+ export interface BackgroundOverlayProps
244
+ extends React.HTMLAttributes<HTMLDivElement> {
245
+ /**
246
+ * Overlay opacity (0-1)
247
+ */
248
+ opacity?: number;
249
+ }
250
+
251
+ /**
252
+ * Solid color overlay layer. Uses semantic bg-overlay token by default.
253
+ * Override with className for different colors.
254
+ */
255
+ const BackgroundOverlay = React.forwardRef<
256
+ HTMLDivElement,
257
+ BackgroundOverlayProps
258
+ >(({ className, opacity = 0.4, style, ...props }, ref) => (
259
+ <div
260
+ ref={ref}
261
+ aria-hidden="true"
262
+ className={backgroundOverlayVariants({ class: className })}
263
+ style={{
264
+ opacity,
265
+ ...style,
266
+ }}
267
+ {...props}
268
+ />
269
+ ));
270
+ BackgroundOverlay.displayName = "Background.Overlay";
271
+
272
+ // =============================================================================
273
+ // Background.Gradient
274
+ // =============================================================================
275
+
276
+ const backgroundGradientVariants = tv({
277
+ base: "absolute inset-0",
278
+ });
279
+
280
+ export interface BackgroundGradientProps
281
+ extends React.HTMLAttributes<HTMLDivElement> {
282
+ /**
283
+ * Gradient direction (Tailwind convention: to-t, to-b, to-l, to-r, etc.)
284
+ * Or CSS gradient direction (to top, to bottom, 45deg, etc.)
285
+ */
286
+ direction?: string;
287
+ /**
288
+ * Starting color (from)
289
+ */
290
+ from?: string;
291
+ /**
292
+ * Optional middle color (via)
293
+ */
294
+ via?: string;
295
+ /**
296
+ * Ending color (to)
297
+ */
298
+ to?: string;
299
+ /**
300
+ * Full custom gradient string (overrides from/via/to)
301
+ */
302
+ gradient?: string;
303
+ }
304
+
305
+ /**
306
+ * Gradient overlay layer. Use for fading backgrounds or creating depth.
307
+ */
308
+ const BackgroundGradient = React.forwardRef<
309
+ HTMLDivElement,
310
+ BackgroundGradientProps
311
+ >(
312
+ (
313
+ {
314
+ className,
315
+ direction = "to-b",
316
+ from = "transparent",
317
+ via,
318
+ to = "black",
319
+ gradient,
320
+ style,
321
+ ...props
322
+ },
323
+ ref,
324
+ ) => {
325
+ // Convert Tailwind-style direction to CSS
326
+ const cssDirection = direction.startsWith("to-")
327
+ ? direction.replace("to-", "to ")
328
+ : direction;
329
+
330
+ const gradientValue =
331
+ gradient ||
332
+ (via
333
+ ? `linear-gradient(${cssDirection}, ${from}, ${via}, ${to})`
334
+ : `linear-gradient(${cssDirection}, ${from}, ${to})`);
335
+
336
+ return (
337
+ <div
338
+ ref={ref}
339
+ aria-hidden="true"
340
+ className={backgroundGradientVariants({ class: className })}
341
+ style={{
342
+ backgroundImage: gradientValue,
343
+ ...style,
344
+ }}
345
+ {...props}
346
+ />
347
+ );
348
+ },
349
+ );
350
+ BackgroundGradient.displayName = "Background.Gradient";
351
+
352
+ // =============================================================================
353
+ // Compound Export
354
+ // =============================================================================
355
+
356
+ const BackgroundCompound = Object.assign(Background, {
357
+ Image: BackgroundImage,
358
+ Video: BackgroundVideo,
359
+ Stream: BackgroundStream,
360
+ Overlay: BackgroundOverlay,
361
+ Gradient: BackgroundGradient,
362
+ });
363
+
364
+ export {
365
+ BackgroundCompound as Background,
366
+ BackgroundImage,
367
+ BackgroundVideo,
368
+ BackgroundStream,
369
+ BackgroundOverlay,
370
+ BackgroundGradient,
371
+ backgroundVariants,
372
+ backgroundImageVariants,
373
+ backgroundVideoVariants,
374
+ backgroundStreamVariants,
375
+ backgroundOverlayVariants,
376
+ backgroundGradientVariants,
377
+ };
@@ -0,0 +1,22 @@
1
+ export type {
2
+ BackgroundGradientProps,
3
+ BackgroundImageProps,
4
+ BackgroundOverlayProps,
5
+ BackgroundProps,
6
+ BackgroundStreamProps,
7
+ BackgroundVideoProps,
8
+ } from "./background";
9
+ export {
10
+ Background,
11
+ BackgroundGradient,
12
+ BackgroundImage,
13
+ BackgroundOverlay,
14
+ BackgroundStream,
15
+ BackgroundVideo,
16
+ backgroundGradientVariants,
17
+ backgroundImageVariants,
18
+ backgroundOverlayVariants,
19
+ backgroundStreamVariants,
20
+ backgroundVariants,
21
+ backgroundVideoVariants,
22
+ } from "./background";
@@ -27,63 +27,112 @@ Playground.argTypes = {
27
27
  control: {
28
28
  type: "radio",
29
29
  },
30
- options: [
31
- "primary",
32
- "primaryOutline",
33
- "secondary",
34
- "charcoal",
35
- "charcoalOutline",
36
- "charcoalOutlineQuiet",
37
- "ivory",
38
- "ivoryOutline",
39
- "ivoryOutlineQuiet",
40
- "gray",
41
- ],
30
+ options: ["solid", "outline", "ghost", "subtle"],
31
+ },
32
+ colorScheme: {
33
+ control: {
34
+ type: "radio",
35
+ },
36
+ options: ["dark", "light"],
42
37
  },
43
38
  };
44
39
  Playground.args = {
45
40
  size: "default",
46
41
  disabled: false,
47
- variant: "primary",
42
+ variant: "solid",
43
+ colorScheme: "dark",
48
44
  };
49
45
 
50
46
  // =============================================================================
51
- // Semantic Variants (recommended)
47
+ // Variants (Dark Color Scheme - for light backgrounds)
52
48
  // =============================================================================
53
49
 
54
- export const Primary = () => <Button variant="primary">Primary</Button>;
50
+ export const Solid = () => <Button variant="solid">Solid</Button>;
55
51
 
56
- export const PrimaryOutline = () => (
57
- <Button variant="primaryOutline">Primary Outline</Button>
58
- );
52
+ export const Outline = () => <Button variant="outline">Outline</Button>;
53
+
54
+ export const Ghost = () => <Button variant="ghost">Ghost</Button>;
59
55
 
60
- export const Secondary = () => <Button variant="secondary">Secondary</Button>;
56
+ export const Subtle = () => <Button variant="subtle">Subtle</Button>;
61
57
 
62
58
  // =============================================================================
63
- // Legacy Variants
59
+ // Variants (Light Color Scheme - for dark backgrounds)
64
60
  // =============================================================================
65
61
 
66
- export const Charcoal = () => <Button variant="charcoal">Charcoal</Button>;
67
-
68
- export const CharcoalOutline = () => (
69
- <Button variant="charcoalOutline">Charcoal Outline</Button>
62
+ const DarkBackground = ({ children }: { children: React.ReactNode }) => (
63
+ <div className="rounded-radius-12 bg-gray-1200 p-spacing-32">{children}</div>
70
64
  );
71
65
 
72
- export const CharcoalOutlineQuiet = () => (
73
- <Button variant="charcoalOutlineQuiet">Charcoal Outline Quiet</Button>
66
+ export const SolidLight = () => (
67
+ <DarkBackground>
68
+ <Button variant="solid" colorScheme="light">
69
+ Solid Light
70
+ </Button>
71
+ </DarkBackground>
74
72
  );
75
73
 
76
- export const Ivory = () => <Button variant="ivory">Ivory</Button>;
74
+ export const OutlineLight = () => (
75
+ <DarkBackground>
76
+ <Button variant="outline" colorScheme="light">
77
+ Outline Light
78
+ </Button>
79
+ </DarkBackground>
80
+ );
77
81
 
78
- export const IvoryOutline = () => (
79
- <Button variant="ivoryOutline">Ivory Outline</Button>
82
+ export const GhostLight = () => (
83
+ <DarkBackground>
84
+ <Button variant="ghost" colorScheme="light">
85
+ Ghost Light
86
+ </Button>
87
+ </DarkBackground>
80
88
  );
81
89
 
82
- export const IvoryOutlineQuiet = () => (
83
- <Button variant="ivoryOutlineQuiet">Ivory Outline Quiet</Button>
90
+ export const SubtleLight = () => (
91
+ <DarkBackground>
92
+ <Button variant="subtle" colorScheme="light">
93
+ Subtle Light
94
+ </Button>
95
+ </DarkBackground>
84
96
  );
85
97
 
86
- export const Gray = () => <Button variant="gray">Gray</Button>;
98
+ // =============================================================================
99
+ // All Variants Comparison
100
+ // =============================================================================
101
+
102
+ export const AllVariants = () => (
103
+ <div className="flex flex-col gap-spacing-32">
104
+ <div>
105
+ <h3 className="mb-spacing-16 text-14 font-medium text-text-secondary">
106
+ Dark Color Scheme (for light backgrounds)
107
+ </h3>
108
+ <div className="flex gap-spacing-16">
109
+ <Button variant="solid">Solid</Button>
110
+ <Button variant="outline">Outline</Button>
111
+ <Button variant="ghost">Ghost</Button>
112
+ <Button variant="subtle">Subtle</Button>
113
+ </div>
114
+ </div>
115
+ <DarkBackground>
116
+ <h3 className="mb-spacing-16 text-14 font-medium text-gray-400">
117
+ Light Color Scheme (for dark backgrounds)
118
+ </h3>
119
+ <div className="flex gap-spacing-16">
120
+ <Button variant="solid" colorScheme="light">
121
+ Solid
122
+ </Button>
123
+ <Button variant="outline" colorScheme="light">
124
+ Outline
125
+ </Button>
126
+ <Button variant="ghost" colorScheme="light">
127
+ Ghost
128
+ </Button>
129
+ <Button variant="subtle" colorScheme="light">
130
+ Subtle
131
+ </Button>
132
+ </div>
133
+ </DarkBackground>
134
+ </div>
135
+ );
87
136
 
88
137
  // =============================================================================
89
138
  // Sizes