@nationaldesignstudio/react 0.1.0 → 0.3.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 (74) hide show
  1. package/dist/component-registry.md +46 -19
  2. package/dist/components/atoms/accordion/accordion.d.ts +7 -7
  3. package/dist/components/atoms/background/background.d.ts +13 -27
  4. package/dist/components/atoms/button/button.d.ts +55 -71
  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/select/select.d.ts +131 -0
  9. package/dist/components/organisms/card/card.d.ts +2 -2
  10. package/dist/components/sections/banner/banner.d.ts +9 -9
  11. package/dist/components/sections/faq-section/faq-section.d.ts +1 -1
  12. package/dist/components/sections/hero/hero.d.ts +115 -18
  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 +9 -9
  16. package/dist/components/sections/two-column-section/two-column-section.d.ts +7 -21
  17. package/dist/index.d.ts +4 -0
  18. package/dist/index.js +11075 -7841
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/form-control.d.ts +105 -0
  21. package/dist/tokens.css +2144 -17341
  22. package/package.json +1 -1
  23. package/src/components/atoms/accordion/accordion.test.tsx +18 -20
  24. package/src/components/atoms/accordion/accordion.tsx +19 -17
  25. package/src/components/atoms/background/background.test.tsx +2 -2
  26. package/src/components/atoms/background/background.tsx +77 -96
  27. package/src/components/atoms/button/button.stories.tsx +42 -0
  28. package/src/components/atoms/button/button.test.tsx +1 -1
  29. package/src/components/atoms/button/button.tsx +38 -103
  30. package/src/components/atoms/button/button.visual.test.tsx +70 -24
  31. package/src/components/atoms/button/icon-button.tsx +80 -188
  32. package/src/components/atoms/input/index.ts +17 -0
  33. package/src/components/atoms/input/input-group.stories.tsx +650 -0
  34. package/src/components/atoms/input/input-group.test.tsx +376 -0
  35. package/src/components/atoms/input/input-group.tsx +384 -0
  36. package/src/components/atoms/input/input.stories.tsx +232 -0
  37. package/src/components/atoms/input/input.test.tsx +183 -0
  38. package/src/components/atoms/input/input.tsx +97 -0
  39. package/src/components/atoms/select/index.ts +18 -0
  40. package/src/components/atoms/select/select.stories.tsx +455 -0
  41. package/src/components/atoms/select/select.tsx +320 -0
  42. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +2 -6
  43. package/src/components/foundation/typography/typography.stories.tsx +401 -0
  44. package/src/components/organisms/card/card.stories.tsx +11 -11
  45. package/src/components/organisms/card/card.test.tsx +5 -3
  46. package/src/components/organisms/card/card.tsx +2 -2
  47. package/src/components/organisms/card/card.visual.test.tsx +6 -6
  48. package/src/components/organisms/navbar/navbar.tsx +2 -2
  49. package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
  50. package/src/components/sections/banner/banner.stories.tsx +5 -1
  51. package/src/components/sections/banner/banner.tsx +10 -10
  52. package/src/components/sections/card-grid/card-grid.tsx +1 -1
  53. package/src/components/sections/faq-section/faq-section.stories.tsx +7 -7
  54. package/src/components/sections/faq-section/faq-section.tsx +5 -5
  55. package/src/components/sections/hero/hero.test.tsx +5 -5
  56. package/src/components/sections/hero/hero.tsx +33 -51
  57. package/src/components/sections/prose/prose.test.tsx +2 -2
  58. package/src/components/sections/prose/prose.tsx +4 -5
  59. package/src/components/sections/river/river.stories.tsx +8 -8
  60. package/src/components/sections/river/river.test.tsx +1 -1
  61. package/src/components/sections/river/river.tsx +2 -4
  62. package/src/components/sections/tout/tout.stories.tsx +31 -7
  63. package/src/components/sections/tout/tout.test.tsx +1 -1
  64. package/src/components/sections/tout/tout.tsx +8 -10
  65. package/src/components/sections/two-column-section/two-column-section.stories.tsx +11 -11
  66. package/src/components/sections/two-column-section/two-column-section.tsx +16 -10
  67. package/src/index.ts +41 -0
  68. package/src/lib/form-control.ts +69 -0
  69. package/src/stories/Introduction.mdx +29 -15
  70. package/src/stories/ThemeProvider.stories.tsx +1 -3
  71. package/src/stories/TokenShowcase.stories.tsx +0 -19
  72. package/src/stories/TokenShowcase.tsx +714 -1366
  73. package/src/styles.css +3 -0
  74. package/src/tests/token-resolution.test.tsx +301 -0
