@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.
Files changed (79) hide show
  1. package/dist/component-registry.md +1310 -127
  2. package/dist/components/atoms/button/button.d.ts +55 -47
  3. package/dist/components/atoms/button/button.figma.d.ts +1 -0
  4. package/dist/components/atoms/input/input.d.ts +24 -24
  5. package/dist/components/atoms/popover/popover.d.ts +195 -0
  6. package/dist/components/atoms/select/select.d.ts +24 -24
  7. package/dist/components/atoms/tooltip/tooltip.d.ts +161 -0
  8. package/dist/components/organisms/card/card.d.ts +1 -1
  9. package/dist/components/sections/hero/hero.d.ts +2 -2
  10. package/dist/components/sections/tout/tout.d.ts +3 -3
  11. package/dist/components/shared/floating-arrow.d.ts +34 -0
  12. package/dist/index.d.ts +8 -0
  13. package/dist/index.js +11602 -8499
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/form-control.d.ts +25 -24
  16. package/dist/tokens.css +4797 -3940
  17. package/package.json +2 -1
  18. package/src/components/atoms/accordion/accordion.stories.tsx +1 -1
  19. package/src/components/atoms/accordion/accordion.tsx +2 -2
  20. package/src/components/atoms/button/button.figma.tsx +37 -0
  21. package/src/components/atoms/button/button.stories.tsx +236 -140
  22. package/src/components/atoms/button/button.test.tsx +289 -5
  23. package/src/components/atoms/button/button.tsx +37 -33
  24. package/src/components/atoms/button/button.visual.test.tsx +26 -76
  25. package/src/components/atoms/button/icon-button.stories.tsx +44 -101
  26. package/src/components/atoms/button/icon-button.test.tsx +26 -94
  27. package/src/components/atoms/button/icon-button.tsx +3 -3
  28. package/src/components/atoms/input/input-group.stories.tsx +4 -8
  29. package/src/components/atoms/input/input-group.test.tsx +14 -28
  30. package/src/components/atoms/input/input-group.tsx +57 -32
  31. package/src/components/atoms/input/input.stories.tsx +14 -18
  32. package/src/components/atoms/input/input.test.tsx +4 -20
  33. package/src/components/atoms/input/input.tsx +16 -9
  34. package/src/components/atoms/pager-control/pager-control.stories.tsx +6 -8
  35. package/src/components/atoms/pager-control/pager-control.tsx +12 -12
  36. package/src/components/atoms/popover/index.ts +30 -0
  37. package/src/components/atoms/popover/popover.stories.tsx +531 -0
  38. package/src/components/atoms/popover/popover.test.tsx +486 -0
  39. package/src/components/atoms/popover/popover.tsx +488 -0
  40. package/src/components/atoms/select/select.tsx +12 -8
  41. package/src/components/atoms/tooltip/index.ts +24 -0
  42. package/src/components/atoms/tooltip/tooltip.stories.tsx +348 -0
  43. package/src/components/atoms/tooltip/tooltip.test.tsx +363 -0
  44. package/src/components/atoms/tooltip/tooltip.tsx +347 -0
  45. package/src/components/dev-tools/dev-toolbar/dev-toolbar.stories.tsx +8 -13
  46. package/src/components/dev-tools/dev-toolbar/dev-toolbar.tsx +3 -3
  47. package/src/components/organisms/card/card.stories.tsx +19 -19
  48. package/src/components/organisms/card/card.tsx +1 -1
  49. package/src/components/organisms/card/card.visual.test.tsx +11 -11
  50. package/src/components/organisms/navbar/navbar.visual.test.tsx +2 -2
  51. package/src/components/organisms/us-gov-banner/us-gov-banner.tsx +2 -2
  52. package/src/components/sections/banner/banner.stories.tsx +1 -5
  53. package/src/components/sections/banner/banner.test.tsx +2 -2
  54. package/src/components/sections/banner/banner.tsx +6 -6
  55. package/src/components/sections/card-grid/card-grid.tsx +4 -4
  56. package/src/components/sections/hero/hero.stories.tsx +7 -7
  57. package/src/components/sections/hero/hero.tsx +10 -11
  58. package/src/components/sections/prose/prose.tsx +2 -2
  59. package/src/components/sections/river/river.test.tsx +3 -3
  60. package/src/components/sections/river/river.tsx +6 -12
  61. package/src/components/sections/tout/tout.stories.tsx +7 -31
  62. package/src/components/sections/tout/tout.tsx +9 -9
  63. package/src/components/sections/two-column-section/two-column-section.tsx +7 -9
  64. package/src/components/shared/floating-arrow.tsx +78 -0
  65. package/src/components/shared/index.ts +5 -0
  66. package/src/index.ts +57 -0
  67. package/src/lib/form-control.ts +8 -6
  68. package/src/stories/grid-system.stories.tsx +309 -0
  69. package/src/stories/{ThemeProvider.stories.tsx → theme-provider.stories.tsx} +7 -19
  70. package/src/stories/{TokenShowcase.stories.tsx → token-showcase.stories.tsx} +1 -1
  71. package/src/stories/{TokenShowcase.tsx → token-showcase.tsx} +34 -34
  72. package/src/styles.css +3 -3
  73. package/src/tests/token-resolution.test.tsx +6 -9
  74. package/src/theme/hooks.ts +1 -1
  75. package/src/theme/index.ts +1 -1
  76. package/src/theme/theme-provider.test.tsx +270 -0
  77. package/src/theme/{ThemeProvider.tsx → theme-provider.tsx} +18 -2
  78. package/src/stories/GridSystem.stories.tsx +0 -84
  79. /package/src/stories/{Introduction.mdx → introduction.mdx} +0 -0
