Appearance
Theming System
Overview
AcqMarketplace implements a comprehensive theming system based on CSS custom properties (variables) with support for dark and light modes. The system uses Tailwind CSS semantic tokens and shadcn/ui components for consistent design.
Theme Architecture
CSS Variables Foundation
File: src/index.css:7-122
The theming system is built on CSS custom properties defined at the root level:
css
:root {
/* Core semantic colors */
--background: 210 20% 98%; /* #F5F7FA */
--foreground: 210 6% 21%; /* #333D4C */
--primary: 0 65% 58%; /* Brand Red #DB5151 */
--secondary: 210 17% 94%; /* #EEF1F6 */
/* System colors */
--destructive: 0 84% 60%; /* #F03D3D */
--success: 150 64% 45%; /* #33B36B */
--warning: 24 100% 59%; /* #FC9231 */
--info: 186 35% 36%; /* #3D7A81 */
/* UI elements */
--border: 220 20% 86%;
--input: 220 20% 91%;
--card: 0 0% 100%;
--popover: 0 0% 100%;
--muted: 210 17% 94%;
--accent: 210 17% 94%;
}
.dark {
/* Dark mode overrides */
--background: 222 20% 9%; /* #111827 */
--foreground: 210 4% 98%;
--primary: 0 65% 65%; /* Adjusted for dark */
/* ... other dark mode values */
}HSL Color Format
All colors use HSL (Hue, Saturation, Lightness) format without the hsl() wrapper:
- Format:
hue saturation% lightness% - Usage:
hsl(var(--primary)) - Benefits: Easy manipulation and consistent alpha channel support
Theme Provider Implementation
ThemeProvider Component
File: src/components/ThemeProvider.tsx
typescript
type Theme = \"dark\" | \"light\" | \"system\"
interface ThemeProviderProps {
children: React.ReactNode
defaultTheme?: Theme
storageKey?: string
}
export function ThemeProvider({
children,
defaultTheme = \"system\",
storageKey = \"vite-ui-theme\",
}: ThemeProviderProps) {
const [theme, setTheme] = useState<Theme>(
() => (localStorage.getItem(storageKey) as Theme) || defaultTheme
)
useEffect(() => {
const root = window.document.documentElement
root.classList.remove(\"light\", \"dark\")
if (theme === \"system\") {
const systemTheme = window.matchMedia(\"(prefers-color-scheme: dark)\")
.matches ? \"dark\" : \"light\"
root.classList.add(systemTheme)
return
}
root.classList.add(theme)
}, [theme])
const value = {
theme,
setTheme: (theme: Theme) => {
localStorage.setItem(storageKey, theme)
setTheme(theme)
},
}
return (
<ThemeProviderContext.Provider value={value}>
{children}
</ThemeProviderContext.Provider>
)
}Using the Theme
typescript
import { useTheme } from "@/components/ThemeProvider"
function ThemeToggle() {
const { theme, setTheme } = useTheme()
return (
<Button
variant="outline"
size="icon"
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
</Button>
)
}Tailwind CSS Integration
Tailwind Configuration
File: tailwind.config.ts:26-87
typescript
theme: {
extend: {
colors: {
// Semantic colors from CSS variables
border: \"hsl(var(--border))\",
input: \"hsl(var(--input))\",
ring: \"hsl(var(--ring))\",
background: \"hsl(var(--background))\",
foreground: \"hsl(var(--foreground))\",
primary: {
DEFAULT: \"hsl(var(--primary))\",
foreground: \"hsl(var(--primary-foreground))\",
},
secondary: {
DEFAULT: \"hsl(var(--secondary))\",
foreground: \"hsl(var(--secondary-foreground))\",
},
// ... other semantic colors
},
},
}Usage in Components
typescript
// Using semantic tokens
<div className="bg-background text-foreground">
<button className="bg-primary text-primary-foreground hover:bg-primary/90">
Primary Action
</button>
<div className="border border-border rounded-lg p-4">
<p className="text-muted-foreground">Secondary content</p>
</div>
</div>Brand Color Palette
Primary Brand Colors
css
/* Brand Red - Primary Action Color */
--primary: 0 65% 58%; /* #DB5151 */
/* Brand Gray Scale */
--gray-50: #F5F7FA;
--gray-100: #EEF1F6;
--gray-200: #E0E5EB;
--gray-300: #CAD0D9;
--gray-400: #9CA3AF;
--gray-500: #6C727F;
--gray-600: #4E5562;
--gray-700: #333D4C;
--gray-800: #1D2735;
--gray-900: #111827;
--gray-950: #030712;System Status Colors
css
/* Success */
--success: 150 64% 45%; /* #33B36B */
/* Warning */
--warning: 24 100% 59%; /* #FC9231 */
/* Error/Destructive */
--destructive: 0 84% 60%; /* #F03D3D */
/* Info */
--info: 186 35% 36%; /* #3D7A81 */Component Theming
shadcn/ui Component Styling
Components automatically inherit theme colors through CSS variables:
typescript
// Button component variations
<Button variant="default">Primary Button</Button> // Uses --primary
<Button variant="secondary">Secondary Button</Button> // Uses --secondary
<Button variant="destructive">Delete Button</Button> // Uses --destructive
<Button variant="outline">Outline Button</Button> // Uses --border
<Button variant="ghost">Ghost Button</Button> // TransparentCustom Component Theming
typescript
function ThemedCard({ children, variant = "default" }: {
children: React.ReactNode;
variant?: "default" | "accent" | "muted";
}) {
const variantStyles = {
default: "bg-card text-card-foreground border-border",
accent: "bg-accent text-accent-foreground border-accent",
muted: "bg-muted text-muted-foreground border-muted",
};
return (
<div className={cn(
"rounded-lg border p-6 shadow-sm",
variantStyles[variant]
)}>
{children}
</div>
);
}Dark Mode Implementation
Automatic System Preference
typescript
// Detects system preference
const prefersDark = window.matchMedia(\"(prefers-color-scheme: dark)\").matches;
// Listen for system changes
useEffect(() => {
const mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");
const handleChange = (e: MediaQueryListEvent) => {
if (theme === \"system\") {
document.documentElement.classList.toggle(\"dark\", e.matches);
}
};
mediaQuery.addEventListener(\"change\", handleChange);
return () => mediaQuery.removeEventListener(\"change\", handleChange);
}, [theme]);Dark Mode Specific Styles
css
/* Dark mode specific utilities */
.dark .glass {
@apply bg-black/5;
}
.dark .glass::before {
background: linear-gradient(225deg, rgba(255,255,255,0.1), rgba(255,255,255,0.02));
}
.dark .diagonal-bg::before {
opacity: 0.1;
}Special Effects and Utilities
Glass Morphism Effect
File: src/index.css:178-201
css
.glass {
@apply bg-white/5 backdrop-blur-lg dark:bg-black/5;
border: 1px solid transparent;
background-clip: padding-box;
position: relative;
}
.glass::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(225deg, rgba(255,255,255,0.2), rgba(255,255,255,0.05));
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
}Gradient Utilities
css
.text-gradient {
@apply bg-clip-text text-transparent bg-gradient-to-r from-primary to-[hsl(var(--success))];
}
.button-gradient {
@apply bg-gradient-to-r from-primary to-[hsl(var(--success))] hover:opacity-90 transition-opacity rounded-full;
}Animation and Transitions
css
.card-hover {
@apply transition-all duration-300 hover:shadow-medium hover:scale-105 hover:border-accent;
}
.glass-hover {
@apply transition-all duration-300 hover:bg-white/10 dark:hover:bg-black/10;
}Typography System
Font Configuration
File: tailwind.config.ts:22-25
typescript
fontFamily: {
'onest': ['Onest', 'sans-serif'],
'sans': ['Onest', 'system-ui', 'sans-serif'],
}Typography Utilities
css
body {
@apply bg-background text-foreground font-sans antialiased;
font-feature-settings: \"rlig\" 1, \"calt\" 1;
}Shadow System
Custom Shadow Variables
File: src/index.css:62-66 and src/index.css:117-120
css
:root {
--shadow-small: 0 1px 2px 0 rgb(0 0 0 / 0.05);
--shadow-medium: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
--shadow-large: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
}
.dark {
--shadow-small: 0 1px 2px 0 rgb(0 0 0 / 0.3);
--shadow-medium: 0 4px 6px -1px rgb(0 0 0 / 0.4), 0 2px 4px -2px rgb(0 0 0 / 0.4);
--shadow-large: 0 20px 25px -5px rgb(0 0 0 / 0.4), 0 8px 10px -6px rgb(0 0 0 / 0.4);
}Shadow Utilities
css
.shadow-small { box-shadow: var(--shadow-small); }
.shadow-medium { box-shadow: var(--shadow-medium); }
.shadow-large { box-shadow: var(--shadow-large); }Theme Customization
Adding New Color Tokens
- Define CSS Variables:
css
:root {
--brand-blue: 210 100% 50%;
--brand-blue-foreground: 0 0% 100%;
}
.dark {
--brand-blue: 210 100% 60%;
--brand-blue-foreground: 210 100% 10%;
}- Add to Tailwind Config:
typescript
colors: {
'brand-blue': {
DEFAULT: \"hsl(var(--brand-blue))\",
foreground: \"hsl(var(--brand-blue-foreground))\",
},
}- Use in Components:
typescript
<Button className="bg-brand-blue text-brand-blue-foreground">
Custom Button
</Button>Creating Theme Variants
typescript
// Create a theme variant hook
function useThemeVariant() {
const { theme } = useTheme();
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>('light');
useEffect(() => {
if (theme === 'system') {
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
? 'dark'
: 'light';
setResolvedTheme(systemTheme);
} else {
setResolvedTheme(theme as 'light' | 'dark');
}
}, [theme]);
return resolvedTheme;
}Best Practices
1. Always Use Semantic Tokens
typescript
// ✅ Good - Uses semantic tokens
<div className="bg-background text-foreground border border-border">
// ❌ Bad - Uses direct colors
<div className="bg-white text-black border border-gray-200">2. Provide Dark Mode Alternatives
typescript
// ✅ Good - Automatic theme support
<div className="bg-card text-card-foreground">
// ✅ Good - Explicit dark mode handling
<div className="bg-white dark:bg-gray-900 text-black dark:text-white">3. Test Both Themes
typescript
// Component should work in both themes
function ThemedComponent() {
return (
<div className="bg-background text-foreground">
<div className="bg-card border border-border rounded-lg p-4">
<p className="text-muted-foreground">This works in both themes</p>
</div>
</div>
);
}4. Use Alpha Channels Consistently
typescript
// ✅ Good - Uses alpha with semantic tokens
<div className="bg-primary/10 border border-primary/20">
// ✅ Good - Explicit alpha values
<div className="bg-black/5 dark:bg-white/5">Related Documentation:
- UI Style Guide - Component styling patterns
- Badge System - Badge color system
- Architecture Overview - Overall system structure
- Accessibility - Theme accessibility considerations