@nationaldesignstudio/react 0.0.17 → 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 +207 -16
  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
@@ -1,2 +1 @@
1
- import { ClassValue } from 'clsx';
2
- export declare function cn(...inputs: ClassValue[]): string;
1
+ export { cnBase as cn } from 'tailwind-variants';
package/dist/tokens.css CHANGED
@@ -294,7 +294,12 @@
294
294
  --color-yellow-v200: color(srgb 1 1 0.3294);
295
295
  --color-yellow-v300: color(srgb 0.9882 0.9216 0.3059);
296
296
  --color-yellow-v400: color(srgb 0.9765 0.8431 0.2863);
297
+ --font-cormorant: Cormorant, serif;
298
+ --font-gloock: Gloock, serif;
297
299
  --font-pp-neue-montreal: "PP Neue Montreal";
300
+ --font-sans: "PP Neue Montreal";
301
+ --font-serif-alt: Cormorant, serif;
302
+ --font-serif: Gloock, serif;
298
303
  --font-weight-medium: 500;
299
304
  --font-weight-regular: 400;
300
305
  --font-weight-semibold: 600;
@@ -601,6 +606,18 @@
601
606
  --spatial-grid-small-columns: 4;
602
607
  --spatial-grid-small-gutter: 12px;
603
608
  --spatial-grid-small-margin: 24px;
609
+ --spatial-layout-medium-large-span: 8;
610
+ --spatial-layout-medium-large-start: 3;
611
+ --spatial-layout-medium-medium-span: 10;
612
+ --spatial-layout-medium-medium-start: 2;
613
+ --spatial-layout-narrow-large-span: 8;
614
+ --spatial-layout-narrow-large-start: 3;
615
+ --spatial-layout-narrow-medium-span: 8;
616
+ --spatial-layout-narrow-medium-start: 3;
617
+ --spatial-layout-wide-large-span: 10;
618
+ --spatial-layout-wide-large-start: 2;
619
+ --spatial-layout-wide-medium-span: 12;
620
+ --spatial-layout-wide-medium-start: 1;
604
621
  --spatial-section-large-gap: 64px;
605
622
  --spatial-section-large-padding: 128px;
606
623
  --spatial-section-medium-gap: 56px;
@@ -957,7 +974,12 @@
957
974
  --color-yellow-v200: var(--color-yellow-v200);
958
975
  --color-yellow-v300: var(--color-yellow-v300);
959
976
  --color-yellow-v400: var(--color-yellow-v400);
977
+ --font-cormorant: var(--font-cormorant);
978
+ --font-gloock: var(--font-gloock);
960
979
  --font-pp-neue-montreal: var(--font-pp-neue-montreal);
980
+ --font-sans: var(--font-sans);
981
+ --font-serif-alt: var(--font-serif-alt);
982
+ --font-serif: var(--font-serif);
961
983
  --font-weight-medium: var(--font-weight-medium);
962
984
  --font-weight-regular: var(--font-weight-regular);
963
985
  --font-weight-semibold: var(--font-weight-semibold);
@@ -995,6 +1017,9 @@
995
1017
  --leading-88: var(--leading-88);
996
1018
  --leading-96: var(--leading-96);
997
1019
  --leading-9: var(--leading-9);
1020
+ --max-width-desktop: 1440px;
1021
+ --max-width-mobile: 320px;
1022
+ --max-width-tablet: 768px;
998
1023
  --radius-0: var(--radius-0);
999
1024
  --radius-10: var(--radius-10);
1000
1025
  --radius-12: var(--radius-12);
@@ -1264,6 +1289,18 @@
1264
1289
  --spatial-grid-small-columns: var(--spatial-grid-small-columns);
1265
1290
  --spatial-grid-small-gutter: var(--spatial-grid-small-gutter);
1266
1291
  --spatial-grid-small-margin: var(--spatial-grid-small-margin);
1292
+ --spatial-layout-medium-large-span: var(--spatial-layout-medium-large-span);
1293
+ --spatial-layout-medium-large-start: var(--spatial-layout-medium-large-start);
1294
+ --spatial-layout-medium-medium-span: var(--spatial-layout-medium-medium-span);
1295
+ --spatial-layout-medium-medium-start: var(--spatial-layout-medium-medium-start);
1296
+ --spatial-layout-narrow-large-span: var(--spatial-layout-narrow-large-span);
1297
+ --spatial-layout-narrow-large-start: var(--spatial-layout-narrow-large-start);
1298
+ --spatial-layout-narrow-medium-span: var(--spatial-layout-narrow-medium-span);
1299
+ --spatial-layout-narrow-medium-start: var(--spatial-layout-narrow-medium-start);
1300
+ --spatial-layout-wide-large-span: var(--spatial-layout-wide-large-span);
1301
+ --spatial-layout-wide-large-start: var(--spatial-layout-wide-large-start);
1302
+ --spatial-layout-wide-medium-span: var(--spatial-layout-wide-medium-span);
1303
+ --spatial-layout-wide-medium-start: var(--spatial-layout-wide-medium-start);
1267
1304
  --spatial-section-large-gap: var(--spatial-section-large-gap);
