@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
@@ -26,13 +26,13 @@ const bannerVariants = tv({
26
26
  "lg:px-spacing-72 lg:pb-spacing-72 lg:pt-0",
27
27
  ],
28
28
  variants: {
29
- variant: {
29
+ colorScheme: {
30
30
  light: "bg-gray-50",
31
31
  dark: "bg-gray-1200",
32
32
  },
33
33
  },
34
34
  defaultVariants: {
35
- variant: "light",
35
+ colorScheme: "light",
36
36
  },
37
37
  });
38
38
 
@@ -74,9 +74,9 @@ export interface BannerProps
74
74
  * />
75
75
  * </div>
76
76
  *
77
- * // Dark variant
77
+ * // Dark colorScheme
78
78
  * <Banner
79
- * variant="dark"
79
+ * colorScheme="dark"
80
80
  * heading="Still Have Questions?"
81
81
  * description="Contact us at support@example.com"
82
82
  * action={<Button variant="secondary">Email Us</Button>}
@@ -84,19 +84,19 @@ export interface BannerProps
84
84
  * ```
85
85
  */
86
86
  const Banner = React.forwardRef<HTMLElement, BannerProps>(
87
- ({ className, variant, heading, description, action, ...props }, ref) => {
87
+ ({ className, colorScheme, heading, description, action, ...props }, ref) => {
88
88
  return (
89
89
  <section
90
90
  ref={ref}
91
- className={bannerVariants({ variant, class: className })}
91
+ className={bannerVariants({ colorScheme, class: className })}
92
92
  {...props}
93
93
  >
94
- {/* Inner container with border-top for dark variant */}
94
+ {/* Inner container with border-top for dark colorScheme */}
95
95
  <div
96
96
  className={cn(
97
97
  // Uses primitive spacing tokens
98
98
  "flex flex-col md:flex-row gap-spacing-20 items-start md:items-center md:justify-between",
99
- variant === "dark" && "border-t border-gray-700 py-spacing-36",
99
+ colorScheme === "dark" && "border-t border-gray-700 py-spacing-36",
100
100
  )}
101
101
  >
102
102
  {/* Copy section */}
@@ -104,7 +104,7 @@ const Banner = React.forwardRef<HTMLElement, BannerProps>(
104
104
  <h2
105
105
  className={cn(
106
106
  "typography-subheading-small",
107
- variant === "dark" ? "text-gray-100" : "text-gray-900",
107
+ colorScheme === "dark" ? "text-gray-100" : "text-gray-900",
108
108
  )}
109
109
  >
110
110
  {heading}
@@ -112,7 +112,7 @@ const Banner = React.forwardRef<HTMLElement, BannerProps>(
112
112
  <p
113
113
  className={cn(
114
114
  "typography-body-small",
115
- variant === "dark" ? "text-gray-500" : "text-gray-800",
115
+ colorScheme === "dark" ? "text-gray-500" : "text-gray-800",
116
116
  )}
117
117
  >
118
118
  {description}
@@ -83,7 +83,7 @@ const CardGrid = React.forwardRef<HTMLElement, CardGridProps>(
83
83
  <h2
84
84
  className={cn(
85
85
  "col-full",
86
- "typography-headline-small text-gray-900",
86
+ "typography-h4 text-gray-900",
87
87
  // Gap after title: mobile default, md: 56px, lg: 64px
88
88
  "mb-spacing-36 md:mb-spacing-56 lg:mb-spacing-64",
89
89
  )}
@@ -9,10 +9,10 @@ const meta = {
9
9
  layout: "fullscreen",
10
10
  },
11
11
  argTypes: {
12
- variant: {
12
+ colorScheme: {
13
13
  control: "select",
14
14
  options: ["dark", "light"],
15
- description: "Color variant",
15
+ description: "Color scheme",
16
16
  },
17
17
  title: {
18
18
  control: "text",
@@ -61,7 +61,7 @@ export const Playground: Story = {
61
61
  ),
62
62
  };
63
63
  Playground.args = {
64
- variant: "dark",
64
+ colorScheme: "light",
65
65
  title: "Frequently Asked Questions",
66
66
  };
67
67
 
@@ -110,12 +110,12 @@ export const Default: Story = {
110
110
  // =============================================================================
111
111
 
112
112
  /**
113
- * Dark variant (default) - dark background with light text
113
+ * Dark variant - dark background with light text
114
114
  */
115
115
  export const DarkVariant: Story = {
116
116
  render: () => (
117
- <FaqSection variant="dark">
118
- <Accordion defaultExpanded="faq-1">
117
+ <FaqSection colorScheme="dark">
118
+ <Accordion colorScheme="dark" defaultExpanded="faq-1">
119
119
  <AccordionItem
120
120
  id="faq-1"
121
121
  title="What is the US Tech Force (Tech Force)?"
@@ -147,7 +147,7 @@ export const DarkVariant: Story = {
147
147
  */
148
148
  export const LightVariant: Story = {
149
149
  render: () => (
150
- <FaqSection variant="light">
150
+ <FaqSection colorScheme="light">
151
151
  <Accordion defaultExpanded="faq-1">
152
152
  <AccordionItem
153
153
  id="faq-1"
@@ -41,7 +41,7 @@ export interface FaqSectionProps
41
41
  * </FaqSection>
42
42
  *
43
43
  * // With custom title
44
- * <FaqSection title="Common Questions" variant="light">
44
+ * <FaqSection title="Common Questions" colorScheme="light">
45
45
  * ...
46
46
  * </FaqSection>
47
47
  * ```
