@djangocfg/ui-core 1.0.1

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 (88) hide show
  1. package/README.md +135 -0
  2. package/package.json +111 -0
  3. package/src/components/accordion.tsx +56 -0
  4. package/src/components/alert-dialog.tsx +142 -0
  5. package/src/components/alert.tsx +59 -0
  6. package/src/components/aspect-ratio.tsx +7 -0
  7. package/src/components/avatar.tsx +50 -0
  8. package/src/components/badge.tsx +36 -0
  9. package/src/components/button-group.tsx +85 -0
  10. package/src/components/button.tsx +111 -0
  11. package/src/components/calendar.tsx +213 -0
  12. package/src/components/card.tsx +76 -0
  13. package/src/components/carousel.tsx +261 -0
  14. package/src/components/chart.tsx +369 -0
  15. package/src/components/checkbox.tsx +29 -0
  16. package/src/components/collapsible.tsx +11 -0
  17. package/src/components/combobox.tsx +182 -0
  18. package/src/components/command.tsx +170 -0
  19. package/src/components/context-menu.tsx +200 -0
  20. package/src/components/copy.tsx +144 -0
  21. package/src/components/dialog.tsx +122 -0
  22. package/src/components/drawer.tsx +137 -0
  23. package/src/components/empty.tsx +104 -0
  24. package/src/components/field.tsx +244 -0
  25. package/src/components/form.tsx +178 -0
  26. package/src/components/hover-card.tsx +29 -0
  27. package/src/components/image-with-fallback.tsx +170 -0
  28. package/src/components/index.ts +86 -0
  29. package/src/components/input-group.tsx +170 -0
  30. package/src/components/input-otp.tsx +81 -0
  31. package/src/components/input.tsx +22 -0
  32. package/src/components/item.tsx +195 -0
  33. package/src/components/kbd.tsx +28 -0
  34. package/src/components/label.tsx +26 -0
  35. package/src/components/multi-select.tsx +222 -0
  36. package/src/components/og-image.tsx +47 -0
  37. package/src/components/popover.tsx +33 -0
  38. package/src/components/portal.tsx +106 -0
  39. package/src/components/preloader.tsx +250 -0
  40. package/src/components/progress.tsx +28 -0
  41. package/src/components/radio-group.tsx +43 -0
  42. package/src/components/resizable.tsx +111 -0
  43. package/src/components/scroll-area.tsx +102 -0
  44. package/src/components/section.tsx +58 -0
  45. package/src/components/select.tsx +158 -0
  46. package/src/components/separator.tsx +31 -0
  47. package/src/components/sheet.tsx +140 -0
  48. package/src/components/skeleton.tsx +15 -0
  49. package/src/components/slider.tsx +28 -0
  50. package/src/components/spinner.tsx +16 -0
  51. package/src/components/sticky.tsx +117 -0
  52. package/src/components/switch.tsx +29 -0
  53. package/src/components/table.tsx +120 -0
  54. package/src/components/tabs.tsx +238 -0
  55. package/src/components/textarea.tsx +22 -0
  56. package/src/components/toast.tsx +129 -0
  57. package/src/components/toaster.tsx +41 -0
  58. package/src/components/toggle-group.tsx +61 -0
  59. package/src/components/toggle.tsx +45 -0
  60. package/src/components/token-icon.tsx +156 -0
  61. package/src/components/tooltip-provider-safe.tsx +43 -0
  62. package/src/components/tooltip.tsx +32 -0
  63. package/src/hooks/index.ts +15 -0
  64. package/src/hooks/useCopy.ts +41 -0
  65. package/src/hooks/useCountdown.ts +73 -0
  66. package/src/hooks/useDebounce.ts +25 -0
  67. package/src/hooks/useDebouncedCallback.ts +58 -0
  68. package/src/hooks/useDebugTools.ts +52 -0
  69. package/src/hooks/useEventsBus.ts +53 -0
  70. package/src/hooks/useImageLoader.ts +95 -0
  71. package/src/hooks/useMediaQuery.ts +40 -0
  72. package/src/hooks/useMobile.tsx +22 -0
  73. package/src/hooks/useToast.ts +194 -0
  74. package/src/index.ts +14 -0
  75. package/src/lib/index.ts +2 -0
  76. package/src/lib/og-image.ts +151 -0
  77. package/src/lib/utils.ts +6 -0
  78. package/src/styles/base.css +20 -0
  79. package/src/styles/globals.css +12 -0
  80. package/src/styles/index.css +25 -0
  81. package/src/styles/sources.css +11 -0
  82. package/src/styles/theme/animations.css +65 -0
  83. package/src/styles/theme/dark.css +49 -0
  84. package/src/styles/theme/light.css +50 -0
  85. package/src/styles/theme/tokens.css +134 -0
  86. package/src/styles/theme.css +22 -0
  87. package/src/styles/utilities.css +187 -0
  88. package/src/types/index.ts +0 -0