1268
1305
  --spatial-section-large-padding: var(--spatial-section-large-padding);
1269
1306
  --spatial-section-medium-gap: var(--spatial-section-medium-gap);
@@ -1411,26 +1448,26 @@
1411
1448
 
1412
1449
  :root {
1413
1450
  /* Semantic Color Tokens */
1414
- --color-accent-brand-soft: color(srgb 0.9686 0.8078 0.749);
1415
- --color-accent-brand: color(srgb 0.9216 0.3098 0.1529);
1416
- --color-bg-muted: color(srgb 1 1 1);
1417
- --color-bg-page: color(srgb 0.9804 0.9804 0.9804);
1418
- --color-bg-section: color(srgb 0.9608 0.9608 0.9608);
1419
- --color-border-divider: color(srgb 0 0 0 / 0.1);
1420
- --color-border-focus: color(srgb 0.9216 0.3098 0.1529);
1421
- --color-border-strong: color(srgb 0 0 0 / 0.2);
1422
- --color-border-subtle: color(srgb 0 0 0 / 0.1);
1423
- --color-button-primary-bg-hover: color(srgb 0.4588 0.4588 0.4588);
1424
- --color-button-primary-bg: color(srgb 0.0784 0.0784 0.0784);
1451
+ --color-accent-brand-soft: color(srgb 0.7686 0.6588 0.4392);
1452
+ --color-accent-brand: color(srgb 0.651 0.5451 0.3686);
1453
+ --color-bg-muted: color(srgb 0.9961 0.9922 0.9765);
1454
+ --color-bg-page: color(srgb 0.9961 0.9922 0.9765);
1455
+ --color-bg-section: color(srgb 0.9608 0.9569 0.9373);
1456
+ --color-border-divider: color(srgb 0.149 0.1569 0.2941 / 0.1);
1457
+ --color-border-focus: color(srgb 0.651 0.5451 0.3686);
1458
+ --color-border-strong: color(srgb 0.149 0.1569 0.2941 / 0.2);
1459
+ --color-border-subtle: color(srgb 0.149 0.1569 0.2941 / 0.1);
1460
+ --color-button-primary-bg-hover: color(srgb 0.149 0.1569 0.2941);
1461
+ --color-button-primary-bg: color(srgb 0.0667 0.0745 0.149);
1425
1462
  --color-button-secondary-bg-hover: color(srgb 1 1 1);
1426
1463
  --color-button-secondary-bg: color(srgb 1 1 1);
1427
- --color-card-background: color(srgb 1 1 1);
1428
- --color-text-inverted: color(srgb 0.9608 0.9608 0.9608);
1464
+ --color-card-background: color(srgb 0.5412 0.4353 0.2588);
1465
+ --color-text-inverted: color(srgb 0.9961 0.9922 0.9765);
1429
1466
  --color-text-link-hover: color(srgb 0.3804 0.3804 0.3804);
1430
1467
  --color-text-link: color(srgb 0.0784 0.0784 0.0784);
1431
- --color-text-muted: color(srgb 0.4588 0.4588 0.4588);
1432
- --color-text-primary: color(srgb 0.0784 0.0784 0.0784);
1433
- --color-text-secondary: color(srgb 0.2588 0.2588 0.2588);
1468
+ --color-text-muted: color(srgb 0.651 0.5451 0.3686);
1469
+ --color-text-primary: color(srgb 0.0667 0.0745 0.149);
1470
+ --color-text-secondary: color(srgb 0.0667 0.0745 0.149);
1434
1471
  --radius-surface-button: var(--radius-6);
1435
1472
  --radius-surface-card: var(--radius-4);
1436
1473
  --surface-button-stroke: 1px;
@@ -1466,6 +1503,102 @@
1466
1503
  }
1467
1504
 
1468
1505
 
