@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.
- package/dist/component-registry.md +46 -19
- package/dist/components/atoms/accordion/accordion.d.ts +7 -7
- package/dist/components/atoms/background/background.d.ts +13 -27
- package/dist/components/atoms/button/button.d.ts +55 -71
- package/dist/components/atoms/button/icon-button.d.ts +62 -110
- package/dist/components/atoms/input/input-group.d.ts +278 -0
- package/dist/components/atoms/input/input.d.ts +121 -0
- package/dist/components/atoms/select/select.d.ts +131 -0
- package/dist/components/organisms/card/card.d.ts +2 -2
- package/dist/components/sections/banner/banner.d.ts +9 -9
- package/dist/components/sections/faq-section/faq-section.d.ts +1 -1
- package/dist/components/sections/hero/hero.d.ts +115 -18
- package/dist/components/sections/prose/prose.d.ts +3 -3
- package/dist/components/sections/river/river.d.ts +1 -1
- package/dist/components/sections/tout/tout.d.ts +9 -9
- package/dist/components/sections/two-column-section/two-column-section.d.ts +7 -21
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11075 -7841
- package/dist/index.js.map +1 -1
- package/dist/lib/form-control.d.ts +105 -0
- package/dist/tokens.css +2144 -17341
- package/package.json +1 -1
- package/src/components/atoms/accordion/accordion.test.tsx +18 -20
- package/src/components/atoms/accordion/accordion.tsx +19 -17
- package/src/components/atoms/background/background.test.tsx +2 -2
- package/src/components/atoms/background/background.tsx +77 -96
- package/src/components/atoms/button/button.stories.tsx +42 -0
- package/src/components/atoms/button/button.test.tsx +1 -1
- package/src/components/atoms/button/button.tsx +38 -103
- package/src/components/atoms/button/button.visual.test.tsx +70 -24
- package/src/components/atoms/button/icon-button.tsx +80 -188
- package/src/components/atoms/input/index.ts +17 -0
- package/src/components/atoms/input/input-group.stories.tsx +650 -0
- package/src/components/atoms/input/input-group.test.tsx +376 -0
- package/src/components/atoms/input/input-group.tsx +384 -0
- package/src/components/atoms/input/input.stories.tsx +232 -0
- package/src/components/atoms/input/input.test.tsx +183 -0
- package/src/components/atoms/input/input.tsx +97 -0
- package/src/components/atoms/select/index.ts +18 -0
- package/src/components/atoms/select/select.stories.tsx +455 -0
- package/src/components/atoms/select/select.tsx +320 -0
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +2 -6
- package/src/components/foundation/typography/typography.stories.tsx +401 -0
- package/src/components/organisms/card/card.stories.tsx +11 -11
- package/src/components/organisms/card/card.test.tsx +5 -3
- package/src/components/organisms/card/card.tsx +2 -2
- package/src/components/organisms/card/card.visual.test.tsx +6 -6
- package/src/components/organisms/navbar/navbar.tsx +2 -2
- package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
- package/src/components/sections/banner/banner.stories.tsx +5 -1
- package/src/components/sections/banner/banner.tsx +10 -10
- package/src/components/sections/card-grid/card-grid.tsx +1 -1
- package/src/components/sections/faq-section/faq-section.stories.tsx +7 -7
- package/src/components/sections/faq-section/faq-section.tsx +5 -5
- package/src/components/sections/hero/hero.test.tsx +5 -5
- package/src/components/sections/hero/hero.tsx +33 -51
- package/src/components/sections/prose/prose.test.tsx +2 -2
- package/src/components/sections/prose/prose.tsx +4 -5
- package/src/components/sections/river/river.stories.tsx +8 -8
- package/src/components/sections/river/river.test.tsx +1 -1
- package/src/components/sections/river/river.tsx +2 -4
- package/src/components/sections/tout/tout.stories.tsx +31 -7
- package/src/components/sections/tout/tout.test.tsx +1 -1
- package/src/components/sections/tout/tout.tsx +8 -10
- package/src/components/sections/two-column-section/two-column-section.stories.tsx +11 -11
- package/src/components/sections/two-column-section/two-column-section.tsx +16 -10
- package/src/index.ts +41 -0
- package/src/lib/form-control.ts +69 -0
- package/src/stories/Introduction.mdx +29 -15
- package/src/stories/ThemeProvider.stories.tsx +1 -3
- package/src/stories/TokenShowcase.stories.tsx +0 -19
- package/src/stories/TokenShowcase.tsx +714 -1366
- package/src/styles.css +3 -0
- 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
|
-
|
|
29
|
+
colorScheme: {
|
|
30
30
|
light: "bg-gray-50",
|
|
31
31
|
dark: "bg-gray-1200",
|
|
32
32
|
},
|
|
33
33
|
},
|
|
34
34
|
defaultVariants: {
|
|
35
|
-
|
|
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
|
|
77
|
+
* // Dark colorScheme
|
|
78
78
|
* <Banner
|
|
79
|
-
*
|
|
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,
|
|
87
|
+
({ className, colorScheme, heading, description, action, ...props }, ref) => {
|
|
88
88
|
return (
|
|
89
89
|
<section
|
|
90
90
|
ref={ref}
|
|
91
|
-
className={bannerVariants({
|
|
91
|
+
className={bannerVariants({ colorScheme, class: className })}
|
|
92
92
|
{...props}
|
|
93
93
|
>
|
|
94
|
-
{/* Inner container with border-top for dark
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
-
|
|
12
|
+
colorScheme: {
|
|
13
13
|
control: "select",
|
|
14
14
|
options: ["dark", "light"],
|
|
15
|
-
description: "Color
|
|
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
|
-
|
|
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
|
|
113
|
+
* Dark variant - dark background with light text
|
|
114
114
|
*/
|
|
115
115
|
export const DarkVariant: Story = {
|
|
116
116
|
render: () => (
|
|
117
|
-
<FaqSection
|
|
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
|
|
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"
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
69
|
+
"[&_h2]:typography-h4",
|
|
70
70
|
// Accordion typography overrides
|
|
71
|
-
"[&_button]:typography-body-large [&_button]:md:typography-
|
|
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-
|
|
71
|
-
await expect.element(hero).toHaveClass(/bg-bg-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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-
|
|
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-
|
|
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-
|
|
56
|
-
* - h3: Uses typography-
|
|
57
|
-
* - Body: Uses typography-body-medium
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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-
|
|
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="
|
|
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-
|
|
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:
|
|
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={
|
|
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={
|
|
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="
|
|
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={
|
|
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={
|
|
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={
|
|
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-
|
|
223
|
+
await expect.element(headline).toHaveClass(/typography-h4/);
|
|
224
224
|
await expect.element(headline).toHaveClass(/text-gray-900/);
|
|
225
225
|
});
|
|
226
226
|
|