# Dynamic Theme System for @librechat/client This theme system allows you to dynamically change colors in your React application using CSS variables and Tailwind CSS. It combines dark/light mode switching with dynamic color theming capabilities. ## Table of Contents - [Overview](#overview) - [How It Works](#how-it-works) - [Basic Usage](#basic-usage) - [Available Theme Colors](#available-theme-colors) - [Creating Custom Themes](#creating-custom-themes) - [Environment Variable Themes](#environment-variable-themes) - [Dark/Light Mode](#darklight-mode) - [Migration Guide](#migration-guide) - [Implementation Details](#implementation-details) - [Troubleshooting](#troubleshooting) ## Overview The theme system provides: 1. **Dark/Light Mode Switching** - Automatic theme switching based on user preference 2. **Dynamic Color Theming** - Change colors at runtime without recompiling CSS 3. **CSS Variable Based** - Uses CSS custom properties for performance 4. **Tailwind Integration** - Works seamlessly with Tailwind CSS utilities 5. **TypeScript Support** - Full type safety for theme definitions ## How It Works The theme system operates in three layers: 1. **CSS Variables Layer**: Default colors defined in your app's CSS 2. **ThemeProvider Layer**: React context that manages theme state and applies CSS variables 3. **Tailwind Layer**: Maps CSS variables to Tailwind utility classes ### Default Behavior (No Custom Theme) - CSS variables cascade from your app's `style.css` definitions - Light mode uses variables under `html` selector - Dark mode uses variables under `.dark` selector - No JavaScript intervention in color values ### Custom Theme Behavior - Only applies when `themeRGB` prop is provided - Overrides CSS variables with `rgb()` formatted values - Maintains compatibility with existing CSS ## Basic Usage ### 1. Install the Component Library ```bash npm install @librechat/client ``` ### 2. Wrap Your App with ThemeProvider ```tsx import { ThemeProvider } from '@librechat/client'; function App() { return ( ); } ``` ### 3. Set Up Your Base CSS Ensure your app has CSS variables defined as fallbacks: ```css /* style.css */ :root { --white: #fff; --gray-800: #212121; --gray-100: #ececec; /* ... other color definitions */ } html { --text-primary: var(--gray-800); --surface-primary: var(--white); /* ... other theme variables */ } .dark { --text-primary: var(--gray-100); --surface-primary: var(--gray-900); /* ... other dark theme variables */ } ``` ### 4. Configure Tailwind Update your `tailwind.config.js`: ```js module.exports = { content: [ './src/**/*.{js,jsx,ts,tsx}', // Include component library files './node_modules/@librechat/client/dist/**/*.js', ], darkMode: ['class'], theme: { extend: { colors: { // Map CSS variables to Tailwind colors 'text-primary': 'var(--text-primary)', 'surface-primary': 'var(--surface-primary)', 'brand-purple': 'var(--brand-purple)', // ... other colors }, }, }, }; ``` ### 5. Use Theme Colors in Components ```tsx function MyComponent() { return (

