@nationaldesignstudio/react 0.2.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/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/prose/prose.d.ts +3 -3
- package/dist/components/sections/river/river.d.ts +1 -1
- package/dist/components/sections/tout/tout.d.ts +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +11034 -7824
- package/dist/index.js.map +1 -1
- package/dist/lib/form-control.d.ts +105 -0
- package/dist/tokens.css +2132 -17329
- package/package.json +1 -1
- package/src/components/atoms/background/background.tsx +71 -109
- 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 +81 -224
- 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 +1 -1
- 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/card-grid/card-grid.tsx +1 -1
- package/src/components/sections/faq-section/faq-section.tsx +2 -2
- package/src/components/sections/hero/hero.test.tsx +5 -5
- 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.test.tsx +1 -1
- package/src/components/sections/tout/tout.tsx +2 -2
- 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
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -103,7 +103,7 @@ export interface ToutProps
|
|
|
103
103
|
* headline="Feature Headline"
|
|
104
104
|
* body="Description of the feature..."
|
|
105
105
|
* primaryAction={<Button>Primary</Button>}
|
|
106
|
-
* secondaryAction={<Button variant="
|
|
106
|
+
* secondaryAction={<Button variant="outline" colorScheme="light">Secondary</Button>}
|
|
107
107
|
* backgroundMedia={
|
|
108
108
|
* <img
|
|
109
109
|
* src="/background.jpg"
|
|
@@ -209,7 +209,7 @@ const Tout = React.forwardRef<HTMLElement, ToutProps>(
|
|
|
209
209
|
>
|
|
210
210
|
<h2
|
|
211
211
|
className={cn(
|
|
212
|
-
"typography-
|
|
212
|
+
"typography-h4",
|
|
213
213
|
isDark ? "text-gray-100" : "text-gray-900",
|
|
214
214
|
)}
|
|
215
215
|
style={{
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,27 @@ export {
|
|
|
35
35
|
IconButton,
|
|
36
36
|
iconButtonVariants,
|
|
37
37
|
} from "./components/atoms/button";
|
|
38
|
+
export type {
|
|
39
|
+
InputGroupAddonProps,
|
|
40
|
+
InputGroupButtonProps,
|
|
41
|
+
InputGroupInputProps,
|
|
42
|
+
InputGroupProps,
|
|
43
|
+
InputGroupTextareaProps,
|
|
44
|
+
InputGroupTextProps,
|
|
45
|
+
InputProps,
|
|
46
|
+
} from "./components/atoms/input";
|
|
47
|
+
export {
|
|
48
|
+
Input,
|
|
49
|
+
InputGroup,
|
|
50
|
+
InputGroupAddon,
|
|
51
|
+
InputGroupButton,
|
|
52
|
+
InputGroupInput,
|
|
53
|
+
InputGroupText,
|
|
54
|
+
InputGroupTextarea,
|
|
55
|
+
inputGroupAddonVariants,
|
|
56
|
+
inputGroupVariants,
|
|
57
|
+
inputVariants,
|
|
58
|
+
} from "./components/atoms/input";
|
|
38
59
|
export type { NdstudioFooterProps } from "./components/atoms/ndstudio-footer";
|
|
39
60
|
export { NdstudioFooter } from "./components/atoms/ndstudio-footer";
|
|
40
61
|
export type { PagerControlProps } from "./components/atoms/pager-control";
|
|
@@ -42,6 +63,26 @@ export {
|
|
|
42
63
|
PagerControl,
|
|
43
64
|
pagerControlVariants,
|
|
44
65
|
} from "./components/atoms/pager-control";
|
|
66
|
+
export type {
|
|
67
|
+
SelectGroupLabelProps,
|
|
68
|
+
SelectGroupProps,
|
|
69
|
+
SelectOptionProps,
|
|
70
|
+
SelectPopupProps,
|
|
71
|
+
SelectProps,
|
|
72
|
+
SelectTriggerProps,
|
|
73
|
+
} from "./components/atoms/select";
|
|
74
|
+
export {
|
|
75
|
+
Select,
|
|
76
|
+
SelectGroup,
|
|
77
|
+
SelectGroupLabel,
|
|
78
|
+
SelectOption,
|
|
79
|
+
SelectPopup,
|
|
80
|
+
SelectRoot,
|
|
81
|
+
SelectTrigger,
|
|
82
|
+
selectOptionVariants,
|
|
83
|
+
selectPopupVariants,
|
|
84
|
+
selectTriggerVariants,
|
|
85
|
+
} from "./components/atoms/select";
|
|
45
86
|
// =============================================================================
|
|
46
87
|
// Dev Tools
|
|
47
88
|
// =============================================================================
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { tv } from "tailwind-variants";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared form control styles for Input, Select, and similar components.
|
|
5
|
+
*
|
|
6
|
+
* These base styles ensure consistent appearance across all form controls:
|
|
7
|
+
* - Consistent height and padding
|
|
8
|
+
* - Unified focus ring and border treatment
|
|
9
|
+
* - Shared hover/disabled states
|
|
10
|
+
*
|
|
11
|
+
* Based on Figma BaseKit / Interface / Input & Dropdown designs.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Base styles shared by all form controls (input, select, etc.)
|
|
16
|
+
*/
|
|
17
|
+
export const formControlBase = [
|
|
18
|
+
// Layout
|
|
19
|
+
"flex w-full items-center",
|
|
20
|
+
// Typography
|
|
21
|
+
"text-16 font-medium leading-14",
|
|
22
|
+
// Border and radius
|
|
23
|
+
"border border-solid border-ui-color-border rounded-radius-6",
|
|
24
|
+
// Background
|
|
25
|
+
"bg-ui-control-background",
|
|
26
|
+
// Transitions
|
|
27
|
+
"transition-[background-color,border-color,box-shadow] duration-150",
|
|
28
|
+
// Focus state
|
|
29
|
+
"outline-none focus-visible:border-ui-accent-base focus-visible:ring-4 focus-visible:ring-ui-color-focus",
|
|
30
|
+
// Hover state (when not focused or disabled)
|
|
31
|
+
"hover:bg-ui-control-background-hover hover:focus-visible:bg-ui-control-background",
|
|
32
|
+
// Disabled state
|
|
33
|
+
"disabled:bg-ui-control-background-disabled disabled:cursor-not-allowed disabled:opacity-50",
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Size variants shared by form controls
|
|
38
|
+
*/
|
|
39
|
+
export const formControlSizes = {
|
|
40
|
+
sm: "h-36 px-12 py-8 text-14",
|
|
41
|
+
default: "h-48 px-16 py-10",
|
|
42
|
+
lg: "h-56 px-20 py-12 text-18",
|
|
43
|
+
} as const;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Error state styles shared by form controls
|
|
47
|
+
*/
|
|
48
|
+
export const formControlError = {
|
|
49
|
+
true: "border-ui-error-color focus-visible:border-ui-error-color focus-visible:ring-ui-error-color/20 text-ui-error-color",
|
|
50
|
+
false: "",
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Form control variants using tailwind-variants
|
|
55
|
+
* Can be composed with other variants for specific components
|
|
56
|
+
*/
|
|
57
|
+
export const formControlVariants = tv({
|
|
58
|
+
base: formControlBase,
|
|
59
|
+
variants: {
|
|
60
|
+
size: formControlSizes,
|
|
61
|
+
error: formControlError,
|
|
62
|
+
},
|
|
63
|
+
defaultVariants: {
|
|
64
|
+
size: "default",
|
|
65
|
+
error: false,
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export type FormControlSize = keyof typeof formControlSizes;
|
|
@@ -40,33 +40,47 @@ function App() {
|
|
|
40
40
|
|
|
41
41
|
The design system provides pre-built typography classes for consistent text styling. Typography utilities use the `typography-` prefix to avoid conflicts with Tailwind's `text-*` color utilities.
|
|
42
42
|
|
|
43
|
-
###
|
|
43
|
+
### Headings
|
|
44
44
|
|
|
45
|
-
For
|
|
45
|
+
For headlines and titles:
|
|
46
46
|
|
|
47
47
|
```html
|
|
48
|
-
<h1 class="typography-
|
|
49
|
-
<
|
|
50
|
-
<
|
|
51
|
-
<
|
|
52
|
-
<
|
|
48
|
+
<h1 class="typography-h1-display">Display Title</h1>
|
|
49
|
+
<h1 class="typography-h1">Page Title</h1>
|
|
50
|
+
<h2 class="typography-h2">Section Header</h2>
|
|
51
|
+
<h3 class="typography-h3">Subsection Header</h3>
|
|
52
|
+
<h4 class="typography-h4">Smaller Header</h4>
|
|
53
|
+
<h5 class="typography-h5">Smallest Header</h5>
|
|
53
54
|
```
|
|
54
55
|
|
|
55
|
-
|
|
56
|
+
### Body Text
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
For body content:
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
```html
|
|
61
|
+
<p class="typography-body-large">Large body text...</p>
|
|
62
|
+
<p class="typography-body-medium">Standard body text...</p>
|
|
63
|
+
<p class="typography-body-small">Small body text...</p>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Supporting Text
|
|
67
|
+
|
|
68
|
+
For captions and labels:
|
|
60
69
|
|
|
61
70
|
```html
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
<p class="typography-brand-medium-body-large">Marketing copy...</p>
|
|
71
|
+
<span class="typography-caption">Caption text</span>
|
|
72
|
+
<span class="typography-overline">OVERLINE TEXT</span>
|
|
65
73
|
```
|
|
66
74
|
|
|
67
|
-
|
|
75
|
+
### UI Typography
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
For buttons and interactive elements:
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<button class="typography-ui-button-large">Large Button</button>
|
|
81
|
+
<button class="typography-ui-button-medium">Medium Button</button>
|
|
82
|
+
<button class="typography-ui-button-small">Small Button</button>
|
|
83
|
+
```
|
|
70
84
|
|
|
71
85
|
## Color Tokens
|
|
72
86
|
|
|
@@ -21,9 +21,7 @@ function ThemeDemo({ title }: { title?: string }) {
|
|
|
21
21
|
<div style={cssVars} className="p-6">
|
|
22
22
|
<div className="bg-bg-page p-6 rounded-lg min-h-[300px]">
|
|
23
23
|
{title && (
|
|
24
|
-
<h2 className="typography-
|
|
25
|
-
{title}
|
|
26
|
-
</h2>
|
|
24
|
+
<h2 className="typography-h5 text-text-primary mb-4">{title}</h2>
|
|
27
25
|
)}
|
|
28
26
|
|
|
29
27
|
{/* Typography showcase */}
|
|
@@ -3,7 +3,6 @@ import type { ComponentProps } from "react";
|
|
|
3
3
|
import {
|
|
4
4
|
ColorTokens,
|
|
5
5
|
ResponsiveTypography,
|
|
6
|
-
SemanticSpacingTokens,
|
|
7
6
|
SpacingTokens,
|
|
8
7
|
TokenShowcase,
|
|
9
8
|
TypographyTokens,
|
|
@@ -57,24 +56,6 @@ export const Typography: Story = {
|
|
|
57
56
|
),
|
|
58
57
|
};
|
|
59
58
|
|
|
60
|
-
export const SemanticSpacing: Story = {
|
|
61
|
-
render: () => (
|
|
62
|
-
<div className="p-8 min-h-screen">
|
|
63
|
-
<div className="max-w-4xl mx-auto">
|
|
64
|
-
<SemanticSpacingTokens />
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
),
|
|
68
|
-
parameters: {
|
|
69
|
-
docs: {
|
|
70
|
-
description: {
|
|
71
|
-
story:
|
|
72
|
-
"Purpose-driven spacing tokens for components (buttons, cards, forms), layouts (hero, sections, containers, grids), and content stacks (vertical rhythm between elements).",
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
|
|
78
59
|
export const Breakpoints: Story = {
|
|
79
60
|
render: () => (
|
|
80
61
|
<div className="p-8">
|