@nationaldesignstudio/react 0.0.14 → 0.0.16

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 (166) hide show
  1. package/dist/tailwind.css +15 -1
  2. package/dist/tokens.css +45 -60
  3. package/package.json +5 -10
  4. package/src/App.css +0 -0
  5. package/src/App.tsx +7 -0
  6. package/src/assets/fonts/PPNeueMontreal-Variable.woff2 +0 -0
  7. package/src/assets/react.svg +1 -0
  8. package/src/components/atoms/accordion/accordion.stories.tsx +228 -0
  9. package/src/components/atoms/accordion/accordion.tsx +219 -0
  10. package/src/components/atoms/accordion/index.ts +6 -0
  11. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-darwin.png +0 -0
  12. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-chromium-linux.png +0 -0
  13. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-darwin.png +0 -0
  14. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-chromium-linux.png +0 -0
  15. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-darwin.png +0 -0
  16. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-charcoal-outline-quiet-chromium-linux.png +0 -0
  17. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-darwin.png +0 -0
  18. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-disabled-chromium-linux.png +0 -0
  19. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-darwin.png +0 -0
  20. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-chromium-linux.png +0 -0
  21. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-darwin.png +0 -0
  22. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-chromium-linux.png +0 -0
  23. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-darwin.png +0 -0
  24. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-ivory-outline-quiet-chromium-linux.png +0 -0
  25. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-darwin.png +0 -0
  26. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-large-chromium-linux.png +0 -0
  27. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-darwin.png +0 -0
  28. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-medium-chromium-linux.png +0 -0
  29. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-darwin.png +0 -0
  30. package/src/components/atoms/button/__screenshots__/button.visual.test.tsx/button-size-small-chromium-linux.png +0 -0
  31. package/src/components/atoms/button/button.stories.tsx +102 -0
  32. package/src/components/atoms/button/button.test.tsx +135 -0
  33. package/src/components/atoms/button/button.tsx +139 -0
  34. package/src/components/atoms/button/button.visual.test.tsx +102 -0
  35. package/src/components/atoms/button/icon-button.stories.tsx +166 -0
  36. package/src/components/atoms/button/icon-button.tsx +120 -0
  37. package/src/components/atoms/button/index.ts +6 -0
  38. package/src/components/atoms/ndstudio-footer/index.ts +1 -0
  39. package/src/components/atoms/ndstudio-footer/ndstudio-footer.tsx +55 -0
  40. package/src/components/atoms/pager-control/index.ts +5 -0
  41. package/src/components/atoms/pager-control/pager-control.stories.tsx +209 -0
  42. package/src/components/atoms/pager-control/pager-control.test.tsx +130 -0
  43. package/src/components/atoms/pager-control/pager-control.tsx +329 -0
  44. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +82 -0
  45. package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +196 -0
  46. package/src/components/dev-tools/dev-toolbar/index.ts +1 -0
  47. package/src/components/dev-tools/grid-overlay/grid-overlay.tsx +41 -0
  48. package/src/components/dev-tools/grid-overlay/index.ts +1 -0
  49. package/src/components/dev-tools/index.ts +2 -0
  50. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-darwin.png +0 -0
  51. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-default-vertical-chromium-linux.png +0 -0
  52. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-darwin.png +0 -0
  53. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-horizontal-chromium-linux.png +0 -0
  54. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-darwin.png +0 -0
  55. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-minimal-chromium-linux.png +0 -0
  56. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-darwin.png +0 -0
  57. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-actions-chromium-linux.png +0 -0
  58. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-darwin.png +0 -0
  59. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-eyebrow-chromium-linux.png +0 -0
  60. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-darwin.png +0 -0
  61. package/src/components/organisms/card/__screenshots__/card.visual.test.tsx/card-without-image-chromium-linux.png +0 -0
  62. package/src/components/organisms/card/card.stories.tsx +293 -0
  63. package/src/components/organisms/card/card.test.tsx +245 -0
  64. package/src/components/organisms/card/card.tsx +225 -0
  65. package/src/components/organisms/card/card.visual.test.tsx +197 -0
  66. package/src/components/organisms/card/index.ts +19 -0
  67. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-darwin.png +0 -0
  68. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-active-link-chromium-linux.png +0 -0
  69. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-darwin.png +0 -0
  70. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-brand-only-chromium-linux.png +0 -0
  71. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-darwin.png +0 -0
  72. package/src/components/organisms/navbar/__screenshots__/navbar.visual.test.tsx/navbar-default-chromium-linux.png +0 -0
  73. package/src/components/organisms/navbar/index.ts +18 -0
  74. package/src/components/organisms/navbar/navbar.stories.tsx +313 -0
  75. package/src/components/organisms/navbar/navbar.test.tsx +190 -0
  76. package/src/components/organisms/navbar/navbar.tsx +323 -0
  77. package/src/components/organisms/navbar/navbar.visual.test.tsx +85 -0
  78. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-darwin.png +0 -0
  79. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-icon-chromium-linux.png +0 -0
  80. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-darwin.png +0 -0
  81. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-custom-text-chromium-linux.png +0 -0
  82. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-darwin.png +0 -0
  83. package/src/components/organisms/us-gov-banner/__screenshots__/us-gov-banner.visual.test.tsx/us-gov-banner-default-chromium-linux.png +0 -0
  84. package/src/components/organisms/us-gov-banner/index.ts +1 -0
  85. package/src/components/organisms/us-gov-banner/us-gov-banner.stories.tsx +35 -0
  86. package/src/components/organisms/us-gov-banner/us-gov-banner.test.tsx +107 -0
  87. package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +73 -0
  88. package/src/components/organisms/us-gov-banner/us-gov-banner.visual.test.tsx +46 -0
  89. package/src/components/sections/banner/banner.stories.tsx +150 -0
  90. package/src/components/sections/banner/banner.test.tsx +185 -0
  91. package/src/components/sections/banner/banner.tsx +130 -0
  92. package/src/components/sections/banner/index.ts +2 -0
  93. package/src/components/sections/card-grid/card-grid.stories.tsx +351 -0
  94. package/src/components/sections/card-grid/card-grid.tsx +116 -0
  95. package/src/components/sections/card-grid/index.ts +1 -0
  96. package/src/components/sections/faq-section/faq-section.stories.tsx +453 -0
  97. package/src/components/sections/faq-section/faq-section.tsx +84 -0
  98. package/src/components/sections/faq-section/index.ts +2 -0
  99. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-darwin.png +0 -0
  100. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-desktop-chromium-linux.png +0 -0
  101. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-darwin.png +0 -0
  102. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-mobile-chromium-linux.png +0 -0
  103. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-darwin.png +0 -0
  104. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a1-tablet-chromium-linux.png +0 -0
  105. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-darwin.png +0 -0
  106. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-desktop-chromium-linux.png +0 -0
  107. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-darwin.png +0 -0
  108. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-mobile-chromium-linux.png +0 -0
  109. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-darwin.png +0 -0
  110. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a2-tablet-chromium-linux.png +0 -0
  111. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-darwin.png +0 -0
  112. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-desktop-chromium-linux.png +0 -0
  113. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-darwin.png +0 -0
  114. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-mobile-chromium-linux.png +0 -0
  115. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-darwin.png +0 -0
  116. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-a3-tablet-chromium-linux.png +0 -0
  117. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-darwin.png +0 -0
  118. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-custom-class-chromium-linux.png +0 -0
  119. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-default-chromium-linux.png +0 -0
  120. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-darwin.png +0 -0
  121. package/src/components/sections/hero/__screenshots__/hero.visual.test.tsx/hero-long-title-chromium-linux.png +0 -0
  122. package/src/components/sections/hero/hero.stories.tsx +274 -0
  123. package/src/components/sections/hero/hero.test.tsx +135 -0
  124. package/src/components/sections/hero/hero.tsx +453 -0
  125. package/src/components/sections/hero/hero.visual.test.tsx +140 -0
  126. package/src/components/sections/hero/index.ts +10 -0
  127. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-darwin.png +0 -0
  128. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-h3-heading-chromium-linux.png +0 -0
  129. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-darwin.png +0 -0
  130. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-paragraphs-chromium-linux.png +0 -0
  131. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-darwin.png +0 -0
  132. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-multiple-sections-chromium-linux.png +0 -0
  133. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-darwin.png +0 -0
  134. package/src/components/sections/prose/__screenshots__/prose.visual.test.tsx/prose-single-section-chromium-linux.png +0 -0
  135. package/src/components/sections/prose/index.ts +6 -0
  136. package/src/components/sections/prose/prose.stories.tsx +144 -0
  137. package/src/components/sections/prose/prose.test.tsx +178 -0
  138. package/src/components/sections/prose/prose.tsx +88 -0
  139. package/src/components/sections/prose/prose.visual.test.tsx +105 -0
  140. package/src/components/sections/river/index.ts +1 -0
  141. package/src/components/sections/river/river.stories.tsx +237 -0
  142. package/src/components/sections/river/river.test.tsx +268 -0
  143. package/src/components/sections/river/river.tsx +173 -0
  144. package/src/components/sections/tout/index.ts +1 -0
  145. package/src/components/sections/tout/tout.stories.tsx +171 -0
  146. package/src/components/sections/tout/tout.test.tsx +242 -0
  147. package/src/components/sections/tout/tout.tsx +270 -0
  148. package/src/components/sections/two-column-section/index.ts +5 -0
  149. package/src/components/sections/two-column-section/two-column-section.stories.tsx +285 -0
  150. package/src/components/sections/two-column-section/two-column-section.tsx +162 -0
  151. package/src/hooks/index.ts +1 -0
  152. package/src/hooks/use-event-listener.ts +73 -0
  153. package/src/index.ts +155 -0
  154. package/src/lib/theme.ts +1000 -0
  155. package/src/lib/utils.ts +6 -0
  156. package/src/main.tsx +13 -0
  157. package/src/stories/GridSystem.stories.tsx +84 -0
  158. package/src/stories/Introduction.mdx +114 -0
  159. package/src/stories/ThemeProvider.stories.tsx +357 -0
  160. package/src/stories/TokenShowcase.stories.tsx +92 -0
  161. package/src/stories/TokenShowcase.tsx +1429 -0
  162. package/src/styles.css +11 -0
  163. package/src/theme/ThemeProvider.tsx +297 -0
  164. package/src/theme/hooks.ts +40 -0
  165. package/src/theme/index.ts +43 -0
  166. package/src/theme/utils.ts +104 -0