Hello World

); } ``` ## Available Theme Colors ### Text Colors - `text-text-primary` - Primary text color - `text-text-secondary` - Secondary text color - `text-text-secondary-alt` - Alternative secondary text - `text-text-tertiary` - Tertiary text color - `text-text-warning` - Warning text color ### Surface Colors - `bg-surface-primary` - Primary background - `bg-surface-secondary` - Secondary background - `bg-surface-tertiary` - Tertiary background - `bg-surface-submit` - Submit button background - `bg-surface-destructive` - Destructive action background - `bg-surface-dialog` - Dialog/modal background - `bg-surface-chat` - Chat interface background ### Border Colors - `border-border-light` - Light border - `border-border-medium` - Medium border - `border-border-heavy` - Heavy border - `border-border-xheavy` - Extra heavy border ### Other Colors - `bg-brand-purple` - Brand purple color - `bg-presentation` - Presentation background - `ring-ring-primary` - Focus ring color ## Creating Custom Themes ### 1. Define Your Theme ```tsx import { IThemeRGB } from '@librechat/client'; export const customTheme: IThemeRGB = { 'rgb-text-primary': '0 0 0', // Black 'rgb-text-secondary': '100 100 100', // Gray 'rgb-surface-primary': '255 255 255', // White 'rgb-surface-submit': '0 128 0', // Green 'rgb-brand-purple': '138 43 226', // Blue Violet // ... define other colors }; ``` ### 2. Use Your Custom Theme ```tsx import { ThemeProvider } from '@librechat/client'; import { customTheme } from './themes/custom'; function App() { return ( ); } ``` ## Environment Variable Themes Load theme colors from environment variables: ### 1. Create Environment Variables ```env # .env.local REACT_APP_THEME_BRAND_PURPLE=171 104 255 REACT_APP_THEME_TEXT_PRIMARY=33 33 33 REACT_APP_THEME_TEXT_SECONDARY=66 66 66 REACT_APP_THEME_SURFACE_PRIMARY=255 255 255 REACT_APP_THEME_SURFACE_SUBMIT=4 120 87 ``` ### 2. Create a Theme Loader ```tsx function getThemeFromEnv(): IThemeRGB | undefined { // Check if any theme environment variables are set const hasThemeEnvVars = Object.keys(process.env).some(key => key.startsWith('REACT_APP_THEME_') ); if (!hasThemeEnvVars) { return undefined; // Use default themes } return { 'rgb-text-primary': process.env.REACT_APP_THEME_TEXT_PRIMARY || '33 33 33', 'rgb-brand-purple': process.env.REACT_APP_THEME_BRAND_PURPLE || '171 104 255', // ... other colors }; } ``` ### 3. Apply Environment Theme ```tsx ``` ## Dark/Light Mode The ThemeProvider handles dark/light mode automatically: ### Using the Theme Hook ```tsx import { useTheme } from '@librechat/client'; function ThemeToggle() { const { theme, setTheme } = useTheme(); return ( ); } ``` ### Theme Options - `'light'` - Force light mode - `'dark'` - Force dark mode - `'system'` - Follow system preference ## Migration Guide If you're migrating from an older theme system: ### 1. Update Imports **Before:** ```tsx import { ThemeContext, ThemeProvider } from '~/hooks/ThemeContext'; ``` **After:** ```tsx import { ThemeContext, ThemeProvider } from '@librechat/client'; ``` ### 2. Update ThemeProvider Usage The new ThemeProvider is backward compatible but adds new capabilities: ```tsx ``` ### 3. Existing Components Components using ThemeContext continue to work without changes: ```tsx // This still works! const { theme, setTheme } = useContext(ThemeContext); ``` ## Implementation Details ### File Structure ``` packages/client/src/theme/ ├── context/ │ └── ThemeProvider.tsx # Main theme provider ├── types/ │ └── index.ts # TypeScript interfaces ├── themes/ │ ├── default.ts # Light theme colors │ ├── dark.ts # Dark theme colors │ └── index.ts # Theme exports ├── utils/ │ ├── applyTheme.ts # Apply CSS variables │ ├── tailwindConfig.ts # Tailwind helpers │ └── createTailwindColors.js ├── README.md # This documentation └── index.ts # Main exports ``` ### CSS Variable Format The theme system uses RGB values in CSS variables: - CSS Variable: `--text-primary: rgb(33 33 33)` - Theme Definition: `'rgb-text-primary': '33 33 33'` - Tailwind Usage: `text-text-primary` ### RGB Format Requirements All color values must be in space-separated RGB format: - ✅ Correct: `'255 255 255'` - ❌ Incorrect: `'#ffffff'` or `'rgb(255, 255, 255)'` This format allows Tailwind to apply opacity modifiers like `bg-surface-primary/50`. ## Troubleshooting ### Common Issues #### 1. Colors Not Applying - **Issue**: Custom theme colors aren't showing - **Solution**: Ensure you're passing the `themeRGB` prop to ThemeProvider - **Check**: CSS variables in DevTools should show `rgb(R G B)` format #### 2. Circular Reference Errors - **Issue**: `--brand-purple: var(--brand-purple)` creates infinite loop - **Solution**: Use direct color values: `--brand-purple: #ab68ff` #### 3. Dark Mode Not Working - **Issue**: Dark mode doesn't switch - **Solution**: Ensure `darkMode: ['class']` is in your Tailwind config - **Check**: The `` element should have `class="dark"` in dark mode #### 4. TypeScript Errors - **Issue**: Type errors when defining themes - **Solution**: Import and use the `IThemeRGB` interface: ```tsx import { IThemeRGB } from '@librechat/client'; ``` ### Debugging Tips 1. **Check CSS Variables**: Use browser DevTools to inspect computed CSS variables 2. **Verify Theme Application**: Look for inline styles on the root element 3. **Console Errors**: Check for validation errors in the console 4. **Test Isolation**: Try a minimal theme to isolate issues ## Examples ### Dynamic Theme Switching ```tsx import { ThemeProvider, defaultTheme, darkTheme } from '@librechat/client'; import { useState } from 'react'; function App() { const [isDark, setIsDark] = useState(false); return ( ); } ``` ### Multi-Theme Selector ```tsx const themes = { default: undefined, // Use CSS defaults ocean: { 'rgb-brand-purple': '0 119 190', 'rgb-surface-primary': '240 248 255', // ... ocean theme colors }, forest: { 'rgb-brand-purple': '34 139 34', 'rgb-surface-primary': '245 255 250', // ... forest theme colors }, }; function App() { const [selectedTheme, setSelectedTheme] = useState('default'); return ( ); } ``` ### Using with the Main Application When using the ThemeProvider in your main application with localStorage persistence: ```tsx import { ThemeProvider } from '@librechat/client'; import { getThemeFromEnv } from './utils'; function App() { const envTheme = getThemeFromEnv(); return ( {/* Your app content */} ); } ``` **Important**: Props passed to ThemeProvider will override stored values on initial mount. Only pass props when you explicitly want to override the user's saved preferences. ## Contributing When adding new theme colors: 1. Add the type definition in `types/index.ts` 2. Add the color to default and dark themes 3. Update the applyTheme mapping 4. Add to Tailwind configuration 5. Document in this README ## License This theme system is part of the @librechat/client package.