@ponti-studios/ui 0.1.2 → 0.4.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 (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -7
  3. package/THIRD_PARTY_NOTICES.md +17 -0
  4. package/dist/index.js +988 -640
  5. package/dist/index.js.map +1 -1
  6. package/package.json +41 -41
  7. package/src/animations.css +42 -383
  8. package/src/components/data-display/accordion.tsx +22 -10
  9. package/src/components/data-display/filter-chip.stories.tsx +38 -0
  10. package/src/components/data-display/filter-chip.tsx +10 -4
  11. package/src/components/data-display/index.ts +5 -0
  12. package/src/components/data-display/metric-card.stories.tsx +48 -0
  13. package/src/components/data-display/metric-card.tsx +20 -0
  14. package/src/components/data-display/sort-controls.stories.tsx +45 -0
  15. package/src/components/data-display/sort-controls.tsx +99 -0
  16. package/src/components/data-display/sort-row.tsx +74 -0
  17. package/src/components/data-display/table.tsx +5 -9
  18. package/src/components/feedback/alert.tsx +4 -4
  19. package/src/components/feedback/empty-state.tsx +16 -6
  20. package/src/components/feedback/file-upload-status-badge.stories.tsx +28 -0
  21. package/src/components/feedback/file-upload-status-badge.tsx +33 -0
  22. package/src/components/feedback/file-upload-status.stories.tsx +57 -0
  23. package/src/components/feedback/file-upload-status.tsx +152 -0
  24. package/src/components/feedback/index.ts +9 -0
  25. package/src/components/feedback/progress-bar.stories.tsx +39 -0
  26. package/src/components/feedback/progress-bar.tsx +21 -0
  27. package/src/components/feedback/progress.stories.tsx +8 -6
  28. package/src/components/feedback/progress.tsx +3 -3
  29. package/src/components/feedback/skeleton.stories.tsx +36 -0
  30. package/src/components/feedback/skeleton.tsx +7 -0
  31. package/src/components/feedback/spinner.tsx +2 -2
  32. package/src/components/feedback/update-guard.stories.tsx +32 -0
  33. package/src/components/feedback/update-guard.tsx +280 -0
  34. package/src/components/forms/calendar.tsx +14 -11
  35. package/src/components/forms/date-month-select.stories.tsx +26 -0
  36. package/src/components/forms/date-month-select.tsx +70 -0
  37. package/src/components/forms/date-picker.tsx +3 -3
  38. package/src/components/forms/drop-zone.stories.tsx +56 -0
  39. package/src/components/forms/drop-zone.tsx +146 -0
  40. package/src/components/forms/entity-select.stories.tsx +65 -0
  41. package/src/components/forms/entity-select.tsx +79 -0
  42. package/src/components/forms/field.tsx +3 -3
  43. package/src/components/forms/group-by-select.stories.tsx +26 -0
  44. package/src/components/forms/group-by-select.tsx +47 -0
  45. package/src/components/forms/index.ts +10 -0
  46. package/src/components/forms/input.stories.tsx +4 -4
  47. package/src/components/forms/input.tsx +1 -1
  48. package/src/components/forms/passkey-management.stories.tsx +81 -0
  49. package/src/components/forms/passkey-management.tsx +138 -0
  50. package/src/components/forms/radio-group.tsx +14 -13
  51. package/src/components/forms/select.stories.tsx +4 -4
  52. package/src/components/forms/select.tsx +47 -48
  53. package/src/components/forms/slider.stories.tsx +5 -2
  54. package/src/components/forms/slider.tsx +39 -17
  55. package/src/components/forms/stepper.tsx +4 -4
  56. package/src/components/forms/switch.stories.tsx +1 -1
  57. package/src/components/forms/switch.tsx +2 -2
  58. package/src/components/forms/textarea.stories.tsx +2 -2
  59. package/src/components/forms/textarea.tsx +1 -1
  60. package/src/components/layout/index.ts +3 -0
  61. package/src/components/layout/scroll-area.stories.tsx +4 -4
  62. package/src/components/layout/scroll-area.tsx +23 -21
  63. package/src/components/layout/section-intro.stories.tsx +29 -0
  64. package/src/components/layout/section-intro.tsx +31 -0
  65. package/src/components/layout/surface-panel.stories.tsx +32 -0
  66. package/src/components/layout/surface-panel.tsx +15 -0
  67. package/src/components/navigation/app-navigation.stories.tsx +2 -2
  68. package/src/components/navigation/app-navigation.tsx +13 -13
  69. package/src/components/navigation/index.ts +4 -0
  70. package/src/components/navigation/pagination-controls.stories.tsx +41 -0
  71. package/src/components/navigation/pagination-controls.tsx +50 -0
  72. package/src/components/navigation/route-link.stories.tsx +30 -0
  73. package/src/components/navigation/route-link.tsx +42 -0
  74. package/src/components/navigation/tabs.stories.tsx +5 -5
  75. package/src/components/navigation/tabs.tsx +8 -8
  76. package/src/components/overlays/alert-dialog.tsx +22 -25
  77. package/src/components/overlays/command.tsx +98 -26
  78. package/src/components/overlays/dialog.tsx +5 -5
  79. package/src/components/overlays/dropdown-menu.stories.tsx +0 -5
  80. package/src/components/overlays/dropdown-menu.tsx +49 -31
  81. package/src/components/overlays/popover.stories.tsx +2 -2
  82. package/src/components/overlays/popover.tsx +43 -14
  83. package/src/components/overlays/sheet.stories.tsx +2 -2
  84. package/src/components/overlays/sheet.tsx +51 -17
  85. package/src/components/primitives/avatar.tsx +9 -5
  86. package/src/components/primitives/badge.tsx +11 -12
  87. package/src/components/primitives/button.tsx +49 -23
  88. package/src/components/primitives/card.stories.tsx +2 -2
  89. package/src/components/primitives/card.tsx +2 -5
  90. package/src/components/primitives/index.ts +2 -0
  91. package/src/components/primitives/label.stories.tsx +1 -1
  92. package/src/components/primitives/label.tsx +5 -6
  93. package/src/components/primitives/status-badge.stories.tsx +52 -0
  94. package/src/components/primitives/status-badge.tsx +33 -0
  95. package/src/constants/chart-colors.ts +7 -7
  96. package/src/hooks/index.ts +1 -0
  97. package/src/hooks/use-sort.ts +20 -0
  98. package/src/index.ts +30 -11
  99. package/src/styles.css +100 -974
  100. package/src/tokens/chart-colors.stories.tsx +192 -0
  101. package/src/tokens/color-systems.stories.tsx +20 -112
  102. package/src/tokens/foundations.stories.tsx +58 -100
  103. package/src/tokens/generated/dark.colors.generated.ts +29 -0
  104. package/src/tokens/generated/dark.css +29 -0
  105. package/src/tokens/generated/foundations.css +148 -0
  106. package/src/tokens/generated/foundations.generated.ts +451 -0
  107. package/src/tokens/generated/light.colors.generated.ts +29 -0
  108. package/src/tokens/generated/light.css +29 -0
  109. package/src/tokens/index.ts +79 -16
  110. package/src/tokens/layout.stories.tsx +23 -91
  111. package/src/tokens/colors.ts +0 -156
  112. package/src/tokens/motion.ts +0 -51
  113. package/src/tokens/radii.ts +0 -10
  114. package/src/tokens/shadows.ts +0 -34
  115. package/src/tokens/spacing.ts +0 -30
  116. package/src/tokens/typography.ts +0 -42
@@ -0,0 +1,192 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+
3
+ import { CHART_CSS_VARS } from "../constants/chart-colors";
4
+
5
+ const meta: Meta = { title: "Foundations/Charts", parameters: { layout: "fullscreen" } };
6
+ export default meta;
7
+ type Story = StoryObj<typeof meta>;
8
+
9
+ const SERIES = [
10
+ { key: "chart1", label: "chart-1" },
11
+ { key: "chart2", label: "chart-2" },
12
+ { key: "chart3", label: "chart-3" },
13
+ { key: "chart4", label: "chart-4" },
14
+ { key: "chart5", label: "chart-5" },
15
+ ] as const;
16
+
17
+ const STATUS = [
18
+ { key: "positive", label: "chart-positive", example: "▲ Up 12%" },
19
+ { key: "negative", label: "chart-negative", example: "▼ Down 4%" },
20
+ { key: "neutral", label: "chart-neutral", example: "— Flat" },
21
+ ] as const;
22
+
23
+ // A handful of arbitrary multi-series datasets, purely to show how the
24
+ // ordinal chart-1..5 palette reads next to itself in a real chart shape.
25
+ const BAR_DATA = [
26
+ [42, 68, 30, 55, 90],
27
+ [70, 25, 60, 40, 35],
28
+ [30, 55, 80, 20, 65],
29
+ ];
30
+
31
+ const LINE_POINTS: Record<string, Array<[number, number]>> = {
32
+ chart1: [
33
+ [0, 60],
34
+ [20, 40],
35
+ [40, 50],
36
+ [60, 20],
37
+ [80, 30],
38
+ [100, 10],
39
+ ],
40
+ chart2: [
41
+ [0, 80],
42
+ [20, 70],
43
+ [40, 75],
44
+ [60, 55],
45
+ [80, 60],
46
+ [100, 45],
47
+ ],
48
+ chart3: [
49
+ [0, 30],
50
+ [20, 45],
51
+ [40, 25],
52
+ [60, 50],
53
+ [80, 35],
54
+ [100, 55],
55
+ ],
56
+ chart4: [
57
+ [0, 90],
58
+ [20, 85],
59
+ [40, 65],
60
+ [60, 70],
61
+ [80, 50],
62
+ [100, 60],
63
+ ],
64
+ chart5: [
65
+ [0, 15],
66
+ [20, 30],
67
+ [40, 20],
68
+ [60, 40],
69
+ [80, 25],
70
+ [100, 20],
71
+ ],
72
+ };
73
+
74
+ export const CategoricalPalette: Story = {
75
+ render: () => (
76
+ <main className="bg-background text-foreground grid min-h-screen gap-10 p-6 md:p-10">
77
+ <header className="grid gap-2">
78
+ <p className="text-muted-foreground text-sm">Foundations / charts</p>
79
+ <h1 className="text-4xl font-semibold tracking-tight">Chart colors</h1>
80
+ <p className="text-muted-foreground max-w-2xl text-base">
81
+ <code className="font-mono">chart-1</code> through <code className="font-mono">
82
+ chart-5
83
+ </code>{" "}
84
+ are an ordinal palette for arbitrary categorical series (e.g. one line per country) —
85
+ there's no fixed meaning to a given slot, so they're assigned by index, not by what the
86
+ data represents. That's different from{" "}
87
+ <code className="font-mono">chart-positive</code>/<code className="font-mono">
88
+ chart-negative
89
+ </code>
90
+ /<code className="font-mono">chart-neutral</code> below, which carry real meaning
91
+ (up/down/flat) and should stay semantic in any chart that shows a directional delta.
92
+ </p>
93
+ </header>
94
+
95
+ <section className="grid gap-4">
96
+ <h2 className="text-2xl font-semibold">Series swatches</h2>
97
+ <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-5">
98
+ {SERIES.map(({ key, label }) => (
99
+ <div
100
+ key={key}
101
+ className="border-border grid min-h-20 content-end rounded-md border p-3"
102
+ style={{ backgroundColor: CHART_CSS_VARS[key] }}
103
+ >
104
+ <span className="bg-background text-foreground w-fit rounded px-1 font-mono text-xs">
105
+ {label}
106
+ </span>
107
+ </div>
108
+ ))}
109
+ </div>
110
+ </section>
111
+
112
+ <section className="border-border bg-card grid gap-5 rounded-xl border p-6">
113
+ <h2 className="text-2xl font-semibold">Grouped bar chart (mock)</h2>
114
+ <p className="text-muted-foreground max-w-2xl text-sm">
115
+ Three unrelated categories, five series each — a stress test for how distinguishable
116
+ the palette stays side by side.
117
+ </p>
118
+ <div className="flex items-end gap-8" style={{ height: 220 }}>
119
+ {BAR_DATA.map((group, groupIndex) => (
120
+ // biome-ignore lint/suspicious/noArrayIndexKey: static mock dataset
121
+ <div key={groupIndex} className="flex h-full items-end gap-1.5">
122
+ {group.map((value, seriesIndex) => (
123
+ <div
124
+ key={SERIES[seriesIndex].key}
125
+ className="w-6 rounded-t-sm"
126
+ style={{
127
+ height: `${value}%`,
128
+ backgroundColor: CHART_CSS_VARS[SERIES[seriesIndex].key],
129
+ }}
130
+ title={SERIES[seriesIndex].label}
131
+ />
132
+ ))}
133
+ </div>
134
+ ))}
135
+ </div>
136
+ <div className="flex flex-wrap gap-4">
137
+ {SERIES.map(({ key, label }) => (
138
+ <div key={key} className="flex items-center gap-2">
139
+ <span
140
+ className="size-2.5 rounded-full"
141
+ style={{ backgroundColor: CHART_CSS_VARS[key] }}
142
+ aria-hidden="true"
143
+ />
144
+ <span className="text-muted-foreground font-mono text-xs">{label}</span>
145
+ </div>
146
+ ))}
147
+ </div>
148
+ </section>
149
+
150
+ <section className="border-border bg-card grid gap-5 rounded-xl border p-6">
151
+ <h2 className="text-2xl font-semibold">Multi-line chart (mock)</h2>
152
+ <svg viewBox="0 0 100 100" preserveAspectRatio="none" className="h-56 w-full">
153
+ <title>Multi-series line chart using the chart-1..5 palette</title>
154
+ {Object.entries(LINE_POINTS).map(([key, points]) => (
155
+ <polyline
156
+ key={key}
157
+ fill="none"
158
+ strokeWidth={1.5}
159
+ vectorEffect="non-scaling-stroke"
160
+ stroke={CHART_CSS_VARS[key as keyof typeof CHART_CSS_VARS]}
161
+ points={points.map(([x, y]) => `${x},${y}`).join(" ")}
162
+ />
163
+ ))}
164
+ </svg>
165
+ </section>
166
+
167
+ <section className="grid gap-4">
168
+ <h2 className="text-2xl font-semibold">Semantic status colors</h2>
169
+ <p className="text-muted-foreground max-w-2xl text-sm">
170
+ Use these — not a numbered slot — whenever a value has an inherent direction.
171
+ </p>
172
+ <div className="grid grid-cols-1 gap-3 sm:grid-cols-3">
173
+ {STATUS.map(({ key, label, example }) => (
174
+ <div key={key} className="border-border grid gap-2 rounded-md border p-3">
175
+ <div className="flex items-center gap-2">
176
+ <span
177
+ className="size-2.5 rounded-full"
178
+ style={{ backgroundColor: CHART_CSS_VARS[key] }}
179
+ aria-hidden="true"
180
+ />
181
+ <span className="text-muted-foreground font-mono text-xs">{label}</span>
182
+ </div>
183
+ <span className="text-lg font-semibold" style={{ color: CHART_CSS_VARS[key] }}>
184
+ {example}
185
+ </span>
186
+ </div>
187
+ ))}
188
+ </div>
189
+ </section>
190
+ </main>
191
+ ),
192
+ };
@@ -1,133 +1,41 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
2
 
3
3
  import { Button } from "../components/primitives/button";
4
- import { colorSystems, colorTokenNames, type ColorMode } from "./colors";
5
-
6
- const meta: Meta = {
7
- title: "Foundations/Colors",
8
- parameters: { layout: "fullscreen" },
9
- };
4
+ import { colorTokenNames } from "./index";
10
5
 
6
+ const meta: Meta = { title: "Foundations/Colors", parameters: { layout: "fullscreen" } };
11
7
  export default meta;
12
8
  type Story = StoryObj<typeof meta>;
13
9
 
14
- const groups = {
15
- surfaces: colorTokenNames.filter((token) => token.startsWith("surface-")),
16
- content: colorTokenNames.filter((token) => token.startsWith("text-")),
17
- structure: colorTokenNames.filter((token) => token.startsWith("border-")),
18
- intent: colorTokenNames.filter(
19
- (token) =>
20
- token.startsWith("accent") ||
21
- token.startsWith("destructive") ||
22
- token.startsWith("success") ||
23
- token.startsWith("warning") ||
24
- token === "overlay-scrim" ||
25
- token === "ring-focus",
26
- ),
27
- };
28
-
29
- function Swatch({ mode, token }: { mode: ColorMode; token: string }) {
30
- const value = colorSystems.ponti[mode][token as keyof (typeof colorSystems.ponti)[typeof mode]];
31
- const foreground = token.startsWith("text-") ? value : colorSystems.ponti[mode]["text-primary"];
32
-
33
- return (
34
- <div className="grid min-w-0 gap-2">
35
- <div
36
- className="flex min-h-16 items-end overflow-hidden rounded-md border p-2"
37
- style={{
38
- background: value,
39
- color: foreground,
40
- borderColor: colorSystems.ponti[mode]["border-default"],
41
- }}
42
- >
43
- <span className="font-mono text-[11px] break-all">Aa</span>
44
- </div>
45
- <span
46
- className="font-mono text-[11px] break-all"
47
- style={{ color: colorSystems.ponti[mode]["text-secondary"] }}
48
- >
49
- {token}
50
- </span>
51
- </div>
52
- );
53
- }
54
-
55
- function ThemePanel({ mode }: { mode: ColorMode }) {
56
- const theme = colorSystems.ponti[mode];
57
-
58
- return (
59
- <section
60
- className="grid min-w-0 gap-6 rounded-xl border p-5"
61
- style={{
62
- background: theme["surface-canvas"],
63
- color: theme["text-primary"],
64
- borderColor: theme["border-default"],
65
- }}
66
- aria-label={`Ponti ${mode} color system`}
67
- >
10
+ export const SystemAppearance: Story = {
11
+ render: () => (
12
+ <main className="bg-background text-foreground grid min-h-screen gap-8 p-6 md:p-10">
68
13
  <header className="grid gap-2">
69
- <p className="font-mono text-xs uppercase" style={{ color: theme["text-secondary"] }}>
70
- Ponti / {mode}
71
- </p>
72
- <h2 className="text-xl font-semibold">Semantic roles</h2>
73
- <p className="max-w-prose text-sm" style={{ color: theme["text-secondary"] }}>
74
- Components consume meaning, while this palette supplies the light or dark value.
14
+ <h1 className="text-2xl font-semibold">System color roles</h1>
15
+ <p className="text-muted-foreground max-w-prose text-sm">
16
+ These semantic roles resolve from the operating system’s light or dark appearance. CSS is
17
+ the single source of their values.
75
18
  </p>
76
19
  </header>
77
20
  <div className="flex flex-wrap gap-2">
78
21
  <Button>Primary action</Button>
79
22
  <Button variant="secondary">Secondary action</Button>
80
23
  <Button variant="outline">Outline action</Button>
81
- <Button variant="ghost">Quiet action</Button>
82
24
  <Button variant="destructive">Delete item</Button>
83
25
  </div>
84
- <div className="grid gap-6">
85
- {Object.entries(groups).map(([title, tokens]) => (
86
- <section key={title} className="grid gap-3">
87
- <h3 className="text-sm font-semibold capitalize">{title}</h3>
88
- <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
89
- {tokens.map((token) => (
90
- <Swatch key={token} mode={mode} token={token} />
91
- ))}
92
- </div>
93
- </section>
26
+ <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
27
+ {colorTokenNames.map((token) => (
28
+ <div
29
+ key={token}
30
+ className="border-border grid min-h-20 content-end rounded-md border p-3"
31
+ style={{ backgroundColor: `var(--color-${token})` }}
32
+ >
33
+ <span className="bg-background text-foreground w-fit rounded px-1 font-mono text-xs">
34
+ {token}
35
+ </span>
36
+ </div>
94
37
  ))}
95
38
  </div>
96
- </section>
97
- );
98
- }
99
-
100
- export const LightAndDark: Story = {
101
- render: () => (
102
- <main className="grid min-h-screen gap-4 bg-slate-100 p-4 md:p-6 dark:bg-slate-950">
103
- <ThemePanel mode="light" />
104
- <ThemePanel mode="dark" />
105
- </main>
106
- ),
107
- };
108
-
109
- export const ButtonStates: Story = {
110
- render: () => (
111
- <main className="bg-canvas text-primary grid min-h-screen gap-6 p-6">
112
- <header className="grid gap-2">
113
- <h1 className="text-2xl font-semibold">Button state matrix</h1>
114
- <p className="text-secondary max-w-prose text-sm">
115
- Inspect hover, focus, press, disabled, destructive, and loading behavior in the toolbar’s
116
- light and dark modes.
117
- </p>
118
- </header>
119
- <div className="grid max-w-3xl gap-3 sm:grid-cols-2">
120
- <Button>Primary action</Button>
121
- <Button variant="secondary">Secondary action</Button>
122
- <Button variant="outline">Outline action</Button>
123
- <Button variant="ghost">Quiet action</Button>
124
- <Button variant="destructive">Delete item</Button>
125
- <Button variant="link">Read more</Button>
126
- <Button disabled>Disabled action</Button>
127
- <Button isLoading loadingLabel="Saving">
128
- Save changes
129
- </Button>
130
- </div>
131
39
  </main>
132
40
  ),
133
41
  };
@@ -1,59 +1,53 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react-vite";
2
2
 
3
- const meta: Meta = {
4
- title: "Foundations/System Lab",
5
- parameters: { layout: "fullscreen" },
6
- };
7
-
3
+ const meta: Meta = { title: "Foundations/Tailwind Defaults", parameters: { layout: "fullscreen" } };
8
4
  export default meta;
9
5
  type Story = StoryObj<typeof meta>;
10
6
 
11
7
  export const SpacingAndType: Story = {
12
8
  render: () => (
13
- <main className="bg-canvas text-primary grid min-h-screen gap-10 p-6 md:p-10">
9
+ <main className="bg-background text-foreground grid min-h-screen gap-10 p-6 md:p-10">
14
10
  <section className="grid gap-6">
15
11
  <header className="grid gap-2">
16
- <p className="body-4 text-secondary">Foundations / rhythm</p>
17
- <h1 className="display-1">Space gives the interface its pace.</h1>
18
- <p className="body-1 text-secondary max-w-2xl">
19
- The specimen uses the shared interval scale and the four local typography roles.
12
+ <p className="text-muted-foreground text-sm">Foundations / rhythm</p>
13
+ <h1 className="text-4xl font-semibold tracking-tight">Tailwind defaults, directly.</h1>
14
+ <p className="text-muted-foreground max-w-2xl text-base">
15
+ The specimen uses the shared Tailwind spacing and type scales.
20
16
  </p>
21
17
  </header>
22
18
  <div className="grid gap-2">
23
- {[4, 8, 12, 16, 24, 32, 48, 64].map((size) => (
19
+ {[1, 2, 3, 4, 6, 8, 12, 16].map((size) => (
24
20
  <div key={size} className="flex items-center gap-3">
25
- <span className="text-secondary w-12 font-mono text-xs tabular-nums">{size}px</span>
21
+ <span className="text-muted-foreground w-12 font-mono text-xs tabular-nums">
22
+ {size}
23
+ </span>
26
24
  <div
27
25
  className="bg-accent h-3 rounded-sm"
28
- style={{ width: size }}
26
+ style={{ width: `calc(var(--spacing) * ${size})` }}
29
27
  aria-hidden="true"
30
28
  />
31
29
  </div>
32
30
  ))}
33
31
  </div>
34
32
  </section>
35
- <section className="border-subtle grid gap-5 rounded-xl border p-6">
36
- <h2 className="heading-2">Typography roles</h2>
33
+ <section className="border-border bg-card grid gap-5 rounded-xl border p-6">
34
+ <h2 className="text-2xl font-semibold">Typography scale</h2>
37
35
  <div className="grid gap-4">
38
- <div>
39
- <p className="text-secondary text-xs uppercase">Display</p>
40
- <p className="display-2">A clear beginning</p>
41
- </div>
42
- <div>
43
- <p className="text-secondary text-xs uppercase">Body</p>
44
- <p className="body-1 max-w-2xl">
45
- Readable content should have room to breathe and should not require the user to decode
46
- the container.
36
+ {(
37
+ [
38
+ "text-xs",
39
+ "text-sm",
40
+ "text-base",
41
+ "text-lg",
42
+ "text-xl",
43
+ "text-2xl",
44
+ "text-4xl",
45
+ ] as const
46
+ ).map((size) => (
47
+ <p key={size} className={`${size} text-foreground`}>
48
+ {size}
47
49
  </p>
48
- </div>
49
- <div>
50
- <p className="text-secondary text-xs uppercase">Label</p>
51
- <p className="body-3 font-medium">Current collection</p>
52
- </div>
53
- <div>
54
- <p className="text-secondary text-xs uppercase">Caption</p>
55
- <p className="body-4 text-secondary">Updated just now</p>
56
- </div>
50
+ ))}
57
51
  </div>
58
52
  </section>
59
53
  </main>
@@ -62,28 +56,26 @@ export const SpacingAndType: Story = {
62
56
 
63
57
  export const Surfaces: Story = {
64
58
  render: () => (
65
- <main className="bg-canvas text-primary grid min-h-screen gap-6 p-6 md:p-10">
59
+ <main className="bg-background text-foreground grid min-h-screen gap-6 p-6 md:p-10">
66
60
  <header className="grid gap-2">
67
- <p className="body-4 text-secondary">Foundations / surfaces</p>
68
- <h1 className="display-1">Quiet layers, clear ownership.</h1>
61
+ <p className="text-muted-foreground text-sm">Foundations / surfaces</p>
62
+ <h1 className="text-4xl font-semibold tracking-tight">Standard semantic layers.</h1>
69
63
  </header>
70
64
  <div className="grid gap-4 md:grid-cols-2">
71
- <div className="bg-canvas border-subtle grid min-h-40 content-start gap-2 rounded-xl border p-5">
72
- <h2 className="heading-3">Canvas</h2>
73
- <p className="body-3 text-secondary">The page background.</p>
74
- </div>
75
- <div className="bg-panel border-subtle grid min-h-40 content-start gap-2 rounded-xl border p-5">
76
- <h2 className="heading-3">Panel</h2>
77
- <p className="body-3 text-secondary">A contained region.</p>
78
- </div>
79
- <div className="bg-raised border-subtle grid min-h-40 content-start gap-2 rounded-xl border p-5">
80
- <h2 className="heading-3">Raised</h2>
81
- <p className="body-3 text-secondary">An overlay or elevated layer.</p>
82
- </div>
83
- <div className="bg-inset border-subtle grid min-h-40 content-start gap-2 rounded-xl border p-5">
84
- <h2 className="heading-3">Inset</h2>
85
- <p className="body-3 text-secondary">A recessed working region.</p>
86
- </div>
65
+ {[
66
+ ["Background", "bg-background"],
67
+ ["Card", "bg-card"],
68
+ ["Popover", "bg-popover"],
69
+ ["Muted", "bg-muted"],
70
+ ].map(([name, className]) => (
71
+ <div
72
+ key={name}
73
+ className={`${className} border-border grid min-h-40 content-start gap-2 rounded-xl border p-5`}
74
+ >
75
+ <h2 className="text-lg font-semibold">{name}</h2>
76
+ <p className="text-muted-foreground text-sm">{className}</p>
77
+ </div>
78
+ ))}
87
79
  </div>
88
80
  </main>
89
81
  ),
@@ -91,61 +83,27 @@ export const Surfaces: Story = {
91
83
 
92
84
  export const Motion: Story = {
93
85
  render: () => (
94
- <main className="bg-canvas text-primary grid min-h-screen gap-8 p-6 md:p-10">
86
+ <main className="bg-background text-foreground grid min-h-screen gap-8 p-6 md:p-10">
95
87
  <header className="grid gap-2">
96
- <p className="body-4 text-secondary">Foundations / motion</p>
97
- <h1 className="display-1">Motion should explain, not perform.</h1>
98
- <p className="body-1 text-secondary max-w-2xl">
99
- Use the Storybook motion-reduction setting to confirm that nonessential movement
100
- disappears.
101
- </p>
88
+ <p className="text-muted-foreground text-sm">Foundations / motion</p>
89
+ <h1 className="text-4xl font-semibold tracking-tight">
90
+ Motion stays standard and reducible.
91
+ </h1>
102
92
  </header>
103
93
  <div className="grid gap-4 sm:grid-cols-3">
104
- <div className="bg-panel border-subtle motion-safe:animate-in motion-safe:fade-in-0 motion-safe:slide-in-from-bottom-2 grid min-h-32 place-content-center gap-2 rounded-xl border p-5 duration-150 motion-reduce:animate-none">
105
- <span className="heading-3">Enter</span>
106
- <span className="body-4 text-secondary">150ms / 6px</span>
94
+ <div className="motion-safe:animate-in motion-safe:fade-in-0 border-border bg-card grid min-h-32 place-content-center gap-2 rounded-xl border p-5 motion-reduce:animate-none">
95
+ <span className="text-lg font-semibold">Enter</span>
96
+ <span className="text-muted-foreground text-sm">150ms</span>
107
97
  </div>
108
- <button className="bg-panel border-subtle text-primary hover:bg-surface-hover active:bg-surface-pressed focus-visible:ring-focus grid min-h-32 place-content-center gap-2 rounded-xl border p-5 text-left focus-visible:ring-2 focus-visible:outline-none motion-safe:transition-colors">
109
- <span className="heading-3">Press</span>
110
- <span className="body-4 text-secondary">Immediate feedback</span>
98
+ <button className="border-border bg-card hover:bg-accent focus-visible:ring-ring grid min-h-32 place-content-center gap-2 rounded-xl border p-5 text-left transition-colors focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none">
99
+ <span className="text-lg font-semibold">Press</span>
100
+ <span className="text-muted-foreground text-sm">System focus</span>
111
101
  </button>
112
- <div className="bg-panel border-subtle grid min-h-32 place-content-center gap-2 rounded-xl border p-5 motion-safe:animate-pulse motion-reduce:animate-none">
113
- <span className="heading-3">Loading</span>
114
- <span className="body-4 text-secondary">Geometry remains stable</span>
102
+ <div className="border-border bg-card grid min-h-32 place-content-center gap-2 rounded-xl border p-5 motion-safe:animate-pulse motion-reduce:animate-none">
103
+ <span className="text-lg font-semibold">Loading</span>
104
+ <span className="text-muted-foreground text-sm">Stable geometry</span>
115
105
  </div>
116
106
  </div>
117
107
  </main>
118
108
  ),
119
109
  };
120
-
121
- export const Accessibility: Story = {
122
- render: () => (
123
- <main className="bg-canvas text-primary grid min-h-screen gap-8 p-6 md:p-10">
124
- <header className="grid gap-2">
125
- <p className="body-4 text-secondary">Foundations / accessibility</p>
126
- <h1 className="display-1">The system should be calm for everyone.</h1>
127
- </header>
128
- <section className="border-subtle grid max-w-2xl gap-5 rounded-xl border p-6">
129
- <label className="grid gap-2">
130
- <span className="body-3 font-medium">Email address</span>
131
- <input
132
- className="bg-canvas border-default text-primary focus-visible:ring-focus h-11 rounded-md border px-3 outline-none focus-visible:ring-2"
133
- placeholder="you@example.com"
134
- />
135
- <span className="body-4 text-secondary">Use a reachable address for notifications.</span>
136
- </label>
137
- <div className="flex flex-wrap gap-3">
138
- <button className="bg-accent text-on-accent focus-visible:ring-focus min-h-11 rounded-md px-4 focus-visible:ring-2 focus-visible:outline-none">
139
- Continue
140
- </button>
141
- <button disabled className="bg-panel text-disabled min-h-11 rounded-md px-4">
142
- Unavailable
143
- </button>
144
- </div>
145
- <p className="body-4 text-destructive" role="alert">
146
- Example error: enter a valid email address.
147
- </p>
148
- </section>
149
- </main>
150
- ),
151
- };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Do not edit directly, this file was auto-generated.
3
+ */
4
+
5
+ export default {
6
+ background: "#111113",
7
+ card: "#18191b",
8
+ popover: "#212225",
9
+ muted: "#18191b",
10
+ "text-primary": "#edeef0",
11
+ "text-secondary": "#b0b4ba",
12
+ tertiary: "#777b84",
13
+ primary: "#3e63dd",
14
+ destructive: "#e5484d",
15
+ success: "#30a46c",
16
+ warning: "#ffc53d",
17
+ "primary-foreground": "#ffffff",
18
+ "destructive-foreground": "#ffffff",
19
+ "border-default": "#43484e",
20
+ "focus-ring": "#3e63dd",
21
+ "chart-1": "#3e63dd",
22
+ "chart-2": "#6e56cf",
23
+ "chart-3": "#12a594",
24
+ "chart-4": "#f76b15",
25
+ "chart-5": "#696e77",
26
+ "chart-positive": "#30a46c",
27
+ "chart-negative": "#e5484d",
28
+ "chart-neutral": "#5a6169",
29
+ };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Do not edit directly, this file was auto-generated.
3
+ */
4
+
5
+ :root {
6
+ --background: #111113;
7
+ --card: #18191b;
8
+ --popover: #212225;
9
+ --muted: #18191b;
10
+ --text-primary: #edeef0;
11
+ --text-secondary: #b0b4ba;
12
+ --tertiary: #777b84;
13
+ --primary: #3e63dd;
14
+ --destructive: #e5484d;
15
+ --success: #30a46c;
16
+ --warning: #ffc53d;
17
+ --primary-foreground: #ffffff;
18
+ --destructive-foreground: #ffffff;
19
+ --border-default: #43484e;
20
+ --focus-ring: #3e63dd;
21
+ --chart-1: #3e63dd;
22
+ --chart-2: #6e56cf;
23
+ --chart-3: #12a594;
24
+ --chart-4: #f76b15;
25
+ --chart-5: #696e77;
26
+ --chart-positive: #30a46c;
27
+ --chart-negative: #e5484d;
28
+ --chart-neutral: #5a6169;
29
+ }