1506
+ @utility typography-bop-body-large {
1507
+ font-family: var(--font-cormorant);
1508
+ font-size: 28px;
1509
+ font-weight: var(--font-weight-medium);
1510
+ line-height: 1.4;
1511
+ letter-spacing: 0;
1512
+ }
1513
+
1514
+ @utility typography-bop-body-medium {
1515
+ font-family: var(--font-cormorant);
1516
+ font-size: 18px;
1517
+ font-weight: var(--font-weight-regular);
1518
+ line-height: 1.5;
1519
+ letter-spacing: 0;
1520
+ }
1521
+
1522
+ @utility typography-bop-body-small {
1523
+ font-family: var(--font-cormorant);
1524
+ font-size: 16px;
1525
+ font-weight: var(--font-weight-regular);
1526
+ line-height: 1.5;
1527
+ letter-spacing: 0;
1528
+ }
1529
+
1530
+ @utility typography-bop-display-hero {
1531
+ font-family: var(--font-gloock);
1532
+ font-size: 12.5vw;
1533
+ font-weight: var(--font-weight-regular);
1534
+ line-height: 1.1;
1535
+ letter-spacing: -0.03em;
1536
+ }
1537
+
1538
+ @utility typography-bop-display-large {
1539
+ font-family: var(--font-gloock);
1540
+ font-size: 8vw;
1541
+ font-weight: var(--font-weight-regular);
1542
+ line-height: 1.1;
1543
+ letter-spacing: -0.025em;
1544
+ }
1545
+
1546
+ @utility typography-bop-display-medium {
1547
+ font-family: var(--font-gloock);
1548
+ font-size: 5vw;
1549
+ font-weight: var(--font-weight-regular);
1550
+ line-height: 1.2;
1551
+ letter-spacing: -0.02em;
1552
+ }
1553
+
1554
+ @utility typography-bop-headline-large {
1555
+ font-family: var(--font-cormorant);
1556
+ font-size: 48px;
1557
+ font-weight: var(--font-weight-medium);
1558
+ line-height: 1.2;
1559
+ letter-spacing: -1px;
1560
+ }
1561
+
1562
+ @utility typography-bop-headline-medium {
1563
+ font-family: var(--font-cormorant);
1564
+ font-size: 32px;
1565
+ font-weight: var(--font-weight-medium);
1566
+ line-height: 0.95;
1567
+ letter-spacing: -1px;
1568
+ }
1569
+
1570
+ @utility typography-bop-headline-small {
1571
+ font-family: var(--font-cormorant);
1572
+ font-size: 24px;
1573
+ font-weight: var(--font-weight-medium);
1574
+ line-height: 1.2;
1575
+ letter-spacing: -0.5px;
1576
+ }
1577
+
1578
+ @utility typography-bop-ui-button {
1579
+ font-family: var(--font-pp-neue-montreal);
1580
+ font-size: 16px;
1581
+ font-weight: var(--font-weight-medium);
1582
+ line-height: 1;
1583
+ letter-spacing: 0.5px;
1584
+ }
1585
+
1586
+ @utility typography-bop-ui-caption {
1587
+ font-family: var(--font-pp-neue-montreal);
1588
+ font-size: 14px;
1589
+ font-weight: var(--font-weight-regular);
1590
+ line-height: 1.4;
1591
+ letter-spacing: 0;
1592
+ }
1593
+
1594
+ @utility typography-bop-ui-nav {
1595
+ font-family: var(--font-pp-neue-montreal);
1596
+ font-size: 13px;
1597
+ font-weight: var(--font-weight-medium);
1598
+ line-height: 0.95;
1599
+ letter-spacing: 0;
1600
+ }
1601
+
1469
1602
  @utility typography-large-body-large {
1470
1603
  font-family: var(--font-pp-neue-montreal);
1471
1604
  font-size: var(--text-21);
@@ -24051,6 +24184,32 @@
24051
24184
  }
24052
24185
 
24053
24186
 
24187
+ /* Font Family Utilities */
24188
+ @utility font-cormorant {
24189
+ font-family: var(--font-cormorant);
24190
+ }
24191
+
24192
+ @utility font-gloock {
24193
+ font-family: var(--font-gloock);
24194
+ }
24195
+
24196
+ @utility font-pp-neue-montreal {
24197
+ font-family: var(--font-pp-neue-montreal);
24198
+ }
24199
+
24200
+ @utility font-sans {
24201
+ font-family: var(--font-sans);
24202
+ }
24203
+
24204
+ @utility font-serif {
24205
+ font-family: var(--font-serif);
24206
+ }
24207
+
24208
+ @utility font-serif-alt {
24209
+ font-family: var(--font-serif-alt);
24210
+ }
24211
+
24212
+
24054
24213
  /* Surface Utilities */
24055
24214
  @utility rounded-surface-button {
24056
24215
  border-radius: var(--radius-surface-button);
@@ -24158,4 +24317,36 @@
24158
24317
  @utility stroke-y-surface-card {
24159
24318
  border-top-width: var(--surface-card-stroke);
24160
24319
  border-bottom-width: var(--surface-card-stroke);
24320
+ }
24321
+
24322
+
24323
+ /* Grid Content Layout Utilities */
24324
+ @utility grid-content-medium {
24325
+ grid-column: 1 / -1;
24326
+ @media (width >= 48rem) {
24327
+ grid-column: 2 / span 10;
24328
+ }
24329
+ @media (width >= 90rem) {
24330
+ grid-column: 3 / span 8;
24331
+ }
24332
+ }
24333
+
24334
+ @utility grid-content-narrow {
24335
+ grid-column: 1 / -1;
24336
+ @media (width >= 48rem) {
24337
+ grid-column: 3 / span 8;
24338
+ }
24339
+ @media (width >= 90rem) {
24340
+ grid-column: 3 / span 8;
24341
+ }
24342
+ }
24343
+
24344
+ @utility grid-content-wide {
24345
+ grid-column: 1 / -1;
24346
+ @media (width >= 48rem) {
24347
+ grid-column: 1 / span 12;
24348
+ }
24349
+ @media (width >= 90rem) {
24350
+ grid-column: 2 / span 10;
24351
+ }
24161
24352
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nationaldesignstudio/react",
3
- "version": "0.0.17",
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";