vinta-schedule-design-system 0.1.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/components.json +21 -0
- package/dist/styles.css +3452 -0
- package/package.json +103 -0
- package/src/layout/app-shell.tsx +115 -0
- package/src/layout/app-topbar.stories.tsx +74 -0
- package/src/layout/app-topbar.tsx +145 -0
- package/src/layout/auth-layout.tsx +45 -0
- package/src/layout/box.stories.tsx +86 -0
- package/src/layout/box.tsx +34 -0
- package/src/layout/center.tsx +24 -0
- package/src/layout/container.stories.tsx +44 -0
- package/src/layout/container.tsx +39 -0
- package/src/layout/divider.tsx +64 -0
- package/src/layout/flex.stories.tsx +109 -0
- package/src/layout/flex.test.tsx +71 -0
- package/src/layout/flex.tsx +112 -0
- package/src/layout/grid.stories.tsx +91 -0
- package/src/layout/grid.tsx +132 -0
- package/src/layout/heading.tsx +53 -0
- package/src/layout/index.ts +47 -0
- package/src/layout/layout-style.ts +291 -0
- package/src/layout/link.stories.tsx +54 -0
- package/src/layout/link.test.tsx +75 -0
- package/src/layout/link.tsx +100 -0
- package/src/layout/navbar.stories.tsx +70 -0
- package/src/layout/navbar.tsx +128 -0
- package/src/layout/page-header.stories.tsx +80 -0
- package/src/layout/page-header.tsx +59 -0
- package/src/layout/section.tsx +22 -0
- package/src/layout/spacer.tsx +21 -0
- package/src/layout/stack.stories.tsx +58 -0
- package/src/layout/stack.tsx +19 -0
- package/src/layout/text.tsx +139 -0
- package/src/layout/typography.stories.tsx +112 -0
- package/src/lib/utils.ts +6 -0
- package/src/story-types.ts +80 -0
- package/src/styles/build.css +33 -0
- package/src/styles/globals.css +9 -0
- package/src/styles/tokens.css +281 -0
- package/src/ui/accordion.stories.tsx +84 -0
- package/src/ui/accordion.tsx +57 -0
- package/src/ui/alert-dialog.stories.tsx +57 -0
- package/src/ui/alert-dialog.tsx +141 -0
- package/src/ui/alert.stories.tsx +46 -0
- package/src/ui/alert.tsx +59 -0
- package/src/ui/avatar.stories.tsx +56 -0
- package/src/ui/avatar.tsx +50 -0
- package/src/ui/badge.stories.tsx +105 -0
- package/src/ui/badge.tsx +57 -0
- package/src/ui/breadcrumb.stories.tsx +69 -0
- package/src/ui/breadcrumb.tsx +140 -0
- package/src/ui/button.stories.tsx +85 -0
- package/src/ui/button.tsx +60 -0
- package/src/ui/card.stories.tsx +62 -0
- package/src/ui/card.tsx +96 -0
- package/src/ui/carousel.stories.tsx +55 -0
- package/src/ui/carousel.tsx +262 -0
- package/src/ui/checkbox.stories.tsx +52 -0
- package/src/ui/checkbox.tsx +30 -0
- package/src/ui/combobox.stories.tsx +138 -0
- package/src/ui/combobox.tsx +217 -0
- package/src/ui/command.tsx +184 -0
- package/src/ui/dialog.stories.tsx +66 -0
- package/src/ui/dialog.tsx +163 -0
- package/src/ui/drawer.stories.tsx +80 -0
- package/src/ui/drawer.tsx +118 -0
- package/src/ui/dropdown-menu.stories.tsx +57 -0
- package/src/ui/dropdown-menu.tsx +201 -0
- package/src/ui/form.stories.tsx +70 -0
- package/src/ui/form.tsx +181 -0
- package/src/ui/input-otp.stories.tsx +52 -0
- package/src/ui/input-otp.tsx +71 -0
- package/src/ui/input.stories.tsx +68 -0
- package/src/ui/input.tsx +22 -0
- package/src/ui/label.stories.tsx +35 -0
- package/src/ui/label.tsx +26 -0
- package/src/ui/navigation-menu.stories.tsx +79 -0
- package/src/ui/navigation-menu.tsx +128 -0
- package/src/ui/pagination.stories.tsx +62 -0
- package/src/ui/pagination.tsx +137 -0
- package/src/ui/popover.tsx +89 -0
- package/src/ui/progress.stories.tsx +33 -0
- package/src/ui/progress.tsx +28 -0
- package/src/ui/radio-group.stories.tsx +52 -0
- package/src/ui/radio-group.tsx +44 -0
- package/src/ui/scheduling-chip.stories.tsx +117 -0
- package/src/ui/scheduling-chip.tsx +62 -0
- package/src/ui/scroll-area.stories.tsx +52 -0
- package/src/ui/scroll-area.tsx +48 -0
- package/src/ui/select.stories.tsx +56 -0
- package/src/ui/select.tsx +159 -0
- package/src/ui/sheet.stories.tsx +63 -0
- package/src/ui/sheet.tsx +141 -0
- package/src/ui/skeleton.stories.tsx +40 -0
- package/src/ui/skeleton.tsx +15 -0
- package/src/ui/sonner.stories.tsx +62 -0
- package/src/ui/sonner.tsx +34 -0
- package/src/ui/switch.stories.tsx +41 -0
- package/src/ui/switch.tsx +29 -0
- package/src/ui/table.stories.tsx +88 -0
- package/src/ui/table.tsx +132 -0
- package/src/ui/tabs.stories.tsx +63 -0
- package/src/ui/tabs.tsx +55 -0
- package/src/ui/textarea.stories.tsx +45 -0
- package/src/ui/textarea.tsx +22 -0
- package/src/ui/tooltip.stories.tsx +48 -0
- package/src/ui/tooltip.tsx +32 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local, structural CSF types — no dependency on any Storybook runtime package.
|
|
3
|
+
*
|
|
4
|
+
* The composer evaluates *.stories.tsx with plain esbuild and does NOT install
|
|
5
|
+
* Storybook (it lives only in devDependencies, which the worker never installs).
|
|
6
|
+
* A value import from `storybook/*`, or even a non-type import of Meta/StoryObj
|
|
7
|
+
* from `@storybook/*`, breaks bundling. These local types are erased at build
|
|
8
|
+
* time (imported as `import type`), so stories bundle cleanly here while still
|
|
9
|
+
* working unchanged in the package's own Storybook (CSF is shape-based).
|
|
10
|
+
*
|
|
11
|
+
* Keep this in sync with the subset of CSF the contract extractor reads:
|
|
12
|
+
* meta.title, meta.component, meta.argTypes, meta.args, meta.parameters.puck.
|
|
13
|
+
*/
|
|
14
|
+
import type { ComponentType, ReactElement, ReactNode } from 'react';
|
|
15
|
+
|
|
16
|
+
/** Controls the extractor supports. Anything else → UNSUPPORTED_CONTROL. */
|
|
17
|
+
export type ControlType =
|
|
18
|
+
| 'text'
|
|
19
|
+
| 'number'
|
|
20
|
+
| 'boolean'
|
|
21
|
+
| 'select'
|
|
22
|
+
| 'radio'
|
|
23
|
+
| 'inline-radio'
|
|
24
|
+
| 'object'
|
|
25
|
+
| 'array';
|
|
26
|
+
|
|
27
|
+
export interface ArgType {
|
|
28
|
+
control?: ControlType | { type: ControlType; [key: string]: unknown };
|
|
29
|
+
options?: readonly (string | number | boolean)[];
|
|
30
|
+
description?: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
table?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Puck contract parameters. Declare container slot props here (each name must be
|
|
37
|
+
* a prop the component actually renders, typed `ReactNode`). The platform wires
|
|
38
|
+
* Puck's SlotComponent for every declared name.
|
|
39
|
+
*/
|
|
40
|
+
export interface PuckParameters {
|
|
41
|
+
slots?: readonly string[];
|
|
42
|
+
[key: string]: unknown;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface Parameters {
|
|
46
|
+
layout?: 'centered' | 'fullscreen' | 'padded';
|
|
47
|
+
puck?: PuckParameters;
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** `_C` mirrors Storybook's `Meta<typeof Component>` call-shape but is unused. */
|
|
52
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
53
|
+
export interface Meta<_C = unknown> {
|
|
54
|
+
title?: string;
|
|
55
|
+
component?: ComponentType<never> | ComponentType<Record<string, unknown>>;
|
|
56
|
+
tags?: readonly string[];
|
|
57
|
+
args?: Record<string, unknown>;
|
|
58
|
+
argTypes?: Record<string, ArgType>;
|
|
59
|
+
parameters?: Parameters;
|
|
60
|
+
decorators?: readonly unknown[];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* `_M` mirrors Storybook's `StoryObj<typeof meta>` call-shape but is unused.
|
|
65
|
+
* `render` args are intentionally loose — a structural CSF shim can't know each
|
|
66
|
+
* component's prop type, and stories spread args straight onto the component.
|
|
67
|
+
*/
|
|
68
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
69
|
+
export interface StoryObj<_M = unknown> {
|
|
70
|
+
name?: string;
|
|
71
|
+
args?: Record<string, unknown>;
|
|
72
|
+
argTypes?: Record<string, ArgType>;
|
|
73
|
+
parameters?: Parameters;
|
|
74
|
+
/** Storybook-only (e.g. the viewport toolbar); ignored by the extractor. */
|
|
75
|
+
globals?: Record<string, unknown>;
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
|
+
render?: (args: any) => ReactElement;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type { ReactNode };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* Build entry for the standalone, publishable stylesheet (dist/styles.css).
|
|
2
|
+
*
|
|
3
|
+
* The composer installs this package from a registry at runtime and its host
|
|
4
|
+
* Tailwind build only scans the composer's own workspace — it never runs this
|
|
5
|
+
* package's `@source` scan. So we precompile a self-contained stylesheet here:
|
|
6
|
+
* the brand tokens (which don't exist in the host) plus every utility the
|
|
7
|
+
* components emit. The Tailwind v4 CLI reads this file (`pnpm run build:css`).
|
|
8
|
+
*
|
|
9
|
+
* Consumers load the result via the `./styles.css` export. In-repo, the app
|
|
10
|
+
* still uses src/app/globals.css (tokens + its own @source); this file is only
|
|
11
|
+
* for external, registry-installed consumers.
|
|
12
|
+
*/
|
|
13
|
+
@import 'tailwindcss';
|
|
14
|
+
@import 'tw-animate-css';
|
|
15
|
+
@import './tokens.css';
|
|
16
|
+
|
|
17
|
+
/* Scan the package source so utilities referenced by components are generated
|
|
18
|
+
* into the shipped stylesheet. Relative to this file (src/styles/). */
|
|
19
|
+
@source '../ui';
|
|
20
|
+
@source '../layout';
|
|
21
|
+
|
|
22
|
+
/* next/font injects --font-dm-sans / --font-geist-mono in the schedule app; a
|
|
23
|
+
* composer host has no next/font. Provide real fallback stacks so shipped CSS
|
|
24
|
+
* renders with sensible fonts standalone (§8). tokens.css only *references*
|
|
25
|
+
* these vars, so defining them here doesn't conflict. */
|
|
26
|
+
@layer base {
|
|
27
|
+
:root {
|
|
28
|
+
--font-dm-sans:
|
|
29
|
+
'DM Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
30
|
+
--font-geist-mono:
|
|
31
|
+
'Geist Mono', ui-monospace, 'SF Mono', 'Cascadia Code', monospace;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* Entry stylesheet for the design-system Storybook. Consuming apps should
|
|
2
|
+
import tailwindcss + tw-animate-css themselves and pull in ./tokens.css
|
|
3
|
+
(see the app's src/app/globals.css). */
|
|
4
|
+
@import 'tailwindcss';
|
|
5
|
+
@import 'tw-animate-css';
|
|
6
|
+
@import './tokens.css';
|
|
7
|
+
|
|
8
|
+
/* Scan the package sources so utilities used by components are generated. */
|
|
9
|
+
@source '../';
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
@custom-variant dark (&:is(.dark *));
|
|
2
|
+
|
|
3
|
+
/* ============================================================================
|
|
4
|
+
Vinta Schedule — Colors & Type Foundations
|
|
5
|
+
Token architecture follows shadcn/ui (Radix-style). Three layers:
|
|
6
|
+
1. Primitive scales --vinta-*, --slate-*, --teal-* (raw, theme-agnostic)
|
|
7
|
+
2. Semantic tokens --background, --primary, --border … (shadcn contract)
|
|
8
|
+
3. Type + spacing --text-*, --space-*, --radius-*
|
|
9
|
+
Source of truth: "Vinta Schedule Design System/colors_and_type.css".
|
|
10
|
+
========================================================================== */
|
|
11
|
+
|
|
12
|
+
:root {
|
|
13
|
+
/* ---- Layer 1 · Primitive color scales ---------------------------------- */
|
|
14
|
+
|
|
15
|
+
/* Vinta Blue — the wordmark color, primary brand ramp (hue ~263) */
|
|
16
|
+
--vinta-50: oklch(0.971 0.014 264);
|
|
17
|
+
--vinta-100: oklch(0.945 0.03 264);
|
|
18
|
+
--vinta-200: oklch(0.895 0.06 264);
|
|
19
|
+
--vinta-300: oklch(0.82 0.105 264);
|
|
20
|
+
--vinta-400: oklch(0.715 0.17 264);
|
|
21
|
+
--vinta-500: oklch(0.62 0.225 264);
|
|
22
|
+
--vinta-600: oklch(0.545 0.258 264); /* #1B4DFF — primary */
|
|
23
|
+
--vinta-700: oklch(0.48 0.245 264);
|
|
24
|
+
--vinta-800: oklch(0.41 0.198 265);
|
|
25
|
+
--vinta-900: oklch(0.345 0.15 266);
|
|
26
|
+
--vinta-950: oklch(0.255 0.098 267);
|
|
27
|
+
|
|
28
|
+
/* Slate — cool neutral ramp for surfaces, text, borders */
|
|
29
|
+
--slate-50: oklch(0.985 0.002 264);
|
|
30
|
+
--slate-100: oklch(0.968 0.004 264);
|
|
31
|
+
--slate-200: oklch(0.929 0.007 264);
|
|
32
|
+
--slate-300: oklch(0.872 0.011 264);
|
|
33
|
+
--slate-400: oklch(0.71 0.018 264);
|
|
34
|
+
--slate-500: oklch(0.566 0.021 264);
|
|
35
|
+
--slate-600: oklch(0.456 0.022 264);
|
|
36
|
+
--slate-700: oklch(0.376 0.022 265);
|
|
37
|
+
--slate-800: oklch(0.279 0.022 266);
|
|
38
|
+
--slate-900: oklch(0.213 0.022 267);
|
|
39
|
+
--slate-950: oklch(0.151 0.02 268);
|
|
40
|
+
|
|
41
|
+
/* Teal — secondary "availability" accent used in scheduling states */
|
|
42
|
+
--teal-100: oklch(0.945 0.045 188);
|
|
43
|
+
--teal-300: oklch(0.82 0.11 188);
|
|
44
|
+
--teal-500: oklch(0.7 0.13 188);
|
|
45
|
+
--teal-600: oklch(0.61 0.118 189);
|
|
46
|
+
--teal-700: oklch(0.52 0.1 190);
|
|
47
|
+
|
|
48
|
+
/* Semantic status hues */
|
|
49
|
+
--green-100: oklch(0.95 0.045 150);
|
|
50
|
+
--green-500: oklch(0.68 0.165 150);
|
|
51
|
+
--green-600: oklch(0.6 0.155 150);
|
|
52
|
+
--amber-100: oklch(0.96 0.05 85);
|
|
53
|
+
--amber-500: oklch(0.79 0.15 80);
|
|
54
|
+
--amber-600: oklch(0.72 0.155 70);
|
|
55
|
+
--red-100: oklch(0.945 0.035 25);
|
|
56
|
+
--red-500: oklch(0.637 0.225 25);
|
|
57
|
+
--red-600: oklch(0.577 0.225 27);
|
|
58
|
+
|
|
59
|
+
/* ---- Layer 2 · Semantic tokens (LIGHT theme · shadcn contract) ---------- */
|
|
60
|
+
--background: var(--slate-50);
|
|
61
|
+
--foreground: var(--slate-950);
|
|
62
|
+
--card: oklch(1 0 0);
|
|
63
|
+
--card-foreground: var(--slate-950);
|
|
64
|
+
--popover: oklch(1 0 0);
|
|
65
|
+
--popover-foreground: var(--slate-950);
|
|
66
|
+
--primary: var(--vinta-600);
|
|
67
|
+
--primary-foreground: oklch(0.99 0.01 264);
|
|
68
|
+
--secondary: var(--slate-100);
|
|
69
|
+
--secondary-foreground: var(--slate-900);
|
|
70
|
+
--muted: var(--slate-100);
|
|
71
|
+
--muted-foreground: var(--slate-500);
|
|
72
|
+
--accent: var(--vinta-50);
|
|
73
|
+
--accent-foreground: var(--vinta-700);
|
|
74
|
+
--destructive: var(--red-600);
|
|
75
|
+
--destructive-foreground: oklch(0.99 0 0);
|
|
76
|
+
--success: var(--green-600);
|
|
77
|
+
--warning: var(--amber-600);
|
|
78
|
+
--border: var(--slate-200);
|
|
79
|
+
--input: var(--slate-200);
|
|
80
|
+
--ring: var(--vinta-600);
|
|
81
|
+
|
|
82
|
+
/* Chart palette */
|
|
83
|
+
--chart-1: var(--vinta-600);
|
|
84
|
+
--chart-2: var(--teal-600);
|
|
85
|
+
--chart-3: var(--amber-500);
|
|
86
|
+
--chart-4: var(--vinta-300);
|
|
87
|
+
--chart-5: var(--slate-400);
|
|
88
|
+
|
|
89
|
+
/* Sidebar (app shell) */
|
|
90
|
+
--sidebar: oklch(1 0 0);
|
|
91
|
+
--sidebar-foreground: var(--slate-800);
|
|
92
|
+
--sidebar-primary: var(--vinta-600);
|
|
93
|
+
--sidebar-primary-foreground: oklch(0.99 0.01 264);
|
|
94
|
+
--sidebar-accent: var(--slate-100);
|
|
95
|
+
--sidebar-accent-foreground: var(--slate-900);
|
|
96
|
+
--sidebar-border: var(--slate-200);
|
|
97
|
+
--sidebar-ring: var(--vinta-600);
|
|
98
|
+
|
|
99
|
+
/* ---- Layer 3 · Radius ------------------------------------------------- */
|
|
100
|
+
--radius: 0.625rem; /* 10px — base control radius */
|
|
101
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
102
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
103
|
+
--radius-lg: var(--radius);
|
|
104
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
105
|
+
--radius-2xl: calc(var(--radius) + 10px);
|
|
106
|
+
|
|
107
|
+
/* ---- Layer 3 · Elevation (soft, cool-tinted) -------------------------- */
|
|
108
|
+
--shadow-xs: 0 1px 2px 0 oklch(0.213 0.022 267 / 0.05);
|
|
109
|
+
--shadow-sm:
|
|
110
|
+
0 1px 3px 0 oklch(0.213 0.022 267 / 0.08),
|
|
111
|
+
0 1px 2px -1px oklch(0.213 0.022 267 / 0.06);
|
|
112
|
+
--shadow-md:
|
|
113
|
+
0 4px 12px -2px oklch(0.213 0.022 267 / 0.1),
|
|
114
|
+
0 2px 6px -2px oklch(0.213 0.022 267 / 0.06);
|
|
115
|
+
--shadow-lg:
|
|
116
|
+
0 12px 28px -6px oklch(0.213 0.022 267 / 0.14),
|
|
117
|
+
0 4px 10px -4px oklch(0.213 0.022 267 / 0.08);
|
|
118
|
+
--shadow-xl: 0 24px 48px -12px oklch(0.213 0.022 267 / 0.18);
|
|
119
|
+
|
|
120
|
+
/* ---- Layer 3 · Typography --------------------------------------------- */
|
|
121
|
+
/* Families injected by next/font (see app/layout.tsx & .storybook/preview). */
|
|
122
|
+
--font-sans:
|
|
123
|
+
var(--font-dm-sans), ui-sans-serif, system-ui, -apple-system, 'Segoe UI',
|
|
124
|
+
sans-serif;
|
|
125
|
+
--font-mono:
|
|
126
|
+
'Geist Mono', 'Geist Mono Fallback', ui-monospace, monospace, ui-monospace,
|
|
127
|
+
'SF Mono', 'Cascadia Code', monospace;
|
|
128
|
+
--font-display: var(--font-dm-sans), ui-sans-serif, system-ui, sans-serif;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* ---- Layer 2 · DARK theme overrides ------------------------------------- */
|
|
132
|
+
.dark {
|
|
133
|
+
--background: var(--slate-950);
|
|
134
|
+
--foreground: var(--slate-50);
|
|
135
|
+
--card: var(--slate-900);
|
|
136
|
+
--card-foreground: var(--slate-50);
|
|
137
|
+
--popover: var(--slate-900);
|
|
138
|
+
--popover-foreground: var(--slate-50);
|
|
139
|
+
--primary: var(--vinta-500);
|
|
140
|
+
--primary-foreground: oklch(0.16 0.02 267);
|
|
141
|
+
--secondary: var(--slate-800);
|
|
142
|
+
--secondary-foreground: var(--slate-100);
|
|
143
|
+
--muted: var(--slate-800);
|
|
144
|
+
--muted-foreground: var(--slate-400);
|
|
145
|
+
--accent: oklch(0.3 0.08 265);
|
|
146
|
+
--accent-foreground: var(--vinta-200);
|
|
147
|
+
--destructive: var(--red-500);
|
|
148
|
+
--destructive-foreground: oklch(0.16 0.02 267);
|
|
149
|
+
--success: var(--green-500);
|
|
150
|
+
--warning: var(--amber-500);
|
|
151
|
+
--border: oklch(1 0 0 / 0.1);
|
|
152
|
+
--input: oklch(1 0 0 / 0.14);
|
|
153
|
+
--ring: var(--vinta-500);
|
|
154
|
+
|
|
155
|
+
--chart-1: var(--vinta-500);
|
|
156
|
+
--chart-2: var(--teal-500);
|
|
157
|
+
--chart-3: var(--amber-500);
|
|
158
|
+
--chart-4: var(--vinta-300);
|
|
159
|
+
--chart-5: var(--slate-400);
|
|
160
|
+
|
|
161
|
+
--sidebar: var(--slate-900);
|
|
162
|
+
--sidebar-foreground: var(--slate-200);
|
|
163
|
+
--sidebar-primary: var(--vinta-500);
|
|
164
|
+
--sidebar-primary-foreground: oklch(0.16 0.02 267);
|
|
165
|
+
--sidebar-accent: var(--slate-800);
|
|
166
|
+
--sidebar-accent-foreground: var(--slate-100);
|
|
167
|
+
--sidebar-border: oklch(1 0 0 / 0.1);
|
|
168
|
+
--sidebar-ring: var(--vinta-500);
|
|
169
|
+
|
|
170
|
+
--shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.3);
|
|
171
|
+
--shadow-sm:
|
|
172
|
+
0 1px 3px 0 oklch(0 0 0 / 0.4), 0 1px 2px -1px oklch(0 0 0 / 0.3);
|
|
173
|
+
--shadow-md: 0 4px 12px -2px oklch(0 0 0 / 0.45);
|
|
174
|
+
--shadow-lg: 0 12px 28px -6px oklch(0 0 0 / 0.55);
|
|
175
|
+
--shadow-xl: 0 24px 48px -12px oklch(0 0 0 / 0.6);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* ---- Tailwind theme mapping — expose tokens as utilities ----------------- */
|
|
179
|
+
@theme inline {
|
|
180
|
+
--color-background: var(--background);
|
|
181
|
+
--color-foreground: var(--foreground);
|
|
182
|
+
--color-card: var(--card);
|
|
183
|
+
--color-card-foreground: var(--card-foreground);
|
|
184
|
+
--color-popover: var(--popover);
|
|
185
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
186
|
+
--color-primary: var(--primary);
|
|
187
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
188
|
+
--color-secondary: var(--secondary);
|
|
189
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
190
|
+
--color-muted: var(--muted);
|
|
191
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
192
|
+
--color-accent: var(--accent);
|
|
193
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
194
|
+
--color-destructive: var(--destructive);
|
|
195
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
196
|
+
--color-success: var(--success);
|
|
197
|
+
--color-warning: var(--warning);
|
|
198
|
+
--color-border: var(--border);
|
|
199
|
+
--color-input: var(--input);
|
|
200
|
+
--color-ring: var(--ring);
|
|
201
|
+
--color-chart-1: var(--chart-1);
|
|
202
|
+
--color-chart-2: var(--chart-2);
|
|
203
|
+
--color-chart-3: var(--chart-3);
|
|
204
|
+
--color-chart-4: var(--chart-4);
|
|
205
|
+
--color-chart-5: var(--chart-5);
|
|
206
|
+
--color-sidebar: var(--sidebar);
|
|
207
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
208
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
209
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
210
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
211
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
212
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
213
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
214
|
+
|
|
215
|
+
/* Brand + scheduling accents — exposes bg-vinta-600, text-teal-700, etc. */
|
|
216
|
+
--color-vinta-50: var(--vinta-50);
|
|
217
|
+
--color-vinta-100: var(--vinta-100);
|
|
218
|
+
--color-vinta-200: var(--vinta-200);
|
|
219
|
+
--color-vinta-300: var(--vinta-300);
|
|
220
|
+
--color-vinta-400: var(--vinta-400);
|
|
221
|
+
--color-vinta-500: var(--vinta-500);
|
|
222
|
+
--color-vinta-600: var(--vinta-600);
|
|
223
|
+
--color-vinta-700: var(--vinta-700);
|
|
224
|
+
--color-vinta-800: var(--vinta-800);
|
|
225
|
+
--color-vinta-900: var(--vinta-900);
|
|
226
|
+
--color-vinta-950: var(--vinta-950);
|
|
227
|
+
--color-teal-100: var(--teal-100);
|
|
228
|
+
--color-teal-300: var(--teal-300);
|
|
229
|
+
--color-teal-500: var(--teal-500);
|
|
230
|
+
--color-teal-600: var(--teal-600);
|
|
231
|
+
--color-teal-700: var(--teal-700);
|
|
232
|
+
|
|
233
|
+
/* Soft status tints — backgrounds for badges / scheduling chips */
|
|
234
|
+
--color-green-100: var(--green-100);
|
|
235
|
+
--color-amber-100: var(--amber-100);
|
|
236
|
+
--color-red-100: var(--red-100);
|
|
237
|
+
|
|
238
|
+
--font-sans: 'Geist', 'Geist Fallback', ui-sans-serif, system-ui, sans-serif;
|
|
239
|
+
--font-mono: 'Geist Mono', 'Geist Mono Fallback', ui-monospace, monospace;
|
|
240
|
+
--font-display: var(--font-display);
|
|
241
|
+
|
|
242
|
+
--radius-sm: var(--radius-sm);
|
|
243
|
+
--radius-md: var(--radius-md);
|
|
244
|
+
--radius-lg: var(--radius-lg);
|
|
245
|
+
--radius-xl: var(--radius-xl);
|
|
246
|
+
--radius-2xl: var(--radius-2xl);
|
|
247
|
+
|
|
248
|
+
--shadow-xs: var(--shadow-xs);
|
|
249
|
+
--shadow-sm: var(--shadow-sm);
|
|
250
|
+
--shadow-md: var(--shadow-md);
|
|
251
|
+
--shadow-lg: var(--shadow-lg);
|
|
252
|
+
--shadow-xl: var(--shadow-xl);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@layer base {
|
|
256
|
+
* {
|
|
257
|
+
@apply border-border outline-ring/50;
|
|
258
|
+
}
|
|
259
|
+
body {
|
|
260
|
+
@apply bg-background text-foreground font-sans;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
@layer utilities {
|
|
265
|
+
.scrollbar-hide {
|
|
266
|
+
-ms-overflow-style: none;
|
|
267
|
+
scrollbar-width: none;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.scrollbar-hide::-webkit-scrollbar {
|
|
271
|
+
display: none;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* Faint 32px grid — marketing hero / CTA backdrops. */
|
|
275
|
+
.grid-bg {
|
|
276
|
+
background-image:
|
|
277
|
+
linear-gradient(var(--border) 1px, transparent 1px),
|
|
278
|
+
linear-gradient(90deg, var(--border) 1px, transparent 1px);
|
|
279
|
+
background-size: 32px 32px;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '../story-types';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Accordion,
|
|
5
|
+
AccordionContent,
|
|
6
|
+
AccordionItem,
|
|
7
|
+
AccordionTrigger,
|
|
8
|
+
} from './accordion';
|
|
9
|
+
|
|
10
|
+
const meta = {
|
|
11
|
+
title: 'Components/Accordion',
|
|
12
|
+
component: Accordion,
|
|
13
|
+
tags: ['autodocs'],
|
|
14
|
+
// Radix Accordion.Root scalars. `children` (the AccordionItem list) is
|
|
15
|
+
// composed content → a slot, so it must NOT also be an argTypes key.
|
|
16
|
+
argTypes: {
|
|
17
|
+
type: {
|
|
18
|
+
control: 'inline-radio',
|
|
19
|
+
options: ['single', 'multiple'],
|
|
20
|
+
description: 'One item open at a time, or many',
|
|
21
|
+
},
|
|
22
|
+
collapsible: {
|
|
23
|
+
control: 'boolean',
|
|
24
|
+
description: 'Allow closing the open item (type="single" only)',
|
|
25
|
+
},
|
|
26
|
+
defaultValue: {
|
|
27
|
+
control: 'text',
|
|
28
|
+
description: 'Value of the item expanded on first render',
|
|
29
|
+
},
|
|
30
|
+
disabled: { control: 'boolean' },
|
|
31
|
+
orientation: {
|
|
32
|
+
control: 'inline-radio',
|
|
33
|
+
options: ['vertical', 'horizontal'],
|
|
34
|
+
},
|
|
35
|
+
dir: { control: 'inline-radio', options: ['ltr', 'rtl'] },
|
|
36
|
+
},
|
|
37
|
+
args: { type: 'single', collapsible: true },
|
|
38
|
+
parameters: { puck: { slots: ['children'] } },
|
|
39
|
+
} satisfies Meta<typeof Accordion>;
|
|
40
|
+
|
|
41
|
+
export default meta;
|
|
42
|
+
type Story = StoryObj<typeof meta>;
|
|
43
|
+
|
|
44
|
+
export const Default: Story = {
|
|
45
|
+
render: (args) => (
|
|
46
|
+
<Accordion {...args} className='w-96'>
|
|
47
|
+
<AccordionItem value='1'>
|
|
48
|
+
<AccordionTrigger>How does calendar sync work?</AccordionTrigger>
|
|
49
|
+
<AccordionContent>
|
|
50
|
+
We poll your connected calendars every few minutes and reconcile
|
|
51
|
+
changes both ways.
|
|
52
|
+
</AccordionContent>
|
|
53
|
+
</AccordionItem>
|
|
54
|
+
<AccordionItem value='2'>
|
|
55
|
+
<AccordionTrigger>Is my data private?</AccordionTrigger>
|
|
56
|
+
<AccordionContent>
|
|
57
|
+
Event details stay encrypted and are never shared with third parties.
|
|
58
|
+
</AccordionContent>
|
|
59
|
+
</AccordionItem>
|
|
60
|
+
<AccordionItem value='3'>
|
|
61
|
+
<AccordionTrigger>Can I cancel anytime?</AccordionTrigger>
|
|
62
|
+
<AccordionContent>
|
|
63
|
+
Yes — no contracts, cancel whenever.
|
|
64
|
+
</AccordionContent>
|
|
65
|
+
</AccordionItem>
|
|
66
|
+
</Accordion>
|
|
67
|
+
),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Multiple: Story = {
|
|
71
|
+
args: { type: 'multiple' },
|
|
72
|
+
render: (args) => (
|
|
73
|
+
<Accordion {...args} className='w-96'>
|
|
74
|
+
<AccordionItem value='1'>
|
|
75
|
+
<AccordionTrigger>Working hours</AccordionTrigger>
|
|
76
|
+
<AccordionContent>Mon–Fri, 9:00 AM – 5:00 PM.</AccordionContent>
|
|
77
|
+
</AccordionItem>
|
|
78
|
+
<AccordionItem value='2'>
|
|
79
|
+
<AccordionTrigger>Buffer time</AccordionTrigger>
|
|
80
|
+
<AccordionContent>10 minutes between appointments.</AccordionContent>
|
|
81
|
+
</AccordionItem>
|
|
82
|
+
</Accordion>
|
|
83
|
+
),
|
|
84
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
|
5
|
+
import { ChevronDown } from 'lucide-react';
|
|
6
|
+
|
|
7
|
+
import { cn } from '../lib/utils';
|
|
8
|
+
|
|
9
|
+
const Accordion = AccordionPrimitive.Root;
|
|
10
|
+
|
|
11
|
+
const AccordionItem = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof AccordionPrimitive.Item>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<AccordionPrimitive.Item
|
|
16
|
+
ref={ref}
|
|
17
|
+
className={cn('border-b', className)}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
));
|
|
21
|
+
AccordionItem.displayName = 'AccordionItem';
|
|
22
|
+
|
|
23
|
+
const AccordionTrigger = React.forwardRef<
|
|
24
|
+
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
26
|
+
>(({ className, children, ...props }, ref) => (
|
|
27
|
+
<AccordionPrimitive.Header className='flex'>
|
|
28
|
+
<AccordionPrimitive.Trigger
|
|
29
|
+
ref={ref}
|
|
30
|
+
className={cn(
|
|
31
|
+
'flex flex-1 items-center justify-between py-4 text-left text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
{children}
|
|
37
|
+
<ChevronDown className='text-muted-foreground h-4 w-4 shrink-0 transition-transform duration-200' />
|
|
38
|
+
</AccordionPrimitive.Trigger>
|
|
39
|
+
</AccordionPrimitive.Header>
|
|
40
|
+
));
|
|
41
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
42
|
+
|
|
43
|
+
const AccordionContent = React.forwardRef<
|
|
44
|
+
React.ElementRef<typeof AccordionPrimitive.Content>,
|
|
45
|
+
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
46
|
+
>(({ className, children, ...props }, ref) => (
|
|
47
|
+
<AccordionPrimitive.Content
|
|
48
|
+
ref={ref}
|
|
49
|
+
className='data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm'
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
<div className={cn('pt-0 pb-4', className)}>{children}</div>
|
|
53
|
+
</AccordionPrimitive.Content>
|
|
54
|
+
));
|
|
55
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
56
|
+
|
|
57
|
+
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '../story-types';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
AlertDialog,
|
|
5
|
+
AlertDialogAction,
|
|
6
|
+
AlertDialogCancel,
|
|
7
|
+
AlertDialogContent,
|
|
8
|
+
AlertDialogDescription,
|
|
9
|
+
AlertDialogFooter,
|
|
10
|
+
AlertDialogHeader,
|
|
11
|
+
AlertDialogTitle,
|
|
12
|
+
AlertDialogTrigger,
|
|
13
|
+
} from './alert-dialog';
|
|
14
|
+
import { Button } from './button';
|
|
15
|
+
|
|
16
|
+
const meta = {
|
|
17
|
+
title: 'Components/AlertDialog',
|
|
18
|
+
component: AlertDialog,
|
|
19
|
+
tags: ['autodocs'],
|
|
20
|
+
// Radix AlertDialog.Root is `Omit<DialogProps, 'modal'>` — an alert dialog is
|
|
21
|
+
// always modal — so `defaultOpen` is its only real scalar prop (`open` /
|
|
22
|
+
// `onOpenChange` are controlled-mode only). `children` (trigger + content) is
|
|
23
|
+
// composed content → a slot.
|
|
24
|
+
argTypes: {
|
|
25
|
+
defaultOpen: {
|
|
26
|
+
control: 'boolean',
|
|
27
|
+
description: 'Open on first render (uncontrolled)',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
args: { defaultOpen: false },
|
|
31
|
+
parameters: { puck: { slots: ['children'] } },
|
|
32
|
+
} satisfies Meta<typeof AlertDialog>;
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
type Story = StoryObj<typeof meta>;
|
|
36
|
+
|
|
37
|
+
export const Default: Story = {
|
|
38
|
+
render: (args) => (
|
|
39
|
+
<AlertDialog {...args}>
|
|
40
|
+
<AlertDialogTrigger asChild>
|
|
41
|
+
<Button variant='destructive'>Cancel appointment</Button>
|
|
42
|
+
</AlertDialogTrigger>
|
|
43
|
+
<AlertDialogContent>
|
|
44
|
+
<AlertDialogHeader>
|
|
45
|
+
<AlertDialogTitle>Cancel this appointment?</AlertDialogTitle>
|
|
46
|
+
<AlertDialogDescription>
|
|
47
|
+
The patient will be notified. This can’t be undone.
|
|
48
|
+
</AlertDialogDescription>
|
|
49
|
+
</AlertDialogHeader>
|
|
50
|
+
<AlertDialogFooter>
|
|
51
|
+
<AlertDialogCancel>Keep it</AlertDialogCancel>
|
|
52
|
+
<AlertDialogAction>Cancel appointment</AlertDialogAction>
|
|
53
|
+
</AlertDialogFooter>
|
|
54
|
+
</AlertDialogContent>
|
|
55
|
+
</AlertDialog>
|
|
56
|
+
),
|
|
57
|
+
};
|