@nationaldesignstudio/react 0.2.0 → 0.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.
Files changed (97) hide show
  1. package/dist/component-registry.md +1310 -127
  2. package/dist/components/atoms/background/background.d.ts +13 -27
  3. package/dist/components/atoms/button/button.d.ts +64 -72
  4. package/dist/components/atoms/button/button.figma.d.ts +1 -0
  5. package/dist/components/atoms/button/icon-button.d.ts +62 -110
  6. package/dist/components/atoms/input/input-group.d.ts +278 -0
  7. package/dist/components/atoms/input/input.d.ts +121 -0
  8. package/dist/components/atoms/popover/popover.d.ts +195 -0
  9. package/dist/components/atoms/select/select.d.ts +131 -0
  10. package/dist/components/atoms/tooltip/tooltip.d.ts +161 -0
  11. package/dist/components/organisms/card/card.d.ts +3 -3
  12. package/dist/components/sections/hero/hero.d.ts +2 -2
  13. package/dist/components/sections/prose/prose.d.ts +3 -3
  14. package/dist/components/sections/river/river.d.ts +1 -1
  15. package/dist/components/sections/tout/tout.d.ts +4 -4
  16. package/dist/components/shared/floating-arrow.d.ts +34 -0
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.js +13935 -7622
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/form-control.d.ts +106 -0
  21. package/dist/tokens.css +4725 -19065
  22. package/package.json +2 -1
  23. package/src/components/atoms/accordion/accordion.stories.tsx +1 -1
  24. package/src/components/atoms/accordion/accordion.tsx +2 -2
  25. package/src/components/atoms/background/background.tsx +71 -109
  26. package/src/components/atoms/button/button.figma.tsx +37 -0
  27. package/src/components/atoms/button/button.stories.tsx +253 -115
  28. package/src/components/atoms/button/button.test.tsx +289 -5
  29. package/src/components/atoms/button/button.tsx +40 -101
  30. package/src/components/atoms/button/button.visual.test.tsx +28 -32
  31. package/src/components/atoms/button/icon-button.stories.tsx +44 -101
  32. package/src/components/atoms/button/icon-button.test.tsx +26 -94
  33. package/src/components/atoms/button/icon-button.tsx +81 -224
  34. package/src/components/atoms/input/index.ts +17 -0
  35. package/src/components/atoms/input/input-group.stories.tsx +646 -0
  36. package/src/components/atoms/input/input-group.test.tsx +362 -0
  37. package/src/components/atoms/input/input-group.tsx +409 -0
  38. package/src/components/atoms/input/input.stories.tsx +228 -0
  39. package/src/components/atoms/input/input.test.tsx +167 -0
  40. package/src/components/atoms/input/input.tsx +104 -0
  41. package/src/components/atoms/pager-control/pager-control.stories.tsx +6 -8
  42. package/src/components/atoms/pager-control/pager-control.tsx +12 -12
  43. package/src/components/atoms/popover/index.ts +30 -0
  44. package/src/components/atoms/popover/popover.stories.tsx +531 -0
  45. package/src/components/atoms/popover/popover.test.tsx +486 -0
  46. package/src/components/atoms/popover/popover.tsx +488 -0
  47. package/src/components/atoms/select/index.ts +18 -0
  48. package/src/components/atoms/select/select.stories.tsx +455 -0
  49. package/src/components/atoms/select/select.tsx +324 -0
  50. package/src/components/atoms/tooltip/index.ts +24 -0
  51. package/src/components/atoms/tooltip/tooltip.stories.tsx +348 -0
  52. package/src/components/atoms/tooltip/tooltip.test.tsx +363 -0
  53. package/src/components/atoms/tooltip/tooltip.tsx +347 -0
  54. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +8 -17
  55. package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +3 -3
  56. package/src/components/foundation/typography/typography.stories.tsx +401 -0
  57. package/src/components/organisms/card/card.stories.tsx +19 -19
  58. package/src/components/organisms/card/card.test.tsx +1 -1
  59. package/src/components/organisms/card/card.tsx +3 -3
  60. package/src/components/organisms/card/card.visual.test.tsx +11 -11
  61. package/src/components/organisms/navbar/navbar.tsx +2 -2
  62. package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
  63. package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +2 -2
  64. package/src/components/sections/banner/banner.stories.tsx +1 -5
  65. package/src/components/sections/banner/banner.test.tsx +2 -2
  66. package/src/components/sections/banner/banner.tsx +6 -6
  67. package/src/components/sections/card-grid/card-grid.tsx +5 -5
  68. package/src/components/sections/faq-section/faq-section.tsx +2 -2
  69. package/src/components/sections/hero/hero.stories.tsx +7 -7
  70. package/src/components/sections/hero/hero.test.tsx +5 -5
  71. package/src/components/sections/hero/hero.tsx +10 -11
  72. package/src/components/sections/prose/prose.test.tsx +2 -2
  73. package/src/components/sections/prose/prose.tsx +6 -7
  74. package/src/components/sections/river/river.stories.tsx +8 -8
  75. package/src/components/sections/river/river.test.tsx +4 -4
  76. package/src/components/sections/river/river.tsx +8 -16
  77. package/src/components/sections/tout/tout.stories.tsx +7 -31
  78. package/src/components/sections/tout/tout.test.tsx +1 -1
  79. package/src/components/sections/tout/tout.tsx +11 -11
  80. package/src/components/sections/two-column-section/two-column-section.tsx +7 -9
  81. package/src/components/shared/floating-arrow.tsx +78 -0
  82. package/src/components/shared/index.ts +5 -0
  83. package/src/index.ts +98 -0
  84. package/src/lib/form-control.ts +71 -0
  85. package/src/stories/grid-system.stories.tsx +309 -0
  86. package/src/stories/{Introduction.mdx → introduction.mdx} +29 -15
  87. package/src/stories/{ThemeProvider.stories.tsx → theme-provider.stories.tsx} +8 -22
  88. package/src/stories/{TokenShowcase.stories.tsx → token-showcase.stories.tsx} +1 -20
  89. package/src/stories/token-showcase.tsx +777 -0
  90. package/src/styles.css +3 -0
  91. package/src/tests/token-resolution.test.tsx +298 -0
  92. package/src/theme/hooks.ts +1 -1
  93. package/src/theme/index.ts +1 -1
  94. package/src/theme/theme-provider.test.tsx +270 -0
  95. package/src/theme/{ThemeProvider.tsx → theme-provider.tsx} +18 -2
  96. package/src/stories/GridSystem.stories.tsx +0 -84
  97. package/src/stories/TokenShowcase.tsx +0 -1429
