Appearance
Theme System Documentation ​
Overview ​
The Coleo Observatory web UI supports both Light and Dark themes, with automatic system preference detection. The theme system is built using CSS variables and Tailwind CSS v4.
Features ​
- Three theme modes: Light, Dark, and System (follows OS preference)
- Instant switching: Theme changes apply immediately without page reload
- Persistent preference: User preference is stored in localStorage and synced to backend API
- Smooth transitions: CSS transitions provide smooth color changes when switching themes
- Accessibility: All color combinations meet WCAG AA contrast standards
User Guide ​
Changing the Theme ​
- Navigate to Settings page
- Under the Appearance section, find the Theme dropdown
- Select your preferred theme:
- Light: Always use light color scheme
- Dark: Always use dark color scheme
- System: Automatically match your device's color scheme preference
Theme Persistence ​
- Your theme preference is saved automatically when you make a selection
- The preference persists across browser sessions using localStorage
- If you're signed in, the preference is also synced to your user profile
- On page load, the theme is applied in this priority order:
- Stored user preference (from backend if signed in)
- Client local override (localStorage)
- System preference (prefers-color-scheme)
- Default to Light
Developer Guide ​
Architecture ​
The theme system consists of:
- CSS Variables (
src/web/src/index.css): Define color values for both light and dark modes - Tailwind Theme Config (
@themedirective in CSS): Maps CSS variables to Tailwind classes - Theme Provider (
src/web/src/lib/theme.tsx): React context for theme state management - Theme Toggle UI (
src/web/src/pages/SettingsPage.tsx): User interface for theme selection
CSS Variables ​
Theme variables are defined in :root for light mode and .dark class for dark mode:
css
:root {
--background: oklch(0.9702 0 0);
--foreground: oklch(0.2103 0.0059 285.89);
--card: oklch(100% 0 0);
--card-foreground: oklch(0.2103 0.0059 285.89);
/* ... more variables */
}
.dark {
--background: oklch(12% 0.005 285.823);
--foreground: oklch(0.9911 0 0);
--card: oklch(0.2103 0.0059 285.89);
--card-foreground: oklch(0.9911 0 0);
/* ... more variables */
}Tailwind Integration ​
The @theme directive maps CSS variables to Tailwind utility classes:
css
@theme {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
/* ... more mappings */
}This allows using Tailwind classes like:
bg-background- Background colortext-foreground- Text colorborder-border- Border colorbg-card- Card background
Using the Theme in Components ​
Components automatically respond to theme changes when using the CSS variable-based Tailwind classes:
tsx
// This component will automatically adapt to theme changes
function MyComponent() {
return (
<div className="bg-card text-card-foreground border border-border">
Content
</div>
);
}Programmatic Theme Access ​
Use the useTheme hook to access or change the theme programmatically:
tsx
import { useTheme } from '@/lib';
function MyComponent() {
const { theme, resolvedTheme, setTheme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
Current: {resolvedTheme}
</button>
);
}The hook returns:
theme: Current theme setting ('light' | 'dark' | 'system')resolvedTheme: Actual applied theme ('light' | 'dark')setTheme(theme): Function to set the themetoggleTheme(): Function to toggle between light and dark
Adding New Theme-Aware Components ​
When creating new components:
Use CSS variable-based Tailwind classes:
bg-background,text-foregroundfor base colorsbg-card,text-card-foregroundfor card surfacesborder-borderfor borderstext-muted-foregroundfor secondary text
Avoid hardcoded colors like
bg-whiteortext-blackTest in both light and dark modes
Ensure contrast ratios meet WCAG AA standards (4.5:1 for normal text, 3:1 for large text)
Color Tokens Reference ​
| Token | Light Mode | Dark Mode | Usage |
|---|---|---|---|
--background | Light gray | Very dark gray | Page background |
--foreground | Dark gray | White | Primary text |
--card | White | Dark gray | Card backgrounds |
--card-foreground | Dark gray | White | Card text |
--muted | Medium gray | Light gray | Secondary text |
--border | Light gray | Dark gray | Borders |
--accent | Blue | Blue | Primary actions, links |
--sidebar | Light gray | Dark gray | Sidebar background |
Testing Themes ​
To test theme switching:
- Open the Settings page
- Toggle between Light, Dark, and System themes
- Verify all components update correctly
- Check that the preference persists after page reload
- Test system preference detection by changing OS theme
Browser Support ​
The theme system uses:
- CSS Custom Properties (variables) - supported in all modern browsers
oklch()color format - supported in Chrome 111+, Firefox 128+, Safari 15.4+prefers-color-schememedia query - supported in all modern browsers
For older browsers, the system gracefully falls back to light mode.
Implementation Notes ​
Tailwind v4 Differences ​
This project uses Tailwind CSS v4, which has significant differences from v3:
- No
tailwind.config.jsfile - configuration is done via CSS using@theme - CSS variables are defined directly in CSS files
- The
@import "tailwindcss"statement includes all Tailwind features
HeroUI v3 Integration ​
The theme system works alongside HeroUI v3 components:
- HeroUI components use their own theming system
- Our CSS variables complement HeroUI's variables
- Some components may need custom overrides for perfect dark mode support
Performance Considerations ​
- Theme switching is instant (no page reload)
- CSS transitions are applied to color properties only
- The
no-transitionsclass prevents flash of wrong theme on initial load - localStorage access is wrapped in try-catch for privacy mode compatibility
