@dtsports/design-system 0.2.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/LICENSE +21 -0
- package/README.md +156 -0
- package/dist/components/accordion.d.ts +19 -0
- package/dist/components/accordion.d.ts.map +1 -0
- package/dist/components/accordion.js +26 -0
- package/dist/components/accordion.js.map +1 -0
- package/dist/components/alert.d.ts +26 -0
- package/dist/components/alert.d.ts.map +1 -0
- package/dist/components/alert.js.map +1 -0
- package/dist/components/app-shell.d.ts +21 -0
- package/dist/components/app-shell.d.ts.map +1 -0
- package/dist/components/app-shell.js +25 -0
- package/dist/components/app-shell.js.map +1 -0
- package/dist/components/aspect-ratio.d.ts +16 -0
- package/dist/components/aspect-ratio.d.ts.map +1 -0
- package/dist/components/aspect-ratio.js +17 -0
- package/dist/components/aspect-ratio.js.map +1 -0
- package/dist/components/avatar-group.d.ts +31 -0
- package/dist/components/avatar-group.d.ts.map +1 -0
- package/dist/components/avatar.d.ts +15 -0
- package/dist/components/avatar.d.ts.map +1 -0
- package/dist/components/avatar.js +20 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +27 -0
- package/dist/components/badge.d.ts.map +1 -0
- package/dist/components/badge.js +51 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/breadcrumb.js.map +1 -0
- package/dist/components/button.d.ts +33 -0
- package/dist/components/button.d.ts.map +1 -0
- package/dist/components/button.js +58 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/calendar.d.ts +15 -0
- package/dist/components/calendar.d.ts.map +1 -0
- package/dist/components/calendar.js +33 -0
- package/dist/components/calendar.js.map +1 -0
- package/dist/components/capacity-bar.d.ts +32 -0
- package/dist/components/capacity-bar.d.ts.map +1 -0
- package/dist/components/capacity-bar.js +29 -0
- package/dist/components/capacity-bar.js.map +1 -0
- package/dist/components/card.d.ts +26 -0
- package/dist/components/card.d.ts.map +1 -0
- package/dist/components/card.js +20 -0
- package/dist/components/checkbox.d.ts +11 -0
- package/dist/components/checkbox.d.ts.map +1 -0
- package/dist/components/checkbox.js +19 -0
- package/dist/components/chip.d.ts +23 -0
- package/dist/components/chip.d.ts.map +1 -0
- package/dist/components/chip.js +32 -0
- package/dist/components/chip.js.map +1 -0
- package/dist/components/collapsible.d.ts +16 -0
- package/dist/components/collapsible.js +22 -0
- package/dist/components/collapsible.js.map +1 -0
- package/dist/components/combobox.d.ts +38 -0
- package/dist/components/combobox.d.ts.map +1 -0
- package/dist/components/combobox.js +17 -0
- package/dist/components/combobox.js.map +1 -0
- package/dist/components/confirm-action.d.ts +36 -0
- package/dist/components/confirm-action.d.ts.map +1 -0
- package/dist/components/confirm-action.js +21 -0
- package/dist/components/confirm-action.js.map +1 -0
- package/dist/components/container.js.map +1 -0
- package/dist/components/date-picker.d.ts.map +1 -0
- package/dist/components/date-picker.js +22 -0
- package/dist/components/date-picker.js.map +1 -0
- package/dist/components/dialog.d.ts +28 -0
- package/dist/components/dialog.d.ts.map +1 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/drawer.js +55 -0
- package/dist/components/drawer.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +39 -0
- package/dist/components/dropdown-menu.d.ts.map +1 -0
- package/dist/components/dropdown-menu.js +47 -0
- package/dist/components/empty-state.d.ts +15 -0
- package/dist/components/empty-state.d.ts.map +1 -0
- package/dist/components/empty-state.js +6 -0
- package/dist/components/form-field.d.ts +32 -0
- package/dist/components/form-field.d.ts.map +1 -0
- package/dist/components/form-field.js +31 -0
- package/dist/components/form-field.js.map +1 -0
- package/dist/components/input.d.ts +10 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +6 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/label.d.ts +11 -0
- package/dist/components/label.d.ts.map +1 -0
- package/dist/components/label.js +13 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/live-pill.d.ts +14 -0
- package/dist/components/live-pill.js +15 -0
- package/dist/components/live-pill.js.map +1 -0
- package/dist/components/metric-tile.d.ts +23 -0
- package/dist/components/metric-tile.d.ts.map +1 -0
- package/dist/components/money-breakdown.d.ts +36 -0
- package/dist/components/money-breakdown.d.ts.map +1 -0
- package/dist/components/money-breakdown.js +12 -0
- package/dist/components/money-breakdown.js.map +1 -0
- package/dist/components/number-input.d.ts +16 -0
- package/dist/components/number-input.d.ts.map +1 -0
- package/dist/components/number-input.js +30 -0
- package/dist/components/number-input.js.map +1 -0
- package/dist/components/pagination.d.ts +27 -0
- package/dist/components/pagination.d.ts.map +1 -0
- package/dist/components/pagination.js +52 -0
- package/dist/components/pagination.js.map +1 -0
- package/dist/components/popover.d.ts +24 -0
- package/dist/components/popover.d.ts.map +1 -0
- package/dist/components/popover.js +28 -0
- package/dist/components/popover.js.map +1 -0
- package/dist/components/progress.d.ts +13 -0
- package/dist/components/progress.d.ts.map +1 -0
- package/dist/components/progress.js +17 -0
- package/dist/components/radio-group.d.ts +10 -0
- package/dist/components/radio-group.d.ts.map +1 -0
- package/dist/components/radio-group.js +17 -0
- package/dist/components/radio-group.js.map +1 -0
- package/dist/components/scroll-area.d.ts.map +1 -0
- package/dist/components/scroll-area.js +22 -0
- package/dist/components/scroll-area.js.map +1 -0
- package/dist/components/select.d.ts +27 -0
- package/dist/components/select.d.ts.map +1 -0
- package/dist/components/select.js +40 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +10 -0
- package/dist/components/separator.d.ts.map +1 -0
- package/dist/components/separator.js +14 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sidebar.d.ts.map +1 -0
- package/dist/components/sidebar.js +48 -0
- package/dist/components/sidebar.js.map +1 -0
- package/dist/components/skeleton.d.ts +23 -0
- package/dist/components/skeleton.js +25 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/slider.d.ts +14 -0
- package/dist/components/slider.d.ts.map +1 -0
- package/dist/components/slider.js +30 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/components/spec.d.ts +35 -0
- package/dist/components/spec.js +31 -0
- package/dist/components/spec.js.map +1 -0
- package/dist/components/spinner.d.ts +20 -0
- package/dist/components/spinner.d.ts.map +1 -0
- package/dist/components/spinner.js +18 -0
- package/dist/components/spinner.js.map +1 -0
- package/dist/components/stack.d.ts +48 -0
- package/dist/components/stack.d.ts.map +1 -0
- package/dist/components/stack.js +43 -0
- package/dist/components/stack.js.map +1 -0
- package/dist/components/status-pill.js +34 -0
- package/dist/components/status-pill.js.map +1 -0
- package/dist/components/switch.d.ts +9 -0
- package/dist/components/switch.d.ts.map +1 -0
- package/dist/components/switch.js +15 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/table.d.ts +31 -0
- package/dist/components/table.d.ts.map +1 -0
- package/dist/components/tabs.d.ts +22 -0
- package/dist/components/tabs.d.ts.map +1 -0
- package/dist/components/tabs.js +28 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +8 -0
- package/dist/components/textarea.d.ts.map +1 -0
- package/dist/components/textarea.js +6 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/theme-provider.d.ts +35 -0
- package/dist/components/theme-provider.d.ts.map +1 -0
- package/dist/components/theme-provider.js.map +1 -0
- package/dist/components/theme-toggle.d.ts +10 -0
- package/dist/components/theme-toggle.d.ts.map +1 -0
- package/dist/components/theme-toggle.js.map +1 -0
- package/dist/components/three-state.d.ts +39 -0
- package/dist/components/three-state.d.ts.map +1 -0
- package/dist/components/three-state.js +24 -0
- package/dist/components/three-state.js.map +1 -0
- package/dist/components/timestamp.d.ts +24 -0
- package/dist/components/timestamp.js +64 -0
- package/dist/components/timestamp.js.map +1 -0
- package/dist/components/toaster.d.ts +27 -0
- package/dist/components/toaster.d.ts.map +1 -0
- package/dist/components/toaster.js +21 -0
- package/dist/components/toaster.js.map +1 -0
- package/dist/components/tooltip.d.ts +28 -0
- package/dist/components/tooltip.d.ts.map +1 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/components/top-bar.d.ts +13 -0
- package/dist/components/top-bar.d.ts.map +1 -0
- package/dist/components/top-bar.js +16 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +7 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +10 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/marketing/footer.d.ts +31 -0
- package/dist/marketing/footer.d.ts.map +1 -0
- package/dist/marketing/footer.js +9 -0
- package/dist/marketing/footer.js.map +1 -0
- package/dist/marketing/hero.d.ts +29 -0
- package/dist/marketing/hero.d.ts.map +1 -0
- package/dist/marketing/hero.js +7 -0
- package/dist/marketing/hero.js.map +1 -0
- package/dist/marketing/index.d.ts +21 -0
- package/dist/marketing/index.d.ts.map +1 -0
- package/dist/marketing/index.js +21 -0
- package/dist/marketing/index.js.map +1 -0
- package/dist/marketing/logo-cloud.d.ts +15 -0
- package/dist/marketing/logo-cloud.d.ts.map +1 -0
- package/dist/marketing/logo-cloud.js +6 -0
- package/dist/marketing/logo-cloud.js.map +1 -0
- package/dist/marketing/nav.d.ts +33 -0
- package/dist/marketing/nav.d.ts.map +1 -0
- package/dist/marketing/nav.js +14 -0
- package/dist/marketing/nav.js.map +1 -0
- package/dist/marketing/prose.d.ts +12 -0
- package/dist/marketing/prose.d.ts.map +1 -0
- package/dist/marketing/prose.js +18 -0
- package/dist/marketing/prose.js.map +1 -0
- package/dist/marketing/section.d.ts +22 -0
- package/dist/marketing/section.d.ts.map +1 -0
- package/dist/marketing/section.js +7 -0
- package/dist/marketing/section.js.map +1 -0
- package/dist/showcase/components/code-block.d.ts +11 -0
- package/dist/showcase/components/code-block.d.ts.map +1 -0
- package/dist/showcase/components/code-block.js +20 -0
- package/dist/showcase/components/code-block.js.map +1 -0
- package/dist/showcase/components/example.d.ts +16 -0
- package/dist/showcase/components/example.d.ts.map +1 -0
- package/dist/showcase/components/example.js +14 -0
- package/dist/showcase/components/example.js.map +1 -0
- package/dist/showcase/components/section.d.ts +21 -0
- package/dist/showcase/components/section.d.ts.map +1 -0
- package/dist/showcase/components/section.js +10 -0
- package/dist/showcase/components/section.js.map +1 -0
- package/dist/showcase/components/swatch.d.ts +17 -0
- package/dist/showcase/components/swatch.d.ts.map +1 -0
- package/dist/showcase/components/swatch.js +6 -0
- package/dist/showcase/components/swatch.js.map +1 -0
- package/dist/showcase/index.d.ts +17 -0
- package/dist/showcase/index.d.ts.map +1 -0
- package/dist/showcase/index.js +17 -0
- package/dist/showcase/index.js.map +1 -0
- package/dist/showcase/page.d.ts +2 -0
- package/dist/showcase/page.d.ts.map +1 -0
- package/dist/showcase/page.js +1085 -0
- package/dist/showcase/page.js.map +1 -0
- package/dist/styles/generated/tokens.d.ts +131 -0
- package/dist/styles/generated/tokens.d.ts.map +1 -0
- package/dist/styles/generated/tokens.js +107 -0
- package/dist/styles/generated/tokens.js.map +1 -0
- package/dist/styles/globals.css +131 -0
- package/dist/styles/tailwind.preset.d.ts +181 -0
- package/dist/styles/tailwind.preset.d.ts.map +1 -0
- package/dist/styles/tailwind.preset.js +114 -0
- package/dist/styles/tailwind.preset.js.map +1 -0
- package/dist/tokens/index.d.ts +176 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +139 -0
- package/dist/tokens/index.js.map +1 -0
- package/dist/tokens/tokens.json +154 -0
- package/package.json +124 -0
- package/src/components/accordion.tsx +76 -0
- package/src/components/alert.tsx +113 -0
- package/src/components/app-shell.tsx +43 -0
- package/src/components/aspect-ratio.tsx +18 -0
- package/src/components/avatar-group.tsx +73 -0
- package/src/components/avatar.tsx +81 -0
- package/src/components/badge.tsx +77 -0
- package/src/components/breadcrumb.tsx +121 -0
- package/src/components/button.tsx +83 -0
- package/src/components/calendar.tsx +85 -0
- package/src/components/capacity-bar.tsx +80 -0
- package/src/components/card.tsx +104 -0
- package/src/components/checkbox.tsx +46 -0
- package/src/components/chip.tsx +74 -0
- package/src/components/collapsible.tsx +54 -0
- package/src/components/combobox.tsx +140 -0
- package/src/components/confirm-action.tsx +111 -0
- package/src/components/container.tsx +39 -0
- package/src/components/date-picker.tsx +98 -0
- package/src/components/dialog.tsx +174 -0
- package/src/components/drawer.tsx +157 -0
- package/src/components/dropdown-menu.tsx +200 -0
- package/src/components/empty-state.tsx +46 -0
- package/src/components/form-field.tsx +118 -0
- package/src/components/input.tsx +29 -0
- package/src/components/label.tsx +35 -0
- package/src/components/live-pill.tsx +35 -0
- package/src/components/metric-tile.tsx +83 -0
- package/src/components/money-breakdown.tsx +92 -0
- package/src/components/number-input.tsx +113 -0
- package/src/components/pagination.tsx +157 -0
- package/src/components/popover.tsx +46 -0
- package/src/components/progress.tsx +34 -0
- package/src/components/radio-group.tsx +46 -0
- package/src/components/scroll-area.tsx +55 -0
- package/src/components/select.tsx +169 -0
- package/src/components/separator.tsx +34 -0
- package/src/components/sidebar.tsx +168 -0
- package/src/components/skeleton.tsx +85 -0
- package/src/components/slider.tsx +58 -0
- package/src/components/spec.tsx +59 -0
- package/src/components/spinner.tsx +36 -0
- package/src/components/stack.tsx +80 -0
- package/src/components/status-pill.tsx +100 -0
- package/src/components/switch.tsx +37 -0
- package/src/components/table.tsx +129 -0
- package/src/components/tabs.tsx +72 -0
- package/src/components/textarea.tsx +27 -0
- package/src/components/theme-provider.tsx +131 -0
- package/src/components/theme-toggle.tsx +34 -0
- package/src/components/three-state.tsx +90 -0
- package/src/components/timestamp.tsx +107 -0
- package/src/components/toaster.tsx +55 -0
- package/src/components/tooltip.tsx +49 -0
- package/src/components/top-bar.tsx +29 -0
- package/src/index.ts +221 -0
- package/src/lib/utils.ts +10 -0
- package/src/marketing/footer.tsx +93 -0
- package/src/marketing/hero.tsx +92 -0
- package/src/marketing/index.ts +24 -0
- package/src/marketing/logo-cloud.tsx +35 -0
- package/src/marketing/nav.tsx +122 -0
- package/src/marketing/prose.tsx +45 -0
- package/src/marketing/section.tsx +77 -0
- package/src/showcase/components/code-block.tsx +66 -0
- package/src/showcase/components/example.tsx +47 -0
- package/src/showcase/components/section.tsx +70 -0
- package/src/showcase/components/swatch.tsx +52 -0
- package/src/showcase/index.tsx +16 -0
- package/src/showcase/page.tsx +4216 -0
- package/src/styles/generated/tokens.ts +119 -0
- package/src/styles/globals.css +131 -0
- package/src/styles/tailwind.preset.ts +134 -0
- package/src/test/a11y.test.tsx +638 -0
- package/src/test/marketing-a11y.test.tsx +138 -0
- package/src/test/setup.ts +47 -0
- package/src/tokens/index.ts +154 -0
- package/src/tokens/tokens.json +154 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* LogoCloud — "trusted by" strip. Children are the logos (imgs or SVGs);
|
|
6
|
+
* give each an accessible name (alt text or aria-label).
|
|
7
|
+
*
|
|
8
|
+
* <LogoCloud label="Trusted by leagues across NYC">
|
|
9
|
+
* <img src="/logos/x.svg" alt="X League" className="h-6" />
|
|
10
|
+
* …
|
|
11
|
+
* </LogoCloud>
|
|
12
|
+
*/
|
|
13
|
+
export interface LogoCloudProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
14
|
+
label?: React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function LogoCloud({
|
|
18
|
+
label,
|
|
19
|
+
className,
|
|
20
|
+
children,
|
|
21
|
+
...props
|
|
22
|
+
}: LogoCloudProps) {
|
|
23
|
+
return (
|
|
24
|
+
<div className={cn("py-10 text-center", className)} {...props}>
|
|
25
|
+
{label && (
|
|
26
|
+
<p className="mb-6 text-xs font-semibold uppercase tracking-[0.08em] text-text-subtle">
|
|
27
|
+
{label}
|
|
28
|
+
</p>
|
|
29
|
+
)}
|
|
30
|
+
<div className="flex flex-wrap items-center justify-center gap-x-10 gap-y-6 opacity-70 grayscale">
|
|
31
|
+
{children}
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Menu, X } from "lucide-react";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* MarketingNav — sticky marketing header: brand left, links center,
|
|
9
|
+
* CTA right, hamburger disclosure on mobile (44px targets, aria-expanded
|
|
10
|
+
* wired). Non-modal on purpose — the page stays scrollable behind it.
|
|
11
|
+
*
|
|
12
|
+
* <MarketingNav
|
|
13
|
+
* brand={<a href="/" aria-label="DT Sports home"><Logo /></a>}
|
|
14
|
+
* links={[
|
|
15
|
+
* { label: "Games", href: "/games" },
|
|
16
|
+
* { label: "Venues", href: "/venues" },
|
|
17
|
+
* { label: "Pricing", href: "/pricing" },
|
|
18
|
+
* ]}
|
|
19
|
+
* cta={<Button variant="accent">Find a game</Button>}
|
|
20
|
+
* />
|
|
21
|
+
*
|
|
22
|
+
* Render links as your router's component via `renderLink` when needed
|
|
23
|
+
* (defaults to a plain <a>).
|
|
24
|
+
*/
|
|
25
|
+
export interface MarketingNavLink {
|
|
26
|
+
label: string;
|
|
27
|
+
href: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface MarketingNavProps extends React.HTMLAttributes<HTMLElement> {
|
|
31
|
+
brand: React.ReactNode;
|
|
32
|
+
links: MarketingNavLink[];
|
|
33
|
+
/** Right-hand CTA (keep to one accent action). */
|
|
34
|
+
cta?: React.ReactNode;
|
|
35
|
+
/** Custom link renderer, e.g. Next.js Link. */
|
|
36
|
+
renderLink?: (link: MarketingNavLink, className: string) => React.ReactNode;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const desktopLinkClass = cn(
|
|
40
|
+
"text-sm font-medium text-text-muted hover:text-foreground transition-colors rounded-sm px-1",
|
|
41
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const mobileLinkClass = cn(
|
|
45
|
+
"flex items-center min-h-11 px-2 text-md font-medium text-foreground rounded-sm",
|
|
46
|
+
"hover:bg-bg-subtle transition-colors",
|
|
47
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
export function MarketingNav({
|
|
51
|
+
brand,
|
|
52
|
+
links,
|
|
53
|
+
cta,
|
|
54
|
+
renderLink,
|
|
55
|
+
className,
|
|
56
|
+
...props
|
|
57
|
+
}: MarketingNavProps) {
|
|
58
|
+
const [open, setOpen] = React.useState(false);
|
|
59
|
+
const panelId = React.useId();
|
|
60
|
+
|
|
61
|
+
const link = (l: MarketingNavLink, cls: string) =>
|
|
62
|
+
renderLink ? (
|
|
63
|
+
<React.Fragment key={l.href}>{renderLink(l, cls)}</React.Fragment>
|
|
64
|
+
) : (
|
|
65
|
+
<a key={l.href} href={l.href} className={cls}>
|
|
66
|
+
{l.label}
|
|
67
|
+
</a>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<header
|
|
72
|
+
className={cn(
|
|
73
|
+
"sticky top-0 z-sticky border-b border-border bg-background/90 backdrop-blur",
|
|
74
|
+
className
|
|
75
|
+
)}
|
|
76
|
+
{...props}
|
|
77
|
+
>
|
|
78
|
+
<nav
|
|
79
|
+
aria-label="Main"
|
|
80
|
+
className="mx-auto flex h-14 max-w-6xl items-center justify-between gap-6 px-6 lg:px-8"
|
|
81
|
+
>
|
|
82
|
+
<div className="flex items-center gap-8">
|
|
83
|
+
{brand}
|
|
84
|
+
<div className="hidden md:flex items-center gap-6">
|
|
85
|
+
{links.map((l) => link(l, desktopLinkClass))}
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
<div className="flex items-center gap-3">
|
|
89
|
+
{cta && <div className="hidden md:block">{cta}</div>}
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
aria-expanded={open}
|
|
93
|
+
aria-controls={panelId}
|
|
94
|
+
aria-label={open ? "Close menu" : "Open menu"}
|
|
95
|
+
onClick={() => setOpen((v) => !v)}
|
|
96
|
+
className={cn(
|
|
97
|
+
"md:hidden inline-flex h-11 w-11 items-center justify-center rounded-sm text-foreground",
|
|
98
|
+
"hover:bg-bg-subtle transition-colors",
|
|
99
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
100
|
+
)}
|
|
101
|
+
>
|
|
102
|
+
{open ? (
|
|
103
|
+
<X className="h-5 w-5" strokeWidth={1.75} aria-hidden="true" />
|
|
104
|
+
) : (
|
|
105
|
+
<Menu className="h-5 w-5" strokeWidth={1.75} aria-hidden="true" />
|
|
106
|
+
)}
|
|
107
|
+
</button>
|
|
108
|
+
</div>
|
|
109
|
+
</nav>
|
|
110
|
+
<div
|
|
111
|
+
id={panelId}
|
|
112
|
+
hidden={!open}
|
|
113
|
+
className="md:hidden border-t border-border bg-background px-4 py-3"
|
|
114
|
+
>
|
|
115
|
+
<div className="flex flex-col gap-1">
|
|
116
|
+
{links.map((l) => link(l, mobileLinkClass))}
|
|
117
|
+
</div>
|
|
118
|
+
{cta && <div className="mt-3 border-t border-border pt-3">{cta}</div>}
|
|
119
|
+
</div>
|
|
120
|
+
</header>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Prose — typographic wrapper for long-form marketing/blog/legal content
|
|
6
|
+
* (rendered markdown, CMS HTML). Styles descendant headings, paragraphs,
|
|
7
|
+
* lists, links, and code without requiring @tailwindcss/typography.
|
|
8
|
+
*
|
|
9
|
+
* <Prose dangerouslySetInnerHTML={{ __html: post.html }} />
|
|
10
|
+
* <Prose>{children}</Prose>
|
|
11
|
+
*/
|
|
12
|
+
export type ProseProps = React.HTMLAttributes<HTMLDivElement>;
|
|
13
|
+
|
|
14
|
+
export function Prose({ className, ...props }: ProseProps) {
|
|
15
|
+
return (
|
|
16
|
+
<div
|
|
17
|
+
className={cn(
|
|
18
|
+
"max-w-2xl text-md leading-relaxed text-foreground",
|
|
19
|
+
// Headings
|
|
20
|
+
"[&_h1]:text-display-sm [&_h1]:font-bold [&_h1]:mt-10 [&_h1]:mb-4",
|
|
21
|
+
"[&_h2]:text-2xl [&_h2]:font-semibold [&_h2]:mt-10 [&_h2]:mb-3",
|
|
22
|
+
"[&_h3]:text-xl [&_h3]:font-semibold [&_h3]:mt-8 [&_h3]:mb-2",
|
|
23
|
+
"[&_h4]:text-lg [&_h4]:font-semibold [&_h4]:mt-6 [&_h4]:mb-2",
|
|
24
|
+
// Body
|
|
25
|
+
"[&_p]:my-4 [&_p]:text-text-muted",
|
|
26
|
+
"[&_strong]:font-semibold [&_strong]:text-foreground",
|
|
27
|
+
// Links — underlined (color is not the only signal)
|
|
28
|
+
"[&_a]:text-brand-forest [&_a]:underline [&_a]:underline-offset-4 hover:[&_a]:text-brand-forest/80",
|
|
29
|
+
// Lists
|
|
30
|
+
"[&_ul]:my-4 [&_ul]:list-disc [&_ul]:pl-6 [&_ul]:text-text-muted",
|
|
31
|
+
"[&_ol]:my-4 [&_ol]:list-decimal [&_ol]:pl-6 [&_ol]:text-text-muted",
|
|
32
|
+
"[&_li]:my-1.5",
|
|
33
|
+
// Quotes, rules, code
|
|
34
|
+
"[&_blockquote]:my-6 [&_blockquote]:border-l-2 [&_blockquote]:border-brand-forest [&_blockquote]:pl-4 [&_blockquote]:italic [&_blockquote]:text-text-muted",
|
|
35
|
+
"[&_hr]:my-8 [&_hr]:border-border",
|
|
36
|
+
"[&_code]:font-mono [&_code]:text-sm [&_code]:bg-bg-muted [&_code]:rounded-sm [&_code]:px-1 [&_code]:py-0.5",
|
|
37
|
+
"[&_pre]:my-4 [&_pre]:overflow-x-auto [&_pre]:rounded [&_pre]:border [&_pre]:border-border [&_pre]:bg-bg-subtle [&_pre]:p-4 [&_pre_code]:bg-transparent [&_pre_code]:p-0",
|
|
38
|
+
// Media
|
|
39
|
+
"[&_img]:my-6 [&_img]:rounded [&_img]:border [&_img]:border-border [&_img]:max-w-full",
|
|
40
|
+
className
|
|
41
|
+
)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Section — a marketing page band with the generous spacing scale.
|
|
6
|
+
* Provides the id/heading wiring for skip-navigation and deep links.
|
|
7
|
+
*
|
|
8
|
+
* <Section id="how-it-works" eyebrow="How it works"
|
|
9
|
+
* title="Three taps to kickoff">
|
|
10
|
+
* …cards / feature grid…
|
|
11
|
+
* </Section>
|
|
12
|
+
*/
|
|
13
|
+
export interface SectionProps extends Omit<
|
|
14
|
+
React.HTMLAttributes<HTMLElement>,
|
|
15
|
+
"title"
|
|
16
|
+
> {
|
|
17
|
+
id: string;
|
|
18
|
+
eyebrow?: React.ReactNode;
|
|
19
|
+
title?: React.ReactNode;
|
|
20
|
+
description?: React.ReactNode;
|
|
21
|
+
/** Center the header block (default start-aligned). */
|
|
22
|
+
centered?: boolean;
|
|
23
|
+
/** Subtle alternating background. */
|
|
24
|
+
tone?: "default" | "subtle";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function Section({
|
|
28
|
+
id,
|
|
29
|
+
eyebrow,
|
|
30
|
+
title,
|
|
31
|
+
description,
|
|
32
|
+
centered = false,
|
|
33
|
+
tone = "default",
|
|
34
|
+
className,
|
|
35
|
+
children,
|
|
36
|
+
...props
|
|
37
|
+
}: SectionProps) {
|
|
38
|
+
const headingId = title ? `${id}-title` : undefined;
|
|
39
|
+
return (
|
|
40
|
+
<section
|
|
41
|
+
id={id}
|
|
42
|
+
aria-labelledby={headingId}
|
|
43
|
+
className={cn(
|
|
44
|
+
"px-6 lg:px-8 py-20 md:py-24 scroll-mt-16",
|
|
45
|
+
tone === "subtle" && "bg-bg-subtle",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
<div className="mx-auto max-w-5xl">
|
|
51
|
+
{(eyebrow || title || description) && (
|
|
52
|
+
<div
|
|
53
|
+
className={cn("mb-12 max-w-2xl", centered && "mx-auto text-center")}
|
|
54
|
+
>
|
|
55
|
+
{eyebrow && (
|
|
56
|
+
<p className="mb-3 text-sm font-semibold uppercase tracking-[0.08em] text-brand-forest">
|
|
57
|
+
{eyebrow}
|
|
58
|
+
</p>
|
|
59
|
+
)}
|
|
60
|
+
{title && (
|
|
61
|
+
<h2
|
|
62
|
+
id={headingId}
|
|
63
|
+
className="text-display-sm font-bold text-foreground text-balance"
|
|
64
|
+
>
|
|
65
|
+
{title}
|
|
66
|
+
</h2>
|
|
67
|
+
)}
|
|
68
|
+
{description && (
|
|
69
|
+
<p className="mt-4 text-lg text-text-muted">{description}</p>
|
|
70
|
+
)}
|
|
71
|
+
</div>
|
|
72
|
+
)}
|
|
73
|
+
{children}
|
|
74
|
+
</div>
|
|
75
|
+
</section>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Check, Copy } from "lucide-react";
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Code block with copy-to-clipboard. No syntax highlighting (intentional —
|
|
9
|
+
* the design system stays decoration-free; we can layer Shiki later if needed).
|
|
10
|
+
*/
|
|
11
|
+
export interface CodeBlockProps {
|
|
12
|
+
code: string;
|
|
13
|
+
language?: string;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function CodeBlock({ code, language, className }: CodeBlockProps) {
|
|
18
|
+
const [copied, setCopied] = React.useState(false);
|
|
19
|
+
|
|
20
|
+
const onCopy = async () => {
|
|
21
|
+
try {
|
|
22
|
+
await navigator.clipboard.writeText(code);
|
|
23
|
+
setCopied(true);
|
|
24
|
+
setTimeout(() => setCopied(false), 1500);
|
|
25
|
+
} catch {
|
|
26
|
+
/* ignore */
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
className={cn(
|
|
33
|
+
"relative rounded border border-border bg-bg-subtle",
|
|
34
|
+
className
|
|
35
|
+
)}
|
|
36
|
+
>
|
|
37
|
+
{language && (
|
|
38
|
+
<div className="flex items-center justify-between border-b border-border px-3 py-1.5">
|
|
39
|
+
<span className="text-[10px] font-medium uppercase tracking-[0.04em] text-text-muted">
|
|
40
|
+
{language}
|
|
41
|
+
</span>
|
|
42
|
+
</div>
|
|
43
|
+
)}
|
|
44
|
+
<button
|
|
45
|
+
type="button"
|
|
46
|
+
onClick={onCopy}
|
|
47
|
+
aria-label={copied ? "Copied" : "Copy code"}
|
|
48
|
+
className={cn(
|
|
49
|
+
"absolute right-2 top-2 inline-flex h-6 w-6 items-center justify-center rounded-sm text-text-muted",
|
|
50
|
+
"transition-colors hover:bg-bg-muted hover:text-foreground",
|
|
51
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
52
|
+
language && "top-7"
|
|
53
|
+
)}
|
|
54
|
+
>
|
|
55
|
+
{copied ? (
|
|
56
|
+
<Check className="h-3.5 w-3.5" strokeWidth={2} />
|
|
57
|
+
) : (
|
|
58
|
+
<Copy className="h-3.5 w-3.5" strokeWidth={1.75} />
|
|
59
|
+
)}
|
|
60
|
+
</button>
|
|
61
|
+
<pre className="overflow-x-auto scrollbar-thin px-3 py-2.5 text-xs leading-relaxed">
|
|
62
|
+
<code className="font-mono text-foreground">{code}</code>
|
|
63
|
+
</pre>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Example renders a labeled preview + code block side-by-side (or stacked
|
|
6
|
+
* on narrow screens). Used throughout the design system docs.
|
|
7
|
+
*/
|
|
8
|
+
export function Example({
|
|
9
|
+
label,
|
|
10
|
+
children,
|
|
11
|
+
className,
|
|
12
|
+
}: {
|
|
13
|
+
label?: string;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
className?: string;
|
|
16
|
+
}) {
|
|
17
|
+
return (
|
|
18
|
+
<div className={cn("space-y-2", className)}>
|
|
19
|
+
{label && (
|
|
20
|
+
<div className="text-xs font-medium uppercase tracking-[0.04em] text-text-muted">
|
|
21
|
+
{label}
|
|
22
|
+
</div>
|
|
23
|
+
)}
|
|
24
|
+
<div className="rounded border border-border bg-background p-5">
|
|
25
|
+
{children}
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Two-column grid: preview on the left, code on the right. */
|
|
32
|
+
export function PreviewCode({
|
|
33
|
+
preview,
|
|
34
|
+
code,
|
|
35
|
+
}: {
|
|
36
|
+
preview: React.ReactNode;
|
|
37
|
+
code: React.ReactNode;
|
|
38
|
+
}) {
|
|
39
|
+
return (
|
|
40
|
+
<div className="grid gap-3 md:grid-cols-2">
|
|
41
|
+
<div className="rounded border border-border bg-background p-5 flex items-center justify-center min-h-[120px]">
|
|
42
|
+
{preview}
|
|
43
|
+
</div>
|
|
44
|
+
<div>{code}</div>
|
|
45
|
+
</div>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "../../lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Section wrapper for the design system docs.
|
|
6
|
+
* Renders an id-anchored heading with description and content body.
|
|
7
|
+
*/
|
|
8
|
+
export interface DocSectionProps {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
description?: string;
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function DocSection({
|
|
17
|
+
id,
|
|
18
|
+
title,
|
|
19
|
+
description,
|
|
20
|
+
children,
|
|
21
|
+
className,
|
|
22
|
+
}: DocSectionProps) {
|
|
23
|
+
return (
|
|
24
|
+
<section
|
|
25
|
+
id={id}
|
|
26
|
+
aria-labelledby={`${id}-title`}
|
|
27
|
+
className={cn("scroll-mt-16 py-10 border-t border-border", className)}
|
|
28
|
+
>
|
|
29
|
+
<div className="mb-6">
|
|
30
|
+
<h2
|
|
31
|
+
id={`${id}-title`}
|
|
32
|
+
className="text-xl font-semibold tracking-[-0.01em] text-foreground"
|
|
33
|
+
>
|
|
34
|
+
<a href={`#${id}`} className="hover:text-brand-forest no-underline">
|
|
35
|
+
{title}
|
|
36
|
+
</a>
|
|
37
|
+
</h2>
|
|
38
|
+
{description && (
|
|
39
|
+
<p className="mt-1.5 text-sm text-text-muted max-w-2xl">
|
|
40
|
+
{description}
|
|
41
|
+
</p>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
<div className="space-y-6">{children}</div>
|
|
45
|
+
</section>
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Subsection within a DocSection. */
|
|
50
|
+
export function DocSubsection({
|
|
51
|
+
title,
|
|
52
|
+
description,
|
|
53
|
+
children,
|
|
54
|
+
className,
|
|
55
|
+
}: {
|
|
56
|
+
title: string;
|
|
57
|
+
description?: string;
|
|
58
|
+
children: React.ReactNode;
|
|
59
|
+
className?: string;
|
|
60
|
+
}) {
|
|
61
|
+
return (
|
|
62
|
+
<div className={cn("", className)}>
|
|
63
|
+
<h3 className="text-md font-semibold mb-1.5">{title}</h3>
|
|
64
|
+
{description && (
|
|
65
|
+
<p className="text-sm text-text-muted mb-3 max-w-2xl">{description}</p>
|
|
66
|
+
)}
|
|
67
|
+
{children}
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { cn } from "../../lib/utils";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Color swatch used in the design system docs.
|
|
5
|
+
* Renders a color tile with token name + value below.
|
|
6
|
+
*/
|
|
7
|
+
export interface SwatchProps {
|
|
8
|
+
token: string;
|
|
9
|
+
/** Tailwind class for the background (e.g. "bg-brand-lime") */
|
|
10
|
+
bg: string;
|
|
11
|
+
/** Color value to display (e.g. "#B4D334" or "hsl(72 65% 52%)") */
|
|
12
|
+
value: string;
|
|
13
|
+
/** Optional: text color sample to show on top of the swatch */
|
|
14
|
+
textSample?: "white" | "black" | "none";
|
|
15
|
+
/** Optional: contrast ratio note */
|
|
16
|
+
contrast?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function Swatch({
|
|
20
|
+
token,
|
|
21
|
+
bg,
|
|
22
|
+
value,
|
|
23
|
+
textSample = "none",
|
|
24
|
+
contrast,
|
|
25
|
+
}: SwatchProps) {
|
|
26
|
+
return (
|
|
27
|
+
<div className="flex flex-col">
|
|
28
|
+
<div
|
|
29
|
+
className={cn(
|
|
30
|
+
"h-16 rounded border border-border flex items-center justify-center",
|
|
31
|
+
bg
|
|
32
|
+
)}
|
|
33
|
+
>
|
|
34
|
+
{textSample !== "none" && (
|
|
35
|
+
<span
|
|
36
|
+
className={cn(
|
|
37
|
+
"text-sm font-medium",
|
|
38
|
+
textSample === "white" ? "text-white" : "text-black"
|
|
39
|
+
)}
|
|
40
|
+
>
|
|
41
|
+
Aa
|
|
42
|
+
</span>
|
|
43
|
+
)}
|
|
44
|
+
</div>
|
|
45
|
+
<div className="mt-1.5 text-xs">
|
|
46
|
+
<div className="font-medium text-foreground">{token}</div>
|
|
47
|
+
<div className="text-text-muted font-mono tabular">{value}</div>
|
|
48
|
+
{contrast && <div className="text-text-subtle mt-0.5">{contrast}</div>}
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mountable design-system documentation page.
|
|
3
|
+
*
|
|
4
|
+
* Default export is the page component itself, so consumers can wire it
|
|
5
|
+
* up as a Next.js page in one line:
|
|
6
|
+
*
|
|
7
|
+
* // apps/<your-app>/app/design-system/page.tsx
|
|
8
|
+
* export { default } from "@dtsports/design-system/showcase";
|
|
9
|
+
*
|
|
10
|
+
* Or used as a regular component anywhere:
|
|
11
|
+
*
|
|
12
|
+
* import { DesignSystemPage } from "@dtsports/design-system/showcase";
|
|
13
|
+
* <DesignSystemPage />
|
|
14
|
+
*/
|
|
15
|
+
export { default } from "./page";
|
|
16
|
+
export { default as DesignSystemPage } from "./page";
|