@@ -50,7 +50,7 @@ const FaqSection = React.forwardRef<HTMLElement, FaqSectionProps>(
50
50
  (
51
51
  {
52
52
  className,
53
- variant = "dark",
53
+ colorScheme = "light",
54
54
  title = "Frequently Asked Questions",
55
55
  children,
56
56
  layout,
@@ -61,14 +61,14 @@ const FaqSection = React.forwardRef<HTMLElement, FaqSectionProps>(
61
61
  return (
62
62
  <TwoColumnSection
63
63
  ref={ref}
64
- variant={variant}
64
+ colorScheme={colorScheme}
65
65
  layout={layout ?? undefined}
66
66
  title={title}
67
67
  className={cn(
68
68
  // Override title typography to be larger
69
- "[&_h2]:typography-subheading-large",
69
+ "[&_h2]:typography-h4",
70
70
  // Accordion typography overrides
71
- "[&_button]:typography-body-large [&_button]:md:typography-subheading-small",
71
+ "[&_button]:typography-body-large [&_button]:md:typography-h5",
72
72
  "[&_[data-accordion-panel]]:typography-body-medium [&_[data-accordion-panel]]:md:typography-body-large",
73
73
  className,
74
74
  )}
@@ -67,8 +67,8 @@ describe("Hero", () => {
67
67
  render(<Hero title="Default" data-testid="hero" />);
68
68
 
69
69
  const hero = page.getByTestId("hero");
70
- // Uses semantic token bg-bg-overlay for default background
71
- await expect.element(hero).toHaveClass(/bg-bg-overlay/);
70
+ // Uses semantic token bg-bg-page for default dark colorScheme
71
+ await expect.element(hero).toHaveClass(/bg-bg-page/);
72
72
  });
73
73
 
74
74
  test("applies minimum height for viewport coverage", async () => {
@@ -115,7 +115,7 @@ describe("Hero", () => {
115
115
  render(<Hero title="A1 Hero" variant="A1" data-testid="hero" />);
116
116
 
117
117
  const hero = page.getByTestId("hero");
118
- await expect.element(hero).toHaveClass(/bg-bg-overlay/);
118
+ await expect.element(hero).toHaveClass(/bg-bg-page/);
119
119
  await expect.element(hero).toHaveClass(/min-h-\[80vh\]/);
120
120
  });
121
121
 
@@ -123,7 +123,7 @@ describe("Hero", () => {
123
123
  render(<Hero title="A2 Hero" variant="A2" data-testid="hero" />);
124
124
 
125
125
  const hero = page.getByTestId("hero");
126
- await expect.element(hero).toHaveClass(/bg-bg-overlay/);
126
+ await expect.element(hero).toHaveClass(/bg-bg-page/);
127
127
  await expect.element(hero).toHaveClass(/min-h-\[80vh\]/);
128
128
  });
129
129
 
@@ -131,7 +131,7 @@ describe("Hero", () => {
131
131
  render(<Hero title="A3 Hero" variant="A3" data-testid="hero" />);
132
132
 
133
133
  const hero = page.getByTestId("hero");
134
- await expect.element(hero).toHaveClass(/bg-bg-overlay/);
134
+ await expect.element(hero).toHaveClass(/bg-bg-page/);
135
135
  await expect.element(hero).toHaveClass(/min-h-\[80vh\]/);
136
136
  });
137
137
  });
@@ -18,56 +18,54 @@ export {
18
18
  type BackgroundVideoProps as HeroBackgroundVideoProps,
19
19
  } from "@/components/atoms/background";
20
20
 
21
- // =============================================================================
22
- // Hero Variants
23
- // =============================================================================
21
+ const DEFAULT_TITLE_TYPOGRAPHY =
22
+ "text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 lg:text-192 lg:leading-192 lg:tracking-192 font-medium";
24
23
 
25
- /**
26
- * Hero variants based on Figma BaseKit / Heros
27
- *
28
- * Slots:
29
- * - root: The outer container
30
- * - top: Full-width slot at top for banners/nav (no padding)
31
- * - content: Padded content area with alignment
32
- *
33
- * Variants:
34
- * - A1: Content aligned at bottom (default)
35
- * - A2: Content aligned at top
36
- * - A3: Content centered
37
- */
38
24
  const heroVariants = tv({
39
25
  slots: {
40
26
  root: "relative flex w-full flex-col overflow-hidden",
41
27
  top: "relative z-10 w-full",
42
28
  content: [
43
29
  "relative z-10 mx-auto flex w-full max-w-screen-xl flex-1 flex-col",
44
- // Mobile padding
45
30
  "p-spacing-20",
46
- // Tablet padding
47
31
  "md:p-spacing-56",
48
32
  ],
33
+ title: DEFAULT_TITLE_TYPOGRAPHY,
49
34
  },
50
35
  variants: {
51
36
  variant: {
52
- // A1: Content at bottom
53
37
  A1: {
54
38
  root: "min-h-[80vh]",
55
39
  content: ["justify-end", "lg:p-spacing-72"],
56
40
  },
57
- // A2: Content at top
58
41
  A2: {
59
42
  root: "min-h-[80vh]",
60
43
  content: ["justify-start", "lg:p-spacing-64"],
61
44
  },
62
- // A3: Content centered
63
45
  A3: {
64
46
  root: "min-h-[80vh]",
65
47
  content: ["items-center justify-center", "lg:p-spacing-64"],
66
48
  },
67
49
  },
50
+ colorScheme: {
51
+ dark: {
52
+ root: "bg-bg-page",
53
+ title: "text-text-primary",
54
+ },
55
+ light: {
56
+ root: "bg-gray-900",
57
+ title: "text-text-inverted",
58
+ },
59
+ },
60
+ hasBackground: {
61
+ true: {
62
+ root: "bg-transparent",
63
+ },
64
+ },
68
65
  },
69
66
  defaultVariants: {
70
67
  variant: "A1",
68
+ colorScheme: "dark",
71
69
  },
72
70
  });
73
71
 
@@ -141,6 +139,12 @@ export interface HeroProps
141
139
  * Default: "text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 lg:text-192 lg:leading-192 lg:tracking-192"
142
140
  */
143
141
  titleClassName?: string;
142
+ /**
143
+ * Color scheme for text content.
144
+ * - dark: Dark text for use on light backgrounds (default)
145
+ * - light: Light text for use on dark backgrounds
146
+ */
147
+ colorScheme?: "dark" | "light";
144
148
  /**
145
149
  * Content for the top slot (full-width, no padding).
146
150
  * Use for USGovBanner, Navigation, etc.
@@ -172,13 +176,6 @@ export interface HeroProps
172
176
  theme?: ComponentTheme;
173
177
  }
174
178
 
175
- /**
176
- * Default responsive typography for hero title using primitive tokens
177
- * Mobile: 64px, Tablet: 128px, Desktop: 192px
178
- */
179
- const DEFAULT_TITLE_TYPOGRAPHY =
180
- "text-64 leading-64 tracking-64 md:text-128 md:leading-128 md:tracking-128 lg:text-192 lg:leading-192 lg:tracking-192 font-medium";
181
-
182
179
  /**
183
180
  * Checks if the background prop is a color string
184
181
  */
@@ -238,6 +235,7 @@ const Hero = React.forwardRef<HTMLElement, HeroProps>(
238
235
  children,
239
236
  title,
240
237
  titleClassName,
238
+ colorScheme = "dark",
241
239
  top,
242
240
  variant,
243
241
  background,
@@ -250,9 +248,13 @@ const Hero = React.forwardRef<HTMLElement, HeroProps>(
250
248
  },
251
249
  ref,
252
250
  ) => {
253
- const styles = heroVariants({ variant });
254
251
  const isColor = isColorString(background);
255
252
  const hasMediaBackground = background && !isColor;
253
+ const styles = heroVariants({
254
+ variant,
255
+ colorScheme,
256
+ hasBackground: !!background,
257
+ });
256
258
  const themeStyles = themeToStyleVars(theme);
257
259
  const combinedStyles = {
258
260
  ...themeStyles,
@@ -264,22 +266,14 @@ const Hero = React.forwardRef<HTMLElement, HeroProps>(
264
266
  return (
265
267
  <section
266
268
  ref={ref}
267
- className={styles.root({
268
- class: [
269
- // Default background color when no background is provided
270
- !background && "bg-bg-overlay",
271
- className,
272
- ],
273
- })}
269
+ className={styles.root({ class: className })}
274
270
  style={
275
271
  Object.keys(combinedStyles).length > 0 ? combinedStyles : undefined
276
272
  }
277
273
  {...props}
278
274
  >
279
- {/* Background slot (image, video, or stream) */}
280
275
  {hasMediaBackground && background}
281
276
 
282
- {/* Overlay */}
283
277
  {hasMediaBackground && overlayOpacity > 0 && (
284
278
  <BackgroundOverlay
285
279
  opacity={overlayOpacity}
@@ -292,23 +286,11 @@ const Hero = React.forwardRef<HTMLElement, HeroProps>(
292
286
  />
293
287
  )}
294
288
 
295
- {/* Top slot - full width, no padding (for banners, nav) */}
296
289
  {top && <div className={styles.top()}>{top}</div>}
297
290
 
298
- {/* Content slot - padded area with alignment */}
299
291
  <div className={styles.content()}>
300
- {/* Title - convenience prop, renders h1 with default typography */}
301
292
  {title && (
302
- <h1
303
- className={[
304
- titleClassName || DEFAULT_TITLE_TYPOGRAPHY,
305
- "text-text-inverted",
306
- ]
307
- .filter(Boolean)
308
- .join(" ")}
309
- >
310
- {title}
311
- </h1>
293
+ <h1 className={styles.title({ class: titleClassName })}>{title}</h1>
312
294
  )}
313
295
 
314
296
  {/* Children - always render if provided */}
@@ -131,7 +131,7 @@ describe("Prose", () => {
131
131
  );
132
132
 
133
133
  const heading = page.getByRole("heading", { name: "H2 Test" });
134
- await expect.element(heading).toHaveClass(/typography-headline-medium/);
134
+ await expect.element(heading).toHaveClass(/typography-h3/);
135
135
  });
136
136
 
137
137
  test("h3 heading has correct typography class", async () => {
@@ -144,7 +144,7 @@ describe("Prose", () => {
144
144
  );
145
145
 
146
146
  const heading = page.getByRole("heading", { name: "H3 Test" });
147
- await expect.element(heading).toHaveClass(/typography-headline-small/);
147
+ await expect.element(heading).toHaveClass(/typography-h4/);
148
148
  });
149
149
  });
150
150
 
@@ -52,15 +52,14 @@ export interface ProseSectionProps extends React.HTMLAttributes<HTMLElement> {
52
52
  * A section within Prose content, containing a heading and body text.
53
53
  *
54
54
  * Responsive typography:
55
- * - h2: Uses typography-headline-medium (42px mobile → 56px tablet → 72px desktop)
56
- * - h3: Uses typography-headline-small (32px mobile → 42px tablet → 56px desktop)
57
- * - Body: Uses typography-body-medium (16px mobile/tablet → 18px desktop)
55
+ * - h2: Uses typography-h3
56
+ * - h3: Uses typography-h4
57
+ * - Body: Uses typography-body-medium
58
58
  */
59
59
  const ProseSection = React.forwardRef<HTMLElement, ProseSectionProps>(
60
60
  ({ className, heading, as = "h2", children, ...props }, ref) => {
61
61
  const Heading = as;
62
- const headingClass =
63
- as === "h2" ? "typography-headline-medium" : "typography-headline-small";
62
+ const headingClass = as === "h2" ? "typography-h3" : "typography-h4";
64
63
 
65
64
  return (
66
65
  <section
@@ -51,7 +51,7 @@ Playground.args = {
51
51
  headline: "Feature Headline",
52
52
  body: "Use rivers to present content with supporting media. They work great for feature highlights, product showcases, and storytelling sections.",
53
53
  primaryAction: <Button>Primary Action</Button>,
54
- secondaryAction: <Button variant="charcoalOutline">Secondary</Button>,
54
+ secondaryAction: <Button variant="outline">Secondary</Button>,
55
55
  media: <PlaceholderImage />,
56
56
  };
57
57
 
@@ -69,7 +69,7 @@ export const VariantA: Story = {
69
69
  headline="Text Left, Media Right"
70
70
  body="Variant A places the text content on the left (9 columns) and media on the right (15 columns) on desktop viewports. On mobile and tablet, they stack vertically."
71
71
  primaryAction={<Button>Primary Action</Button>}
72
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
72
+ secondaryAction={<Button variant="outline">Secondary</Button>}
73
73
  media={<PlaceholderImage />}
74
74
  />
75
75
  ),
@@ -85,7 +85,7 @@ export const VariantB: Story = {
85
85
  headline="Media Left, Text Right"
86
86
  body="Variant B places the media on the left (15 columns) and text content on the right (9 columns) on desktop viewports. On mobile and tablet, they stack vertically with text first."
87
87
  primaryAction={<Button>Primary Action</Button>}
88
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
88
+ secondaryAction={<Button variant="outline">Secondary</Button>}
89
89
  media={<PlaceholderImage />}
90
90
  />
91
91
  ),
@@ -102,7 +102,7 @@ export const VariantADesktop: Story = {
102
102
  headline="Desktop View"
103
103
  body="On desktop (lg, 1440px), the content spans 9 columns and the media spans 15 columns in a horizontal layout."
104
104
  primaryAction={<Button>Primary</Button>}
105
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
105
+ secondaryAction={<Button variant="outline">Secondary</Button>}
106
106
  media={<PlaceholderImage />}
107
107
  />
108
108
  ),
@@ -118,7 +118,7 @@ export const VariantATablet: Story = {
118
118
  headline="Tablet View"
119
119
  body="On tablet (md, 768px), the content and media stack vertically with the text above the media."
120
120
  primaryAction={<Button>Primary</Button>}
121
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
121
+ secondaryAction={<Button variant="outline">Secondary</Button>}
122
122
  media={<PlaceholderImage />}
123
123
  />
124
124
  ),
@@ -135,7 +135,7 @@ export const VariantAMobile: Story = {
135
135
  body="On mobile (sm, 320px), content is stacked with smaller button sizing."
136
136
  primaryAction={<Button size="sm">Primary</Button>}
137
137
  secondaryAction={
138
- <Button size="sm" variant="charcoalOutline">
138
+ <Button size="sm" variant="outline">
139
139
  Secondary
140
140
  </Button>
141
141
  }
@@ -154,7 +154,7 @@ export const VariantBDesktop: Story = {
154
154
  headline="Desktop View - Reversed"
155
155
  body="Variant B reverses the layout, placing media on the left and content on the right."
156
156
  primaryAction={<Button>Primary</Button>}
157
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
157
+ secondaryAction={<Button variant="outline">Secondary</Button>}
158
158
  media={<PlaceholderImage />}
159
159
  />
160
160
  ),
@@ -224,7 +224,7 @@ export const WithImage: Story = {
224
224
  headline="Real World Example"
225
225
  body="Rivers work great with actual images, videos, or any media content. The media column is designed to accommodate various aspect ratios."
226
226
  primaryAction={<Button>Get Started</Button>}
227
- secondaryAction={<Button variant="charcoalOutline">Learn More</Button>}
227
+ secondaryAction={<Button variant="outline">Learn More</Button>}
228
228
  media={
229
229
  <img
230
230
  src="https://images.unsplash.com/photo-1551434678-e076c223a692?w=800&h=600&fit=crop"
@@ -246,7 +246,7 @@ describe("River", () => {
246
246
  );
247
247
 
248
248
  const headline = page.getByRole("heading", { level: 2 });
249
- await expect.element(headline).toHaveClass(/typography-headline-small/);
249
+ await expect.element(headline).toHaveClass(/typography-h4/);
250
250
  await expect.element(headline).toHaveClass(/text-gray-900/);
251
251
  });
252
252
 
@@ -77,7 +77,7 @@ export interface RiverProps
77
77
  * headline="Feature Headline"
78
78
  * body="Description of the feature..."
79
79
  * primaryAction={<Button>Primary</Button>}
80
- * secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
80
+ * secondaryAction={<Button variant="outline">Secondary</Button>}
81
81
  * media={<img src="..." alt="Feature" />}
82
82
  * />
83
83
  * </div>
@@ -107,9 +107,7 @@ const River = React.forwardRef<HTMLElement, RiverProps>(
107
107
  >
108
108
  {/* Text content with 16px gap - uses primitive spacing tokens */}
109
109
  <div className="flex flex-col gap-spacing-16">
110
- <h2 className="typography-headline-small text-gray-900">
111
- {headline}
112
- </h2>
110
+ <h2 className="typography-h4 text-gray-900">{headline}</h2>
113
111
  <p className="typography-body-small text-gray-800">{body}</p>
114
112
  </div>
115
113
 
@@ -43,7 +43,11 @@ 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: <Button variant="charcoalOutline">Secondary</Button>,
46
+ secondaryAction: (
47
+ <Button variant="outline" colorScheme="light">
48
+ Secondary
49
+ </Button>
50
+ ),
47
51
  backgroundMedia: <PlaceholderBackground />,
48
52
  };
49
53
 
@@ -57,7 +61,11 @@ export const Desktop: Story = {
57
61
  headline="Brand-Large/Headline/Small"
58
62
  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."
59
63
  primaryAction={<Button>Primary</Button>}
60
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
64
+ secondaryAction={
65
+ <Button variant="outline" colorScheme="light">
66
+ Secondary
67
+ </Button>
68
+ }
61
69
  backgroundMedia={<PlaceholderBackground />}
62
70
  />
63
71
  ),
@@ -72,7 +80,11 @@ export const Tablet: Story = {
72
80
  headline="Brand-Large/Headline/Small"
73
81
  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."
74
82
  primaryAction={<Button>Primary</Button>}
75
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
83
+ secondaryAction={
84
+ <Button variant="outline" colorScheme="light">
85
+ Secondary
86
+ </Button>
87
+ }
76
88
  backgroundMedia={<PlaceholderBackground />}
77
89
  />
78
90
  ),
@@ -88,7 +100,7 @@ export const Mobile: Story = {
88
100
  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."
89
101
  primaryAction={<Button size="sm">Primary</Button>}
90
102
  secondaryAction={
91
- <Button size="sm" variant="charcoalOutline">
103
+ <Button size="sm" variant="outline" colorScheme="light">
92
104
  Secondary
93
105
  </Button>
94
106
  }
@@ -113,7 +125,11 @@ export const WithImage: Story = {
113
125
  headline="Work with Purpose"
114
126
  body="Join a team that's building the future of government services. We're looking for passionate individuals who want to make a difference."
115
127
  primaryAction={<Button>View Careers</Button>}
116
- secondaryAction={<Button variant="charcoalOutline">Learn More</Button>}
128
+ secondaryAction={
129
+ <Button variant="outline" colorScheme="light">
130
+ Learn More
131
+ </Button>
132
+ }
117
133
  backgroundMedia={<ImageBackground />}
118
134
  />
119
135
  ),
@@ -142,7 +158,11 @@ export const WithVideoPlaceholder: Story = {
142
158
  headline="Experience Innovation"
143
159
  body="See how modern technology is transforming the way government serves its citizens."
144
160
  primaryAction={<Button>Watch Video</Button>}
145
- secondaryAction={<Button variant="charcoalOutline">Learn More</Button>}
161
+ secondaryAction={
162
+ <Button variant="outline" colorScheme="light">
163
+ Learn More
164
+ </Button>
165
+ }
146
166
  backgroundMedia={
147
167
  <div className="absolute inset-0 bg-gray-800 flex items-center justify-center">
148
168
  <span className="text-gray-400 typography-body-small">
@@ -163,7 +183,11 @@ export const WithNdstudioFooter: Story = {
163
183
  headline="Work with Purpose"
164
184
  body="Join a team that's building the future of government services. We're looking for passionate individuals who want to make a difference."
165
185
  primaryAction={<Button>View Careers</Button>}
166
- secondaryAction={<Button variant="charcoalOutline">Learn More</Button>}
186
+ secondaryAction={
187
+ <Button variant="outline" colorScheme="light">
188
+ Learn More
189
+ </Button>
190
+ }
167
191
  backgroundMedia={<ImageBackground />}
168
192
  footer={<NdstudioFooter />}
169
193
  />
@@ -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