@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
@@ -43,11 +43,7 @@ Playground.args = {
43
43
  headline: "Brand-Large/Headline/Small",
44
44
  body: "A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action.",
45
45
  primaryAction: <Button>Primary</Button>,
46
- secondaryAction: (
47
- <Button variant="outline" colorScheme="light">
48
- Secondary
49
- </Button>
50
- ),
46
+ secondaryAction: <Button variant="secondary-outline">Secondary</Button>,
51
47
  backgroundMedia: <PlaceholderBackground />,
52
48
  };
53
49
 
@@ -61,11 +57,7 @@ export const Desktop: Story = {
61
57
  headline="Brand-Large/Headline/Small"
62
58
  body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
63
59
  primaryAction={<Button>Primary</Button>}
64
- secondaryAction={
65
- <Button variant="outline" colorScheme="light">
66
- Secondary
67
- </Button>
68
- }
60
+ secondaryAction={<Button variant="secondary-outline">Secondary</Button>}
69
61
  backgroundMedia={<PlaceholderBackground />}
70
62
  />
71
63
  ),
@@ -80,11 +72,7 @@ export const Tablet: Story = {
80
72
  headline="Brand-Large/Headline/Small"
81
73
  body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
82
74
  primaryAction={<Button>Primary</Button>}
83
- secondaryAction={
84
- <Button variant="outline" colorScheme="light">
85
- Secondary
86
- </Button>
87
- }
75
+ secondaryAction={<Button variant="secondary-outline">Secondary</Button>}
88
76
  backgroundMedia={<PlaceholderBackground />}
89
77
  />
90
78
  ),