@@ -16,10 +16,8 @@ const toutVariants = tv({
16
16
  "h-[600px] md:h-[750px] lg:h-[900px]",
17
17
  ],
18
18
  variants: {
19
- variant: {
20
- // Default light content styling
19
+ colorScheme: {
21
20
  light: "",
22
- // Dark content styling
23
21
  dark: "",
24
22
  },
25
23
  align: {
@@ -28,7 +26,7 @@ const toutVariants = tv({
28
26
  },
29
27
  },
30
28
  defaultVariants: {
31
- variant: "light",
29
+ colorScheme: "light",
32
30
  align: "left",
33
31
  },
34
32
  });
@@ -39,7 +37,7 @@ const toutVariants = tv({
39
37
  * A full-bleed section with a background image and overlaid content.
40
38
  * Content can be positioned on the left side or centered.
41
39
  *
42
- * Variants:
40
+ * Color schemes:
43
41
  * - light: Light text styling (default)
44
42
  * - dark: Dark text styling
45
43
  *
@@ -105,7 +103,7 @@ export interface ToutProps
105
103
  * headline="Feature Headline"
106
104
  * body="Description of the feature..."
107
105
  * primaryAction={<Button>Primary</Button>}
108
- * secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
106
+ * secondaryAction={<Button variant="outline" colorScheme="light">Secondary</Button>}
109
107
  * backgroundMedia={
110
108
  * <img
111
109
  * src="/background.jpg"
@@ -121,7 +119,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
121
119
  (
122
120
  {
123
121
  className,
124
- variant = "light",
122
+ colorScheme = "light",
125
123
  align = "left",
126
124
  headline,
127
125
  body,
@@ -136,13 +134,13 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
136
134
  ref,
137
135
  ) => {
138
136
  const isCentered = align === "center";
139
- const isDark = variant === "dark";
137
+ const isDark = colorScheme === "dark";
140
138
  const themeStyles = themeToStyleVars(theme);
141
139
 
142
140
  return (
143
141
  <section
144
142
  ref={ref}
145
- className={toutVariants({ variant, align, class: className })}
143
+ className={toutVariants({ colorScheme, align, class: className })}
146
144
  style={{ ...themeStyles, ...style }}
147
145
  {...props}
148
146
  >
@@ -211,7 +209,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
211
209
  >
212
210
  <h2
213
211
  className={cn(
214
- "typography-headline-small",
212
+ "typography-h4",
215
213
  isDark ? "text-gray-100" : "text-gray-900",
216
214
  )}
217
215
  style={{
@@ -8,10 +8,10 @@ const meta: Meta<typeof TwoColumnSection> = {
8
8
  layout: "fullscreen",
9
9
  },
10
10
  argTypes: {
11
- variant: {
11
+ colorScheme: {
12
12
  control: "select",
13
13
  options: ["dark", "light"],
14
- description: "Color variant",
14
+ description: "Color scheme",
15
15
  },
16
16
  title: {
17
17
  control: "text",
@@ -35,7 +35,7 @@ export const Playground: Story = {
35
35
  render: (args) => <TwoColumnSection {...args} />,
36
36
  };
37
37
  Playground.args = {
38
- variant: "dark",
38
+ colorScheme: "dark",
39
39
  title: "US Tech Force",
40
40
  lead: "The US Tech Force is recruiting an elite corps of engineers to build the next generation of government technology. Backed by the White House, Tech Force will tackle the most complex and large-scale civic and defense challenges of our era – from administering critical financial infrastructure at the Treasury Department to advancing cutting-edge programs at the Department of War – and everything in between.",
41
41
  children: (
@@ -73,7 +73,7 @@ Playground.args = {
73
73
  export const DarkVariant: Story = {
74
74
  render: () => (
75
75
  <TwoColumnSection
76
- variant="dark"
76
+ colorScheme="dark"
77
77
  title="US Tech Force"
78
78
  lead="The US Tech Force is recruiting an elite corps of engineers to build the next generation of government technology."
79
79
  >
@@ -95,7 +95,7 @@ export const DarkVariant: Story = {
95
95
  export const LightVariant: Story = {
96
96
  render: () => (
97
97
  <TwoColumnSection
98
- variant="light"
98
+ colorScheme="light"
99
99
  title="US Tech Force"
100
100
  lead="The US Tech Force is recruiting an elite corps of engineers to build the next generation of government technology."
101
101
  >
@@ -118,7 +118,7 @@ export const LightVariant: Story = {
118
118
  export const Desktop: Story = {
119
119
  render: () => (
120
120
  <TwoColumnSection
121
- variant="dark"
121
+ colorScheme="dark"
122
122
  title="US Tech Force"
123
123
  lead="The US Tech Force is recruiting an elite corps of engineers to build the next generation of government technology. Backed by the White House, Tech Force will tackle the most complex and large-scale civic and defense challenges of our era."
124
124
  >
@@ -151,7 +151,7 @@ export const Desktop: Story = {
151
151
  export const Tablet: Story = {
152
152
  render: () => (
153
153
  <TwoColumnSection
154
- variant="dark"
154
+ colorScheme="dark"
155
155
  title="US Tech Force"
156
156
  lead="The US Tech Force is recruiting an elite corps of engineers to build the next generation of government technology. Backed by the White House, Tech Force will tackle the most complex and large-scale civic and defense challenges of our era."
157
157
  >
@@ -184,7 +184,7 @@ export const Tablet: Story = {
184
184
  export const Mobile: Story = {
185
185
  render: () => (
186
186
  <TwoColumnSection
187
- variant="dark"
187
+ colorScheme="dark"
188
188
  title="US Tech Force"
189
189
  lead="The US Tech Force is recruiting an elite corps of engineers to build the next generation of government technology. Backed by the White House, Tech Force will tackle the most complex and large-scale civic and defense challenges of our era."
190
190
  >
@@ -223,7 +223,7 @@ export const Mobile: Story = {
223
223
  */
224
224
  export const WithoutLead: Story = {
225
225
  render: () => (
226
- <TwoColumnSection variant="dark" title="About the Program">
226
+ <TwoColumnSection colorScheme="dark" title="About the Program">
227
227
  <p>
228
228
  Through a two-year program, participants will work in teams reporting
229
229
  directly to agency leadership. In collaboration with leading technology
@@ -245,7 +245,7 @@ export const WithoutLead: Story = {
245
245
  export const RichLeadContent: Story = {
246
246
  render: () => (
247
247
  <TwoColumnSection
248
- variant="dark"
248
+ colorScheme="dark"
249
249
  title="Our Mission"
250
250
  lead={
251
251
  <>
@@ -275,7 +275,7 @@ export const RichLeadContent: Story = {
275
275
  export const ShortContent: Story = {
276
276
  render: () => (
277
277
  <TwoColumnSection
278
- variant="dark"
278
+ colorScheme="dark"
279
279
  title="Join Us"
280
280
  lead="Build the future of American government technology."
281
281
  >
@@ -23,19 +23,17 @@ const twoColumnSectionVariants = tv({
23
23
  "lg:px-spacing-72 lg:pt-spacing-72 lg:pb-spacing-112",
24
24
  ],
25
25
  variants: {
26
- variant: {
26
+ colorScheme: {
27
27
  dark: "bg-gray-1200",
28
28
  light: "bg-white",
29
29
  },
30
30
  layout: {
31
- /** Default 24-column grid with asymmetric split (title: 9, content: 15) */
32
31
  asymmetric: "",
33
- /** Equal 2-column layout at md+ breakpoints */
34
32
  equal: "",
35
33
  },
36
34
  },
37
35
  defaultVariants: {
38
- variant: "dark",
36
+ colorScheme: "dark",
39
37
  layout: "asymmetric",
40
38
  },
41
39
  });
@@ -86,14 +84,22 @@ export interface TwoColumnSectionProps
86
84
  */
87
85
  const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
88
86
  (
89
- { className, variant = "dark", layout, title, lead, children, ...props },
87
+ {
88
+ className,
89
+ colorScheme = "dark",
90
+ layout,
91
+ title,
92
+ lead,
93
+ children,
94
+ ...props
95
+ },
90
96
  ref,
91
97
  ) => {
92
98
  return (
93
99
  <section
94
100
  ref={ref}
95
101
  className={twoColumnSectionVariants({
96
- variant,
102
+ colorScheme,
97
103
  layout,
98
104
  class: className,
99
105
  })}
@@ -103,7 +109,7 @@ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
103
109
  <div
104
110
  className={cn(
105
111
  "border-t pt-spacing-36",
106
- variant === "dark" ? "border-gray-700" : "border-gray-300",
112
+ colorScheme === "dark" ? "border-gray-700" : "border-gray-300",
107
113
  // Grid layout - uses primitive spacing tokens
108
114
  "grid grid-cols-1 gap-spacing-56",
109
115
  layout === "equal"
@@ -115,7 +121,7 @@ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
115
121
  <h2
116
122
  className={cn(
117
123
  "typography-subheading-medium",
118
- variant === "dark" ? "text-gray-100" : "text-gray-900",
124
+ colorScheme === "dark" ? "text-gray-100" : "text-gray-900",
119
125
  // Column span based on layout
120
126
  layout !== "equal" && "lg:col-span-9",
121
127
  )}
@@ -135,7 +141,7 @@ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
135
141
  <div
136
142
  className={cn(
137
143
  "typography-body-large",
138
- variant === "dark" ? "text-gray-100" : "text-gray-900",
144
+ colorScheme === "dark" ? "text-gray-100" : "text-gray-900",
139
145
  )}
140
146
  >
141
147
  {typeof lead === "string" ? <p>{lead}</p> : lead}
@@ -146,7 +152,7 @@ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
146
152
  <div
147
153
  className={cn(
148
154
  "typography-body-medium flex flex-col gap-[1em]",
149
- variant === "dark" ? "text-gray-400" : "text-gray-600",
155
+ colorScheme === "dark" ? "text-gray-400" : "text-gray-600",
150
156
  )}
151
157
  >
152
158
  {children}
package/src/index.ts CHANGED
@@ -35,6 +35,27 @@ export {
35
35
  IconButton,
36
36
  iconButtonVariants,
37
37
  } from "./components/atoms/button";
38
+ export type {
39
+ InputGroupAddonProps,
40
+ InputGroupButtonProps,
41
+ InputGroupInputProps,
42
+ InputGroupProps,
43
+ InputGroupTextareaProps,
44
+ InputGroupTextProps,
45
+ InputProps,
46
+ } from "./components/atoms/input";
47
+ export {
48
+ Input,
49
+ InputGroup,
50
+ InputGroupAddon,
51
+ InputGroupButton,
52
+ InputGroupInput,
53
+ InputGroupText,
54
+ InputGroupTextarea,
55
+ inputGroupAddonVariants,
56
+ inputGroupVariants,
57
+ inputVariants,
58
+ } from "./components/atoms/input";
38
59
  export type { NdstudioFooterProps } from "./components/atoms/ndstudio-footer";
39
60
  export { NdstudioFooter } from "./components/atoms/ndstudio-footer";
40
61
  export type { PagerControlProps } from "./components/atoms/pager-control";
@@ -42,6 +63,26 @@ export {
42
63
  PagerControl,
43
64
  pagerControlVariants,
44
65
  } from "./components/atoms/pager-control";
66
+ export type {
67
+ SelectGroupLabelProps,
68
+ SelectGroupProps,
69
+ SelectOptionProps,
70
+ SelectPopupProps,
71
+ SelectProps,
72
+ SelectTriggerProps,
73
+ } from "./components/atoms/select";
74
+ export {
75
+ Select,
76
+ SelectGroup,
77
+ SelectGroupLabel,
78
+ SelectOption,
79
+ SelectPopup,
80
+ SelectRoot,
81
+ SelectTrigger,
82
+ selectOptionVariants,
83
+ selectPopupVariants,
84
+ selectTriggerVariants,
85
+ } from "./components/atoms/select";
45
86
  // =============================================================================
46
87
  // Dev Tools
47
88
  // =============================================================================
@@ -0,0 +1,69 @@
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
23
+ "border border-solid border-ui-color-border rounded-radius-6",
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-ui-accent-base 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
+ */
39
+ export const formControlSizes = {
40
+ sm: "h-36 px-12 py-8 text-14",
41
+ default: "h-48 px-16 py-10",
42
+ lg: "h-56 px-20 py-12 text-18",
43
+ } as const;
44
+
45
+ /**
46
+ * Error state styles shared by form controls
47
+ */
48
+ export const formControlError = {
49
+ true: "border-ui-error-color focus-visible:border-ui-error-color focus-visible:ring-ui-error-color/20 text-ui-error-color",
50
+ false: "",
51
+ } as const;
52
+
53
+ /**
54
+ * Form control variants using tailwind-variants
55
+ * Can be composed with other variants for specific components
56
+ */
57
+ export const formControlVariants = tv({
58
+ base: formControlBase,
59
+ variants: {
60
+ size: formControlSizes,
61
+ error: formControlError,
62
+ },
63
+ defaultVariants: {
64
+ size: "default",
65
+ error: false,
66
+ },
67
+ });
68
+
69
+ export type FormControlSize = keyof typeof formControlSizes;
@@ -40,33 +40,47 @@ function App() {
40
40
 
41
41
  The design system provides pre-built typography classes for consistent text styling. Typography utilities use the `typography-` prefix to avoid conflicts with Tailwind's `text-*` color utilities.
42
42
 
43
- ### Product Typography
43
+ ### Headings
44
44
 
45
- For app UIs, dashboards, and interfaces:
45
+ For headlines and titles:
46
46
 
47
47
  ```html
48
- <h1 class="typography-product-title-large">Page Title</h1>
49
- <h2 class="typography-product-headline-medium">Section Header</h2>
50
- <p class="typography-product-body-medium">Body text content...</p>
51
- <span class="typography-product-caption-small">Caption text</span>
52
- <button class="typography-product-button-medium">Click me</button>
48
+ <h1 class="typography-h1-display">Display Title</h1>
49
+ <h1 class="typography-h1">Page Title</h1>
50
+ <h2 class="typography-h2">Section Header</h2>
51
+ <h3 class="typography-h3">Subsection Header</h3>
52
+ <h4 class="typography-h4">Smaller Header</h4>
53
+ <h5 class="typography-h5">Smallest Header</h5>
53
54
  ```
54
55
 
55
- Available styles: `typography-product-{category}-{size}` where category is: `title`, `headline`, `body`, `label`, `caption`, `button`, `input`, `link`, `navigation`, `overline`, `tabbar`
56
+ ### Body Text
56
57
 
57
- ### Brand Typography
58
+ For body content:
58
59
 
59
- For landing pages and promotional content:
60
+ ```html
61
+ <p class="typography-body-large">Large body text...</p>
62
+ <p class="typography-body-medium">Standard body text...</p>
63
+ <p class="typography-body-small">Small body text...</p>
64
+ ```
65
+
66
+ ### Supporting Text
67
+
68
+ For captions and labels:
60
69
 
61
70
  ```html
62
- <h1 class="typography-brand-medium-display-hero">Hero</h1>
63
- <h2 class="typography-brand-medium-headline-large">Big Headline</h2>
64
- <p class="typography-brand-medium-body-large">Marketing copy...</p>
71
+ <span class="typography-caption">Caption text</span>
72
+ <span class="typography-overline">OVERLINE TEXT</span>
65
73
  ```
66
74
 
67
- Available namespaces: `brand-large` (desktop), `brand-medium` (tablet), `brand-small` (mobile)
75
+ ### UI Typography
68
76
 
69
- Available styles: `typography-brand-{breakpoint}-{category}-{size}` where category is: `display`, `headline`, `subheading`, `body`, `label`, `caption`, `button`, `link`
77
+ For buttons and interactive elements:
78
+
79
+ ```html
80
+ <button class="typography-ui-button-large">Large Button</button>
81
+ <button class="typography-ui-button-medium">Medium Button</button>
82
+ <button class="typography-ui-button-small">Small Button</button>
83
+ ```
70
84
 
71
85
  ## Color Tokens
72
86
 
@@ -21,9 +21,7 @@ function ThemeDemo({ title }: { title?: string }) {
21
21
  <div style={cssVars} className="p-6">
22
22
  <div className="bg-bg-page p-6 rounded-lg min-h-[300px]">
23
23
  {title && (
24
- <h2 className="typography-subheading-small text-text-primary mb-4">
25
- {title}
26
- </h2>
24
+ <h2 className="typography-h5 text-text-primary mb-4">{title}</h2>
27
25
  )}
28
26
 
29
27
  {/* Typography showcase */}
@@ -3,7 +3,6 @@ import type { ComponentProps } from "react";
3
3
  import {
4
4
  ColorTokens,
5
5
  ResponsiveTypography,
6
- SemanticSpacingTokens,
7
6
  SpacingTokens,
8
7
  TokenShowcase,
9
8
  TypographyTokens,
@@ -57,24 +56,6 @@ export const Typography: Story = {
57
56
  ),
58
57
  };
59
58
 
60
- export const SemanticSpacing: Story = {
61
- render: () => (
62
- <div className="p-8 min-h-screen">
63
- <div className="max-w-4xl mx-auto">
64
- <SemanticSpacingTokens />
65
- </div>
66
- </div>
67
- ),
68
- parameters: {
69
- docs: {
70
- description: {
71
- story:
72
- "Purpose-driven spacing tokens for components (buttons, cards, forms), layouts (hero, sections, containers, grids), and content stacks (vertical rhythm between elements).",
73
- },
74
- },
75
- },
76
- };
77
-
78
59
  export const Breakpoints: Story = {
79
60
  render: () => (
80
61
  <div className="p-8">