@nationaldesignstudio/react 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/component-registry.md +1310 -127
- package/dist/components/atoms/button/button.d.ts +55 -47
- package/dist/components/atoms/button/button.figma.d.ts +1 -0
- package/dist/components/atoms/input/input.d.ts +24 -24
- package/dist/components/atoms/popover/popover.d.ts +195 -0
- package/dist/components/atoms/select/select.d.ts +24 -24
- package/dist/components/atoms/tooltip/tooltip.d.ts +161 -0
- package/dist/components/organisms/card/card.d.ts +1 -1
- package/dist/components/sections/hero/hero.d.ts +2 -2
- package/dist/components/sections/tout/tout.d.ts +3 -3
- package/dist/components/shared/floating-arrow.d.ts +34 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +11602 -8499
- package/dist/index.js.map +1 -1
- package/dist/lib/form-control.d.ts +25 -24
- package/dist/tokens.css +4797 -3940
- package/package.json +2 -1
- package/src/components/atoms/accordion/accordion.stories.tsx +1 -1
- package/src/components/atoms/accordion/accordion.tsx +2 -2
- package/src/components/atoms/button/button.figma.tsx +37 -0
- package/src/components/atoms/button/button.stories.tsx +236 -140
- package/src/components/atoms/button/button.test.tsx +289 -5
- package/src/components/atoms/button/button.tsx +37 -33
- package/src/components/atoms/button/button.visual.test.tsx +26 -76
- package/src/components/atoms/button/icon-button.stories.tsx +44 -101
- package/src/components/atoms/button/icon-button.test.tsx +26 -94
- package/src/components/atoms/button/icon-button.tsx +3 -3
- package/src/components/atoms/input/input-group.stories.tsx +4 -8
- package/src/components/atoms/input/input-group.test.tsx +14 -28
- package/src/components/atoms/input/input-group.tsx +57 -32
- package/src/components/atoms/input/input.stories.tsx +14 -18
- package/src/components/atoms/input/input.test.tsx +4 -20
- package/src/components/atoms/input/input.tsx +16 -9
- package/src/components/atoms/pager-control/pager-control.stories.tsx +6 -8
- package/src/components/atoms/pager-control/pager-control.tsx +12 -12
- package/src/components/atoms/popover/index.ts +30 -0
- package/src/components/atoms/popover/popover.stories.tsx +531 -0
- package/src/components/atoms/popover/popover.test.tsx +486 -0
- package/src/components/atoms/popover/popover.tsx +488 -0
- package/src/components/atoms/select/select.tsx +12 -8
- package/src/components/atoms/tooltip/index.ts +24 -0
- package/src/components/atoms/tooltip/tooltip.stories.tsx +348 -0
- package/src/components/atoms/tooltip/tooltip.test.tsx +363 -0
- package/src/components/atoms/tooltip/tooltip.tsx +347 -0
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +8 -13
- package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +3 -3
- package/src/components/organisms/card/card.stories.tsx +19 -19
- package/src/components/organisms/card/card.tsx +1 -1
- package/src/components/organisms/card/card.visual.test.tsx +11 -11
- package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
- package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +2 -2
- package/src/components/sections/banner/banner.stories.tsx +1 -5
- package/src/components/sections/banner/banner.test.tsx +2 -2
- package/src/components/sections/banner/banner.tsx +6 -6
- package/src/components/sections/card-grid/card-grid.tsx +4 -4
- package/src/components/sections/hero/hero.stories.tsx +7 -7
- package/src/components/sections/hero/hero.tsx +10 -11
- package/src/components/sections/prose/prose.tsx +2 -2
- package/src/components/sections/river/river.test.tsx +3 -3
- package/src/components/sections/river/river.tsx +6 -12
- package/src/components/sections/tout/tout.stories.tsx +7 -31
- package/src/components/sections/tout/tout.tsx +9 -9
- package/src/components/sections/two-column-section/two-column-section.tsx +7 -9
- package/src/components/shared/floating-arrow.tsx +78 -0
- package/src/components/shared/index.ts +5 -0
- package/src/index.ts +57 -0
- package/src/lib/form-control.ts +8 -6
- package/src/stories/grid-system.stories.tsx +309 -0
- package/src/stories/{ThemeProvider.stories.tsx → theme-provider.stories.tsx} +7 -19
- package/src/stories/{TokenShowcase.stories.tsx → token-showcase.stories.tsx} +1 -1
- package/src/stories/{TokenShowcase.tsx → token-showcase.tsx} +34 -34
- package/src/styles.css +3 -3
- package/src/tests/token-resolution.test.tsx +6 -9
- package/src/theme/hooks.ts +1 -1
- package/src/theme/index.ts +1 -1
- package/src/theme/theme-provider.test.tsx +270 -0
- package/src/theme/{ThemeProvider.tsx → theme-provider.tsx} +18 -2
- package/src/stories/GridSystem.stories.tsx +0 -84
- /package/src/stories/{Introduction.mdx → introduction.mdx} +0 -0
|
@@ -143,11 +143,7 @@ export const CustomBackground: Story = {
|
|
|
143
143
|
<Banner
|
|
144
144
|
heading="Custom Styled Banner"
|
|
145
145
|
description="This banner has a custom background color applied via className."
|
|
146
|
-
action={
|
|
147
|
-
<Button variant="outline" colorScheme="light">
|
|
148
|
-
Action
|
|
149
|
-
</Button>
|
|
150
|
-
}
|
|
146
|
+
action={<Button variant="secondary-outline">Action</Button>}
|
|
151
147
|
className="bg-gray-1000 text-gray-50 [&_h2]:text-gray-50 [&_p]:text-gray-200"
|
|
152
148
|
/>
|
|
153
149
|
),
|
|
@@ -156,8 +156,8 @@ describe("Banner", () => {
|
|
|
156
156
|
|
|
157
157
|
const banner = page.getByTestId("banner");
|
|
158
158
|
// Mobile padding
|
|
159
|
-
await expect.element(banner).toHaveClass(/px-
|
|
160
|
-
await expect.element(banner).toHaveClass(/py-
|
|
159
|
+
await expect.element(banner).toHaveClass(/px-20/);
|
|
160
|
+
await expect.element(banner).toHaveClass(/py-32/);
|
|
161
161
|
});
|
|
162
162
|
});
|
|
163
163
|
|
|
@@ -21,9 +21,9 @@ const bannerVariants = tv({
|
|
|
21
21
|
"col-full",
|
|
22
22
|
// Responsive padding: mobile -> tablet -> desktop
|
|
23
23
|
// Uses primitive spacing tokens
|
|
24
|
-
"px-
|
|
25
|
-
"md:p-
|
|
26
|
-
"lg:px-
|
|
24
|
+
"px-20 py-32",
|
|
25
|
+
"md:p-56",
|
|
26
|
+
"lg:px-72 lg:pb-72 lg:pt-0",
|
|
27
27
|
],
|
|
28
28
|
variants: {
|
|
29
29
|
colorScheme: {
|
|
@@ -95,12 +95,12 @@ const Banner = React.forwardRef<HTMLElement, BannerProps>(
|
|
|
95
95
|
<div
|
|
96
96
|
className={cn(
|
|
97
97
|
// Uses primitive spacing tokens
|
|
98
|
-
"flex flex-col md:flex-row gap-
|
|
99
|
-
colorScheme === "dark" && "border-t border-gray-700 py-
|
|
98
|
+
"flex flex-col md:flex-row gap-20 items-start md:items-center md:justify-between",
|
|
99
|
+
colorScheme === "dark" && "border-t border-gray-700 py-36",
|
|
100
100
|
)}
|
|
101
101
|
>
|
|
102
102
|
{/* Copy section */}
|
|
103
|
-
<div className="flex flex-col gap-
|
|
103
|
+
<div className="flex flex-col gap-6 items-start">
|
|
104
104
|
<h2
|
|
105
105
|
className={cn(
|
|
106
106
|
"typography-subheading-small",
|
|
@@ -16,9 +16,9 @@ const cardGridVariants = tv({
|
|
|
16
16
|
base: [
|
|
17
17
|
"grid-container",
|
|
18
18
|
// Small (mobile): 72px y padding
|
|
19
|
-
"py-
|
|
19
|
+
"py-72",
|
|
20
20
|
// Large (desktop): 128px y padding
|
|
21
|
-
"lg:py-
|
|
21
|
+
"lg:py-128",
|
|
22
22
|
],
|
|
23
23
|
variants: {
|
|
24
24
|
variant: {
|
|
@@ -85,7 +85,7 @@ const CardGrid = React.forwardRef<HTMLElement, CardGridProps>(
|
|
|
85
85
|
"col-full",
|
|
86
86
|
"typography-h4 text-gray-900",
|
|
87
87
|
// Gap after title: mobile default, md: 56px, lg: 64px
|
|
88
|
-
"mb-
|
|
88
|
+
"mb-36 md:mb-56 lg:mb-64",
|
|
89
89
|
)}
|
|
90
90
|
>
|
|
91
91
|
{title}
|
|
@@ -96,7 +96,7 @@ const CardGrid = React.forwardRef<HTMLElement, CardGridProps>(
|
|
|
96
96
|
className={cn(
|
|
97
97
|
"col-full",
|
|
98
98
|
// Mobile: single column
|
|
99
|
-
"grid grid-cols-1 gap-
|
|
99
|
+
"grid grid-cols-1 gap-20",
|
|
100
100
|
// Tablet: 2 columns
|
|
101
101
|
"md:grid-cols-2",
|
|
102
102
|
// Desktop: 3 columns for variant A, 2 columns for variant B
|
|
@@ -159,7 +159,7 @@ export const WithCloudflareStream: Story = {
|
|
|
159
159
|
* Mock banner component for story demonstration
|
|
160
160
|
*/
|
|
161
161
|
const MockBanner = () => (
|
|
162
|
-
<div className="bg-gray-1200 px-
|
|
162
|
+
<div className="bg-gray-1200 px-16 py-8 text-center text-14 text-text-inverted">
|
|
163
163
|
An official website of the United States government
|
|
164
164
|
</div>
|
|
165
165
|
);
|
|
@@ -168,10 +168,10 @@ const MockBanner = () => (
|
|
|
168
168
|
* Mock navigation component for story demonstration
|
|
169
169
|
*/
|
|
170
170
|
const MockNavigation = () => (
|
|
171
|
-
<nav className="flex items-center justify-between px-
|
|
172
|
-
<div className="flex items-center gap-
|
|
171
|
+
<nav className="flex items-center justify-between px-56 py-16">
|
|
172
|
+
<div className="flex items-center gap-24">
|
|
173
173
|
<div className="size-48 rounded-full bg-gray-50" />
|
|
174
|
-
<div className="flex gap-
|
|
174
|
+
<div className="flex gap-24 text-14 text-text-inverted">
|
|
175
175
|
<span className="cursor-pointer hover:opacity-80">Link 1</span>
|
|
176
176
|
<span className="cursor-pointer hover:opacity-80">Link 2</span>
|
|
177
177
|
</div>
|
|
@@ -199,7 +199,7 @@ export const WithTopSlot: Story = {
|
|
|
199
199
|
*/
|
|
200
200
|
export const WithRoundedCorners: Story = {
|
|
201
201
|
render: () => (
|
|
202
|
-
<div className="bg-bg-page p-
|
|
202
|
+
<div className="bg-bg-page p-32">
|
|
203
203
|
<Hero
|
|
204
204
|
variant="A1"
|
|
205
205
|
title="Rounded Corners"
|
|
@@ -234,7 +234,7 @@ export const WithTopAndRoundedCorners: Story = {
|
|
|
234
234
|
</>
|
|
235
235
|
}
|
|
236
236
|
>
|
|
237
|
-
<p className="mt-
|
|
237
|
+
<p className="mt-16 max-w-[560px] text-20 text-text-inverted">
|
|
238
238
|
A subtitle or description can be added as children
|
|
239
239
|
</p>
|
|
240
240
|
</Hero>
|
|
@@ -261,7 +261,7 @@ export const UsingSubComponents: Story = {
|
|
|
261
261
|
<h1 className="text-64 font-medium text-text-inverted lg:text-128">
|
|
262
262
|
Custom Layout
|
|
263
263
|
</h1>
|
|
264
|
-
<p className="mt-
|
|
264
|
+
<p className="mt-16 text-20 text-text-inverted">
|
|
265
265
|
Using HeroHeader and HeroContent sub-components
|
|
266
266
|
</p>
|
|
267
267
|
</HeroContent>
|
|
@@ -18,8 +18,7 @@ export {
|
|
|
18
18
|
type BackgroundVideoProps as HeroBackgroundVideoProps,
|
|
19
19
|
} from "@/components/atoms/background";
|
|
20
20
|
|
|
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";
|
|
21
|
+
const DEFAULT_TITLE_TYPOGRAPHY = "typography-h1-display";
|
|
23
22
|
|
|
24
23
|
const heroVariants = tv({
|
|
25
24
|
slots: {
|
|
@@ -27,8 +26,8 @@ const heroVariants = tv({
|
|
|
27
26
|
top: "relative z-10 w-full",
|
|
28
27
|
content: [
|
|
29
28
|
"relative z-10 mx-auto flex w-full max-w-screen-xl flex-1 flex-col",
|
|
30
|
-
"p-
|
|
31
|
-
"md:p-
|
|
29
|
+
"p-20",
|
|
30
|
+
"md:p-56",
|
|
32
31
|
],
|
|
33
32
|
title: DEFAULT_TITLE_TYPOGRAPHY,
|
|
34
33
|
},
|
|
@@ -36,15 +35,15 @@ const heroVariants = tv({
|
|
|
36
35
|
variant: {
|
|
37
36
|
A1: {
|
|
38
37
|
root: "min-h-[80vh]",
|
|
39
|
-
content: ["justify-end", "lg:p-
|
|
38
|
+
content: ["justify-end", "lg:p-72"],
|
|
40
39
|
},
|
|
41
40
|
A2: {
|
|
42
41
|
root: "min-h-[80vh]",
|
|
43
|
-
content: ["justify-start", "lg:p-
|
|
42
|
+
content: ["justify-start", "lg:p-64"],
|
|
44
43
|
},
|
|
45
44
|
A3: {
|
|
46
45
|
root: "min-h-[80vh]",
|
|
47
|
-
content: ["items-center justify-center", "lg:p-
|
|
46
|
+
content: ["items-center justify-center", "lg:p-64"],
|
|
48
47
|
},
|
|
49
48
|
},
|
|
50
49
|
colorScheme: {
|
|
@@ -80,9 +79,9 @@ const heroHeaderVariants = tv({
|
|
|
80
79
|
const heroContentVariants = tv({
|
|
81
80
|
base: [
|
|
82
81
|
"relative z-10 mx-auto flex w-full max-w-screen-xl flex-1 flex-col",
|
|
83
|
-
"p-
|
|
84
|
-
"md:p-
|
|
85
|
-
"lg:p-
|
|
82
|
+
"p-20",
|
|
83
|
+
"md:p-56",
|
|
84
|
+
"lg:p-72",
|
|
86
85
|
],
|
|
87
86
|
});
|
|
88
87
|
|
|
@@ -136,7 +135,7 @@ export interface HeroProps
|
|
|
136
135
|
title?: string;
|
|
137
136
|
/**
|
|
138
137
|
* Custom typography class for the title using primitive tokens.
|
|
139
|
-
* Default: "
|
|
138
|
+
* Default: "typography-h1-display"
|
|
140
139
|
*/
|
|
141
140
|
titleClassName?: string;
|
|
142
141
|
/**
|
|
@@ -23,7 +23,7 @@ const Prose = React.forwardRef<HTMLDivElement, ProseProps>(
|
|
|
23
23
|
className={cn(
|
|
24
24
|
"flex w-full max-w-[700px] flex-col overflow-hidden",
|
|
25
25
|
// Responsive gap between sections
|
|
26
|
-
"gap-
|
|
26
|
+
"gap-56 md:gap-72 xl:gap-96",
|
|
27
27
|
className,
|
|
28
28
|
)}
|
|
29
29
|
{...props}
|
|
@@ -67,7 +67,7 @@ const ProseSection = React.forwardRef<HTMLElement, ProseSectionProps>(
|
|
|
67
67
|
className={cn(
|
|
68
68
|
"flex w-full flex-col",
|
|
69
69
|
// Responsive gap between heading and body
|
|
70
|
-
"gap-
|
|
70
|
+
"gap-24 md:gap-40",
|
|
71
71
|
className,
|
|
72
72
|
)}
|
|
73
73
|
{...props}
|
|
@@ -177,9 +177,9 @@ describe("River", () => {
|
|
|
177
177
|
|
|
178
178
|
const river = page.getByTestId("river");
|
|
179
179
|
// Mobile padding
|
|
180
|
-
await expect.element(river).toHaveClass(/px-
|
|
181
|
-
await expect.element(river).toHaveClass(/pt-
|
|
182
|
-
await expect.element(river).toHaveClass(/pb-
|
|
180
|
+
await expect.element(river).toHaveClass(/px-20/);
|
|
181
|
+
await expect.element(river).toHaveClass(/pt-72/);
|
|
182
|
+
await expect.element(river).toHaveClass(/pb-20/);
|
|
183
183
|
});
|
|
184
184
|
});
|
|
185
185
|
|
|
@@ -16,11 +16,11 @@ const riverVariants = tv({
|
|
|
16
16
|
base: [
|
|
17
17
|
"col-full",
|
|
18
18
|
// Small (mobile): 20px x, 72px top, 20px bottom
|
|
19
|
-
"px-
|
|
19
|
+
"px-20 pt-72 pb-20",
|
|
20
20
|
// Medium (tablet): 56px x, 96px y
|
|
21
|
-
"md:px-
|
|
21
|
+
"md:px-56 md:py-96",
|
|
22
22
|
// Large (desktop): 72px x, 128px y
|
|
23
|
-
"lg:px-
|
|
23
|
+
"lg:px-72 lg:py-128",
|
|
24
24
|
],
|
|
25
25
|
variants: {
|
|
26
26
|
variant: {
|
|
@@ -106,17 +106,14 @@ const River = React.forwardRef<HTMLElement, RiverProps>(
|
|
|
106
106
|
)}
|
|
107
107
|
>
|
|
108
108
|
{/* Text content with 16px gap - uses primitive spacing tokens */}
|
|
109
|
-
<div className="flex flex-col gap-
|
|
109
|
+
<div className="flex flex-col gap-16">
|
|
110
110
|
<h2 className="typography-h4 text-gray-900">{headline}</h2>
|
|
111
111
|
<p className="typography-body-small text-gray-800">{body}</p>
|
|
112
112
|
</div>
|
|
113
113
|
|
|
114
114
|
{/* Buttons with 36px gap from text, responsive sizes - uses primitive spacing tokens */}
|
|
115
115
|
<div
|
|
116
|
-
className={cn(
|
|
117
|
-
"flex flex-row gap-spacing-16 mt-spacing-36",
|
|
118
|
-
"[&>*]:flex-shrink-0",
|
|
119
|
-
)}
|
|
116
|
+
className={cn("flex flex-row gap-16 mt-36", "[&>*]:flex-shrink-0")}
|
|
120
117
|
>
|
|
121
118
|
{primaryAction}
|
|
122
119
|
{secondaryAction}
|
|
@@ -145,10 +142,7 @@ const River = React.forwardRef<HTMLElement, RiverProps>(
|
|
|
145
142
|
>
|
|
146
143
|
{/* Inner grid container for 24-col layout - uses primitive spacing tokens */}
|
|
147
144
|
<div
|
|
148
|
-
className={cn(
|
|
149
|
-
"grid grid-cols-1 gap-spacing-36",
|
|
150
|
-
"lg:grid-cols-24 lg:gap-spacing-36",
|
|
151
|
-
)}
|
|
145
|
+
className={cn("grid grid-cols-1 gap-36", "lg:grid-cols-24 lg:gap-36")}
|
|
152
146
|
>
|
|
153
147
|
{variant === "B" ? (
|
|
154
148
|
<>
|
|
@@ -43,11 +43,7 @@ Playground.args = {
|
|
|
43
43
|
headline: "Brand-Large/Headline/Small",
|
|
44
44
|
body: "A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action.",
|
|
45
45
|
primaryAction: <Button>Primary</Button>,
|
|
46
|
-
secondaryAction:
|
|
47
|
-
<Button variant="outline" colorScheme="light">
|
|
48
|
-
Secondary
|
|
49
|
-
</Button>
|
|
50
|
-
),
|
|
46
|
+
secondaryAction: <Button variant="secondary-outline">Secondary</Button>,
|
|
51
47
|
backgroundMedia: <PlaceholderBackground />,
|
|
52
48
|
};
|
|
53
49
|
|
|
@@ -61,11 +57,7 @@ export const Desktop: Story = {
|
|
|
61
57
|
headline="Brand-Large/Headline/Small"
|
|
62
58
|
body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
|
|
63
59
|
primaryAction={<Button>Primary</Button>}
|
|
64
|
-
secondaryAction={
|
|
65
|
-
<Button variant="outline" colorScheme="light">
|
|
66
|
-
Secondary
|
|
67
|
-
</Button>
|
|
68
|
-
}
|
|
60
|
+
secondaryAction={<Button variant="secondary-outline">Secondary</Button>}
|
|
69
61
|
backgroundMedia={<PlaceholderBackground />}
|
|
70
62
|
/>
|
|
71
63
|
),
|
|
@@ -80,11 +72,7 @@ export const Tablet: Story = {
|
|
|
80
72
|
headline="Brand-Large/Headline/Small"
|
|
81
73
|
body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
|
|
82
74
|
primaryAction={<Button>Primary</Button>}
|
|
83
|
-
secondaryAction={
|
|
84
|
-
<Button variant="outline" colorScheme="light">
|
|
85
|
-
Secondary
|
|
86
|
-
</Button>
|
|
87
|
-
}
|
|
75
|
+
secondaryAction={<Button variant="secondary-outline">Secondary</Button>}
|
|
88
76
|
backgroundMedia={<PlaceholderBackground />}
|
|
89
77
|
/>
|
|
90
78
|
),
|
|
@@ -100,7 +88,7 @@ export const Mobile: Story = {
|
|
|
100
88
|
body="A river pattern stacks content in a simple vertical flow: one clear heading, a short block of copy, then the next step. It's ideal for guiding citizens through a process or story, keeping focus moving straight down the page with minimal choices and well-timed calls to action."
|
|
101
89
|
primaryAction={<Button size="sm">Primary</Button>}
|
|
102
90
|
secondaryAction={
|
|
103
|
-
<Button size="sm" variant="outline"
|
|
91
|
+
<Button size="sm" variant="secondary-outline">
|
|
104
92
|
Secondary
|
|
105
93
|
</Button>
|
|
106
94
|
}
|
|
@@ -125,11 +113,7 @@ export const WithImage: Story = {
|
|
|
125
113
|
headline="Work with Purpose"
|
|
126
114
|
body="Join a team that's building the future of government services. We're looking for passionate individuals who want to make a difference."
|
|
127
115
|
primaryAction={<Button>View Careers</Button>}
|
|
128
|
-
secondaryAction={
|
|
129
|
-
<Button variant="outline" colorScheme="light">
|
|
130
|
-
Learn More
|
|
131
|
-
</Button>
|
|
132
|
-
}
|
|
116
|
+
secondaryAction={<Button variant="secondary-outline">Learn More</Button>}
|
|
133
117
|
backgroundMedia={<ImageBackground />}
|
|
134
118
|
/>
|
|
135
119
|
),
|
|
@@ -158,11 +142,7 @@ export const WithVideoPlaceholder: Story = {
|
|
|
158
142
|
headline="Experience Innovation"
|
|
159
143
|
body="See how modern technology is transforming the way government serves its citizens."
|
|
160
144
|
primaryAction={<Button>Watch Video</Button>}
|
|
161
|
-
secondaryAction={
|
|
162
|
-
<Button variant="outline" colorScheme="light">
|
|
163
|
-
Learn More
|
|
164
|
-
</Button>
|
|
165
|
-
}
|
|
145
|
+
secondaryAction={<Button variant="secondary-outline">Learn More</Button>}
|
|
166
146
|
backgroundMedia={
|
|
167
147
|
<div className="absolute inset-0 bg-gray-800 flex items-center justify-center">
|
|
168
148
|
<span className="text-gray-400 typography-body-small">
|
|
@@ -183,11 +163,7 @@ export const WithNdstudioFooter: Story = {
|
|
|
183
163
|
headline="Work with Purpose"
|
|
184
164
|
body="Join a team that's building the future of government services. We're looking for passionate individuals who want to make a difference."
|
|
185
165
|
primaryAction={<Button>View Careers</Button>}
|
|
186
|
-
secondaryAction={
|
|
187
|
-
<Button variant="outline" colorScheme="light">
|
|
188
|
-
Learn More
|
|
189
|
-
</Button>
|
|
190
|
-
}
|
|
166
|
+
secondaryAction={<Button variant="secondary-outline">Learn More</Button>}
|
|
191
167
|
backgroundMedia={<ImageBackground />}
|
|
192
168
|
footer={<NdstudioFooter />}
|
|
193
169
|
/>
|
|
@@ -93,9 +93,9 @@ export interface ToutProps
|
|
|
93
93
|
*
|
|
94
94
|
* This component is self-contained with its own grid.
|
|
95
95
|
* Grid setup:
|
|
96
|
-
* - Desktop (lg): 24 columns, gap-
|
|
97
|
-
* - Tablet (md): 12 columns, gap-
|
|
98
|
-
* - Mobile: 4 columns, gap-
|
|
96
|
+
* - Desktop (lg): 24 columns, gap-20, content spans 9 cols
|
|
97
|
+
* - Tablet (md): 12 columns, gap-20, content spans 9 cols
|
|
98
|
+
* - Mobile: 4 columns, gap-20, content spans all 4 cols
|
|
99
99
|
*
|
|
100
100
|
* @example
|
|
101
101
|
* ```tsx
|
|
@@ -163,7 +163,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
|
|
|
163
163
|
// Grid setup with responsive columns
|
|
164
164
|
"grid w-full h-full",
|
|
165
165
|
// Mobile: 4 columns with gap-20
|
|
166
|
-
"grid-cols-4 gap-
|
|
166
|
+
"grid-cols-4 gap-20",
|
|
167
167
|
// Tablet (md): 12 columns
|
|
168
168
|
"md:grid-cols-12",
|
|
169
169
|
// Desktop (lg): 24 columns
|
|
@@ -171,9 +171,9 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
|
|
|
171
171
|
// Max width and centering like grid-container
|
|
172
172
|
"max-w-[var(--breakpoint-lg)] mx-auto",
|
|
173
173
|
// Responsive margins matching grid-container - uses primitive spacing tokens
|
|
174
|
-
"px-
|
|
174
|
+
"px-20 md:px-56 lg:px-72",
|
|
175
175
|
// Vertical padding to position content at bottom - uses primitive spacing tokens
|
|
176
|
-
"py-
|
|
176
|
+
"py-36 md:py-56 lg:py-72",
|
|
177
177
|
)}
|
|
178
178
|
style={{
|
|
179
179
|
// Grid spacing theme overrides
|
|
@@ -193,7 +193,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
|
|
|
193
193
|
"flex flex-col",
|
|
194
194
|
isCentered ? "justify-start items-center" : "justify-end",
|
|
195
195
|
// Responsive gap between text and buttons - uses primitive spacing tokens
|
|
196
|
-
"gap-
|
|
196
|
+
"gap-28 md:gap-36",
|
|
197
197
|
// Mobile: all 4 cols
|
|
198
198
|
"col-span-4",
|
|
199
199
|
// Tablet & Desktop: 9 cols left-aligned, full width centered
|
|
@@ -203,7 +203,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
|
|
|
203
203
|
{/* Text content stack - uses primitive spacing tokens */}
|
|
204
204
|
<div
|
|
205
205
|
className={cn(
|
|
206
|
-
"flex flex-col gap-
|
|
206
|
+
"flex flex-col gap-16",
|
|
207
207
|
isCentered && "items-center text-center",
|
|
208
208
|
)}
|
|
209
209
|
>
|
|
@@ -243,7 +243,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
|
|
|
243
243
|
"flex flex-row",
|
|
244
244
|
isCentered ? "justify-center" : "items-start",
|
|
245
245
|
// Responsive gap between buttons
|
|
246
|
-
"gap-
|
|
246
|
+
"gap-8 md:gap-12",
|
|
247
247
|
"[&>*]:flex-shrink-0",
|
|
248
248
|
)}
|
|
249
249
|
>
|
|
@@ -16,11 +16,11 @@ const twoColumnSectionVariants = tv({
|
|
|
16
16
|
base: [
|
|
17
17
|
"w-full",
|
|
18
18
|
// Small (mobile): 20px x, 56px top, 20px bottom
|
|
19
|
-
"px-
|
|
19
|
+
"px-20 pt-56 pb-20",
|
|
20
20
|
// Medium (tablet): 56px x, 56px y
|
|
21
|
-
"md:px-
|
|
21
|
+
"md:px-56 md:py-56",
|
|
22
22
|
// Large (desktop): 72px x, 72px top, 112px bottom
|
|
23
|
-
"lg:px-
|
|
23
|
+
"lg:px-72 lg:pt-72 lg:pb-112",
|
|
24
24
|
],
|
|
25
25
|
variants: {
|
|
26
26
|
colorScheme: {
|
|
@@ -108,13 +108,11 @@ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
|
|
|
108
108
|
{/* Inner container with border-top - uses primitive spacing tokens */}
|
|
109
109
|
<div
|
|
110
110
|
className={cn(
|
|
111
|
-
"border-t pt-
|
|
111
|
+
"border-t pt-36",
|
|
112
112
|
colorScheme === "dark" ? "border-gray-700" : "border-gray-300",
|
|
113
113
|
// Grid layout - uses primitive spacing tokens
|
|
114
|
-
"grid grid-cols-1 gap-
|
|
115
|
-
layout === "equal"
|
|
116
|
-
? "md:grid-cols-2"
|
|
117
|
-
: "lg:grid-cols-24 lg:gap-spacing-56",
|
|
114
|
+
"grid grid-cols-1 gap-56",
|
|
115
|
+
layout === "equal" ? "md:grid-cols-2" : "lg:grid-cols-24 lg:gap-56",
|
|
118
116
|
)}
|
|
119
117
|
>
|
|
120
118
|
{/* Title column */}
|
|
@@ -132,7 +130,7 @@ const TwoColumnSection = React.forwardRef<HTMLElement, TwoColumnSectionProps>(
|
|
|
132
130
|
{/* Content column - uses primitive spacing tokens */}
|
|
133
131
|
<div
|
|
134
132
|
className={cn(
|
|
135
|
-
"flex flex-col gap-
|
|
133
|
+
"flex flex-col gap-56",
|
|
136
134
|
layout !== "equal" && "lg:col-span-15",
|
|
137
135
|
)}
|
|
138
136
|
>
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { tv } from "tailwind-variants";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared arrow variants for floating UI components
|
|
6
|
+
*
|
|
7
|
+
* Used by Tooltip, Popover, and other floating components.
|
|
8
|
+
* Handles positioning based on the side attribute.
|
|
9
|
+
*/
|
|
10
|
+
export const floatingArrowVariants = tv({
|
|
11
|
+
base: [
|
|
12
|
+
// Display flex to properly size the arrow container
|
|
13
|
+
"flex",
|
|
14
|
+
// Positioning based on floating side (uses spacing tokens since --spacing: 1px)
|
|
15
|
+
"data-[side=bottom]:-top-7",
|
|
16
|
+
"data-[side=left]:-right-12 data-[side=left]:rotate-90",
|
|
17
|
+
"data-[side=right]:-left-12 data-[side=right]:-rotate-90",
|
|
18
|
+
"data-[side=top]:-bottom-7 data-[side=top]:rotate-180",
|
|
19
|
+
// Animation - follows popup
|
|
20
|
+
"data-[starting-style]:opacity-0",
|
|
21
|
+
"data-[ending-style]:opacity-0",
|
|
22
|
+
],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export interface FloatingArrowSvgProps {
|
|
26
|
+
/** CSS class for the main fill color (e.g., "fill-tooltip-bg" or "fill-overlay-background") */
|
|
27
|
+
fillClassName: string;
|
|
28
|
+
/** CSS class for the border color using fill-* (e.g., "fill-overlay-border") - renders as outline behind main fill */
|
|
29
|
+
borderClassName?: string;
|
|
30
|
+
/** Additional className for the SVG element */
|
|
31
|
+
className?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* FloatingArrowSvg
|
|
36
|
+
*
|
|
37
|
+
* A shared arrow SVG component for floating UI elements.
|
|
38
|
+
* Use with Tooltip, Popover, Dropdown, and other floating components.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* // For dark tooltip (no border)
|
|
43
|
+
* <FloatingArrowSvg fillClassName="fill-tooltip-bg" />
|
|
44
|
+
*
|
|
45
|
+
* // For light popover with border
|
|
46
|
+
* <FloatingArrowSvg
|
|
47
|
+
* fillClassName="fill-overlay-background"
|
|
48
|
+
* borderClassName="fill-overlay-border"
|
|
49
|
+
* />
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export const FloatingArrowSvg = ({
|
|
53
|
+
fillClassName,
|
|
54
|
+
borderClassName,
|
|
55
|
+
className,
|
|
56
|
+
}: FloatingArrowSvgProps) => (
|
|
57
|
+
<svg
|
|
58
|
+
width="20"
|
|
59
|
+
height="10"
|
|
60
|
+
viewBox="0 0 20 10"
|
|
61
|
+
fill="none"
|
|
62
|
+
className={cn("block", className)}
|
|
63
|
+
aria-hidden="true"
|
|
64
|
+
>
|
|
65
|
+
{/* Main fill shape */}
|
|
66
|
+
<path
|
|
67
|
+
d="M9.66437 2.60207L4.80758 6.97318C4.07308 7.63423 3.11989 8 2.13172 8H0V10H20V8H18.5349C17.5468 8 16.5936 7.63423 15.8591 6.97318L11.0023 2.60207C10.622 2.2598 10.0447 2.25979 9.66437 2.60207Z"
|
|
68
|
+
className={fillClassName}
|
|
69
|
+
/>
|
|
70
|
+
{/* Border stroke (rendered on top for outline effect) */}
|
|
71
|
+
{borderClassName && (
|
|
72
|
+
<path
|
|
73
|
+
d="M8.99542 1.85876C9.75604 1.17425 10.9106 1.17422 11.6713 1.85878L16.5281 6.22989C17.0789 6.72568 17.7938 7.00001 18.5349 7.00001L15.89 7L11.0023 2.60207C10.622 2.2598 10.0447 2.2598 9.66436 2.60207L4.77734 7L2.13171 7.00001C2.87284 7.00001 3.58774 6.72568 4.13861 6.22989L8.99542 1.85876Z"
|
|
74
|
+
className={borderClassName}
|
|
75
|
+
/>
|
|
76
|
+
)}
|
|
77
|
+
</svg>
|
|
78
|
+
);
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nationaldesignstudio/react
|
|
3
|
+
* Design system components for React applications
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
// =============================================================================
|
|
2
7
|
// Atoms
|
|
3
8
|
// =============================================================================
|
|
@@ -63,6 +68,35 @@ export {
|
|
|
63
68
|
PagerControl,
|
|
64
69
|
pagerControlVariants,
|
|
65
70
|
} from "./components/atoms/pager-control";
|
|
71
|
+
export type {
|
|
72
|
+
PopoverArrowProps,
|
|
73
|
+
PopoverBackdropProps,
|
|
74
|
+
PopoverCloseProps,
|
|
75
|
+
PopoverDescriptionProps,
|
|
76
|
+
PopoverPopupProps,
|
|
77
|
+
PopoverPortalProps,
|
|
78
|
+
PopoverPositionerProps,
|
|
79
|
+
PopoverProps,
|
|
80
|
+
PopoverRootProps,
|
|
81
|
+
PopoverTitleProps,
|
|
82
|
+
PopoverTriggerProps,
|
|
83
|
+
} from "./components/atoms/popover";
|
|
84
|
+
export {
|
|
85
|
+
Popover,
|
|
86
|
+
PopoverArrow,
|
|
87
|
+
PopoverBackdrop,
|
|
88
|
+
PopoverClose,
|
|
89
|
+
PopoverDescription,
|
|
90
|
+
PopoverParts,
|
|
91
|
+
PopoverPopup,
|
|
92
|
+
PopoverPortal,
|
|
93
|
+
PopoverPositioner,
|
|
94
|
+
PopoverRoot,
|
|
95
|
+
PopoverTitle,
|
|
96
|
+
PopoverTrigger,
|
|
97
|
+
popoverArrowVariants,
|
|
98
|
+
popoverPopupVariants,
|
|
99
|
+
} from "./components/atoms/popover";
|
|
66
100
|
export type {
|
|
67
101
|
SelectGroupLabelProps,
|
|
68
102
|
SelectGroupProps,
|
|
@@ -83,6 +117,29 @@ export {
|
|
|
83
117
|
selectPopupVariants,
|
|
84
118
|
selectTriggerVariants,
|
|
85
119
|
} from "./components/atoms/select";
|
|
120
|
+
export type {
|
|
121
|
+
TooltipArrowProps,
|
|
122
|
+
TooltipPopupProps,
|
|
123
|
+
TooltipPortalProps,
|
|
124
|
+
TooltipPositionerProps,
|
|
125
|
+
TooltipProps,
|
|
126
|
+
TooltipProviderProps,
|
|
127
|
+
TooltipRootProps,
|
|
128
|
+
TooltipTriggerProps,
|
|
129
|
+
} from "./components/atoms/tooltip";
|
|
130
|
+
export {
|
|
131
|
+
Tooltip,
|
|
132
|
+
TooltipArrow,
|
|
133
|
+
TooltipParts,
|
|
134
|
+
TooltipPopup,
|
|
135
|
+
TooltipPortal,
|
|
136
|
+
TooltipPositioner,
|
|
137
|
+
TooltipProvider,
|
|
138
|
+
TooltipRoot,
|
|
139
|
+
TooltipTrigger,
|
|
140
|
+
tooltipArrowVariants,
|
|
141
|
+
tooltipPopupVariants,
|
|
142
|
+
} from "./components/atoms/tooltip";
|
|
86
143
|
// =============================================================================
|
|
87
144
|
// Dev Tools
|
|
88
145
|
// =============================================================================
|
package/src/lib/form-control.ts
CHANGED
|
@@ -19,14 +19,14 @@ export const formControlBase = [
|
|
|
19
19
|
"flex w-full items-center",
|
|
20
20
|
// Typography
|
|
21
21
|
"text-16 font-medium leading-14",
|
|
22
|
-
// Border and radius
|
|
23
|
-
"border border-solid border-ui-color-border rounded-
|
|
22
|
+
// Border and radius - uses surface ui radius for theming support
|
|
23
|
+
"border border-solid border-ui-color-border rounded-surface-ui-medium",
|
|
24
24
|
// Background
|
|
25
25
|
"bg-ui-control-background",
|
|
26
26
|
// Transitions
|
|
27
27
|
"transition-[background-color,border-color,box-shadow] duration-150",
|
|
28
28
|
// Focus state
|
|
29
|
-
"outline-none focus-visible:border-
|
|
29
|
+
"outline-none focus-visible:border-border-focus focus-visible:ring-4 focus-visible:ring-ui-color-focus",
|
|
30
30
|
// Hover state (when not focused or disabled)
|
|
31
31
|
"hover:bg-ui-control-background-hover hover:focus-visible:bg-ui-control-background",
|
|
32
32
|
// Disabled state
|
|
@@ -35,11 +35,13 @@ export const formControlBase = [
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* Size variants shared by form controls
|
|
38
|
+
* Uses spatial tokens for consistent sizing across form controls
|
|
38
39
|
*/
|
|
39
40
|
export const formControlSizes = {
|
|
40
|
-
sm: "h-
|
|
41
|
-
default:
|
|
42
|
-
|
|
41
|
+
sm: "h-spatial-ui-control-height-small px-spatial-ui-control-padding-x-small py-spatial-ui-control-padding-y-small text-14",
|
|
42
|
+
default:
|
|
43
|
+
"h-spatial-ui-control-height-medium px-spatial-ui-control-padding-x-medium py-spatial-ui-control-padding-y-medium",
|
|
44
|
+
lg: "h-spatial-ui-control-height-large px-spatial-ui-control-padding-x-large py-spatial-ui-control-padding-y-large text-18",
|
|
43
45
|
} as const;
|
|
44
46
|
|
|
45
47
|
/**
|