@@ -0,0 +1,285 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { TwoColumnSection } from ".";
3
+
4
+ const meta: Meta<typeof TwoColumnSection> = {
5
+ title: "Sections/TwoColumnSection",
6
+ component: TwoColumnSection,
7
+ parameters: {
8
+ layout: "fullscreen",
9
+ },
10
+ argTypes: {
11
+ variant: {
12
+ control: "select",
13
+ options: ["dark", "light"],
14
+ description: "Color variant",
15
+ },
16
+ title: {
17
+ control: "text",
18
+ description: "The title text",
19
+ },
20
+ lead: {
21
+ control: "text",
22
+ description: "Lead content (prominent)",
23
+ },
24
+ },
25
+ } as Meta<typeof TwoColumnSection>;
26
+
27
+ export default meta;
28
+ type Story = StoryObj<typeof TwoColumnSection>;
29
+
30
+ // =============================================================================
31
+ // Playground
32
+ // =============================================================================
33
+
34
+ export const Playground: Story = {
35
+ render: (args) => <TwoColumnSection {...args} />,
36
+ };
37
+ Playground.args = {
38
+ variant: "dark",
39
+ title: "US Tech Force",
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
+ children: (
42
+ <>
43
+ <p>
44
+ Through a two-year program, participants will work in teams reporting
45
+ directly to agency leadership. In collaboration with leading technology
46
+ companies, participants will receive technical training, engage with
47
+ industry leaders, and work closely with senior managers from companies
48
+ partnering with the Tech Force.
49
+ </p>
50
+ <p>
51
+ Upon completing the program, engineers can seek employment with the
52
+ partnering private-sector companies for potential full-time roles –
53
+ demonstrating the value of combining civil service with technical
54
+ expertise.
55
+ </p>
56
+ <p>
57
+ If you are highly skilled in the areas of software engineering,
58
+ artificial intelligence, cybersecurity, data analytics, or technical
59
+ project management and want to build the future of American government
60
+ technology, join the Tech Force today.
61
+ </p>
62
+ </>
63
+ ),
64
+ };
65
+
66
+ // =============================================================================
67
+ // Variants
68
+ // =============================================================================
69
+
70
+ /**
71
+ * Dark variant (default) - dark background with light text
72
+ */
73
+ export const DarkVariant: Story = {
74
+ render: () => (
75
+ <TwoColumnSection
76
+ variant="dark"
77
+ title="US Tech Force"
78
+ lead="The US Tech Force is recruiting an elite corps of engineers to build the next generation of government technology."
79
+ >
80
+ <p>
81
+ Through a two-year program, participants will work in teams reporting
82
+ directly to agency leadership.
83
+ </p>
84
+ <p>
85
+ Upon completing the program, engineers can seek employment with the
86
+ partnering private-sector companies for potential full-time roles.
87
+ </p>
88
+ </TwoColumnSection>
89
+ ),
90
+ };
91
+
92
+ /**
93
+ * Light variant - light background with dark text
94
+ */
95
+ export const LightVariant: Story = {
96
+ render: () => (
97
+ <TwoColumnSection
98
+ variant="light"
99
+ title="US Tech Force"
100
+ lead="The US Tech Force is recruiting an elite corps of engineers to build the next generation of government technology."
101
+ >
102
+ <p>
103
+ Through a two-year program, participants will work in teams reporting
104
+ directly to agency leadership.
105
+ </p>
106
+ <p>
107
+ Upon completing the program, engineers can seek employment with the
108
+ partnering private-sector companies for potential full-time roles.
109
+ </p>
110
+ </TwoColumnSection>
111
+ ),
112
+ };
113
+
114
+ // =============================================================================
115
+ // Responsive Variants
116
+ // =============================================================================
117
+
118
+ export const Desktop: Story = {
119
+ render: () => (
120
+ <TwoColumnSection
121
+ variant="dark"
122
+ title="US Tech Force"
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
+ >
125
+ <p>
126
+ Through a two-year program, participants will work in teams reporting
127
+ directly to agency leadership. In collaboration with leading technology
128
+ companies, participants will receive technical training, engage with
129
+ industry leaders, and work closely with senior managers from companies
130
+ partnering with the Tech Force.
131
+ </p>
132
+ <p>
133
+ Upon completing the program, engineers can seek employment with the
134
+ partnering private-sector companies for potential full-time roles –
135
+ demonstrating the value of combining civil service with technical
136
+ expertise.
137
+ </p>
138
+ <p>
139
+ If you are highly skilled in the areas of software engineering,
140
+ artificial intelligence, cybersecurity, data analytics, or technical
141
+ project management and want to build the future of American government
142
+ technology, join the Tech Force today.
143
+ </p>
144
+ </TwoColumnSection>
145
+ ),
146
+ globals: {
147
+ viewport: { value: "lg", isRotated: false },
148
+ },
149
+ };
150
+
151
+ export const Tablet: Story = {
152
+ render: () => (
153
+ <TwoColumnSection
154
+ variant="dark"
155
+ title="US Tech Force"
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
+ >
158
+ <p>
159
+ Through a two-year program, participants will work in teams reporting
160
+ directly to agency leadership. In collaboration with leading technology
161
+ companies, participants will receive technical training, engage with
162
+ industry leaders, and work closely with senior managers from companies
163
+ partnering with the Tech Force.
164
+ </p>
165
+ <p>
166
+ Upon completing the program, engineers can seek employment with the
167
+ partnering private-sector companies for potential full-time roles –
168
+ demonstrating the value of combining civil service with technical
169
+ expertise.
170
+ </p>
171
+ <p>
172
+ If you are highly skilled in the areas of software engineering,
173
+ artificial intelligence, cybersecurity, data analytics, or technical
174
+ project management and want to build the future of American government
175
+ technology, join the Tech Force today.
176
+ </p>
177
+ </TwoColumnSection>
178
+ ),
179
+ globals: {
180
+ viewport: { value: "md", isRotated: false },
181
+ },
182
+ };
183
+
184
+ export const Mobile: Story = {
185
+ render: () => (
186
+ <TwoColumnSection
187
+ variant="dark"
188
+ title="US Tech Force"
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
+ >
191
+ <p>
192
+ Through a two-year program, participants will work in teams reporting
193
+ directly to agency leadership. In collaboration with leading technology
194
+ companies, participants will receive technical training, engage with
195
+ industry leaders, and work closely with senior managers from companies
196
+ partnering with the Tech Force.
197
+ </p>
198
+ <p>
199
+ Upon completing the program, engineers can seek employment with the
200
+ partnering private-sector companies for potential full-time roles –
201
+ demonstrating the value of combining civil service with technical
202
+ expertise.
203
+ </p>
204
+ <p>
205
+ If you are highly skilled in the areas of software engineering,
206
+ artificial intelligence, cybersecurity, data analytics, or technical
207
+ project management and want to build the future of American government
208
+ technology, join the Tech Force today.
209
+ </p>
210
+ </TwoColumnSection>
211
+ ),
212
+ globals: {
213
+ viewport: { value: "sm", isRotated: false },
214
+ },
215
+ };
216
+
217
+ // =============================================================================
218
+ // Content Variations
219
+ // =============================================================================
220
+
221
+ /**
222
+ * Without lead content - just title and body
223
+ */
224
+ export const WithoutLead: Story = {
225
+ render: () => (
226
+ <TwoColumnSection variant="dark" title="About the Program">
227
+ <p>
228
+ Through a two-year program, participants will work in teams reporting
229
+ directly to agency leadership. In collaboration with leading technology
230
+ companies, participants will receive technical training, engage with
231
+ industry leaders, and work closely with senior managers from companies
232
+ partnering with the Tech Force.
233
+ </p>
234
+ <p>
235
+ Upon completing the program, engineers can seek employment with the
236
+ partnering private-sector companies for potential full-time roles.
237
+ </p>
238
+ </TwoColumnSection>
239
+ ),
240
+ };
241
+
242
+ /**
243
+ * With rich lead content (ReactNode)
244
+ */
245
+ export const RichLeadContent: Story = {
246
+ render: () => (
247
+ <TwoColumnSection
248
+ variant="dark"
249
+ title="Our Mission"
250
+ lead={
251
+ <>
252
+ <p>
253
+ The US Tech Force is recruiting an elite corps of engineers to build
254
+ the next generation of government technology.
255
+ </p>
256
+ <p>
257
+ Backed by the White House, Tech Force will tackle the most complex
258
+ and large-scale civic and defense challenges of our era.
259
+ </p>
260
+ </>
261
+ }
262
+ >
263
+ <p>
264
+ From administering critical financial infrastructure at the Treasury
265
+ Department to advancing cutting-edge programs at the Department of War –
266
+ and everything in between.
267
+ </p>
268
+ </TwoColumnSection>
269
+ ),
270
+ };
271
+
272
+ /**
273
+ * Short content example
274
+ */
275
+ export const ShortContent: Story = {
276
+ render: () => (
277
+ <TwoColumnSection
278
+ variant="dark"
279
+ title="Join Us"
280
+ lead="Build the future of American government technology."
281
+ >
282
+ <p>Applications are now open for qualified engineers.</p>
283
+ </TwoColumnSection>
284
+ ),
285
+ };
@@ -0,0 +1,162 @@
1
+ import * as React from "react";
2
+ import { tv, type VariantProps } from "tailwind-variants";
3
+ import { cn } from "@/lib/utils";
4
+
5
+ /**
6
+ * TwoColumnSection component for text-heavy content sections
7
+ *
8
+ * Layout:
9
+ * - Desktop (lg+): Title left, content right with border-top divider
10
+ * - Mobile/Tablet: Stacked vertically
11
+ *
12
+ * Uses the 24-column grid system.
13
+ */
14
+ const twoColumnSectionVariants = tv({
15
+ // Base styles - responsive padding using primitive spacing tokens
16
+ base: [
17
+ "w-full",
18
+ // Small (mobile): 20px x, 56px top, 20px bottom
19
+ "px-spacing-20 pt-spacing-56 pb-spacing-20",
20
+ // Medium (tablet): 56px x, 56px y
21
+ "md:px-spacing-56 md:py-spacing-56",
22
+ // Large (desktop): 72px x, 72px top, 112px bottom
23
+ "lg:px-spacing-72 lg:pt-spacing-72 lg:pb-spacing-112",
24
+ ],
25
+ variants: {
26
+ variant: {
27
+ dark: "bg-gray-1200",
28
+ light: "bg-white",
29
+ },
30
+ layout: {
31
+ /** Default 24-column grid with asymmetric split (title: 9, content: 15) */
32
+ asymmetric: "",
33
+ /** Equal 2-column layout at md+ breakpoints */
34
+ equal: "",
35
+ },
36
+ },
37
+ defaultVariants: {
38
+ variant: "dark",
39
+ layout: "asymmetric",
40
+ },
41
+ });
42
+
43
+ export interface TwoColumnSectionProps
44
+ extends React.HTMLAttributes<HTMLElement>,
45
+ VariantProps<typeof twoColumnSectionVariants> {
46
+ /**
47
+ * The title text displayed in the left column
48
+ */
49
+ title: string;
50
+ /**
51
+ * Lead content - prominently styled (brighter text)
52
+ * Can be a string or ReactNode for rich content
53
+ */
54
+ lead?: React.ReactNode;
55
+ /**
56
+ * Body content - secondary styled (muted text)
57
+ * Can be a string or ReactNode for rich content
58
+ */
59
+ children: React.ReactNode;
60
+ /**
61
+ * Layout style for the columns
62
+ * - "asymmetric" (default): Uses 24-column grid with ~40/60 split (title: 9, content: 15)
63
+ * - "equal": Simple 2-column equal-width layout at md+ breakpoints
64
+ */
65
+ layout?: "asymmetric" | "equal";
66
+ }
67
+
68
+ /**
69
+ * TwoColumnSection component for text-heavy content with title/content split.
70
+ *
71
+ * Layout:
72
+ * - Mobile/Tablet: Stacked (title above content)
73
+ * - Desktop (lg+): Title left (~40%), Content right (~60%)
74
+ *
75
+ * @example
76
+ * ```tsx
77
+ * <TwoColumnSection
78
+ * title="US Tech Force"
79
+ * lead="The US Tech Force is recruiting an elite corps of engineers..."
80
+ * variant="dark"
81
+ * >
82
+ * <p>Through a two-year program, participants will work...</p>
83
+ * <p>Upon completing the program, engineers can seek...</p>
84
+ * </TwoColumnSection>
85
+ * ```
86
+ */
87
+ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
88
+ (
89
+ { className, variant = "dark", layout, title, lead, children, ...props },
90
+ ref,
91
+ ) => {
92
+ return (
93
+ <section
94
+ ref={ref}
95
+ className={twoColumnSectionVariants({
96
+ variant,
97
+ layout,
98
+ class: className,
99
+ })}
100
+ {...props}
101
+ >
102
+ {/* Inner container with border-top - uses primitive spacing tokens */}
103
+ <div
104
+ className={cn(
105
+ "border-t pt-spacing-36",
106
+ variant === "dark" ? "border-gray-700" : "border-gray-300",
107
+ // Grid layout - uses primitive spacing tokens
108
+ "grid grid-cols-1 gap-spacing-56",
109
+ layout === "equal"
110
+ ? "md:grid-cols-2"
111
+ : "lg:grid-cols-24 lg:gap-spacing-56",
112
+ )}
113
+ >
114
+ {/* Title column */}
115
+ <h2
116
+ className={cn(
117
+ "typography-subheading-medium",
118
+ variant === "dark" ? "text-gray-100" : "text-gray-900",
119
+ // Column span based on layout
120
+ layout !== "equal" && "lg:col-span-9",
121
+ )}
122
+ >
123
+ {title}
124
+ </h2>
125
+
126
+ {/* Content column - uses primitive spacing tokens */}
127
+ <div
128
+ className={cn(
129
+ "flex flex-col gap-spacing-56",
130
+ layout !== "equal" && "lg:col-span-15",
131
+ )}
132
+ >
133
+ {/* Lead content - brighter/prominent */}
134
+ {lead && (
135
+ <div
136
+ className={cn(
137
+ "typography-body-large",
138
+ variant === "dark" ? "text-gray-100" : "text-gray-900",
139
+ )}
140
+ >
141
+ {typeof lead === "string" ? <p>{lead}</p> : lead}
142
+ </div>
143
+ )}
144
+
145
+ {/* Body content - muted */}
146
+ <div
147
+ className={cn(
148
+ "typography-body-medium flex flex-col gap-[1em]",
149
+ variant === "dark" ? "text-gray-400" : "text-gray-600",
150
+ )}
151
+ >
152
+ {children}
153
+ </div>
154
+ </div>
155
+ </div>
156
+ </section>
157
+ );
158
+ },
159
+ );
160
+ TwoColumnSection.displayName = "TwoColumnSection";
161
+
162
+ export { TwoColumnSection, twoColumnSectionVariants };
@@ -0,0 +1 @@
1
+ export { useEventListener } from "./use-event-listener";
@@ -0,0 +1,73 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ type EventMap<T> = T extends Window
6
+ ? WindowEventMap
7
+ : T extends Document
8
+ ? DocumentEventMap
9
+ : T extends HTMLElement
10
+ ? HTMLElementEventMap
11
+ : never;
12
+
13
+ /**
14
+ * Custom hook that attaches an event listener to a specified element.
15
+ * Automatically handles cleanup on unmount and when dependencies change.
16
+ *
17
+ * @param eventName - The name of the event to listen for
18
+ * @param handler - The callback function to execute when the event fires
19
+ * @param element - The element to attach the listener to (defaults to window)
20
+ * @param options - Optional event listener options
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * // Listen to window resize
25
+ * useEventListener('resize', handleResize);
26
+ *
27
+ * // Listen to element scroll
28
+ * useEventListener('scroll', handleScroll, containerRef.current);
29
+ *
30
+ * // With options
31
+ * useEventListener('scroll', handleScroll, window, { passive: true });
32
+ * ```
33
+ */
34
+ export function useEventListener<
35
+ T extends Window | Document | HTMLElement,
36
+ K extends keyof EventMap<T>,
37
+ >(
38
+ eventName: K,
39
+ handler: (event: EventMap<T>[K]) => void,
40
+ element?: T | null,
41
+ options?: boolean | AddEventListenerOptions,
42
+ ): void {
43
+ // Store the handler in a ref to avoid re-subscribing on every render
44
+ const savedHandler = React.useRef(handler);
45
+
46
+ // Update ref when handler changes
47
+ React.useLayoutEffect(() => {
48
+ savedHandler.current = handler;
49
+ }, [handler]);
50
+
51
+ React.useEffect(() => {
52
+ // Default to window if no element is provided
53
+ const targetElement = element ?? window;
54
+
55
+ if (!targetElement?.addEventListener) {
56
+ return;
57
+ }
58
+
59
+ const eventListener = (event: Event) => {
60
+ savedHandler.current(event as EventMap<T>[K]);
61
+ };
62
+
63
+ targetElement.addEventListener(eventName as string, eventListener, options);
64
+
65
+ return () => {
66
+ targetElement.removeEventListener(
67
+ eventName as string,
68
+ eventListener,
69
+ options,
70
+ );
71
+ };
72
+ }, [eventName, element, options]);
73
+ }
package/src/index.ts ADDED
@@ -0,0 +1,155 @@
1
+ // =============================================================================
2
+ // Atoms
3
+ // =============================================================================
4
+ export type {
5
+ AccordionItemProps,
6
+ AccordionProps,
7
+ } from "./components/atoms/accordion";
8
+ export { Accordion, AccordionItem } from "./components/atoms/accordion";
9
+ export type { ButtonProps, IconButtonProps } from "./components/atoms/button";
10
+ export {
11
+ Button,
12
+ buttonVariants,
13
+ IconButton,
14
+ iconButtonVariants,
15
+ } from "./components/atoms/button";
16
+ export type { NdstudioFooterProps } from "./components/atoms/ndstudio-footer";
17
+ export { NdstudioFooter } from "./components/atoms/ndstudio-footer";
18
+ export type { PagerControlProps } from "./components/atoms/pager-control";
19
+ export {
20
+ PagerControl,
21
+ pagerControlVariants,
22
+ } from "./components/atoms/pager-control";
23
+ // =============================================================================
24
+ // Dev Tools
25
+ // =============================================================================
26
+ export type { DevToolbarProps, GridOverlayProps } from "./components/dev-tools";
27
+ export { DevToolbar, GridOverlay } from "./components/dev-tools";
28
+ // =============================================================================
29
+ // Organisms
30
+ // =============================================================================
31
+ export type {
32
+ CardActionsProps,
33
+ CardBodyProps,
34
+ CardContentProps,
35
+ CardDescriptionProps,
36
+ CardEyebrowProps,
37
+ CardImageProps,
38
+ CardProps,
39
+ CardTitleProps,
40
+ } from "./components/organisms/card";
41
+ export {
42
+ Card,
43
+ CardActions,
44
+ CardBody,
45
+ CardContent,
46
+ CardDescription,
47
+ CardEyebrow,
48
+ CardImage,
49
+ CardTitle,
50
+ cardVariants,
51
+ } from "./components/organisms/card";
52
+ export type {
53
+ NavbarActionsProps,
54
+ NavbarBrandProps,
55
+ NavbarLinkProps,
56
+ NavbarLinksProps,
57
+ NavbarMobileMenuButtonProps,
58
+ NavbarMobileMenuLinkProps,
59
+ NavbarMobileMenuProps,
60
+ NavbarProps,
61
+ } from "./components/organisms/navbar";
62
+ export {
63
+ Navbar,
64
+ NavbarActions,
65
+ NavbarBrand,
66
+ NavbarLink,
67
+ NavbarLinks,
68
+ NavbarMobileMenu,
69
+ NavbarMobileMenuButton,
70
+ NavbarMobileMenuLink,
71
+ } from "./components/organisms/navbar";
72
+ export type { USGovBannerProps } from "./components/organisms/us-gov-banner";
73
+ export { USGovBanner } from "./components/organisms/us-gov-banner";
74
+ // =============================================================================
75
+ // Sections
76
+ // =============================================================================
77
+ export type { BannerProps } from "./components/sections/banner";
78
+ export { Banner, bannerVariants } from "./components/sections/banner";
79
+ export type { CardGridProps } from "./components/sections/card-grid";
80
+ export { CardGrid, cardGridVariants } from "./components/sections/card-grid";
81
+ export type { FaqSectionProps } from "./components/sections/faq-section";
82
+ export { FaqSection } from "./components/sections/faq-section";
83
+ export type {
84
+ HeroBackgroundImageProps,
85
+ HeroBackgroundStreamProps,
86
+ HeroBackgroundVideoProps,
87
+ HeroProps,
88
+ } from "./components/sections/hero";
89
+ export {
90
+ DEFAULT_TITLE_TYPOGRAPHY,
91
+ Hero,
92
+ HeroBackground,
93
+ heroVariants,
94
+ } from "./components/sections/hero";
95
+ export type {
96
+ ProseProps,
97
+ ProseSectionProps,
98
+ } from "./components/sections/prose";
99
+ export { Prose, ProseSection } from "./components/sections/prose";
100
+ export type { RiverProps } from "./components/sections/river";
101
+ export { River, riverVariants } from "./components/sections/river";
102
+ export type { ToutProps } from "./components/sections/tout";
103
+ export { Tout } from "./components/sections/tout";
104
+ export type { TwoColumnSectionProps } from "./components/sections/two-column-section";
105
+ export {
106
+ TwoColumnSection,
107
+ twoColumnSectionVariants,
108
+ } from "./components/sections/two-column-section";
109
+ // =============================================================================
110
+ // Theme
111
+ // =============================================================================
112
+ export type {
113
+ ButtonTheme,
114
+ ColorToken,
115
+ ComponentTheme,
116
+ ComponentThemeColors,
117
+ ComponentThemeSpatial,
118
+ ComponentThemeSurface,
119
+ FontSizeToken,
120
+ RadiusToken,
121
+ SpacingToken,
122
+ } from "./lib/theme";
123
+ export {
124
+ buttonThemeToStyleVars,
125
+ FONT_SIZES,
126
+ fontSizeToClass,
127
+ responsiveTypographyClass,
128
+ themeToStyleVars,
129
+ } from "./lib/theme";
130
+
131
+ // =============================================================================
132
+ // Utilities
133
+ // =============================================================================
134
+ export { cn } from "./lib/utils";
135
+
136
+ // =============================================================================
137
+ // Theme
138
+ // =============================================================================
139
+ export {
140
+ applyTheme,
141
+ createThemeStyle,
142
+ filterCSSVars,
143
+ getToken,
144
+ mergeCSSVars,
145
+ removeTheme,
146
+ type ThemeContextValue,
147
+ // Provider and hooks
148
+ ThemeProvider,
149
+ type ThemeProviderProps,
150
+ // Utilities
151
+ toCSSVars,
152
+ useCSSVars,
153
+ useTheme,
154
+ useThemeTokens,
155
+ } from "./theme";