@@ -0,0 +1,488 @@
1
+ "use client";
2
+
3
+ import { Popover as BasePopover } from "@base-ui-components/react/popover";
4
+ import * as React from "react";
5
+ import { tv, type VariantProps } from "tailwind-variants";
6
+ import { cn } from "@/lib/utils";
7
+ import {
8
+ FloatingArrowSvg,
9
+ floatingArrowVariants,
10
+ } from "../../shared/floating-arrow";
11
+
12
+ /**
13
+ * Popover popup variants
14
+ *
15
+ * Uses semantic overlay tokens for themeable styling:
16
+ * - color.overlay.background - Light background
17
+ * - color.overlay.border - Subtle border
18
+ * - color.overlay.text - Primary text
19
+ * - surface.overlay.radius - Rounded corners
20
+ * - spatial.component.overlay.padding/gap - Consistent spacing
21
+ */
22
+ const popoverPopupVariants = tv({
23
+ base: [
24
+ // Layout - uses component overlay tokens
25
+ "p-spatial-component-overlay-padding",
26
+ // Background - uses overlay background token
27
+ "bg-overlay-background",
28
+ // Border - uses overlay border token
29
+ "border border-overlay-border",
30
+ // Text - uses overlay text token
31
+ "text-overlay-text",
32
+ // Border radius - uses surface overlay token
33
+ "rounded-surface-overlay",
34
+ // Shadow for elevation
35
+ "shadow-lg",
36
+ // Allow arrow to extend outside popup bounds
37
+ "overflow-visible",
38
+ // Animation
39
+ "origin-[var(--transform-origin)]",
40
+ "transition-[transform,scale,opacity] duration-150",
41
+ "data-[starting-style]:scale-95 data-[starting-style]:opacity-0",
42
+ "data-[ending-style]:scale-95 data-[ending-style]:opacity-0",
43
+ // Ensure it's above other content (higher than backdrop z-40)
44
+ "z-50",
45
+ // Max width for reasonable content display
46
+ "max-w-[320px]",
47
+ ],
48
+ variants: {
49
+ variant: {
50
+ default: "",
51
+ // Future variants can be added here
52
+ },
53
+ },
54
+ defaultVariants: {
55
+ variant: "default",
56
+ },
57
+ });
58
+
59
+ /**
60
+ * Popover arrow variants - uses shared floating arrow variants
61
+ */
62
+ const popoverArrowVariants = floatingArrowVariants;
63
+
64
+ // ============================================================================
65
+ // Popover Root
66
+ // ============================================================================
67
+
68
+ export interface PopoverRootProps extends BasePopover.Root.Props {
69
+ children: React.ReactNode;
70
+ }
71
+
72
+ /**
73
+ * Popover Root
74
+ *
75
+ * Groups all popover parts. Does not render an element.
76
+ */
77
+ const PopoverRoot = ({ children, ...props }: PopoverRootProps) => {
78
+ return <BasePopover.Root {...props}>{children}</BasePopover.Root>;
79
+ };
80
+
81
+ // ============================================================================
82
+ // Popover Trigger
83
+ // ============================================================================
84
+
85
+ export interface PopoverTriggerProps
86
+ extends React.ComponentProps<typeof BasePopover.Trigger> {
87
+ className?: string;
88
+ }
89
+
90
+ /**
91
+ * Popover Trigger
92
+ *
93
+ * The element that triggers the popover on click.
94
+ * Renders as the child element with popover behavior attached.
95
+ * When children is a single React element, uses `render` prop to avoid wrapper element.
96
+ */
97
+ const PopoverTrigger = React.forwardRef<HTMLButtonElement, PopoverTriggerProps>(
98
+ ({ className, children, ...props }, ref) => {
99
+ // If children is a single React element, use render prop to avoid wrapper
100
+ const isSingleElement = React.isValidElement(children);
101
+
102
+ if (isSingleElement) {
103
+ return (
104
+ <BasePopover.Trigger
105
+ ref={ref}
106
+ className={className}
107
+ render={children}
108
+ {...props}
109
+ />
110
+ );
111
+ }
112
+
113
+ return (
114
+ <BasePopover.Trigger ref={ref} className={className} {...props}>
115
+ {children}
116
+ </BasePopover.Trigger>
117
+ );
118
+ },
119
+ );
120
+ PopoverTrigger.displayName = "PopoverTrigger";
121
+
122
+ // ============================================================================
123
+ // Popover Portal
124
+ // ============================================================================
125
+
126
+ export interface PopoverPortalProps extends BasePopover.Portal.Props {
127
+ children: React.ReactNode;
128
+ }
129
+
130
+ /**
131
+ * Popover Portal
132
+ *
133
+ * Renders the popover popup in a portal outside the DOM hierarchy.
134
+ */
135
+ const PopoverPortal = ({ children, ...props }: PopoverPortalProps) => {
136
+ return <BasePopover.Portal {...props}>{children}</BasePopover.Portal>;
137
+ };
138
+
139
+ // ============================================================================
140
+ // Popover Backdrop
141
+ // ============================================================================
142
+
143
+ export interface PopoverBackdropProps
144
+ extends Omit<React.ComponentProps<typeof BasePopover.Backdrop>, "className"> {
145
+ className?: string;
146
+ }
147
+
148
+ /**
149
+ * Popover Backdrop
150
+ *
151
+ * Optional backdrop element that can be used to close the popover on click.
152
+ */
153
+ const PopoverBackdrop = React.forwardRef<HTMLDivElement, PopoverBackdropProps>(
154
+ ({ className, ...props }, ref) => {
155
+ return (
156
+ <BasePopover.Backdrop
157
+ ref={ref}
158
+ className={cn("fixed inset-0 z-40", className)}
159
+ {...props}
160
+ />
161
+ );
162
+ },
163
+ );
164
+ PopoverBackdrop.displayName = "PopoverBackdrop";
165
+
166
+ // ============================================================================
167
+ // Popover Positioner
168
+ // ============================================================================
169
+
170
+ export interface PopoverPositionerProps
171
+ extends Omit<
172
+ React.ComponentProps<typeof BasePopover.Positioner>,
173
+ "className"
174
+ > {
175
+ className?: string;
176
+ }
177
+
178
+ /**
179
+ * Popover Positioner
180
+ *
181
+ * Positions the popover popup relative to the trigger.
182
+ */
183
+ const PopoverPositioner = React.forwardRef<
184
+ HTMLDivElement,
185
+ PopoverPositionerProps
186
+ >(({ className, side = "bottom", sideOffset = 8, ...props }, ref) => {
187
+ return (
188
+ <BasePopover.Positioner
189
+ ref={ref}
190
+ side={side}
191
+ sideOffset={sideOffset}
192
+ className={cn("z-50", className)}
193
+ {...props}
194
+ />
195
+ );
196
+ });
197
+ PopoverPositioner.displayName = "PopoverPositioner";
198
+
199
+ // ============================================================================
200
+ // Popover Popup
201
+ // ============================================================================
202
+
203
+ export interface PopoverPopupProps
204
+ extends Omit<React.ComponentProps<typeof BasePopover.Popup>, "className">,
205
+ VariantProps<typeof popoverPopupVariants> {
206
+ className?: string;
207
+ }
208
+
209
+ /**
210
+ * Popover Popup
211
+ *
212
+ * The popover content container with styled appearance.
213
+ */
214
+ const PopoverPopup = React.forwardRef<HTMLDivElement, PopoverPopupProps>(
215
+ ({ className, variant, ...props }, ref) => {
216
+ return (
217
+ <BasePopover.Popup
218
+ ref={ref}
219
+ className={cn(popoverPopupVariants({ variant }), className)}
220
+ {...props}
221
+ />
222
+ );
223
+ },
224
+ );
225
+ PopoverPopup.displayName = "PopoverPopup";
226
+
227
+ // ============================================================================
228
+ // Popover Arrow
229
+ // ============================================================================
230
+
231
+ export interface PopoverArrowProps
232
+ extends Omit<React.ComponentProps<typeof BasePopover.Arrow>, "className"> {
233
+ className?: string;
234
+ }
235
+
236
+ /**
237
+ * Popover Arrow
238
+ *
239
+ * Visual pointer element for the popover.
240
+ * Uses shared FloatingArrowSvg with overlay color tokens for fill and border.
241
+ */
242
+ const PopoverArrow = React.forwardRef<HTMLDivElement, PopoverArrowProps>(
243
+ ({ className, ...props }, ref) => {
244
+ return (
245
+ <BasePopover.Arrow
246
+ ref={ref}
247
+ className={cn(popoverArrowVariants(), className)}
248
+ {...props}
249
+ >
250
+ <FloatingArrowSvg
251
+ fillClassName="fill-overlay-background"
252
+ borderClassName="fill-overlay-border"
253
+ />
254
+ </BasePopover.Arrow>
255
+ );
256
+ },
257
+ );
258
+ PopoverArrow.displayName = "PopoverArrow";
259
+
260
+ // ============================================================================
261
+ // Popover Title
262
+ // ============================================================================
263
+
264
+ export interface PopoverTitleProps
265
+ extends Omit<React.ComponentProps<typeof BasePopover.Title>, "className"> {
266
+ className?: string;
267
+ }
268
+
269
+ /**
270
+ * Popover Title
271
+ *
272
+ * Title element for the popover content.
273
+ */
274
+ const PopoverTitle = React.forwardRef<HTMLHeadingElement, PopoverTitleProps>(
275
+ ({ className, ...props }, ref) => {
276
+ return (
277
+ <BasePopover.Title
278
+ ref={ref}
279
+ className={cn("typography-body-md-md font-semibold", className)}
280
+ {...props}
281
+ />
282
+ );
283
+ },
284
+ );
285
+ PopoverTitle.displayName = "PopoverTitle";
286
+
287
+ // ============================================================================
288
+ // Popover Description
289
+ // ============================================================================
290
+
291
+ export interface PopoverDescriptionProps
292
+ extends Omit<
293
+ React.ComponentProps<typeof BasePopover.Description>,
294
+ "className"
295
+ > {
296
+ className?: string;
297
+ }
298
+
299
+ /**
300
+ * Popover Description
301
+ *
302
+ * Description element for the popover content.
303
+ */
304
+ const PopoverDescription = React.forwardRef<
305
+ HTMLParagraphElement,
306
+ PopoverDescriptionProps
307
+ >(({ className, ...props }, ref) => {
308
+ return (
309
+ <BasePopover.Description
310
+ ref={ref}
311
+ className={cn(
312
+ "typography-body-sm-md text-overlay-text-muted mt-8",
313
+ className,
314
+ )}
315
+ {...props}
316
+ />
317
+ );
318
+ });
319
+ PopoverDescription.displayName = "PopoverDescription";
320
+
321
+ // ============================================================================
322
+ // Popover Close
323
+ // ============================================================================
324
+
325
+ export interface PopoverCloseProps
326
+ extends Omit<React.ComponentProps<typeof BasePopover.Close>, "className"> {
327
+ className?: string;
328
+ }
329
+
330
+ /**
331
+ * Popover Close
332
+ *
333
+ * Close button for the popover.
334
+ */
335
+ const PopoverClose = React.forwardRef<HTMLButtonElement, PopoverCloseProps>(
336
+ ({ className, ...props }, ref) => {
337
+ return (
338
+ <BasePopover.Close
339
+ ref={ref}
340
+ className={cn(
341
+ "absolute right-8 top-8 rounded-surface-ui-small p-4",
342
+ "text-overlay-text-muted hover:text-overlay-text",
343
+ "hover:bg-bg-section focus:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
344
+ className,
345
+ )}
346
+ {...props}
347
+ />
348
+ );
349
+ },
350
+ );
351
+ PopoverClose.displayName = "PopoverClose";
352
+
353
+ // ============================================================================
354
+ // Simple Popover Component
355
+ // ============================================================================
356
+
357
+ export interface PopoverProps {
358
+ /** The content to show in the popover */
359
+ children: React.ReactNode;
360
+ /** The element that triggers the popover */
361
+ trigger: React.ReactNode;
362
+ /** Title for the popover (optional) */
363
+ title?: React.ReactNode;
364
+ /** Side of the trigger to show the popover */
365
+ side?: "top" | "bottom" | "left" | "right";
366
+ /** Offset from the trigger */
367
+ sideOffset?: number;
368
+ /** Alignment along the side */
369
+ align?: "start" | "center" | "end";
370
+ /** Whether to show an arrow */
371
+ showArrow?: boolean;
372
+ /** Whether to show a close button */
373
+ showClose?: boolean;
374
+ /** Controlled open state */
375
+ open?: boolean;
376
+ /** Default open state */
377
+ defaultOpen?: boolean;
378
+ /** Callback when open state changes */
379
+ onOpenChange?: (open: boolean) => void;
380
+ /** Additional className for the popup */
381
+ className?: string;
382
+ }
383
+
384
+ /**
385
+ * Popover
386
+ *
387
+ * A simple, pre-composed popover component for common use cases.
388
+ * For more complex needs, use the compound components directly.
389
+ *
390
+ * @example
391
+ * ```tsx
392
+ * <Popover
393
+ * trigger={<Button>Click me</Button>}
394
+ * title="Popover Title"
395
+ * >
396
+ * <p>This is the popover content.</p>
397
+ * </Popover>
398
+ * ```
399
+ */
400
+ const Popover = ({
401
+ children,
402
+ trigger,
403
+ title,
404
+ side = "bottom",
405
+ sideOffset = 8,
406
+ align = "center",
407
+ showArrow = true,
408
+ showClose = false,
409
+ open,
410
+ defaultOpen,
411
+ onOpenChange,
412
+ className,
413
+ }: PopoverProps) => {
414
+ return (
415
+ <PopoverRoot
416
+ open={open}
417
+ defaultOpen={defaultOpen}
418
+ onOpenChange={onOpenChange}
419
+ >
420
+ <PopoverTrigger>{trigger}</PopoverTrigger>
421
+ <PopoverPortal>
422
+ <PopoverPositioner side={side} sideOffset={sideOffset} align={align}>
423
+ <PopoverPopup className={className}>
424
+ {showArrow && <PopoverArrow />}
425
+ {showClose && (
426
+ <PopoverClose>
427
+ <svg
428
+ width="12"
429
+ height="12"
430
+ viewBox="0 0 12 12"
431
+ fill="none"
432
+ aria-hidden="true"
433
+ >
434
+ <path
435
+ d="M1.5 1.5L10.5 10.5M1.5 10.5L10.5 1.5"
436
+ stroke="currentColor"
437
+ strokeWidth="1.5"
438
+ strokeLinecap="round"
439
+ />
440
+ </svg>
441
+ <span className="sr-only">Close</span>
442
+ </PopoverClose>
443
+ )}
444
+ {title && <PopoverTitle>{title}</PopoverTitle>}
445
+ {title ? (
446
+ <PopoverDescription>{children}</PopoverDescription>
447
+ ) : (
448
+ children
449
+ )}
450
+ </PopoverPopup>
451
+ </PopoverPositioner>
452
+ </PopoverPortal>
453
+ </PopoverRoot>
454
+ );
455
+ };
456
+
457
+ // ============================================================================
458
+ // Compound Component Export
459
+ // ============================================================================
460
+
461
+ export const PopoverParts = Object.assign(PopoverRoot, {
462
+ Root: PopoverRoot,
463
+ Trigger: PopoverTrigger,
464
+ Portal: PopoverPortal,
465
+ Backdrop: PopoverBackdrop,
466
+ Positioner: PopoverPositioner,
467
+ Popup: PopoverPopup,
468
+ Arrow: PopoverArrow,
469
+ Title: PopoverTitle,
470
+ Description: PopoverDescription,
471
+ Close: PopoverClose,
472
+ });
473
+
474
+ export {
475
+ Popover,
476
+ PopoverRoot,
477
+ PopoverTrigger,
478
+ PopoverPortal,
479
+ PopoverBackdrop,
480
+ PopoverPositioner,
481
+ PopoverPopup,
482
+ PopoverArrow,
483
+ PopoverTitle,
484
+ PopoverDescription,
485
+ PopoverClose,
486
+ popoverPopupVariants,
487
+ popoverArrowVariants,
488
+ };
@@ -0,0 +1,18 @@
1
+ export {
2
+ Select,
3
+ SelectGroup,
4
+ SelectGroupLabel,
5
+ type SelectGroupLabelProps,
6
+ type SelectGroupProps,
7
+ SelectOption,
8
+ type SelectOptionProps,
9
+ SelectPopup,
10
+ type SelectPopupProps,
11
+ type SelectProps,
12
+ SelectRoot,
13
+ SelectTrigger,
14
+ type SelectTriggerProps,
15
+ selectOptionVariants,
16
+ selectPopupVariants,
17
+ selectTriggerVariants,
18
+ } from "./select";