@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.
Files changed (58) hide show
  1. package/dist/components/atoms/background/background.d.ts +13 -27
  2. package/dist/components/atoms/button/button.d.ts +55 -71
  3. package/dist/components/atoms/button/icon-button.d.ts +62 -110
  4. package/dist/components/atoms/input/input-group.d.ts +278 -0
  5. package/dist/components/atoms/input/input.d.ts +121 -0
  6. package/dist/components/atoms/select/select.d.ts +131 -0
  7. package/dist/components/organisms/card/card.d.ts +2 -2
  8. package/dist/components/sections/prose/prose.d.ts +3 -3
  9. package/dist/components/sections/river/river.d.ts +1 -1
  10. package/dist/components/sections/tout/tout.d.ts +1 -1
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.js +11034 -7824
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/form-control.d.ts +105 -0
  15. package/dist/tokens.css +2132 -17329
  16. package/package.json +1 -1
  17. package/src/components/atoms/background/background.tsx +71 -109
  18. package/src/components/atoms/button/button.stories.tsx +42 -0
  19. package/src/components/atoms/button/button.test.tsx +1 -1
  20. package/src/components/atoms/button/button.tsx +38 -103
  21. package/src/components/atoms/button/button.visual.test.tsx +70 -24
  22. package/src/components/atoms/button/icon-button.tsx +81 -224
  23. package/src/components/atoms/input/index.ts +17 -0
  24. package/src/components/atoms/input/input-group.stories.tsx +650 -0
  25. package/src/components/atoms/input/input-group.test.tsx +376 -0
  26. package/src/components/atoms/input/input-group.tsx +384 -0
  27. package/src/components/atoms/input/input.stories.tsx +232 -0
  28. package/src/components/atoms/input/input.test.tsx +183 -0
  29. package/src/components/atoms/input/input.tsx +97 -0
  30. package/src/components/atoms/select/index.ts +18 -0
  31. package/src/components/atoms/select/select.stories.tsx +455 -0
  32. package/src/components/atoms/select/select.tsx +320 -0
  33. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +2 -6
  34. package/src/components/foundation/typography/typography.stories.tsx +401 -0
  35. package/src/components/organisms/card/card.stories.tsx +11 -11
  36. package/src/components/organisms/card/card.test.tsx +1 -1
  37. package/src/components/organisms/card/card.tsx +2 -2
  38. package/src/components/organisms/card/card.visual.test.tsx +6 -6
  39. package/src/components/organisms/navbar/navbar.tsx +2 -2
  40. package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
  41. package/src/components/sections/card-grid/card-grid.tsx +1 -1
  42. package/src/components/sections/faq-section/faq-section.tsx +2 -2
  43. package/src/components/sections/hero/hero.test.tsx +5 -5
  44. package/src/components/sections/prose/prose.test.tsx +2 -2
  45. package/src/components/sections/prose/prose.tsx +4 -5
  46. package/src/components/sections/river/river.stories.tsx +8 -8
  47. package/src/components/sections/river/river.test.tsx +1 -1
  48. package/src/components/sections/river/river.tsx +2 -4
  49. package/src/components/sections/tout/tout.test.tsx +1 -1
  50. package/src/components/sections/tout/tout.tsx +2 -2
  51. package/src/index.ts +41 -0
  52. package/src/lib/form-control.ts +69 -0
  53. package/src/stories/Introduction.mdx +29 -15
  54. package/src/stories/ThemeProvider.stories.tsx +1 -3
  55. package/src/stories/TokenShowcase.stories.tsx +0 -19
  56. package/src/stories/TokenShowcase.tsx +714 -1366
  57. package/src/styles.css +3 -0
  58. 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-headline-medium (42px mobile → 56px tablet → 72px desktop)
56
- * - h3: Uses typography-headline-small (32px mobile → 42px tablet → 56px desktop)
57
- * - Body: Uses typography-body-medium (16px mobile/tablet → 18px desktop)
55
+ * - h2: Uses typography-h3
56
+ * - h3: Uses typography-h4
57
+ * - Body: Uses typography-body-medium
58
58
  */
