@mohammad-irfan/create-dashboard-kit 1.0.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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +153 -0
  3. package/bin/create-dashboard.js +12 -0
  4. package/dist/cli.d.ts +7 -0
  5. package/dist/cli.js +223 -0
  6. package/dist/configBuilder.d.ts +8 -0
  7. package/dist/configBuilder.js +573 -0
  8. package/dist/generator.d.ts +9 -0
  9. package/dist/generator.js +223 -0
  10. package/dist/presets.d.ts +8 -0
  11. package/dist/presets.js +72 -0
  12. package/dist/prompts.d.ts +2 -0
  13. package/dist/prompts.js +121 -0
  14. package/dist/templateEngine.d.ts +3 -0
  15. package/dist/templateEngine.js +31 -0
  16. package/dist/types.d.ts +54 -0
  17. package/dist/types.js +1 -0
  18. package/dist/validation.d.ts +16 -0
  19. package/dist/validation.js +73 -0
  20. package/package.json +56 -0
  21. package/templates/source/components/controls/Button.tsx +69 -0
  22. package/templates/source/components/controls/Input.tsx +59 -0
  23. package/templates/source/components/controls/SearchBar.tsx +44 -0
  24. package/templates/source/components/controls/Select.tsx +61 -0
  25. package/templates/source/components/controls/ThemeToggle.tsx +23 -0
  26. package/templates/source/components/feedback/NotificationCenter.tsx +151 -0
  27. package/templates/source/components/feedback/Skeleton.tsx +22 -0
  28. package/templates/source/components/feedback/StatusBadge.tsx +35 -0
  29. package/templates/source/components/feedback/statusBadgeStyles.ts +28 -0
  30. package/templates/source/components/icons/IconSet.tsx +235 -0
  31. package/templates/source/components/layout/AppShell.tsx +70 -0
  32. package/templates/source/components/layout/Header.tsx +90 -0
  33. package/templates/source/components/layout/PageHeader.tsx +27 -0
  34. package/templates/source/components/layout/Sidebar.tsx +202 -0
  35. package/templates/source/components/metrics/MetricRow.tsx +61 -0
  36. package/templates/source/components/metrics/StatCard.tsx +49 -0
  37. package/templates/source/components/navigation/NavItem.tsx +75 -0
  38. package/templates/source/components/navigation/ShortcutPill.tsx +39 -0
  39. package/templates/source/components/surfaces/EmptyState.tsx +36 -0
  40. package/templates/source/components/surfaces/HeroSurface.tsx +64 -0
  41. package/templates/source/components/surfaces/Panel.tsx +43 -0
  42. package/templates/source/components/surfaces/PriorityCard.tsx +75 -0
  43. package/templates/source/components/surfaces/QuietListCard.tsx +146 -0
  44. package/templates/source/design-system/animations.css +25 -0
  45. package/templates/source/design-system/tokens.css +74 -0
  46. package/templates/source/design-system/typography.css +34 -0
  47. package/templates/source/theme/ThemeContext.tsx +82 -0
  48. package/templates/source/theme/useTheme.ts +10 -0