@@ -0,0 +1,194 @@
1
+ "use client"
2
+
3
+ // Inspired by react-hot-toast library
4
+ import * as React from 'react';
5
+
6
+ import type {
7
+ ToastActionElement,
8
+ ToastProps,
9
+ } from "../components/toast"
10
+
11
+ const TOAST_LIMIT = 1
12
+ const TOAST_REMOVE_DELAY = 1000000
13
+
14
+ type ToasterToast = ToastProps & {
15
+ id: string
16
+ title?: React.ReactNode
17
+ description?: React.ReactNode
18
+ action?: ToastActionElement
19
+ }
20
+
21
+ const actionTypes = {
22
+ ADD_TOAST: "ADD_TOAST",
23
+ UPDATE_TOAST: "UPDATE_TOAST",
24
+ DISMISS_TOAST: "DISMISS_TOAST",
25
+ REMOVE_TOAST: "REMOVE_TOAST",
26
+ } as const
27
+
28
+ let count = 0
29
+
30
+ function genId() {
31
+ count = (count + 1) % Number.MAX_SAFE_INTEGER
32
+ return count.toString()
33
+ }
34
+
35
+ type ActionType = typeof actionTypes
36
+
37
+ type Action =
38
+ | {
39
+ type: ActionType["ADD_TOAST"]
40
+ toast: ToasterToast
41
+ }
42
+ | {
43
+ type: ActionType["UPDATE_TOAST"]
44
+ toast: Partial<ToasterToast>
45
+ }
46
+ | {
47
+ type: ActionType["DISMISS_TOAST"]
48
+ toastId?: ToasterToast["id"]
49
+ }
50
+ | {
51
+ type: ActionType["REMOVE_TOAST"]
52
+ toastId?: ToasterToast["id"]
53
+ }
54
+
55
+ interface State {
56
+ toasts: ToasterToast[]
57
+ }
58
+
59
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
60
+
61
+ const addToRemoveQueue = (toastId: string) => {
62
+ if (toastTimeouts.has(toastId)) {
63
+ return
64
+ }
65
+
66
+ const timeout = setTimeout(() => {
67
+ toastTimeouts.delete(toastId)
68
+ dispatch({
69
+ type: "REMOVE_TOAST",
70
+ toastId: toastId,
71
+ })
72
+ }, TOAST_REMOVE_DELAY)
73
+
74
+ toastTimeouts.set(toastId, timeout)
75
+ }
76
+
77
+ export const reducer = (state: State, action: Action): State => {
78
+ switch (action.type) {
79
+ case "ADD_TOAST":
80
+ return {
81
+ ...state,
82
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
83
+ }
84
+
85
+ case "UPDATE_TOAST":
86
+ return {
87
+ ...state,
88
+ toasts: state.toasts.map((t) =>
89
+ t.id === action.toast.id ? { ...t, ...action.toast } : t
90
+ ),
91
+ }
92
+
93
+ case "DISMISS_TOAST": {
94
+ const { toastId } = action
95
+
96
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
97
+ // but I'll keep it here for simplicity
98
+ if (toastId) {
99
+ addToRemoveQueue(toastId)
100
+ } else {
101
+ state.toasts.forEach((toast) => {
102
+ addToRemoveQueue(toast.id)
103
+ })
104
+ }
105
+
106
+ return {
107
+ ...state,
108
+ toasts: state.toasts.map((t) =>
109
+ t.id === toastId || toastId === undefined
110
+ ? {
111
+ ...t,
112
+ open: false,
113
+ }
114
+ : t
115
+ ),
116
+ }
117
+ }
118
+ case "REMOVE_TOAST":
119
+ if (action.toastId === undefined) {
120
+ return {
121
+ ...state,
122
+ toasts: [],
123
+ }
124
+ }
125
+ return {
126
+ ...state,
127
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
128
+ }
129
+ }
130
+ }
131
+
132
+ const listeners: Array<(state: State) => void> = []
133
+
134
+ let memoryState: State = { toasts: [] }
135
+
136
+ function dispatch(action: Action) {
137
+ memoryState = reducer(memoryState, action)
138
+ listeners.forEach((listener) => {
139
+ listener(memoryState)
140
+ })
141
+ }
142
+
143
+ type Toast = Omit<ToasterToast, "id">
144
+
145
+ function toast({ ...props }: Toast) {
146
+ const id = genId()
147
+
148
+ const update = (props: ToasterToast) =>
149
+ dispatch({
150
+ type: "UPDATE_TOAST",
151
+ toast: { ...props, id },
152
+ })
153
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
154
+
155
+ dispatch({
156
+ type: "ADD_TOAST",
157
+ toast: {
158
+ ...props,
159
+ id,
160
+ open: true,
161
+ onOpenChange: (open) => {
162
+ if (!open) dismiss()
163
+ },
164
+ },
165
+ })
166
+
167
+ return {
168
+ id: id,
169
+ dismiss,
170
+ update,
171
+ }
172
+ }
173
+
174
+ function useToast() {
175
+ const [state, setState] = React.useState<State>(memoryState)
176
+
177
+ React.useEffect(() => {
178
+ listeners.push(setState)
179
+ return () => {
180
+ const index = listeners.indexOf(setState)
181
+ if (index > -1) {
182
+ listeners.splice(index, 1)
183
+ }
184
+ }
185
+ }, [state])
186
+
187
+ return {
188
+ ...state,
189
+ toast,
190
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
191
+ }
192
+ }
193
+
194
+ export { useToast, toast }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ // ============================================================================
2
+ // @djangocfg/ui-core - Main Export File
3
+ // Pure React components without Next.js/Browser Storage dependencies
4
+ // For use in Electron, Vite, CRA apps
5
+ // ============================================================================
6
+
7
+ // Re-export everything from components
8
+ export * from './components';
9
+
10
+ // Re-export hooks
11
+ export * from './hooks';
12
+
13
+ // Re-export lib utilities
14
+ export * from './lib';
@@ -0,0 +1,2 @@
1
+ export * from "./utils";
2
+ export * from "./og-image";
@@ -0,0 +1,151 @@
1
+ /**
2
+ * OG Image URL Generation Utilities
3
+ *
4
+ * Client-side utilities for generating OG image URLs.
5
+ */
6
+
7
+ /** Default OG Image API base URL */
8
+ const DEFAULT_OG_IMAGE_BASE_URL = 'https://djangocfg.com/api/og';
9
+
10
+ /**
11
+ * Encode string to base64 with Unicode support
12
+ * Works in both browser and Node.js environments
13
+ */
14
+ function encodeBase64(str: string): string {
15
+ // Node.js environment
16
+ if (typeof Buffer !== 'undefined') {
17
+ return Buffer.from(str, 'utf-8').toString('base64');
18
+ }
19
+ // Browser environment - handle Unicode via UTF-8 encoding
20
+ const utf8Bytes = new TextEncoder().encode(str);
21
+ const binaryString = Array.from(utf8Bytes, byte => String.fromCharCode(byte)).join('');
22
+ return btoa(binaryString);
23
+ }
24
+
25
+ /**
26
+ * OG Image URL parameters
27
+ */
28
+ export interface OgImageUrlParams {
29
+ /** Page title */
30
+ title: string;
31
+ /** Page description (optional) */
32
+ description?: string;
33
+ /** Site name (optional) */
34
+ siteName?: string;
35
+ /** Logo URL (optional) */
36
+ logo?: string;
37
+ /** Background type: 'gradient' or 'solid' */
38
+ backgroundType?: 'gradient' | 'solid';
39
+ /** Gradient start color (hex) */
40
+ gradientStart?: string;
41
+ /** Gradient end color (hex) */
42
+ gradientEnd?: string;
43
+ /** Background color (for solid type) */
44
+ backgroundColor?: string;
45
+ /** Title font size (px) */
46
+ titleSize?: number;
47
+ /** Title font weight */
48
+ titleWeight?: number;
49
+ /** Title text color */
50
+ titleColor?: string;
51
+ /** Description font size (px) */
52
+ descriptionSize?: number;
53
+ /** Description text color */
54
+ descriptionColor?: string;
55
+ /** Site name font size (px) */
56
+ siteNameSize?: number;
57
+ /** Site name text color */
58
+ siteNameColor?: string;
59
+ /** Padding (px) */
60
+ padding?: number;
61
+ /** Logo size (px) */
62
+ logoSize?: number;
63
+ /** Show logo flag */
64
+ showLogo?: boolean;
65
+ /** Show site name flag */
66
+ showSiteName?: boolean;
67
+ /** Additional custom parameters */
68
+ [key: string]: string | number | boolean | undefined;
69
+ }
70
+
71
+ /**
72
+ * Options for generating OG image URL
73
+ */
74
+ export interface GenerateOgImageUrlOptions {
75
+ /**
76
+ * Base URL of the OG image API route
77
+ * @default 'https://djangocfg.com/api/og'
78
+ */
79
+ baseUrl?: string;
80
+ /**
81
+ * If true, encode params as base64 for safer URLs
82
+ * @default true
83
+ */
84
+ useBase64?: boolean;
85
+ }
86
+
87
+ /**
88
+ * Generate OG image URL with query parameters or base64 encoding
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * // Using default baseUrl (https://djangocfg.com/api/og)
93
+ * const url = generateOgImageUrl({ title: 'My Page Title' });
94
+ *
95
+ * // With custom baseUrl
96
+ * const url = generateOgImageUrl({ title: 'My Page' }, { baseUrl: '/api/og' });
97
+ * ```
98
+ */
99
+ export function generateOgImageUrl(
100
+ params: OgImageUrlParams,
101
+ options: GenerateOgImageUrlOptions = {}
102
+ ): string {
103
+ const { baseUrl = DEFAULT_OG_IMAGE_BASE_URL, useBase64 = true } = options;
104
+
105
+ if (useBase64) {
106
+ const cleanParams: Record<string, string | number | boolean> = {};
107
+ Object.entries(params).forEach(([key, value]) => {
108
+ if (value !== undefined && value !== null && value !== '') {
109
+ cleanParams[key] = value;
110
+ }
111
+ });
112
+
113
+ const jsonString = JSON.stringify(cleanParams);
114
+ const base64Data = encodeBase64(jsonString);
115
+ return `${baseUrl}/${base64Data}`;
116
+ } else {
117
+ const searchParams = new URLSearchParams();
118
+ Object.entries(params).forEach(([key, value]) => {
119
+ if (value !== undefined && value !== null && value !== '') {
120
+ searchParams.append(key, String(value));
121
+ }
122
+ });
123
+ const query = searchParams.toString();
124
+ return query ? `${baseUrl}?${query}` : baseUrl;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Get absolute OG image URL from relative path
130
+ */
131
+ export function getAbsoluteOgImageUrl(relativePath: string, siteUrl: string): string {
132
+ // If path is already an absolute URL, return as-is
133
+ if (relativePath.startsWith('http://') || relativePath.startsWith('https://')) {
134
+ return relativePath;
135
+ }
136
+ const cleanSiteUrl = siteUrl.replace(/\/$/, '');
137
+ const cleanPath = relativePath.startsWith('/') ? relativePath : `/${relativePath}`;
138
+ return `${cleanSiteUrl}${cleanPath}`;
139
+ }
140
+
141
+ /**
142
+ * Create OG image URL builder with preset configuration
143
+ */
144
+ export function createOgImageUrlBuilder(
145
+ defaults: Partial<OgImageUrlParams> = {},
146
+ options: GenerateOgImageUrlOptions = {}
147
+ ) {
148
+ return (params: OgImageUrlParams): string => {
149
+ return generateOgImageUrl({ ...defaults, ...params }, options);
150
+ };
151
+ }
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Base Styles
3
+ * Foundation styles that apply to the entire application
4
+ * Compatible with Tailwind CSS v4
5
+ */
6
+
7
+ /* Base element styles */
8
+ * {
9
+ border-color: hsl(var(--border));
10
+ }
11
+
12
+ html {
13
+ font-weight: 500;
14
+ }
15
+
16
+ body {
17
+ background-color: hsl(var(--background));
18
+ color: hsl(var(--foreground));
19
+ font-feature-settings: "rlig" 1, "calt" 1;
20
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @deprecated
3
+ * This file is kept for backward compatibility.
4
+ * Please import from './index.css' instead.
5
+ *
6
+ * The styles are now modular and split into:
7
+ * - theme.css (CSS variables)
8
+ * - base.css (base styles)
9
+ * - utilities.css (custom utilities)
10
+ */
11
+
12
+ @import "./index.css";
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Unrealon UI Styles
3
+ * Main entry point for @djangocfg/ui package styles
4
+ *
5
+ * Import order (critical for Tailwind v4):
6
+ * 1. Theme variables FIRST (so Tailwind can use them)
7
+ * 2. Tailwind CSS v4 (compiles with access to theme variables)
8
+ * 3. Animation utilities (tw-animate-css for Tailwind v4)
9
+ * 4. Base styles (override Tailwind defaults)
10
+ * 5. Custom utilities (extend Tailwind utilities)
11
+ *
12
+ * Usage: Import this file in your app's main CSS or _app.tsx
13
+ */
14
+
15
+ /* 1. Theme variables MUST come first */
16
+ @import "./theme.css";
17
+
18
+ /* 2. Source detection for Tailwind v4 monorepo */
19
+ @import "./sources.css";
20
+
21
+ /* 3. Base styles */
22
+ @import "./base.css";
23
+
24
+ /* 5. Custom utilities */
25
+ @import "./utilities.css";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Tailwind v4 Source Detection for UI Package
3
+ *
4
+ * @source directive tells Tailwind v4 where to scan for utility classes.
5
+ * In monorepo, this is critical because automatic detection may not work.
6
+ *
7
+ * "./" scans the entire UI package starting from this file's location.
8
+ */
9
+ @source "../components";
10
+ @source "../layouts";
11
+ @source "../blocks";
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Custom Animations
3
+ *
4
+ * Smooth, professional animations for UI elements
5
+ * Optimized for performance with transform and opacity only
6
+ */
7
+
8
+ /* Smooth Floating Animation - Multi-directional movement */
9
+ @keyframes float {
10
+ 0%, 100% { transform: translate(0, 0) scale(1); }
11
+ 33% { transform: translate(30px, -30px) scale(1.05); }
12
+ 66% { transform: translate(-20px, 20px) scale(0.95); }
13
+ }
14
+
15
+ /* Slow Vertical Float - Gentle up/down movement */
16
+ @keyframes float-slow {
17
+ 0%, 100% { transform: translate(0, 0); }
18
+ 50% { transform: translate(0, -50px); }
19
+ }
20
+
21
+ /* Horizontal Float with Rotation */
22
+ @keyframes float-x {
23
+ 0%, 100% { transform: translate(0, 0) rotate(0deg); }
24
+ 50% { transform: translate(100px, 0) rotate(180deg); }
25
+ }
26
+
27
+ /* Diagonal Float - Complex multi-point movement */
28
+ @keyframes float-diagonal {
29
+ 0%, 100% { transform: translate(0, 0) scale(1); }
30
+ 25% { transform: translate(50px, 50px) scale(1.1); }
31
+ 50% { transform: translate(100px, -50px) scale(0.9); }
32
+ 75% { transform: translate(-50px, 50px) scale(1.05); }
33
+ }
34
+
35
+ /* Morph - Organic shape transformation */
36
+ @keyframes morph {
37
+ 0%, 100% {
38
+ border-radius: 60% 40% 30% 70% / 60% 30% 70% 40%;
39
+ opacity: 0.3;
40
+ }
41
+ 25% {
42
+ border-radius: 30% 60% 70% 40% / 50% 60% 30% 60%;
43
+ opacity: 0.5;
44
+ }
45
+ 50% {
46
+ border-radius: 50% 60% 30% 60% / 30% 60% 70% 40%;
47
+ opacity: 0.4;
48
+ }
49
+ 75% {
50
+ border-radius: 60% 40% 60% 30% / 60% 30% 60% 40%;
51
+ opacity: 0.6;
52
+ }
53
+ }
54
+
55
+ /* Gradient Shift - Color animation with hue rotation */
56
+ @keyframes gradient-shift {
57
+ 0%, 100% {
58
+ background-position: 0% 50%;
59
+ filter: hue-rotate(0deg);
60
+ }
61
+ 50% {
62
+ background-position: 100% 50%;
63
+ filter: hue-rotate(45deg);
64
+ }
65
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Dark Theme Colors
3
+ *
4
+ * Vercel-inspired dark theme with true black backgrounds
5
+ * High contrast with subtle grays for comfortable viewing
6
+ */
7
+
8
+ .dark {
9
+ /* Base colors - True black background with subtle grays */
10
+ --background: 0 0% 4%;
11
+ --foreground: 0 0% 98%;
12
+ --card: 0 0% 8%;
13
+ --card-foreground: 0 0% 98%;
14
+ --popover: 0 0% 12%;
15
+ --popover-foreground: 0 0% 98%;
16
+ --primary: 217 91% 60%;
17
+ --primary-foreground: 0 0% 100%;
18
+ --secondary: 0 0% 98%;
19
+ --secondary-foreground: 0 0% 9%;
20
+ --muted: 0 0% 10%;
21
+ --muted-foreground: 0 0% 60%;
22
+ --accent: 0 0% 15%;
23
+ --accent-foreground: 0 0% 98%;
24
+ --destructive: 0 84% 60%;
25
+ --destructive-foreground: 0 0% 98%;
26
+ --border: 0 0% 15%;
27
+ --input: 0 0% 15%;
28
+ --ring: 217 91% 60%;
29
+
30
+ /* Sidebar Dark Theme - Darker black sidebar */
31
+ --sidebar-background: 0 0% 0%;
32
+ --sidebar-foreground: 0 0% 98%;
33
+ --sidebar-primary: 217 91% 60%;
34
+ --sidebar-primary-foreground: 0 0% 100%;
35
+ --sidebar-accent: 0 0% 10%;
36
+ --sidebar-accent-foreground: 0 0% 98%;
37
+ --sidebar-border: 0 0% 15%;
38
+ --sidebar-ring: 217 91% 60%;
39
+
40
+ /* Surface gradient dark */
41
+ --surface-gradient: linear-gradient(to bottom, hsl(var(--background)), hsl(var(--background) / 0.8));
42
+
43
+ /* Chart colors - same as light theme for consistency */
44
+ --chart-1: 217 91% 60%; /* Blue - CPU */
45
+ --chart-2: 142 76% 36%; /* Green - Memory */
46
+ --chart-3: 262 83% 58%; /* Purple - Disk */
47
+ --chart-4: 26 90% 57%; /* Orange */
48
+ --chart-5: 346 77% 50%; /* Red */
49
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Light Theme Colors
3
+ *
4
+ * Vercel-inspired light theme with clean whites and subtle grays
5
+ * High contrast for optimal readability
6
+ */
7
+
8
+ :root {
9
+ /* Base colors - Clean white with neutral grays */
10
+ --background: 0 0% 96%;
11
+ --foreground: 0 0% 9%;
12
+ --card: 0 0% 100%;
13
+ --card-foreground: 0 0% 9%;
14
+ --popover: 0 0% 100%;
15
+ --popover-foreground: 0 0% 9%;
16
+ --primary: 217 91% 60%;
17
+ --primary-foreground: 0 0% 100%;
18
+ --secondary: 0 0% 9%;
19
+ --secondary-foreground: 0 0% 98%;
20
+ --muted: 0 0% 96%;
21
+ --muted-foreground: 0 0% 45%;
22
+ --accent: 0 0% 92%;
23
+ --accent-foreground: 0 0% 9%;
24
+ --destructive: 0 84% 60%;
25
+ --destructive-foreground: 0 0% 98%;
26
+ --border: 0 0% 90%;
27
+ --input: 0 0% 90%;
28
+ --ring: 217 91% 60%;
29
+ --radius: 0.5rem;
30
+
31
+ /* Sidebar Light Theme - Subtle gray background */
32
+ --sidebar-background: 0 0% 98%;
33
+ --sidebar-foreground: 0 0% 9%;
34
+ --sidebar-primary: 217 91% 60%;
35
+ --sidebar-primary-foreground: 0 0% 100%;
36
+ --sidebar-accent: 0 0% 94%;
37
+ --sidebar-accent-foreground: 0 0% 9%;
38
+ --sidebar-border: 0 0% 90%;
39
+ --sidebar-ring: 217 91% 60%;
40
+
41
+ /* Surface gradient */
42
+ --surface-gradient: linear-gradient(to bottom, hsl(var(--background)), hsl(var(--background) / 0.8));
43
+
44
+ /* Chart colors - vibrant and distinct */
45
+ --chart-1: 217 91% 60%; /* Blue - CPU */
46
+ --chart-2: 142 76% 36%; /* Green - Memory */
47
+ --chart-3: 262 83% 58%; /* Purple - Disk */
48
+ --chart-4: 26 90% 57%; /* Orange */
49
+ --chart-5: 346 77% 50%; /* Red */
50
+ }