diff --git a/src/App.css b/src/App.css index 88339c3..cbb287c 100644 --- a/src/App.css +++ b/src/App.css @@ -1,6 +1,129 @@ @import "tailwindcss"; -/* Custom animations for shimmer effect */ +/* ============================================ + THEME DEFINITION - Tailwind v4 + Semantic color system that references Tailwind colors + ============================================ */ + +@theme { + /* PRIMARY - Main brand color (references Blue) */ + --color-primary-50: var(--color-blue-50); + --color-primary-100: var(--color-blue-100); + --color-primary-200: var(--color-blue-200); + --color-primary-300: var(--color-blue-300); + --color-primary-400: var(--color-blue-400); + --color-primary-500: var(--color-blue-500); + --color-primary-600: var(--color-blue-600); + --color-primary-700: var(--color-blue-700); + --color-primary-800: var(--color-blue-800); + --color-primary-900: var(--color-blue-900); + --color-primary-950: var(--color-blue-950); + + /* SUCCESS - Positive states, completion (references Green) */ + --color-success-50: var(--color-green-50); + --color-success-100: var(--color-green-100); + --color-success-200: var(--color-green-200); + --color-success-300: var(--color-green-300); + --color-success-400: var(--color-green-400); + --color-success-500: var(--color-green-500); + --color-success-600: var(--color-green-600); + --color-success-700: var(--color-green-700); + --color-success-800: var(--color-green-800); + --color-success-900: var(--color-green-900); + --color-success-950: var(--color-green-950); + + /* WARNING - Caution, waiting states (references Amber) */ + --color-warning-50: var(--color-amber-50); + --color-warning-100: var(--color-amber-100); + --color-warning-200: var(--color-amber-200); + --color-warning-300: var(--color-amber-300); + --color-warning-400: var(--color-amber-400); + --color-warning-500: var(--color-amber-500); + --color-warning-600: var(--color-amber-600); + --color-warning-700: var(--color-amber-700); + --color-warning-800: var(--color-amber-800); + --color-warning-900: var(--color-amber-900); + --color-warning-950: var(--color-amber-950); + + /* DANGER - Errors, destructive actions (references Red) */ + --color-danger-50: var(--color-red-50); + --color-danger-100: var(--color-red-100); + --color-danger-200: var(--color-red-200); + --color-danger-300: var(--color-red-300); + --color-danger-400: var(--color-red-400); + --color-danger-500: var(--color-red-500); + --color-danger-600: var(--color-red-600); + --color-danger-700: var(--color-red-700); + --color-danger-800: var(--color-red-800); + --color-danger-900: var(--color-red-900); + --color-danger-950: var(--color-red-950); + + /* INFO - Informational states (references Cyan) */ + --color-info-50: var(--color-cyan-50); + --color-info-100: var(--color-cyan-100); + --color-info-200: var(--color-cyan-200); + --color-info-300: var(--color-cyan-300); + --color-info-400: var(--color-cyan-400); + --color-info-500: var(--color-cyan-500); + --color-info-600: var(--color-cyan-600); + --color-info-700: var(--color-cyan-700); + --color-info-800: var(--color-cyan-800); + --color-info-900: var(--color-cyan-900); + --color-info-950: var(--color-cyan-950); + + /* ACCENT - Progress, sewing states (references Purple) */ + --color-accent-50: var(--color-purple-50); + --color-accent-100: var(--color-purple-100); + --color-accent-200: var(--color-purple-200); + --color-accent-300: var(--color-purple-300); + --color-accent-400: var(--color-purple-400); + --color-accent-500: var(--color-purple-500); + --color-accent-600: var(--color-purple-600); + --color-accent-700: var(--color-purple-700); + --color-accent-800: var(--color-purple-800); + --color-accent-900: var(--color-purple-900); + --color-accent-950: var(--color-purple-950); + + /* SECONDARY - Upload operations (references Orange) */ + --color-secondary-50: var(--color-orange-50); + --color-secondary-100: var(--color-orange-100); + --color-secondary-200: var(--color-orange-200); + --color-secondary-300: var(--color-orange-300); + --color-secondary-400: var(--color-orange-400); + --color-secondary-500: var(--color-orange-500); + --color-secondary-600: var(--color-orange-600); + --color-secondary-700: var(--color-orange-700); + --color-secondary-800: var(--color-orange-800); + --color-secondary-900: var(--color-orange-900); + --color-secondary-950: var(--color-orange-950); + + /* TERTIARY - Pattern canvas theme (references Teal) */ + --color-tertiary-500: var(--color-teal-500); + --color-tertiary-600: var(--color-teal-600); + + /* Canvas/Konva-specific colors for embroidery rendering */ + --color-canvas-grid: #e0e0e0; + --color-canvas-origin: #888888; + --color-canvas-hoop: #2196F3; + --color-canvas-bounds: #ff0000; + --color-canvas-position: #ff0000; +} + +/* Dark Mode Overrides - Media Query */ +@media (prefers-color-scheme: dark) { + @theme { + /* Canvas colors adjusted for dark mode */ + --color-canvas-grid: #404040; + --color-canvas-origin: #999999; + /* hoop, bounds, position stay the same for visibility */ + } +} + +/* ============================================ + CUSTOM ANIMATIONS + ============================================ */ + +/* Shimmer effect for progress bars */ @keyframes shimmer { 0% { transform: translateX(-100%); @@ -44,13 +167,13 @@ } } -/* Pulse glow effect */ +/* Pulse glow effect - uses primary-600 */ @keyframes pulseGlow { 0%, 100% { - box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4); + box-shadow: 0 0 0 0 rgb(37 99 235 / 0.4); /* primary-600 with 40% opacity */ } 50% { - box-shadow: 0 0 0 8px rgba(59, 130, 246, 0); + box-shadow: 0 0 0 8px rgb(37 99 235 / 0); /* primary-600 fully transparent */ } } diff --git a/src/App.tsx b/src/App.tsx index c9bb4cc..9a6f49c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -67,7 +67,7 @@ function App() { }, [resumedPattern, resumeFileName, pesData, setPattern, setPatternOffset]); return ( -
Not Connected
+
Your browser doesn't support Web Bluetooth, which is required to connect to your embroidery machine.
Please try one of these options:
Cached: "{resumeFileName}"
@@ -110,21 +110,21 @@ export function MachineConnection({ {/* Error/Info Display */} {errorInfo && ( errorInfo.isInformational ? ( -
@@ -51,7 +51,7 @@ export function PatternSummaryCard() { {isDeleting ? ( <> diff --git a/src/components/ProgressMonitor.tsx b/src/components/ProgressMonitor.tsx index fe210ca..11c3692 100644 --- a/src/components/ProgressMonitor.tsx +++ b/src/components/ProgressMonitor.tsx @@ -154,22 +154,22 @@ export function ProgressMonitor() { }, [colorBlocks]); const stateIndicatorColors = { - idle: "bg-blue-50 dark:bg-blue-900/20 border-blue-600", - info: "bg-blue-50 dark:bg-blue-900/20 border-blue-600", - active: "bg-yellow-50 dark:bg-yellow-900/20 border-yellow-500", - waiting: "bg-yellow-50 dark:bg-yellow-900/20 border-yellow-500", - warning: "bg-yellow-50 dark:bg-yellow-900/20 border-yellow-500", - complete: "bg-green-50 dark:bg-green-900/20 border-green-600", - success: "bg-green-50 dark:bg-green-900/20 border-green-600", - interrupted: "bg-red-50 dark:bg-red-900/20 border-red-600", - error: "bg-red-50 dark:bg-red-900/20 border-red-600", - danger: "bg-red-50 dark:bg-red-900/20 border-red-600", + idle: "bg-info-50 dark:bg-info-900/20 border-info-600", + info: "bg-info-50 dark:bg-info-900/20 border-info-600", + active: "bg-warning-50 dark:bg-warning-900/20 border-warning-500", + waiting: "bg-warning-50 dark:bg-warning-900/20 border-warning-500", + warning: "bg-warning-50 dark:bg-warning-900/20 border-warning-500", + complete: "bg-success-50 dark:bg-success-900/20 border-success-600", + success: "bg-success-50 dark:bg-success-900/20 border-success-600", + interrupted: "bg-danger-50 dark:bg-danger-900/20 border-danger-600", + error: "bg-danger-50 dark:bg-danger-900/20 border-danger-600", + danger: "bg-danger-50 dark:bg-danger-900/20 border-danger-600", }; return ( - + - + Sewing Progress @@ -185,7 +185,7 @@ export function ProgressMonitor() { {/* Pattern Info */} {patternInfo && ( - + Total Stitches @@ -193,7 +193,7 @@ export function ProgressMonitor() { {totalStitches.toLocaleString()} - + Total Time @@ -201,7 +201,7 @@ export function ProgressMonitor() { {totalMinutes} min - + Speed @@ -217,13 +217,13 @@ export function ProgressMonitor() { - + Current Stitch @@ -232,7 +232,7 @@ export function ProgressMonitor() { {totalStitches.toLocaleString()} - + Time @@ -249,22 +249,22 @@ export function ProgressMonitor() { (() => { const iconMap = { ready: ( - + ), active: ( - + ), waiting: ( - + ), complete: ( - + ), interrupted: ( - + ), error: ( - + ), }; @@ -297,7 +297,7 @@ export function ProgressMonitor() { {colorBlocks.map((block, index) => { const isCompleted = currentStitch >= block.endStitch; @@ -319,10 +319,10 @@ export function ProgressMonitor() { ref={isCurrent ? currentBlockRef : null} className={`p-2.5 rounded-lg border-2 transition-all duration-300 ${ isCompleted - ? "border-green-600 bg-green-50 dark:bg-green-900/20" + ? "border-success-600 bg-success-50 dark:bg-success-900/20" : isCurrent - ? "border-purple-600 bg-purple-50 dark:bg-purple-900/20 shadow-lg shadow-purple-600/20 animate-pulseGlow" - : "border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-800/50 opacity-70" + ? "border-accent-600 bg-accent-50 dark:bg-accent-900/20 shadow-lg shadow-accent-600/20 animate-pulseGlow" + : "border-gray-200 dark:border-gray-600 bg-gray-300 dark:bg-gray-800/50 opacity-70" }`} role="listitem" aria-label={`Thread ${block.colorIndex + 1}, ${block.stitchCount} stitches, ${isCompleted ? "completed" : isCurrent ? "in progress" : "pending"}`} @@ -374,12 +374,12 @@ export function ProgressMonitor() { {/* Status icon */} {isCompleted ? ( ) : isCurrent ? ( ) : ( @@ -394,7 +394,7 @@ export function ProgressMonitor() { {isCurrent && ( @@ -436,7 +436,7 @@ export function ProgressMonitor() { diff --git a/src/components/SkeletonLoader.tsx b/src/components/SkeletonLoader.tsx index 73b4f4d..a4582e7 100644 --- a/src/components/SkeletonLoader.tsx +++ b/src/components/SkeletonLoader.tsx @@ -23,7 +23,7 @@ export function PatternCanvasSkeleton() { - + @@ -50,7 +50,7 @@ export function PatternInfoSkeleton() { return ( - + {[1, 2, 3, 4].map((i) => ( @@ -73,7 +73,7 @@ export function MachineConnectionSkeleton() { - + diff --git a/src/components/WorkflowStepper.tsx b/src/components/WorkflowStepper.tsx index 43d213b..da0d176 100644 --- a/src/components/WorkflowStepper.tsx +++ b/src/components/WorkflowStepper.tsx @@ -320,11 +320,11 @@ export function WorkflowStepper() { return ( {/* Progress bar background */} - + {/* Progress bar fill */} {step.label} @@ -400,35 +400,35 @@ export function WorkflowStepper() { if (!content) return null; const colorClasses = { - info: 'bg-blue-50 dark:bg-blue-900/95 border-blue-600 dark:border-blue-500', - success: 'bg-green-50 dark:bg-green-900/95 border-green-600 dark:border-green-500', - warning: 'bg-yellow-50 dark:bg-yellow-900/95 border-yellow-600 dark:border-yellow-500', - error: 'bg-red-50 dark:bg-red-900/95 border-red-600 dark:border-red-500', - progress: 'bg-cyan-50 dark:bg-cyan-900/95 border-cyan-600 dark:border-cyan-500' + info: 'bg-info-50 dark:bg-info-900/95 border-info-600 dark:border-info-500', + success: 'bg-success-50 dark:bg-success-900/95 border-success-600 dark:border-success-500', + warning: 'bg-warning-50 dark:bg-warning-900/95 border-warning-600 dark:border-warning-500', + error: 'bg-danger-50 dark:bg-danger-900/95 border-danger-600 dark:border-danger-500', + progress: 'bg-info-50 dark:bg-info-900/95 border-info-600 dark:border-info-500' }; const iconColorClasses = { - info: 'text-blue-600 dark:text-blue-400', - success: 'text-green-600 dark:text-green-400', - warning: 'text-yellow-600 dark:text-yellow-400', - error: 'text-red-600 dark:text-red-400', - progress: 'text-cyan-600 dark:text-cyan-400' + info: 'text-info-600 dark:text-info-400', + success: 'text-success-600 dark:text-success-400', + warning: 'text-warning-600 dark:text-warning-400', + error: 'text-danger-600 dark:text-danger-400', + progress: 'text-info-600 dark:text-info-400' }; const textColorClasses = { - info: 'text-blue-900 dark:text-blue-200', - success: 'text-green-900 dark:text-green-200', - warning: 'text-yellow-900 dark:text-yellow-200', - error: 'text-red-900 dark:text-red-200', - progress: 'text-cyan-900 dark:text-cyan-200' + info: 'text-info-900 dark:text-info-200', + success: 'text-success-900 dark:text-success-200', + warning: 'text-warning-900 dark:text-warning-200', + error: 'text-danger-900 dark:text-danger-200', + progress: 'text-info-900 dark:text-info-200' }; const descColorClasses = { - info: 'text-blue-800 dark:text-blue-300', - success: 'text-green-800 dark:text-green-300', - warning: 'text-yellow-800 dark:text-yellow-300', - error: 'text-red-800 dark:text-red-300', - progress: 'text-cyan-800 dark:text-cyan-300' + info: 'text-info-800 dark:text-info-300', + success: 'text-success-800 dark:text-success-300', + warning: 'text-warning-800 dark:text-warning-300', + error: 'text-danger-800 dark:text-danger-300', + progress: 'text-info-800 dark:text-info-300' }; const listColorClasses = { diff --git a/src/index.css b/src/index.css index 1cd48aa..c969a03 100644 --- a/src/index.css +++ b/src/index.css @@ -5,8 +5,15 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - background-color: #f5f5f5; - color: #212529; + background-color: var(--color-gray-50); + color: var(--color-gray-900); +} + +@media (prefers-color-scheme: dark) { + body { + background-color: var(--color-gray-900); + color: var(--color-gray-50); + } } code { diff --git a/src/styles/designTokens.ts b/src/styles/designTokens.ts deleted file mode 100644 index 50b53ee..0000000 --- a/src/styles/designTokens.ts +++ /dev/null @@ -1,135 +0,0 @@ -/** - * Design Tokens - Semantic Color System - * - * These tokens provide meaningful names for colors used throughout the app. - * Instead of using arbitrary colors like "blue-600", use semantic names that - * describe the purpose: "primary", "success", "warning", etc. - */ - -export const colors = { - // Primary - Main brand color for primary actions - primary: { - DEFAULT: '#2563eb', // blue-600 - hover: '#1d4ed8', // blue-700 - active: '#1e40af', // blue-800 - light: '#dbeafe', // blue-50 - border: '#93c5fd', // blue-300 - }, - - // Success - Positive states, completion - success: { - DEFAULT: '#16a34a', // green-600 - hover: '#15803d', // green-700 - active: '#166534', // green-800 - light: '#dcfce7', // green-50 - border: '#86efac', // green-300 - }, - - // Warning - Caution, waiting states - warning: { - DEFAULT: '#f59e0b', // amber-500 - hover: '#d97706', // amber-600 - active: '#b45309', // amber-700 - light: '#fef3c7', // amber-50 - border: '#fcd34d', // amber-300 - }, - - // Danger - Errors, destructive actions - danger: { - DEFAULT: '#dc2626', // red-600 - hover: '#b91c1c', // red-700 - active: '#991b1b', // red-800 - light: '#fee2e2', // red-50 - border: '#fca5a5', // red-300 - }, - - // Info - Informational states - info: { - DEFAULT: '#0891b2', // cyan-600 - hover: '#0e7490', // cyan-700 - active: '#155e75', // cyan-800 - light: '#cffafe', // cyan-50 - border: '#67e8f9', // cyan-300 - }, - - // Neutral - Secondary actions, borders, backgrounds - neutral: { - DEFAULT: '#4b5563', // gray-600 - hover: '#374151', // gray-700 - active: '#1f2937', // gray-800 - light: '#f9fafb', // gray-50 - border: '#d1d5db', // gray-300 - text: '#6b7280', // gray-500 - }, -}; - -/** - * Button Classes - Reusable button styles - */ -export const buttonClasses = { - // Base styles for all buttons - base: 'px-4 py-2.5 rounded-lg font-semibold text-sm transition-all duration-150 cursor-pointer focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed', - - // Primary button - primary: `bg-[${colors.primary.DEFAULT}] text-white hover:bg-[${colors.primary.hover}] active:bg-[${colors.primary.active}] hover:shadow-lg active:scale-[0.98] focus:ring-[${colors.primary.border}]`, - - // Success button - success: `bg-[${colors.success.DEFAULT}] text-white hover:bg-[${colors.success.hover}] active:bg-[${colors.success.active}] hover:shadow-lg active:scale-[0.98] focus:ring-[${colors.success.border}]`, - - // Warning button - warning: `bg-[${colors.warning.DEFAULT}] text-white hover:bg-[${colors.warning.hover}] active:bg-[${colors.warning.active}] hover:shadow-lg active:scale-[0.98] focus:ring-[${colors.warning.border}]`, - - // Danger button - danger: `bg-[${colors.danger.DEFAULT}] text-white hover:bg-[${colors.danger.hover}] active:bg-[${colors.danger.active}] hover:shadow-lg active:scale-[0.98] focus:ring-[${colors.danger.border}]`, - - // Secondary/Neutral button - secondary: `bg-[${colors.neutral.DEFAULT}] text-white hover:bg-[${colors.neutral.hover}] active:bg-[${colors.neutral.active}] hover:shadow-lg active:scale-[0.98] focus:ring-[${colors.neutral.border}]`, -}; - -/** - * Typography Scale - */ -export const typography = { - // Headings - h1: 'text-2xl font-bold', - h2: 'text-xl font-semibold', - h3: 'text-lg font-semibold', - h4: 'text-base font-semibold', - - // Body text - body: 'text-sm', // 14px - standard body - bodyLarge: 'text-base', // 16px - bodySmall: 'text-xs', // 12px - minimum size - - // Labels - label: 'text-xs font-medium text-gray-600', - - // Values - value: 'text-sm font-semibold text-gray-900', -}; - -/** - * Spacing Scale - */ -export const spacing = { - xs: 'gap-2', // 8px - sm: 'gap-3', // 12px - md: 'gap-4', // 16px - lg: 'gap-6', // 24px - - // Padding - paddingXs: 'p-2', // 8px - paddingSm: 'p-3', // 12px - paddingMd: 'p-4', // 16px - paddingLg: 'p-6', // 24px -}; - -/** - * Alert/Status Box Classes - */ -export const alertClasses = { - success: `bg-[${colors.success.light}] text-green-800 border border-[${colors.success.border}] px-3 py-2 rounded-lg text-sm`, - warning: `bg-[${colors.warning.light}] text-amber-800 border border-[${colors.warning.border}] px-3 py-2 rounded-lg text-sm`, - danger: `bg-[${colors.danger.light}] text-red-800 border border-[${colors.danger.border}] px-3 py-2 rounded-lg text-sm`, - info: `bg-[${colors.info.light}] text-cyan-800 border border-[${colors.info.border}] px-3 py-2 rounded-lg text-sm`, -}; diff --git a/src/utils/cssVariables.ts b/src/utils/cssVariables.ts new file mode 100644 index 0000000..512cd2e --- /dev/null +++ b/src/utils/cssVariables.ts @@ -0,0 +1,19 @@ +/** + * Get CSS variable value from document root + */ +export function getCSSVariable(name: string): string { + return getComputedStyle(document.documentElement) + .getPropertyValue(name) + .trim(); +} + +/** + * Canvas color helpers + */ +export const canvasColors = { + grid: () => getCSSVariable('--color-canvas-grid'), + origin: () => getCSSVariable('--color-canvas-origin'), + hoop: () => getCSSVariable('--color-canvas-hoop'), + bounds: () => getCSSVariable('--color-canvas-bounds'), + position: () => getCSSVariable('--color-canvas-position'), +};