@@ -0,0 +1,49 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export interface StatCardProps {
4
+ label: string
5
+ value: ReactNode
6
+ icon?: ReactNode
7
+ iconClassName?: string
8
+ hint?: ReactNode
9
+ onClick?: () => void
10
+ className?: string
11
+ }
12
+
13
+ export function StatCard({
14
+ label,
15
+ value,
16
+ icon,
17
+ iconClassName,
18
+ hint,
19
+ onClick,
20
+ className = '',
21
+ }: StatCardProps) {
22
+ return (
23
+ <div
24
+ onClick={onClick}
25
+ className={`group rounded-2xl border border-border/40 bg-surface dark:bg-surface-elevated/40 p-5 shadow-2xs transition-all duration-200 hover:-translate-y-0.5 hover:border-primary/40 hover:shadow-md ${
26
+ onClick ? 'cursor-pointer' : ''
27
+ } ${className}`}
28
+ >
29
+ <div className="flex items-start justify-between gap-3">
30
+ <div className="min-w-0">
31
+ <p className="text-sm font-medium text-muted-foreground">{label}</p>
32
+ <p className="mt-2 text-3xl font-bold tracking-tight text-foreground tabular-nums group-hover:text-primary transition-colors">
33
+ {value}
34
+ </p>
35
+ {hint ? <p className="mt-1 text-xs text-muted-foreground/70">{hint}</p> : null}
36
+ </div>
37
+ {icon ? (
38
+ <span
39
+ className={`flex h-11 w-11 shrink-0 items-center justify-center rounded-xl transition-transform duration-200 group-hover:scale-105 ${
40
+ iconClassName ?? 'bg-primary/10 text-primary'
41
+ }`}
42
+ >
43
+ {icon}
44
+ </span>
45
+ ) : null}
46
+ </div>
47
+ </div>
48
+ )
49
+ }
@@ -0,0 +1,75 @@
1
+ import type { ComponentType, SVGProps } from 'react'
2
+ import { renderNamedIcon } from '../icons/IconSet.tsx'
3
+
4
+ export interface NavItemProps {
5
+ label: string
6
+ to: string
7
+ icon?: string | ComponentType<SVGProps<SVGSVGElement>>
8
+ badge?: string | number
9
+ active?: boolean
10
+ collapsed?: boolean
11
+ onClick?: () => void
12
+ className?: string
13
+ }
14
+
15
+ export function NavItem({
16
+ label,
17
+ to,
18
+ icon: IconComponent,
19
+ badge,
20
+ active = false,
21
+ collapsed = false,
22
+ onClick,
23
+ className = '',
24
+ }: NavItemProps) {
25
+ const base = `group relative flex items-center rounded-full text-sm transition-all duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring ${
26
+ collapsed ? 'justify-center h-10 w-10 mx-auto' : 'gap-3.5 px-4 py-2.5 mx-2'
27
+ }`
28
+
29
+ const stateClass = active
30
+ ? `${base} bg-primary/12 font-medium text-primary shadow-2xs`
31
+ : `${base} font-normal text-muted-foreground hover:bg-muted/60 hover:text-foreground`
32
+
33
+ return (
34
+ <a
35
+ href={to}
36
+ onClick={(e) => {
37
+ if (onClick) {
38
+ e.preventDefault()
39
+ onClick()
40
+ }
41
+ }}
42
+ title={collapsed ? label : undefined}
43
+ aria-label={collapsed ? label : undefined}
44
+ aria-current={active ? 'page' : undefined}
45
+ className={`${stateClass} ${className}`}
46
+ >
47
+ {IconComponent ? (
48
+ typeof IconComponent === 'function' ? (
49
+ <IconComponent className="h-5 w-5 shrink-0" />
50
+ ) : typeof IconComponent === 'string' ? (
51
+ renderNamedIcon(IconComponent, { className: 'h-5 w-5 shrink-0' })
52
+ ) : (
53
+ <span className="h-5 w-5 shrink-0 flex items-center justify-center font-bold text-xs">
54
+ {IconComponent}
55
+ </span>
56
+ )
57
+ ) : null}
58
+
59
+ {!collapsed ? <span className="truncate">{label}</span> : null}
60
+
61
+ {badge !== undefined ? (
62
+ <span
63
+ aria-hidden="true"
64
+ className={`inline-flex items-center justify-center rounded-full bg-primary font-bold text-primary-foreground ${
65
+ collapsed
66
+ ? 'absolute -right-0.5 -top-0.5 h-4 min-w-4 px-1 text-[10px]'
67
+ : 'ml-auto h-5 min-w-5 px-1.5 text-xs'
68
+ }`}
69
+ >
70
+ {badge}
71
+ </span>
72
+ ) : null}
73
+ </a>
74
+ )
75
+ }
@@ -0,0 +1,39 @@
1
+ import type { ComponentType, ReactNode, SVGProps } from 'react'
2
+
3
+ export interface ShortcutPillProps {
4
+ label: string
5
+ to: string
6
+ icon?: ComponentType<SVGProps<SVGSVGElement>> | ReactNode
7
+ onClick?: () => void
8
+ className?: string
9
+ }
10
+
11
+ export function ShortcutPill({
12
+ label,
13
+ to,
14
+ icon: IconComponent,
15
+ onClick,
16
+ className = '',
17
+ }: ShortcutPillProps) {
18
+ return (
19
+ <a
20
+ href={to}
21
+ onClick={(e) => {
22
+ if (onClick) {
23
+ e.preventDefault()
24
+ onClick()
25
+ }
26
+ }}
27
+ className={`inline-flex items-center gap-2 rounded-full border border-border/60 bg-surface dark:bg-surface-elevated/40 px-3.5 py-1.5 text-xs font-medium text-muted-foreground hover:border-primary/40 hover:bg-muted/60 hover:text-foreground transition-all shadow-2xs ${className}`}
28
+ >
29
+ {IconComponent ? (
30
+ typeof IconComponent === 'function' ? (
31
+ <IconComponent className="h-3.5 w-3.5 text-muted-foreground/80" />
32
+ ) : (
33
+ IconComponent
34
+ )
35
+ ) : null}
36
+ <span>{label}</span>
37
+ </a>
38
+ )
39
+ }
@@ -0,0 +1,36 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export interface EmptyStateProps {
4
+ title: string
5
+ description?: string
6
+ icon?: ReactNode
7
+ action?: ReactNode
8
+ className?: string
9
+ }
10
+
11
+ export function EmptyState({
12
+ title,
13
+ description,
14
+ icon,
15
+ action,
16
+ className = '',
17
+ }: EmptyStateProps) {
18
+ return (
19
+ <div
20
+ className={`rounded-2xl border border-dashed border-border/70 bg-surface dark:bg-surface-elevated/40 p-10 sm:p-14 text-center shadow-2xs ${className}`}
21
+ >
22
+ {icon ? (
23
+ <div className="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-full bg-primary/10 text-primary">
24
+ {icon}
25
+ </div>
26
+ ) : null}
27
+ <h3 className="text-base sm:text-lg font-bold text-foreground">{title}</h3>
28
+ {description ? (
29
+ <p className="mx-auto mt-1 max-w-md text-xs sm:text-sm text-muted-foreground leading-relaxed">
30
+ {description}
31
+ </p>
32
+ ) : null}
33
+ {action ? <div className="mt-5 flex flex-wrap items-center justify-center gap-3">{action}</div> : null}
34
+ </div>
35
+ )
36
+ }
@@ -0,0 +1,64 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export interface HeroSurfaceProps {
4
+ greeting: string
5
+ title?: string
6
+ description?: string
7
+ statusContext?: ReactNode
8
+ actions?: ReactNode
9
+ illustration?: ReactNode
10
+ className?: string
11
+ }
12
+
13
+ export function HeroSurface({
14
+ greeting,
15
+ title,
16
+ description,
17
+ statusContext,
18
+ actions,
19
+ illustration,
20
+ className = '',
21
+ }: HeroSurfaceProps) {
22
+ return (
23
+ <section
24
+ className={`relative overflow-hidden rounded-[28px] border border-border/40 bg-surface dark:bg-surface-elevated/70 p-8 sm:p-10 lg:p-12 shadow-2xs transition-colors ${className}`}
25
+ >
26
+ <div className="flex flex-col justify-between gap-8 lg:flex-row lg:items-center">
27
+ {/* LEFT COPY & ACTIONS */}
28
+ <div className="max-w-xl space-y-4">
29
+ <div className="space-y-2">
30
+ <h1 className="text-3xl font-bold tracking-tight text-foreground sm:text-4xl lg:text-4xl">
31
+ {greeting}
32
+ </h1>
33
+ {title ? (
34
+ <p className="text-lg font-medium text-foreground/90">{title}</p>
35
+ ) : null}
36
+ {description ? (
37
+ <p className="text-sm sm:text-base text-muted-foreground leading-relaxed">
38
+ {description}
39
+ </p>
40
+ ) : null}
41
+ </div>
42
+
43
+ {actions ? <div className="flex flex-wrap items-center gap-3 pt-2">{actions}</div> : null}
44
+
45
+ {statusContext ? (
46
+ <div className="pt-2 text-xs font-medium text-muted-foreground">
47
+ {statusContext}
48
+ </div>
49
+ ) : null}
50
+ </div>
51
+
52
+ {/* RIGHT ILLUSTRATION / GRAPHIC SLOT */}
53
+ {illustration ? (
54
+ <div
55
+ aria-hidden="true"
56
+ className="relative flex shrink-0 items-center justify-center select-none py-4 lg:py-0"
57
+ >
58
+ {illustration}
59
+ </div>
60
+ ) : null}
61
+ </div>
62
+ </section>
63
+ )
64
+ }
@@ -0,0 +1,43 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export interface PanelProps {
4
+ title: string
5
+ titleId?: string
6
+ icon?: ReactNode
7
+ action?: ReactNode
8
+ children: ReactNode
9
+ className?: string
10
+ bodyClassName?: string
11
+ }
12
+
13
+ export function Panel({
14
+ title,
15
+ titleId,
16
+ icon,
17
+ action,
18
+ children,
19
+ className = '',
20
+ bodyClassName = 'p-5',
21
+ }: PanelProps) {
22
+ return (
23
+ <section
24
+ aria-labelledby={titleId}
25
+ className={`rounded-2xl border border-border bg-surface dark:bg-surface-elevated/50 shadow-2xs transition-colors ${className}`}
26
+ >
27
+ <div className="flex items-center justify-between gap-4 border-b border-border/40 px-5 py-4">
28
+ <div className="flex min-w-0 items-center gap-2.5">
29
+ {icon ? (
30
+ <span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-xl bg-muted text-muted-foreground">
31
+ {icon}
32
+ </span>
33
+ ) : null}
34
+ <h2 id={titleId} className="truncate text-sm font-semibold text-foreground">
35
+ {title}
36
+ </h2>
37
+ </div>
38
+ {action}
39
+ </div>
40
+ <div className={bodyClassName}>{children}</div>
41
+ </section>
42
+ )
43
+ }
@@ -0,0 +1,75 @@
1
+ import type { ReactNode } from 'react'
2
+ import { SparklesIcon } from '../icons/IconSet.tsx'
3
+
4
+ export interface PriorityCardProps {
5
+ heading?: string
6
+ badgeText?: string
7
+ badgeClassName?: string
8
+ icon?: ReactNode
9
+ title: string
10
+ subtitle?: string
11
+ description: string
12
+ action?: ReactNode
13
+ className?: string
14
+ }
15
+
16
+ export function PriorityCard({
17
+ heading = 'Next Best Action',
18
+ badgeText,
19
+ badgeClassName = 'border-primary/20 bg-primary/10 text-primary',
20
+ icon,
21
+ title,
22
+ subtitle,
23
+ description,
24
+ action,
25
+ className = '',
26
+ }: PriorityCardProps) {
27
+ return (
28
+ <section className={`space-y-3 ${className}`}>
29
+ {/* SECTION HEADER WITH CIRCULAR ACCENT BADGE */}
30
+ <div className="flex items-center justify-between">
31
+ <div className="flex items-center gap-2.5">
32
+ <span
33
+ aria-hidden="true"
34
+ className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-primary/12 text-primary"
35
+ >
36
+ {icon || <SparklesIcon className="h-4 w-4" />}
37
+ </span>
38
+ <h2 className="text-sm font-semibold tracking-normal text-foreground">
39
+ {heading}
40
+ </h2>
41
+ </div>
42
+ {badgeText ? (
43
+ <span
44
+ className={`inline-flex items-center rounded-full border px-2.5 py-0.5 text-[11px] font-medium ${badgeClassName}`}
45
+ >
46
+ {badgeText}
47
+ </span>
48
+ ) : null}
49
+ </div>
50
+
51
+ {/* QUIET FEATURED CARD */}
52
+ <div className="rounded-2xl border border-border/40 bg-surface dark:bg-surface-elevated/60 p-6 shadow-2xs transition-colors">
53
+ <div className="flex flex-col justify-between gap-4 sm:flex-row sm:items-start">
54
+ <div className="space-y-2 min-w-0 max-w-xl">
55
+ <div>
56
+ <h3 className="text-base sm:text-lg font-bold text-foreground tracking-tight">
57
+ {title}
58
+ </h3>
59
+ {subtitle ? (
60
+ <p className="mt-0.5 text-xs font-medium text-muted-foreground">
61
+ {subtitle}
62
+ </p>
63
+ ) : null}
64
+ </div>
65
+ <p className="text-xs sm:text-sm text-muted-foreground leading-relaxed">
66
+ {description}
67
+ </p>
68
+ </div>
69
+
70
+ {action ? <div className="shrink-0 pt-1 sm:pt-0">{action}</div> : null}
71
+ </div>
72
+ </div>
73
+ </section>
74
+ )
75
+ }
@@ -0,0 +1,146 @@
1
+ import type { ReactNode } from 'react'
2
+
3
+ export interface ListItemData {
4
+ id: string
5
+ title: string
6
+ subtitle?: string
7
+ avatarText?: string
8
+ avatarIcon?: ReactNode
9
+ statusBadge?: ReactNode
10
+ metaText?: string
11
+ action?: ReactNode
12
+ onClick?: () => void
13
+ }
14
+
15
+ export interface QuietListCardProps {
16
+ title: string
17
+ icon?: ReactNode
18
+ iconClassName?: string
19
+ viewAllLink?: {
20
+ label?: string
21
+ to: string
22
+ onClick?: () => void
23
+ }
24
+ items: ListItemData[]
25
+ emptyMessage?: string
26
+ emptySubtext?: string
27
+ emptyAction?: ReactNode
28
+ className?: string
29
+ }
30
+
31
+ export function QuietListCard({
32
+ title,
33
+ icon,
34
+ iconClassName = 'bg-primary/12 text-primary',
35
+ viewAllLink,
36
+ items,
37
+ emptyMessage = 'No items found',
38
+ emptySubtext = 'Get started by creating your first item.',
39
+ emptyAction,
40
+ className = '',
41
+ }: QuietListCardProps) {
42
+ return (
43
+ <section className={`space-y-3 ${className}`}>
44
+ {/* SECTION HEADER */}
45
+ <div className="flex items-center justify-between">
46
+ <div className="flex items-center gap-2.5">
47
+ {icon ? (
48
+ <span
49
+ aria-hidden="true"
50
+ className={`flex h-7 w-7 shrink-0 items-center justify-center rounded-full ${iconClassName}`}
51
+ >
52
+ {icon}
53
+ </span>
54
+ ) : null}
55
+ <h2 className="text-sm font-semibold tracking-normal text-foreground">
56
+ {title}
57
+ </h2>
58
+ </div>
59
+
60
+ {viewAllLink ? (
61
+ <a
62
+ href={viewAllLink.to}
63
+ onClick={(e) => {
64
+ if (viewAllLink.onClick) {
65
+ e.preventDefault()
66
+ viewAllLink.onClick()
67
+ }
68
+ }}
69
+ className="inline-flex items-center gap-1 text-xs font-semibold text-primary hover:text-primary/80 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-md px-1 py-0.5"
70
+ >
71
+ <span>{viewAllLink.label || 'View all'}</span>
72
+ <span aria-hidden="true">→</span>
73
+ </a>
74
+ ) : null}
75
+ </div>
76
+
77
+ {/* QUIET LIST CONTAINER */}
78
+ <div className="rounded-2xl border border-border/40 bg-surface dark:bg-surface-elevated/60 shadow-2xs overflow-hidden transition-colors">
79
+ {items.length === 0 ? (
80
+ <div className="p-8 text-center">
81
+ {icon ? (
82
+ <div className="mx-auto mb-3 flex h-10 w-10 items-center justify-center rounded-full bg-muted text-muted-foreground">
83
+ {icon}
84
+ </div>
85
+ ) : null}
86
+ <p className="text-sm font-semibold text-foreground">{emptyMessage}</p>
87
+ {emptySubtext ? (
88
+ <p className="mt-1 text-xs text-muted-foreground max-w-sm mx-auto">
89
+ {emptySubtext}
90
+ </p>
91
+ ) : null}
92
+ {emptyAction ? <div className="mt-4">{emptyAction}</div> : null}
93
+ </div>
94
+ ) : (
95
+ <ul className="divide-y divide-border/30">
96
+ {items.map((item) => (
97
+ <li
98
+ key={item.id}
99
+ onClick={item.onClick}
100
+ className={`group flex flex-col justify-between gap-3 px-5 py-3.5 hover:bg-muted/30 transition-colors sm:flex-row sm:items-center ${
101
+ item.onClick ? 'cursor-pointer' : ''
102
+ }`}
103
+ >
104
+ <div className="flex items-center gap-3.5 min-w-0 flex-1">
105
+ {item.avatarText ? (
106
+ <span
107
+ aria-hidden="true"
108
+ className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-muted/60 text-xs font-bold text-foreground border border-border/40 group-hover:border-primary/40 group-hover:bg-primary/10 group-hover:text-primary transition-colors"
109
+ >
110
+ {item.avatarText}
111
+ </span>
112
+ ) : item.avatarIcon ? (
113
+ <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-muted/60 text-muted-foreground border border-border/40">
114
+ {item.avatarIcon}
115
+ </span>
116
+ ) : null}
117
+
118
+ <div className="min-w-0 flex-1">
119
+ <p className="truncate text-sm font-semibold text-foreground">
120
+ {item.title}
121
+ </p>
122
+ {item.subtitle ? (
123
+ <p className="truncate text-xs text-muted-foreground">
124
+ {item.subtitle}
125
+ </p>
126
+ ) : null}
127
+ </div>
128
+ </div>
129
+
130
+ <div className="flex shrink-0 items-center justify-between gap-3 sm:gap-4 pl-12 sm:pl-0">
131
+ {item.statusBadge}
132
+ {item.metaText ? (
133
+ <span className="text-xs tabular-nums text-muted-foreground/80 whitespace-nowrap">
134
+ {item.metaText}
135
+ </span>
136
+ ) : null}
137
+ {item.action}
138
+ </div>
139
+ </li>
140
+ ))}
141
+ </ul>
142
+ )}
143
+ </div>
144
+ </section>
145
+ )
146
+ }
@@ -0,0 +1,25 @@
1
+ /* ============================================================================
2
+ * Dashboard Starter Kit — Animations & Transitions
3
+ * ============================================================================
4
+ */
5
+
6
+ /* Theme-flip cross-fade easing (~200ms) */
7
+ @media (prefers-reduced-motion: no-preference) {
8
+ .theme-transition {
9
+ transition-property: background-color, border-color, color, fill, stroke, box-shadow, outline-color;
10
+ transition-duration: 200ms;
11
+ transition-timing-function: ease;
12
+ }
13
+ }
14
+
15
+ /* Reusable utility to hide visible scrollbars while preserving scrolling */
16
+ .no-scrollbar,
17
+ .scrollbar-none {
18
+ -ms-overflow-style: none;
19
+ scrollbar-width: none;
20
+ }
21
+
22
+ .no-scrollbar::-webkit-scrollbar,
23
+ .scrollbar-none::-webkit-scrollbar {
24
+ display: none;
25
+ }
@@ -0,0 +1,74 @@
1
+ /* ============================================================================
2
+ * Dashboard Starter Kit — Design Tokens
3
+ * ============================================================================
4
+ * Single source of truth for semantic color tokens across Light & Dark modes.
5
+ * Mapped to Tailwind color utilities via `@theme inline` so components consume
6
+ * `bg-surface`, `text-foreground`, `border-border`, `bg-primary`, etc.
7
+ */
8
+
9
+ :root,
10
+ [data-theme="light"] {
11
+ --background: #f4f5f7;
12
+ --foreground: #0f172a;
13
+ --muted-foreground: #64748b;
14
+ --surface: #ffffff;
15
+ --surface-elevated: #ffffff;
16
+ --muted: #eef1f4;
17
+ --border: #e3e7ec;
18
+ --input: #ffffff;
19
+ --canvas-background: #eef3fb;
20
+
21
+ /* Brand Primary */
22
+ --primary: #2563eb;
23
+ --primary-foreground: #ffffff;
24
+ --ring: #2563eb;
25
+
26
+ /* Semantic Feedback */
27
+ --success: #059669;
28
+ --success-fg: #047857;
29
+ --success-bg: #ecfdf5;
30
+ --warning: #d97706;
31
+ --warning-fg: #b45309;
32
+ --warning-bg: #fffbeb;
33
+ --danger: #dc2626;
34
+ --danger-fg: #b91c1c;
35
+ --danger-bg: #fef2f2;
36
+ --info: #0284c7;
37
+ --info-fg: #0369a1;
38
+ --info-bg: #f0f9ff;
39
+
40
+ color-scheme: light;
41
+ }
42
+
43
+ [data-theme="dark"] {
44
+ --background: #0b0c0e;
45
+ --foreground: #f3f5f7;
46
+ --muted-foreground: #9aa4b2;
47
+ --surface: #151719;
48
+ --surface-elevated: #1c1f22;
49
+ --muted: #22262b;
50
+ --border: #2a2f35;
51
+ --input: #1a1a1a;
52
+ --canvas-background: #181a1f;
53
+
54
+ /* Brand Primary */
55
+ --primary: #3b82f6;
56
+ --primary-foreground: #ffffff;
57
+ --ring: #3b82f6;
58
+
59
+ /* Semantic Feedback */
60
+ --success: #10b981;
61
+ --success-fg: #6ee7b7;
62
+ --success-bg: rgba(16, 185, 129, 0.15);
63
+ --warning: #f59e0b;
64
+ --warning-fg: #fcd34d;
65
+ --warning-bg: rgba(245, 158, 11, 0.15);
66
+ --danger: #ef4444;
67
+ --danger-fg: #fca5a5;
68
+ --danger-bg: rgba(239, 68, 68, 0.15);
69
+ --info: #38bdf8;
70
+ --info-fg: #7dd3fc;
71
+ --info-bg: rgba(56, 189, 248, 0.15);
72
+
73
+ color-scheme: dark;
74
+ }
@@ -0,0 +1,34 @@
1
+ /* ============================================================================
2
+ * Dashboard Starter Kit — Typography System
3
+ * ============================================================================
4
+ * Standard typographic rules, line heights, and tabular layout utilities.
5
+ */
6
+
7
+ /* Antialiasing for crisp text rendering across dark/light themes */
8
+ .dashboard-font-base {
9
+ font-family: 'DM Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
+ -webkit-font-smoothing: antialiased;
11
+ -moz-osx-font-smoothing: grayscale;
12
+ }
13
+
14
+ /* Numeric tabular display ensures uniform number column widths */
15
+ .tabular-nums {
16
+ font-variant-numeric: tabular-nums;
17
+ }
18
+
19
+ /* Heading letter-spacing tokens */
20
+ .tracking-tight {
21
+ letter-spacing: -0.025em;
22
+ }
23
+
24
+ .tracking-tighter {
25
+ letter-spacing: -0.05em;
26
+ }
27
+
28
+ .tracking-wide {
29
+ letter-spacing: 0.025em;
30
+ }
31
+
32
+ .tracking-wider {
33
+ letter-spacing: 0.05em;
34
+ }