@@ -3,6 +3,28 @@ import { page, userEvent } from "vitest/browser";
3
3
  import { render } from "vitest-browser-react";
4
4
  import { Button } from "./button";
5
5
 
6
+ /**
7
+ * Helper to get computed styles of an element
8
+ */
9
+ function getStyles(element: HTMLElement) {
10
+ return window.getComputedStyle(element);
11
+ }
12
+
13
+ /**
14
+ * Helper to convert rgb/rgba to hex for easier comparison
15
+ */
16
+ function _rgbToHex(rgb: string): string {
17
+ // Handle rgba format
18
+ const match = rgb.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/);
19
+ if (!match) return rgb;
20
+
21
+ const r = parseInt(match[1], 10);
22
+ const g = parseInt(match[2], 10);
23
+ const b = parseInt(match[3], 10);
24
+
25
+ return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
26
+ }
27
+
6
28
  describe("Button", () => {
7
29
  describe("Accessibility", () => {
8
30
  test("has correct button role", async () => {
@@ -124,12 +146,274 @@ describe("Button", () => {
124
146
  });
125
147
  });
126
148
 
127
- describe("Variants", () => {
128
- test("applies primary variant classes by default", async () => {
129
- render(<Button>Default</Button>);
149
+ describe("Variant Styles", () => {
150
+ test("default variant has dark background", async () => {
151
+ render(<Button variant="default">Default</Button>);
130
152
  const button = page.getByRole("button", { name: "Default" });
131
- // Button uses semantic token classes
132
- await expect.element(button).toHaveClass(/bg-ui-button-primary-bg/);
153
+ await expect.element(button).toBeInTheDocument();
154
+
155
+ const element = button.element();
156
+ const styles = getStyles(element);
157
+
158
+ // Default variant should have a dark background (gray-1200)
159
+ // The actual color depends on CSS variables, but it should not be transparent
160
+ expect(styles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
161
+ expect(styles.backgroundColor).not.toBe("transparent");
162
+ });
163
+
164
+ test("primary variant has blue background", async () => {
165
+ render(<Button variant="primary">Primary</Button>);
166
+ const button = page.getByRole("button", { name: "Primary" });
167
+ await expect.element(button).toBeInTheDocument();
168
+
169
+ const element = button.element();
170
+ const styles = getStyles(element);
171
+
172
+ // Primary variant should have a visible background
173
+ expect(styles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
174
+ expect(styles.backgroundColor).not.toBe("transparent");
175
+ });
176
+
177
+ test("destructive variant has red-tinted background", async () => {
178
+ render(<Button variant="destructive">Delete</Button>);
179
+ const button = page.getByRole("button", { name: "Delete" });
180
+ await expect.element(button).toBeInTheDocument();
181
+
182
+ const element = button.element();
183
+ const styles = getStyles(element);
184
+
185
+ // Destructive variant should have a visible background
186
+ expect(styles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
187
+ expect(styles.backgroundColor).not.toBe("transparent");
188
+ });
189
+
190
+ test("ghost variant has transparent background", async () => {
191
+ render(<Button variant="ghost">Ghost</Button>);
192
+ const button = page.getByRole("button", { name: "Ghost" });
193
+ await expect.element(button).toBeInTheDocument();
194
+
195
+ const element = button.element();
196
+ const styles = getStyles(element);
197
+
198
+ // Ghost variant should have transparent background
199
+ expect(
200
+ styles.backgroundColor === "rgba(0, 0, 0, 0)" ||
201
+ styles.backgroundColor === "transparent",
202
+ ).toBe(true);
203
+ });
204
+
205
+ test("link variant has no background and underline on hover behavior", async () => {
206
+ render(<Button variant="link">Link</Button>);
207
+ const button = page.getByRole("button", { name: "Link" });
208
+ await expect.element(button).toBeInTheDocument();
209
+
210
+ const element = button.element();
211
+ const styles = getStyles(element);
212
+
213
+ // Link variant should have transparent background
214
+ expect(
215
+ styles.backgroundColor === "rgba(0, 0, 0, 0)" ||
216
+ styles.backgroundColor === "transparent",
217
+ ).toBe(true);
218
+ });
219
+
220
+ test("outline variant has visible border", async () => {
221
+ render(<Button variant="outline">Outline</Button>);
222
+ const button = page.getByRole("button", { name: "Outline" });
223
+ await expect.element(button).toBeInTheDocument();
224
+
225
+ const element = button.element();
226
+ const styles = getStyles(element);
227
+
228
+ // Outline variant should have a visible border
229
+ const borderWidth = parseFloat(styles.borderWidth) || 0;
230
+ expect(borderWidth).toBeGreaterThan(0);
231
+ });
232
+
233
+ test("secondary variant has light gray background with border", async () => {
234
+ render(<Button variant="secondary">Secondary</Button>);
235
+ const button = page.getByRole("button", { name: "Secondary" });
236
+ await expect.element(button).toBeInTheDocument();
237
+
238
+ const element = button.element();
239
+ const styles = getStyles(element);
240
+
241
+ // Secondary variant should have a visible background
242
+ expect(styles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
243
+ expect(styles.backgroundColor).not.toBe("transparent");
244
+
245
+ // Secondary should also have a border
246
+ const borderWidth = parseFloat(styles.borderWidth) || 0;
247
+ expect(borderWidth).toBeGreaterThan(0);
248
+ });
249
+ });
250
+
251
+ describe("Size Styles", () => {
252
+ test("small size has 32px height", async () => {
253
+ render(<Button size="sm">Small</Button>);
254
+ const button = page.getByRole("button", { name: "Small" });
255
+ await expect.element(button).toBeInTheDocument();
256
+
257
+ const element = button.element();
258
+ const styles = getStyles(element);
259
+
260
+ // Small button should be 32px height
261
+ expect(styles.height).toBe("32px");
262
+ });
263
+
264
+ test("default size has 36px height", async () => {
265
+ render(<Button size="default">Default</Button>);
266
+ const button = page.getByRole("button", { name: "Default" });
267
+ await expect.element(button).toBeInTheDocument();
268
+
269
+ const element = button.element();
270
+ const styles = getStyles(element);
271
+
272
+ // Default button should be 36px height
273
+ expect(styles.height).toBe("36px");
274
+ });
275
+
276
+ test("large size has 40px height", async () => {
277
+ render(<Button size="lg">Large</Button>);
278
+ const button = page.getByRole("button", { name: "Large" });
279
+ await expect.element(button).toBeInTheDocument();
280
+
281
+ const element = button.element();
282
+ const styles = getStyles(element);
283
+
284
+ // Large button should be 40px height
285
+ expect(styles.height).toBe("40px");
286
+ });
287
+
288
+ test("size affects padding appropriately", async () => {
289
+ render(
290
+ <>
291
+ <Button size="sm">Small</Button>
292
+ <Button size="lg">Large</Button>
293
+ </>,
294
+ );
295
+ const smallButton = page.getByRole("button", { name: "Small" });
296
+ const largeButton = page.getByRole("button", { name: "Large" });
297
+
298
+ const smallStyles = getStyles(smallButton.element());
299
+ const largeStyles = getStyles(largeButton.element());
300
+
301
+ const smallPaddingLeft = parseFloat(smallStyles.paddingLeft);
302
+ const largePaddingLeft = parseFloat(largeStyles.paddingLeft);
303
+
304
+ // Large should have more padding than small
305
+ expect(largePaddingLeft).toBeGreaterThan(smallPaddingLeft);
306
+ });
307
+
308
+ test("size affects border radius appropriately", async () => {
309
+ render(
310
+ <>
311
+ <Button size="sm">Small</Button>
312
+ <Button size="lg">Large</Button>
313
+ </>,
314
+ );
315
+ const smallButton = page.getByRole("button", { name: "Small" });
316
+ const largeButton = page.getByRole("button", { name: "Large" });
317
+
318
+ const smallStyles = getStyles(smallButton.element());
319
+ const largeStyles = getStyles(largeButton.element());
320
+
321
+ const smallRadius = parseFloat(smallStyles.borderRadius);
322
+ const largeRadius = parseFloat(largeStyles.borderRadius);
323
+
324
+ // Large should have more border radius than small
325
+ // Figma: sm=4px, default=6px, lg=10px
326
+ expect(largeRadius).toBeGreaterThan(smallRadius);
327
+ });
328
+ });
329
+
330
+ describe("Disabled State Styles", () => {
331
+ test("disabled button has reduced opacity", async () => {
332
+ render(<Button disabled>Disabled</Button>);
333
+ const button = page.getByRole("button", { name: "Disabled" });
334
+ await expect.element(button).toBeInTheDocument();
335
+
336
+ const element = button.element();
337
+ const styles = getStyles(element);
338
+
339
+ // Disabled button should have opacity of 0.5
340
+ expect(parseFloat(styles.opacity)).toBe(0.5);
341
+ });
342
+
343
+ test("disabled button has pointer-events none", async () => {
344
+ render(<Button disabled>Disabled</Button>);
345
+ const button = page.getByRole("button", { name: "Disabled" });
346
+ await expect.element(button).toBeInTheDocument();
347
+
348
+ const element = button.element();
349
+ const styles = getStyles(element);
350
+
351
+ expect(styles.pointerEvents).toBe("none");
352
+ });
353
+ });
354
+
355
+ describe("Data Attributes", () => {
356
+ test("button has correct data-variant attribute", async () => {
357
+ render(<Button variant="destructive">Test</Button>);
358
+ const button = page.getByRole("button", { name: "Test" });
359
+
360
+ await expect
361
+ .element(button)
362
+ .toHaveAttribute("data-variant", "destructive");
363
+ });
364
+
365
+ test("button has correct data-size attribute", async () => {
366
+ render(<Button size="lg">Test</Button>);
367
+ const button = page.getByRole("button", { name: "Test" });
368
+
369
+ await expect.element(button).toHaveAttribute("data-size", "lg");
370
+ });
371
+
372
+ test("default variant and size are reflected in data attributes", async () => {
373
+ render(<Button>Test</Button>);
374
+ const button = page.getByRole("button", { name: "Test" });
375
+
376
+ await expect.element(button).toHaveAttribute("data-variant", "default");
377
+ await expect.element(button).toHaveAttribute("data-size", "default");
378
+ });
379
+ });
380
+
381
+ describe("Layout Styles", () => {
382
+ test("button uses flexbox for content alignment", async () => {
383
+ render(<Button>Test</Button>);
384
+ const button = page.getByRole("button", { name: "Test" });
385
+ await expect.element(button).toBeInTheDocument();
386
+
387
+ const element = button.element();
388
+ const styles = getStyles(element);
389
+
390
+ expect(styles.display).toBe("inline-flex");
391
+ expect(styles.alignItems).toBe("center");
392
+ expect(styles.justifyContent).toBe("center");
393
+ });
394
+
395
+ test("button has cursor pointer", async () => {
396
+ render(<Button>Test</Button>);
397
+ const button = page.getByRole("button", { name: "Test" });
398
+ await expect.element(button).toBeInTheDocument();
399
+
400
+ const element = button.element();
401
+ const styles = getStyles(element);
402
+
403
+ expect(styles.cursor).toBe("pointer");
404
+ });
405
+
406
+ test("button has gap for icon-text spacing", async () => {
407
+ render(<Button>Test</Button>);
408
+ const button = page.getByRole("button", { name: "Test" });
409
+ await expect.element(button).toBeInTheDocument();
410
+
411
+ const element = button.element();
412
+ const styles = getStyles(element);
413
+
414
+ // Gap should be set (6px from Figma)
415
+ const gap = parseFloat(styles.gap);
416
+ expect(gap).toBeGreaterThan(0);
133
417
  });
134
418
  });
135
419
  });
@@ -12,17 +12,18 @@ import { type ButtonTheme, buttonThemeToStyleVars } from "../../../lib/theme";
12
12
  * Button component based on Figma Button component
13
13
  *
14
14
  * Variants (matches Figma):
15
- * - primary: Filled brand button (indigo background)
16
- * - primary-outline: Outlined brand button (indigo border/text)
17
- * - secondary: Filled neutral button (white background, for dark backgrounds)
18
- * - secondary-outline: Outlined neutral button (white border/text, for dark backgrounds)
19
- * - ghost: Transparent button with subtle hover (for light backgrounds)
20
- * - ghost-inverse: Transparent button with subtle hover (for dark backgrounds)
15
+ * - primary: Blue filled button for primary actions
16
+ * - default: Dark filled button for secondary prominence
17
+ * - secondary: Light gray filled button with subtle border
18
+ * - destructive: Red filled button for destructive actions
19
+ * - outline: Bordered button with transparent background
20
+ * - ghost: Transparent button with subtle hover
21
+ * - link: Text-only button with underline on hover
21
22
  *
22
- * Sizes:
23
- * - lg: Large buttons (56px height)
24
- * - md: Medium buttons (40px height) - default
25
- * - sm: Small buttons (28px height)
23
+ * Sizes (matches Figma):
24
+ * - sm: Small buttons (32px height)
25
+ * - default: Default buttons (36px height)
26
+ * - lg: Large buttons (40px height)
26
27
  *
27
28
  * For icon-only buttons, use the IconButton component instead.
28
29
  *
@@ -30,40 +31,43 @@ import { type ButtonTheme, buttonThemeToStyleVars } from "../../../lib/theme";
30
31
  * Pass a `theme` prop to override default colors via CSS custom properties.
31
32
  */
32
33
  const buttonVariants = tv({
33
- base: "inline-flex items-center justify-center gap-6 whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 border-solid",
34
+ base: "inline-flex items-center justify-center gap-spatial-ui-button-gap-icon-text whitespace-nowrap transition-colors duration-150 cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-1 disabled:pointer-events-none disabled:opacity-50",
34
35
  variants: {
35
36
  variant: {
36
- // Primary - filled brand button
37
+ // Primary - blue filled button
37
38
  primary:
38
- "bg-button-primary-bg text-button-primary-text hover:bg-button-primary-bg-hover hover:text-button-primary-text-hover border-transparent focus-visible:ring-button-primary-bg",
39
- // Primary Outline - outlined brand button
40
- "primary-outline":
41
- "bg-button-primary-outline-bg text-button-primary-outline-text border border-button-primary-outline-border hover:bg-button-primary-outline-bg-hover hover:text-button-primary-outline-text-hover hover:border-button-primary-outline-border-hover focus-visible:ring-button-primary-outline-border",
42
- // Secondary - filled neutral button (for dark backgrounds)
39
+ "bg-button-primary-bg text-button-primary-text hover:bg-button-primary-bg-hover active:bg-button-primary-bg-active border-transparent",
40
+ // Default - dark filled button
41
+ default:
42
+ "bg-button-default-bg text-button-default-text hover:bg-button-default-bg-hover active:bg-button-default-bg-active border-transparent",
43
+ // Secondary - light gray filled with subtle border
43
44
  secondary:
44
- "bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg-hover hover:text-button-secondary-text-hover border-transparent focus-visible:ring-button-secondary-bg focus-visible:ring-offset-gray-1000",
45
- // Secondary Outline - outlined neutral button (for dark backgrounds)
46
- "secondary-outline":
47
- "bg-button-secondary-outline-bg text-button-secondary-outline-text border border-button-secondary-outline-border hover:bg-button-secondary-outline-bg-hover hover:text-button-secondary-outline-text-hover hover:border-button-secondary-outline-border-hover focus-visible:ring-button-secondary-outline-border focus-visible:ring-offset-gray-1000",
48
- // Ghost - transparent button (for light backgrounds)
45
+ "bg-button-secondary-bg text-button-secondary-text hover:bg-button-secondary-bg-hover active:bg-button-secondary-bg-active border border-button-secondary-border",
46
+ // Destructive - red filled button
47
+ destructive:
48
+ "bg-button-destructive-bg text-button-destructive-text hover:bg-button-destructive-bg-hover active:bg-button-destructive-bg-active border-transparent",
49
+ // Outline - bordered with transparent background
50
+ outline:
51
+ "bg-button-outline-bg text-button-outline-text hover:bg-button-outline-bg-hover active:bg-button-outline-bg-active border border-button-outline-border hover:border-button-outline-border-hover",
52
+ // Ghost - transparent with subtle hover
49
53
  ghost:
50
- "bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg-hover hover:text-button-ghost-text-hover border-transparent focus-visible:ring-gray-1000",
51
- // Ghost Inverse - transparent button (for dark backgrounds)
52
- "ghost-inverse":
53
- "bg-button-ghost-inverse-bg text-button-ghost-inverse-text hover:bg-button-ghost-inverse-bg-hover hover:text-button-ghost-inverse-text-hover border-transparent focus-visible:ring-gray-50 focus-visible:ring-offset-gray-1000",
54
+ "bg-button-ghost-bg text-button-ghost-text hover:bg-button-ghost-bg-hover active:bg-button-ghost-bg-active border-transparent",
55
+ // Link - text only with underline on hover
56
+ link: "bg-transparent text-button-link-text hover:text-button-link-text-hover hover:underline active:text-button-link-text-hover border-transparent underline-offset-4",
54
57
  // Themed - uses CSS custom properties for styling
55
58
  themed:
56
59
  "[background:var(--btn-bg)] [color:var(--btn-text)] [border-color:var(--btn-border-color,transparent)] hover:[background:var(--btn-bg-hover,var(--btn-bg))] active:[background:var(--btn-bg-active,var(--btn-bg-hover,var(--btn-bg)))]",
57
60
  },
58
61
  size: {
59
- lg: "px-32 py-20 h-56 rounded-10 typography-large-button-large",
60
- md: "px-20 py-12 h-40 rounded-6 typography-medium-button-medium",
61
- sm: "px-12 py-8 h-28 rounded-4 typography-small-button-small",
62
+ sm: "typography-ui-text-xs h-spatial-ui-button-height-small px-spatial-ui-button-padding-x-small py-spatial-ui-button-padding-y-small rounded-surface-button-small",
63
+ default:
64
+ "typography-ui-text-sm h-spatial-ui-button-height-medium px-spatial-ui-button-padding-x-medium py-spatial-ui-button-padding-y-medium rounded-surface-button-medium",
65
+ lg: "typography-ui-text-md h-spatial-ui-button-height-large px-spatial-ui-button-padding-x-large py-spatial-ui-button-padding-y-large rounded-surface-button-large",
62
66
  },
63
67
  },
64
68
  defaultVariants: {
65
- variant: "primary",
66
- size: "md",
69
+ variant: "default",
70
+ size: "default",
67
71
  },
68
72
  });
69
73
 
@@ -100,8 +104,8 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
100
104
  const combinedStyles = hasTheme ? { ...themeStyles, ...style } : style;
101
105
 
102
106
  // Resolve actual values for data attributes
103
- const resolvedVariant = effectiveVariant ?? "primary";
104
- const resolvedSize = size ?? "md";
107
+ const resolvedVariant = effectiveVariant ?? "default";
108
+ const resolvedSize = size ?? "default";
105
109
 
106
110
  return (
107
111
  <BaseButton
@@ -4,115 +4,66 @@ import { page } from "vitest/browser";
4
4
  import { Button } from "./button";
5
5
 
6
6
  describe("Button Visual Regression", () => {
7
- // Solid variants
8
- test("solid dark variant renders correctly", async () => {
9
- render(
10
- <Button variant="solid" colorScheme="dark">
11
- Solid Dark Button
12
- </Button>,
13
- );
14
-
15
- await expect(
16
- page.getByRole("button", { name: "Solid Dark Button" }),
17
- ).toMatchScreenshot("button-solid-dark");
18
- });
19
-
20
- test("solid light variant renders correctly", async () => {
21
- render(
22
- <div style={{ background: "#1a1a1a", padding: "20px" }}>
23
- <Button variant="solid" colorScheme="light">
24
- Solid Light Button
25
- </Button>
26
- </div>,
27
- );
7
+ test("primary variant renders correctly", async () => {
8
+ render(<Button variant="primary">Primary Button</Button>);
28
9
 
29
10
  await expect(
30
- page.getByRole("button", { name: "Solid Light Button" }),
31
- ).toMatchScreenshot("button-solid-light");
11
+ page.getByRole("button", { name: "Primary Button" }),
12
+ ).toMatchScreenshot("button-primary");
32
13
  });
33
14
 
34
- // Outline variants
35
- test("outline dark variant renders correctly", async () => {
36
- render(
37
- <Button variant="outline" colorScheme="dark">
38
- Outline Dark Button
39
- </Button>,
40
- );
15
+ test("primary-outline variant renders correctly", async () => {
16
+ render(<Button variant="primary-outline">Primary Outline Button</Button>);
41
17
 
42
18
  await expect(
43
- page.getByRole("button", { name: "Outline Dark Button" }),
44
- ).toMatchScreenshot("button-outline-dark");
19
+ page.getByRole("button", { name: "Primary Outline Button" }),
20
+ ).toMatchScreenshot("button-primary-outline");
45
21
  });
46
22
 
47
- test("outline light variant renders correctly", async () => {
23
+ test("secondary variant renders correctly", async () => {
48
24
  render(
49
25
  <div style={{ background: "#1a1a1a", padding: "20px" }}>
50
- <Button variant="outline" colorScheme="light">
51
- Outline Light Button
52
- </Button>
26
+ <Button variant="secondary">Secondary Button</Button>
53
27
  </div>,
54
28
  );
55
29
 
56
30
  await expect(
57
- page.getByRole("button", { name: "Outline Light Button" }),
58
- ).toMatchScreenshot("button-outline-light");
59
- });
60
-
61
- // Ghost variants
62
- test("ghost dark variant renders correctly", async () => {
63
- render(
64
- <Button variant="ghost" colorScheme="dark">
65
- Ghost Dark Button
66
- </Button>,
67
- );
68
-
69
- await expect(
70
- page.getByRole("button", { name: "Ghost Dark Button" }),
71
- ).toMatchScreenshot("button-ghost-dark");
31
+ page.getByRole("button", { name: "Secondary Button" }),
32
+ ).toMatchScreenshot("button-secondary");
72
33
  });
73
34
 
74
- test("ghost light variant renders correctly", async () => {
35
+ test("secondary-outline variant renders correctly", async () => {
75
36
  render(
76
37
  <div style={{ background: "#1a1a1a", padding: "20px" }}>
77
- <Button variant="ghost" colorScheme="light">
78
- Ghost Light Button
79
- </Button>
38
+ <Button variant="secondary-outline">Secondary Outline Button</Button>
80
39
  </div>,
81
40
  );
82
41
 
83
42
  await expect(
84
- page.getByRole("button", { name: "Ghost Light Button" }),
85
- ).toMatchScreenshot("button-ghost-light");
43
+ page.getByRole("button", { name: "Secondary Outline Button" }),
44
+ ).toMatchScreenshot("button-secondary-outline");
86
45
  });
87
46
 
88
- // Subtle variants
89
- test("subtle dark variant renders correctly", async () => {
90
- render(
91
- <Button variant="subtle" colorScheme="dark">
92
- Subtle Dark Button
93
- </Button>,
94
- );
47
+ test("ghost variant renders correctly", async () => {
48
+ render(<Button variant="ghost">Ghost Button</Button>);
95
49
 
96
50
  await expect(
97
- page.getByRole("button", { name: "Subtle Dark Button" }),
98
- ).toMatchScreenshot("button-subtle-dark");
51
+ page.getByRole("button", { name: "Ghost Button" }),
52
+ ).toMatchScreenshot("button-ghost");
99
53
  });
100
54
 
101
- test("subtle light variant renders correctly", async () => {
55
+ test("ghost-inverse variant renders correctly", async () => {
102
56
  render(
103
57
  <div style={{ background: "#1a1a1a", padding: "20px" }}>
104
- <Button variant="subtle" colorScheme="light">
105
- Subtle Light Button
106
- </Button>
58
+ <Button variant="ghost-inverse">Ghost Inverse Button</Button>
107
59
  </div>,
108
60
  );
109
61
 
110
62
  await expect(
111
- page.getByRole("button", { name: "Subtle Light Button" }),
112
- ).toMatchScreenshot("button-subtle-light");
63
+ page.getByRole("button", { name: "Ghost Inverse Button" }),
64
+ ).toMatchScreenshot("button-ghost-inverse");
113
65
  });
114
66
 
115
- // Size variants
116
67
  test("small size renders correctly", async () => {
117
68
  render(<Button size="sm">Small Button</Button>);
118
69
 
@@ -121,8 +72,8 @@ describe("Button Visual Regression", () => {
121
72
  ).toMatchScreenshot("button-size-small");
122
73
  });
123
74
 
124
- test("medium (default) size renders correctly", async () => {
125
- render(<Button size="default">Medium Button</Button>);
75
+ test("medium size renders correctly", async () => {
76
+ render(<Button size="md">Medium Button</Button>);
126
77
 
127
78
  await expect(
128
79
  page.getByRole("button", { name: "Medium Button" }),
@@ -137,7 +88,6 @@ describe("Button Visual Regression", () => {
137
88
  ).toMatchScreenshot("button-size-large");
138
89
  });
139
90
 
140
- // Disabled state
141
91
  test("disabled state renders correctly", async () => {
142
92
  render(<Button disabled>Disabled Button</Button>);
143
93