@@ -100,7 +88,7 @@ export const Mobile: Story = {
100
88
  body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
101
89
  primaryAction={<Button size="sm">Primary</Button>}
102
90
  secondaryAction={
103
- <Button size="sm" variant="outline" colorScheme="light">
91
+ <Button size="sm" variant="secondary-outline">
104
92
  Secondary
105
93
  </Button>
106
94
  }
@@ -125,11 +113,7 @@ export const WithImage: Story = {
125
113
  headline="Work with Purpose"
126
114
  body="Join a team that's building the future of government services. We're looking for passionate individuals who want to make a difference."
127
115
  primaryAction={<Button>View Careers</Button>}
128
- secondaryAction={
129
- <Button variant="outline" colorScheme="light">
130
- Learn More
131
- </Button>
132
- }
116
+ secondaryAction={<Button variant="secondary-outline">Learn More</Button>}
133
117
  backgroundMedia={<ImageBackground />}
134
118
  />
135
119
  ),
@@ -158,11 +142,7 @@ export const WithVideoPlaceholder: Story = {
158
142
  headline="Experience Innovation"
159
143
  body="See how modern technology is transforming the way government serves its citizens."
160
144
  primaryAction={<Button>Watch Video</Button>}
161
- secondaryAction={
162
- <Button variant="outline" colorScheme="light">
163
- Learn More
164
- </Button>
165
- }
145
+ secondaryAction={<Button variant="secondary-outline">Learn More</Button>}
166
146
  backgroundMedia={
167
147
  <div className="absolute inset-0 bg-gray-800 flex items-center justify-center">
168
148
  <span className="text-gray-400 typography-body-small">
@@ -183,11 +163,7 @@ export const WithNdstudioFooter: Story = {
183
163
  headline="Work with Purpose"
184
164
  body="Join a team that's building the future of government services. We're looking for passionate individuals who want to make a difference."
185
165
  primaryAction={<Button>View Careers</Button>}
186
- secondaryAction={
187
- <Button variant="outline" colorScheme="light">
188
- Learn More
189
- </Button>
190
- }
166
+ secondaryAction={<Button variant="secondary-outline">Learn More</Button>}
191
167
  backgroundMedia={<ImageBackground />}
192
168
  footer={<NdstudioFooter />}
193
169
  />
@@ -220,7 +220,7 @@ describe("Tout", () => {
220
220
  );
221
221
 
222
222
  const headline = page.getByRole("heading", { level: 2 });
223
- await expect.element(headline).toHaveClass(/typography-headline-small/);
223
+ await expect.element(headline).toHaveClass(/typography-h4/);
224
224
  await expect.element(headline).toHaveClass(/text-gray-900/);
225
225
  });
226
226
 
@@ -93,9 +93,9 @@ export interface ToutProps
93
93
  *
94
94
  * This component is self-contained with its own grid.
95
95
  * Grid setup:
96
- * - Desktop (lg): 24 columns, gap-spacing-20, content spans 9 cols
97
- * - Tablet (md): 12 columns, gap-spacing-20, content spans 9 cols
98
- * - Mobile: 4 columns, gap-spacing-20, content spans all 4 cols
96
+ * - Desktop (lg): 24 columns, gap-20, content spans 9 cols
97
+ * - Tablet (md): 12 columns, gap-20, content spans 9 cols
98
+ * - Mobile: 4 columns, gap-20, content spans all 4 cols
99
99
  *
100
100
  * @example
101
101
  * ```tsx
@@ -103,7 +103,7 @@ export interface ToutProps
103
103
  * headline="Feature Headline"
104
104
  * body="Description of the feature..."
105
105
  * primaryAction={<Button>Primary</Button>}
106
- * secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
106
+ * secondaryAction={<Button variant="outline" colorScheme="light">Secondary</Button>}
107
107
  * backgroundMedia={
108
108
  * <img
109
109
  * src="/background.jpg"
@@ -163,7 +163,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
163
163
  // Grid setup with responsive columns
164
164
  "grid w-full h-full",
165
165
  // Mobile: 4 columns with gap-20
166
- "grid-cols-4 gap-spacing-20",
166
+ "grid-cols-4 gap-20",
167
167
  // Tablet (md): 12 columns
168
168
  "md:grid-cols-12",
169
169
  // Desktop (lg): 24 columns
@@ -171,9 +171,9 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
171
171
  // Max width and centering like grid-container
172
172
  "max-w-[var(--breakpoint-lg)] mx-auto",
173
173
  // Responsive margins matching grid-container - uses primitive spacing tokens
174
- "px-spacing-20 md:px-spacing-56 lg:px-spacing-72",
174
+ "px-20 md:px-56 lg:px-72",
175
175
  // Vertical padding to position content at bottom - uses primitive spacing tokens
176
- "py-spacing-36 md:py-spacing-56 lg:py-spacing-72",
176
+ "py-36 md:py-56 lg:py-72",
177
177
  )}
178
178
  style={{
179
179
  // Grid spacing theme overrides
@@ -193,7 +193,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
193
193
  "flex flex-col",
194
194
  isCentered ? "justify-start items-center" : "justify-end",
195
195
  // Responsive gap between text and buttons - uses primitive spacing tokens
196
- "gap-spacing-28 md:gap-spacing-36",
196
+ "gap-28 md:gap-36",
197
197
  // Mobile: all 4 cols
198
198
  "col-span-4",
199
199
  // Tablet & Desktop: 9 cols left-aligned, full width centered
@@ -203,13 +203,13 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
203
203
  {/* Text content stack - uses primitive spacing tokens */}
204
204
  <div
205
205
  className={cn(
206
- "flex flex-col gap-spacing-16",
206
+ "flex flex-col gap-16",
207
207
  isCentered && "items-center text-center",
208
208
  )}
209
209
  >
210
210
  <h2
211
211
  className={cn(
212
- "typography-headline-small",
212
+ "typography-h4",
213
213
  isDark ? "text-gray-100" : "text-gray-900",
214
214
  )}
215
215
  style={{
@@ -243,7 +243,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
243
243
  "flex flex-row",
244
244
  isCentered ? "justify-center" : "items-start",
245
245
  // Responsive gap between buttons
246
- "gap-spacing-8 md:gap-spacing-12",
246
+ "gap-8 md:gap-12",
247
247
  "[&>*]:flex-shrink-0",
248
248
  )}
249
249
  >
@@ -16,11 +16,11 @@ const twoColumnSectionVariants = tv({
16
16
  base: [
17
17
  "w-full",
18
18
  // Small (mobile): 20px x, 56px top, 20px bottom
19
- "px-spacing-20 pt-spacing-56 pb-spacing-20",
19
+ "px-20 pt-56 pb-20",
20
20
  // Medium (tablet): 56px x, 56px y
21
- "md:px-spacing-56 md:py-spacing-56",
21
+ "md:px-56 md:py-56",
22
22
  // Large (desktop): 72px x, 72px top, 112px bottom
23
- "lg:px-spacing-72 lg:pt-spacing-72 lg:pb-spacing-112",
23
+ "lg:px-72 lg:pt-72 lg:pb-112",
24
24
  ],
25
25
  variants: {
26
26
  colorScheme: {
@@ -108,13 +108,11 @@ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
108
108
  {/* Inner container with border-top - uses primitive spacing tokens */}
109
109
  <div
110
110
  className={cn(
111
- "border-t pt-spacing-36",
111
+ "border-t pt-36",
112
112
  colorScheme === "dark" ? "border-gray-700" : "border-gray-300",
113
113
  // Grid layout - uses primitive spacing tokens
114
- "grid grid-cols-1 gap-spacing-56",
115
- layout === "equal"
116
- ? "md:grid-cols-2"
117
- : "lg:grid-cols-24 lg:gap-spacing-56",
114
+ "grid grid-cols-1 gap-56",
115
+ layout === "equal" ? "md:grid-cols-2" : "lg:grid-cols-24 lg:gap-56",
118
116
  )}
119
117
  >
120
118
  {/* Title column */}
@@ -132,7 +130,7 @@ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
132
130
  {/* Content column - uses primitive spacing tokens */}
133
131
  <div
134
132
  className={cn(
135
- "flex flex-col gap-spacing-56",
133
+ "flex flex-col gap-56",
136
134
  layout !== "equal" && "lg:col-span-15",
137
135
  )}
138
136
  >
@@ -0,0 +1,78 @@
1
+ import { tv } from "tailwind-variants";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ /**
5
+ * Shared arrow variants for floating UI components
6
+ *
7
+ * Used by Tooltip, Popover, and other floating components.
8
+ * Handles positioning based on the side attribute.
9
+ */
10
+ export const floatingArrowVariants = tv({
11
+ base: [
12
+ // Display flex to properly size the arrow container
13
+ "flex",
14
+ // Positioning based on floating side (uses spacing tokens since --spacing: 1px)
15
+ "data-[side=bottom]:-top-7",
16
+ "data-[side=left]:-right-12 data-[side=left]:rotate-90",
17
+ "data-[side=right]:-left-12 data-[side=right]:-rotate-90",
18
+ "data-[side=top]:-bottom-7 data-[side=top]:rotate-180",
19
+ // Animation - follows popup
20
+ "data-[starting-style]:opacity-0",
21
+ "data-[ending-style]:opacity-0",
22
+ ],
23
+ });
24
+
25
+ export interface FloatingArrowSvgProps {
26
+ /** CSS class for the main fill color (e.g., "fill-tooltip-bg" or "fill-overlay-background") */
27
+ fillClassName: string;
28
+ /** CSS class for the border color using fill-* (e.g., "fill-overlay-border") - renders as outline behind main fill */
29
+ borderClassName?: string;
30
+ /** Additional className for the SVG element */
31
+ className?: string;
32
+ }
33
+
34
+ /**
35
+ * FloatingArrowSvg
36
+ *
37
+ * A shared arrow SVG component for floating UI elements.
38
+ * Use with Tooltip, Popover, Dropdown, and other floating components.
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * // For dark tooltip (no border)
43
+ * <FloatingArrowSvg fillClassName="fill-tooltip-bg" />
44
+ *
45
+ * // For light popover with border
46
+ * <FloatingArrowSvg
47
+ * fillClassName="fill-overlay-background"
48
+ * borderClassName="fill-overlay-border"
49
+ * />
50
+ * ```
51
+ */
52
+ export const FloatingArrowSvg = ({
53
+ fillClassName,
54
+ borderClassName,
55
+ className,
56
+ }: FloatingArrowSvgProps) => (
57
+ <svg
58
+ width="20"
59
+ height="10"
60
+ viewBox="0 0 20 10"
61
+ fill="none"
62
+ className={cn("block", className)}
63
+ aria-hidden="true"
64
+ >
65
+ {/* Main fill shape */}
66
+ <path
67
+ d="M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z"
68
+ className={fillClassName}
69
+ />
70
+ {/* Border stroke (rendered on top for outline effect) */}
71
+ {borderClassName && (
72
+ <path
73
+ d="M8.99542 1.85876C9.75604 1.17425 10.9106 1.17422 11.6713 1.85878L16.5281 6.22989C17.0789 6.72568 17.7938 7.00001 18.5349 7.00001L15.89 7L11.0023 2.60207C10.622 2.2598 10.0447 2.2598 9.66436 2.60207L4.77734 7L2.13171 7.00001C2.87284 7.00001 3.58774 6.72568 4.13861 6.22989L8.99542 1.85876Z"
74
+ className={borderClassName}
75
+ />
76
+ )}
77
+ </svg>
78
+ );
@@ -0,0 +1,5 @@
1
+ export {
2
+ FloatingArrowSvg,
3
+ type FloatingArrowSvgProps,
4
+ floatingArrowVariants,
5
+ } from "./floating-arrow";
package/src/index.ts CHANGED
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @nationaldesignstudio/react
3
+ * Design system components for React applications
4
+ */
5
+
1
6
  // =============================================================================
2
7
  // Atoms
3
8
  // =============================================================================
@@ -35,6 +40,27 @@ export {
35
40
  IconButton,
36
41
  iconButtonVariants,
37
42
  } from "./components/atoms/button";
43
+ export type {
44
+ InputGroupAddonProps,
45
+ InputGroupButtonProps,
46
+ InputGroupInputProps,
47
+ InputGroupProps,
48
+ InputGroupTextareaProps,
49
+ InputGroupTextProps,
50
+ InputProps,
51
+ } from "./components/atoms/input";
52
+ export {
53
+ Input,
54
+ InputGroup,
55
+ InputGroupAddon,
56
+ InputGroupButton,
57
+ InputGroupInput,
58
+ InputGroupText,
59
+ InputGroupTextarea,
60
+ inputGroupAddonVariants,
61
+ inputGroupVariants,
62
+ inputVariants,
63
+ } from "./components/atoms/input";
38
64
  export type { NdstudioFooterProps } from "./components/atoms/ndstudio-footer";
39
65
  export { NdstudioFooter } from "./components/atoms/ndstudio-footer";
40
66
  export type { PagerControlProps } from "./components/atoms/pager-control";
@@ -42,6 +68,78 @@ export {
42
68
  PagerControl,
43
69
  pagerControlVariants,
44
70
  } from "./components/atoms/pager-control";
71
+ export type {
72
+ PopoverArrowProps,
73
+ PopoverBackdropProps,
74
+ PopoverCloseProps,
75
+ PopoverDescriptionProps,
76
+ PopoverPopupProps,
77
+ PopoverPortalProps,
78
+ PopoverPositionerProps,
79
+ PopoverProps,
80
+ PopoverRootProps,
81
+ PopoverTitleProps,
82
+ PopoverTriggerProps,
83
+ } from "./components/atoms/popover";
84
+ export {
85
+ Popover,
86
+ PopoverArrow,
87
+ PopoverBackdrop,
88
+ PopoverClose,
89
+ PopoverDescription,
90
+ PopoverParts,
91
+ PopoverPopup,
92
+ PopoverPortal,
93
+ PopoverPositioner,
94
+ PopoverRoot,
95
+ PopoverTitle,
96
+ PopoverTrigger,
97
+ popoverArrowVariants,
98
+ popoverPopupVariants,
99
+ } from "./components/atoms/popover";
100
+ export type {
101
+ SelectGroupLabelProps,
102
+ SelectGroupProps,
103
+ SelectOptionProps,
104
+ SelectPopupProps,
105
+ SelectProps,
106
+ SelectTriggerProps,
107
+ } from "./components/atoms/select";
108
+ export {
109
+ Select,
110
+ SelectGroup,
111
+ SelectGroupLabel,
112
+ SelectOption,
113
+ SelectPopup,
114
+ SelectRoot,
115
+ SelectTrigger,
116
+ selectOptionVariants,
117
+ selectPopupVariants,
118
+ selectTriggerVariants,
119
+ } from "./components/atoms/select";
120
+ export type {
121
+ TooltipArrowProps,
122
+ TooltipPopupProps,
123
+ TooltipPortalProps,
124
+ TooltipPositionerProps,
125
+ TooltipProps,
126
+ TooltipProviderProps,
127
+ TooltipRootProps,
128
+ TooltipTriggerProps,
129
+ } from "./components/atoms/tooltip";
130
+ export {
131
+ Tooltip,
132
+ TooltipArrow,
133
+ TooltipParts,
134
+ TooltipPopup,
135
+ TooltipPortal,
136
+ TooltipPositioner,
137
+ TooltipProvider,
138
+ TooltipRoot,
139
+ TooltipTrigger,
140
+ tooltipArrowVariants,
141
+ tooltipPopupVariants,
142
+ } from "./components/atoms/tooltip";
45
143
  // =============================================================================
46
144
  // Dev Tools
47
145
  // =============================================================================
@@ -0,0 +1,71 @@
1
+ import { tv } from "tailwind-variants";
2
+
3
+ /**
4
+ * Shared form control styles for Input, Select, and similar components.
5
+ *
6
+ * These base styles ensure consistent appearance across all form controls:
7
+ * - Consistent height and padding
8
+ * - Unified focus ring and border treatment
9
+ * - Shared hover/disabled states
10
+ *
11
+ * Based on Figma BaseKit / Interface / Input & Dropdown designs.
12
+ */
13
+
14
+ /**
15
+ * Base styles shared by all form controls (input, select, etc.)
16
+ */
17
+ export const formControlBase = [
18
+ // Layout
19
+ "flex w-full items-center",
20
+ // Typography
21
+ "text-16 font-medium leading-14",
22
+ // Border and radius - uses surface ui radius for theming support
23
+ "border border-solid border-ui-color-border rounded-surface-ui-medium",
24
+ // Background
25
+ "bg-ui-control-background",
26
+ // Transitions
27
+ "transition-[background-color,border-color,box-shadow] duration-150",
28
+ // Focus state
29
+ "outline-none focus-visible:border-border-focus focus-visible:ring-4 focus-visible:ring-ui-color-focus",
30
+ // Hover state (when not focused or disabled)
31
+ "hover:bg-ui-control-background-hover hover:focus-visible:bg-ui-control-background",
32
+ // Disabled state
33
+ "disabled:bg-ui-control-background-disabled disabled:cursor-not-allowed disabled:opacity-50",
34
+ ];
35
+
36
+ /**
37
+ * Size variants shared by form controls
38
+ * Uses spatial tokens for consistent sizing across form controls
39
+ */
40
+ export const formControlSizes = {
41
+ sm: "h-spatial-ui-control-height-small px-spatial-ui-control-padding-x-small py-spatial-ui-control-padding-y-small text-14",
42
+ default:
43
+ "h-spatial-ui-control-height-medium px-spatial-ui-control-padding-x-medium py-spatial-ui-control-padding-y-medium",
44
+ lg: "h-spatial-ui-control-height-large px-spatial-ui-control-padding-x-large py-spatial-ui-control-padding-y-large text-18",
45
+ } as const;
46
+
47
+ /**
48
+ * Error state styles shared by form controls
49
+ */
50
+ export const formControlError = {
51
+ true: "border-ui-error-color focus-visible:border-ui-error-color focus-visible:ring-ui-error-color/20 text-ui-error-color",
52
+ false: "",
53
+ } as const;
54
+
55
+ /**
56
+ * Form control variants using tailwind-variants
57
+ * Can be composed with other variants for specific components
58
+ */
59
+ export const formControlVariants = tv({
60
+ base: formControlBase,
61
+ variants: {
62
+ size: formControlSizes,
63
+ error: formControlError,
64
+ },
65
+ defaultVariants: {
66
+ size: "default",
67
+ error: false,
68
+ },
69
+ });
70
+
71
+ export type FormControlSize = keyof typeof formControlSizes;