59
59
  const ProseSection = React.forwardRef<HTMLElement, ProseSectionProps>(
60
60
  ({ className, heading, as = "h2", children, ...props }, ref) => {
61
61
  const Heading = as;
62
- const headingClass =
63
- as === "h2" ? "typography-headline-medium" : "typography-headline-small";
62
+ const headingClass = as === "h2" ? "typography-h3" : "typography-h4";
64
63
 
65
64
  return (
66
65
  <section
@@ -51,7 +51,7 @@ Playground.args = {
51
51
  headline: "Feature Headline",
52
52
  body: "Use rivers to present content with supporting media. They work great for feature highlights, product showcases, and storytelling sections.",
53
53
  primaryAction: <Button>Primary Action</Button>,
54
- secondaryAction: <Button variant="charcoalOutline">Secondary</Button>,
54
+ secondaryAction: <Button variant="outline">Secondary</Button>,
55
55
  media: <PlaceholderImage />,
56
56
  };
57
57
 
@@ -69,7 +69,7 @@ export const VariantA: Story = {
69
69
  headline="Text Left, Media Right"
70
70
  body="Variant A places the text content on the left (9 columns) and media on the right (15 columns) on desktop viewports. On mobile and tablet, they stack vertically."
71
71
  primaryAction={<Button>Primary Action</Button>}
72
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
72
+ secondaryAction={<Button variant="outline">Secondary</Button>}
73
73
  media={<PlaceholderImage />}
74
74
  />
75
75
  ),
@@ -85,7 +85,7 @@ export const VariantB: Story = {
85
85
  headline="Media Left, Text Right"
86
86
  body="Variant B places the media on the left (15 columns) and text content on the right (9 columns) on desktop viewports. On mobile and tablet, they stack vertically with text first."
87
87
  primaryAction={<Button>Primary Action</Button>}
88
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
88
+ secondaryAction={<Button variant="outline">Secondary</Button>}
89
89
  media={<PlaceholderImage />}
90
90
  />
91
91
  ),
@@ -102,7 +102,7 @@ export const VariantADesktop: Story = {
102
102
  headline="Desktop View"
103
103
  body="On desktop (lg, 1440px), the content spans 9 columns and the media spans 15 columns in a horizontal layout."
104
104
  primaryAction={<Button>Primary</Button>}
105
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
105
+ secondaryAction={<Button variant="outline">Secondary</Button>}
106
106
  media={<PlaceholderImage />}
107
107
  />
108
108
  ),
@@ -118,7 +118,7 @@ export const VariantATablet: Story = {
118
118
  headline="Tablet View"
119
119
  body="On tablet (md, 768px), the content and media stack vertically with the text above the media."
120
120
  primaryAction={<Button>Primary</Button>}
121
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
121
+ secondaryAction={<Button variant="outline">Secondary</Button>}
122
122
  media={<PlaceholderImage />}
123
123
  />
124
124
  ),
@@ -135,7 +135,7 @@ export const VariantAMobile: Story = {
135
135
  body="On mobile (sm, 320px), content is stacked with smaller button sizing."
136
136
  primaryAction={<Button size="sm">Primary</Button>}
137
137
  secondaryAction={
138
- <Button size="sm" variant="charcoalOutline">
138
+ <Button size="sm" variant="outline">
139
139
  Secondary
140
140
  </Button>
141
141
  }
@@ -154,7 +154,7 @@ export const VariantBDesktop: Story = {
154
154
  headline="Desktop View - Reversed"
155
155
  body="Variant B reverses the layout, placing media on the left and content on the right."
156
156
  primaryAction={<Button>Primary</Button>}
157
- secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
157
+ secondaryAction={<Button variant="outline">Secondary</Button>}
158
158
  media={<PlaceholderImage />}
159
159
  />
160
160
  ),
@@ -224,7 +224,7 @@ export const WithImage: Story = {
224
224
  headline="Real World Example"
225
225
  body="Rivers work great with actual images, videos, or any media content. The media column is designed to accommodate various aspect ratios."
226
226
  primaryAction={<Button>Get Started</Button>}
227
- secondaryAction={<Button variant="charcoalOutline">Learn More</Button>}
227
+ secondaryAction={<Button variant="outline">Learn More</Button>}
228
228
  media={
229
229
  <img
230
230
  src="https://images.unsplash.com/photo-1551434678-e076c223a692?w=800&h=600&fit=crop"
@@ -246,7 +246,7 @@ describe("River", () => {
246
246
  );
247
247
 
248
248
  const headline = page.getByRole("heading", { level: 2 });
249
- await expect.element(headline).toHaveClass(/typography-headline-small/);
249
+ await expect.element(headline).toHaveClass(/typography-h4/);
250
250
  await expect.element(headline).toHaveClass(/text-gray-900/);
251
251
  });
252
252
 
@@ -77,7 +77,7 @@ export interface RiverProps
77
77
  * headline="Feature Headline"
78
78
  * body="Description of the feature..."
79
79
  * primaryAction={<Button>Primary</Button>}
80
- * secondaryAction={<Button variant="charcoalOutline">Secondary</Button>}
80
+ * secondaryAction={<Button variant="outline">Secondary</Button>}
81
81
  * media={<img src="..." alt="Feature" />}
82
82
  * />
83
83
  * </div>
@@ -107,9 +107,7 @@ const River = React.forwardRef<HTMLElement, RiverProps>(
107
107
  >
108
108
  {/* Text content with 16px gap - uses primitive spacing tokens */}
109
109
  <div className="flex flex-col gap-spacing-16">
110
- <h2 className="typography-headline-small text-gray-900">
111
- {headline}
112
- </h2>
110
+ <h2 className="typography-h4 text-gray-900">{headline}</h2>
113
111
  <p className="typography-body-small text-gray-800">{body}</p>
114
112
  </div>
115
113
 
@@ -220,7 +220,7 @@ describe("Tout", () => {
220
220
  );
221
221
 
222
222
  const headline = page.getByRole("heading", { level: 2 });
223
- await expect.element(headline).toHaveClass(/typography-headline-small/);
223
+ await expect.element(headline).toHaveClass(/typography-h4/);
224
224
  await expect.element(headline).toHaveClass(/text-gray-900/);
225
225
  });
226
226
 
@@ -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="charcoalOutline">Secondary</Button>}
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-headline-small",
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
- ### Product Typography
43
+ ### Headings
44
44
 
45
- For app UIs, dashboards, and interfaces:
45
+ For headlines and titles:
46
46
 
47
47
  ```html
48
- <h1 class="typography-product-title-large">Page Title</h1>
49
- <h2 class="typography-product-headline-medium">Section Header</h2>
50
- <p class="typography-product-body-medium">Body text content...</p>
51
- <span class="typography-product-caption-small">Caption text</span>
52
- <button class="typography-product-button-medium">Click me</button>
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
- Available styles: `typography-product-{category}-{size}` where category is: `title`, `headline`, `body`, `label`, `caption`, `button`, `input`, `link`, `navigation`, `overline`, `tabbar`
56
+ ### Body Text
56
57
 
57
- ### Brand Typography
58
+ For body content:
58
59
 
59
- For landing pages and promotional content:
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
- <h1 class="typography-brand-medium-display-hero">Hero</h1>
63
- <h2 class="typography-brand-medium-headline-large">Big Headline</h2>
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
- Available namespaces: `brand-large` (desktop), `brand-medium` (tablet), `brand-small` (mobile)
75
+ ### UI Typography
68
76
 
69
- Available styles: `typography-brand-{breakpoint}-{category}-{size}` where category is: `display`, `headline`, `subheading`, `body`, `label`, `caption`, `button`, `link`
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-subheading-small text-text-primary mb-4">
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">