- Overview
- Project Structure
- Installation
- Features
- Component Architecture
- Theme System
- Usage Guide
- Customization
- API Reference
- Troubleshooting
- Best Practices
- Contributing
- Advanced Configuration
- Additional Resources
Theme Switcher is a React application that allows users to switch between multiple themes with both light and dark modes. It features a responsive design, customizable UI elements, and persistent user preferences. The project leverages JSON for dynamic theme management, enabling easy addition and modification of themes without altering the core application code.
Tech Stack:
- React 18+
- Tailwind CSS
- Lucide React (for icons)
- JSON for external theme definitions
The project is organized to separate concerns clearly, with a focus on maintainability and scalability.
theme-switcher/
├── node_modules/
├── public/
│ ├── index.html
│ ├── favicon.ico
│ ├── manifest.json
│ └── themes.json # <-- External theme definitions
├── src/
│ ├── components/ # Reusable React components
│ │ ├── Header.jsx
│ │ ├── CustomizationModal.jsx
│ │ ├── InteractiveCard.jsx
│ │ ├── ThemeCarousel.jsx
│ │ └── ThemePreview.jsx
│ ├── App.jsx # Main application component
│ ├── index.js # Entry point
│ └── index.css # Tailwind CSS imports
├── package.json # Dependencies and scripts
├── tailwind.config.js # Tailwind configuration
└── README.md # Project documentation
Follow these steps to set up and run the Theme Switcher project locally.
- Node.js (v14.0.0 or higher)
- npm (v6.0.0 or higher)
-
Clone the Repository
git clone <repository-url> cd theme-switcher
-
Install Dependencies
npm install
-
Add
themes.json- Ensure that
themes.jsonis placed inside thepublic/directory. An examplethemes.jsonis provided below.
- Ensure that
-
Start Development Server
npm start
- The application will run at http://localhost:3000.
-
Build for Production
npm run build
- The optimized production build will be in the
build/directory.
- The optimized production build will be in the
- 404 Errors When Loading
themes.json- Ensure that
themes.jsonis correctly placed in thepublic/directory. - Verify that the file is named exactly
themes.json(case-sensitive). - Check for correct JSON syntax to prevent parsing errors.
- Ensure that
- Dynamic Theme Loading: Themes are defined externally in a JSON file, allowing easy additions and modifications.
- Multiple Theme Options: Supports various themes with light and dark variants.
- Persistent Preferences: User-selected themes and customization settings are saved in
localStorage. - Responsive Design: Optimized for various screen sizes.
- Customizable UI Elements: Adjust font sizes and spacing preferences.
- Interactive Previews: Real-time preview of themes and customizations.
- Classic Light/Dark
- Warm Light/Dark
- Cool Light/Dark
- Forest Light/Dark
- Rose Light/Dark
- And more variations (e.g., Ocean Light/Dark)
- Font Size Adjustment: Choose between small, medium, and large font sizes.
- Spacing Controls: Adjust spacing levels to suit your preference.
- Color Scheme Selection: Pick from a range of predefined color schemes.
- Layout Preferences: Modify layout settings as needed.
The application is built using modular React components, each responsible for specific functionality.
App
├── ThemeContext.Provider
│ ├── Header
│ │ └── SettingsModal
│ └── ThemeCarousel
└── ThemePreview
- React Context: Utilized for theme management and passing down state through the component tree.
- Local Storage: Used to persist user preferences across sessions.
- Component-Level State: Manages UI interactions within individual components.
Themes are now managed externally using a themes.json file located in the public/ directory. This approach allows for easy scalability and maintenance.
{
"light1": {
"name": "Classic Light",
"colors": {
"primary": "#2563eb",
"background": "#ffffff",
"text": "#1f2937",
"secondary": "#f3f4f6",
"accent": "#3b82f6"
}
},
"light2": {
"name": "Warm Light",
"colors": {
"primary": "#d97706",
"background": "#fffbeb",
"text": "#292524",
"secondary": "#fef3c7",
"accent": "#f59e0b"
}
},
"light3": {
"name": "Cool Light",
"colors": {
"primary": "#0891b2",
"background": "#f0fdfa",
"text": "#134e4a",
"secondary": "#ccfbf1",
"accent": "#06b6d4"
}
},
"light4": {
"name": "Forest Light",
"colors": {
"primary": "#059669",
"background": "#f0fdf4",
"text": "#064e3b",
"secondary": "#dcfce7",
"accent": "#10b981"
}
},
"light5": {
"name": "Rose Light",
"colors": {
"primary": "#e11d48",
"background": "#fff1f2",
"text": "#881337",
"secondary": "#ffe4e6",
"accent": "#f43f5e"
}
},
"dark1": {
"name": "Classic Dark",
"colors": {
"primary": "#60a5fa",
"background": "#1f2937",
"text": "#f3f4f6",
"secondary": "#374151",
"accent": "#3b82f6"
}
},
"dark2": {
"name": "Deep Dark",
"colors": {
"primary": "#8b5cf6",
"background": "#18181b",
"text": "#fafafa",
"secondary": "#27272a",
"accent": "#a78bfa"
}
},
"dark3": {
"name": "Midnight",
"colors": {
"primary": "#6366f1",
"background": "#020617",
"text": "#e2e8f0",
"secondary": "#1e293b",
"accent": "#818cf8"
}
},
"dark4": {
"name": "Forest Dark",
"colors": {
"primary": "#10b981",
"background": "#022c22",
"text": "#ecfdf5",
"secondary": "#064e3b",
"accent": "#34d399"
}
},
"dark5": {
"name": "Ruby Dark",
"colors": {
"primary": "#f43f5e",
"background": "#1c1917",
"text": "#fecdd3",
"secondary": "#292524",
"accent": "#fb7185"
}
}
}-
Fetch
themes.json:- The application fetches
themes.jsonfrom thepublic/directory during initialization.
- The application fetches
-
Parse and Store Themes:
- Parsed themes are stored in the application state and provided via React Context.
-
Dynamic Theme Application:
- Components consume the current theme from Context and apply styles accordingly.
- Scalability: Easily add or modify themes without altering application code.
- Maintainability: Centralized theme definitions simplify management.
- Flexibility: Supports nested properties and complex data structures.
- Start the Application
npm start
- Access the Application
- Open http://localhost:3000 in your browser.
- Switch Themes
- Use the theme carousel to browse and select different themes.
- Customize Settings
- Click the settings icon in the header to adjust font sizes and spacing preferences.
- Interact with UI Elements
- Explore interactive components like buttons and color palettes to see the active theme in action.
-
Update
themes.json- Open
public/themes.jsonand add a new theme following the existing structure.
Example: Adding "Ocean Light" and "Ocean Dark" Themes
{ "light6": { "name": "Ocean Light", "colors": { "primary": "#0ea5e9", "background": "#e0f2fe", "text": "#034e7b", "secondary": "#bae6fd", "accent": "#0284c7" } }, "dark6": { "name": "Ocean Dark", "colors": { "primary": "#0284c7", "background": "#0e7490", "text": "#f0fdfa", "secondary": "#075985", "accent": "#0369a1" } } }- Ensure that each new theme has a unique key and includes all required color properties.
- Open
-
Save
themes.json- Changes to
themes.jsonare automatically fetched when the application reloads.
- Changes to
-
Reload the Application
- Refresh your browser to see the new themes appear in the carousel.
- Open Settings Modal
- Click the settings icon in the header.
- Adjust Font Size
- Select between small, medium, or large font sizes.
- Adjust Spacing
- Use the slider to modify spacing levels.
- Save Changes
- Click "Save Changes" to apply and persist your preferences.
- User selections for theme, font size, and spacing are saved in
localStorageand persist across sessions.
-
Open
themes.json- Located in the
public/directory.
- Located in the
-
Add a New Theme Entry
- Follow the existing structure to define a new theme.
Example: Adding "Sunset Light" and "Sunset Dark" Themes
{ "light7": { "name": "Sunset Light", "colors": { "primary": "#fb923c", "background": "#fff7ed", "text": "#7c2d12", "secondary": "#fef3c7", "accent": "#ea580c" } }, "dark7": { "name": "Sunset Dark", "colors": { "primary": "#ea580c", "background": "#7c2d12", "text": "#fef3c7", "secondary": "#a16207", "accent": "#c2410c" } } } -
Save and Refresh
- Save
themes.jsonand refresh the application to see the new themes.
- Save
Ensure that any custom components utilize the current theme from ThemeContext for styling.
Example: Creating a Custom Component with Dynamic Styles
import React, { useContext } from 'react';
import { ThemeContext } from './App';
const CustomButton = () => {
const { currentTheme, themes } = useContext(ThemeContext);
return (
<button
className="px-4 py-2 rounded text-white"
style={{ backgroundColor: themes[currentTheme].colors.primary }}
>
Custom Button
</button>
);
};
export default CustomButton;Provides theme-related data and functions to the application.
{
currentTheme: string, // Current active theme key
setCurrentTheme: function, // Function to update the current theme
isDark: boolean, // Indicates if the current theme is dark
toggleDark: function, // Function to toggle between light and dark themes
fontSize: string, // Current font size setting ('small', 'medium', 'large')
setFontSize: function, // Function to update font size
spacing: number, // Current spacing level (1-8)
setSpacing: function, // Function to update spacing
themes: object // Object containing all theme definitions
}Defines the properties of each theme.
{
"light1": {
"name": "Classic Light",
"colors": {
"primary": "#2563eb",
"background": "#ffffff",
"text": "#1f2937",
"secondary": "#f3f4f6",
"accent": "#3b82f6"
}
},
// ...additional themes
}Properties:
- name: Human-readable name of the theme.
- colors: Object containing color properties.
- primary: Main color for primary actions.
- background: Background color of the application.
- text: Primary text color.
- secondary: Secondary color for less prominent elements.
- accent: Accent color for highlights and emphasis.
-
Issue: The application fails to fetch
themes.json, resulting in a 404 error. -
Possible Causes:
- Incorrect File Placement
- Ensure
themes.jsonis located in thepublic/directory.
- Ensure
- Incorrect File Naming
- Verify the file is named exactly
themes.json(case-sensitive).
- Verify the file is named exactly
- JSON Syntax Errors
- Ensure
themes.jsonis valid JSON. Use a JSON validator if necessary.
- Ensure
- Server Issues
- If running a local server, ensure it's correctly serving the
public/directory.
- If running a local server, ensure it's correctly serving the
- Incorrect File Placement
-
Solutions:
- Verify File Location
- Confirm that
public/themes.jsonexists.
- Confirm that
- Check File Naming
- Ensure there are no typos in the filename.
- Validate JSON
- Use tools like JSONLint to validate the syntax.
- Restart Development Server
- Sometimes, restarting the server can resolve path issues.
npm start
- Verify File Location
-
Issue: Selected themes do not reflect the defined colors or styles.
-
Possible Causes:
- Incorrect Theme Key
- Ensure the theme key used in
setCurrentThemematches one inthemes.json.
- Ensure the theme key used in
- Missing Color Properties
- Each theme must include all required color properties (
primary,background,text,secondary,accent).
- Each theme must include all required color properties (
- Caching Issues
- Browser caching might prevent updated themes from loading.
- Incorrect Theme Key
-
Solutions:
- Verify Theme Keys
- Check that theme keys are consistent across
themes.jsonand the application.
- Check that theme keys are consistent across
- Ensure Complete Theme Definitions
- All themes should include all necessary color properties.
- Clear Browser Cache
- Clear cache or perform a hard refresh to load the latest
themes.json. - Hard Refresh Shortcut:
- Windows/Linux:
Ctrl + F5 - Mac:
Cmd + Shift + R
- Windows/Linux:
- Clear cache or perform a hard refresh to load the latest
- Verify Theme Keys
-
Issue: The application encounters errors during runtime, causing it to crash or fail to render.
-
Possible Causes:
- Malformed
themes.json- Syntax errors or missing properties can cause parsing issues.
- Incorrect Context Usage
- Components might not be correctly consuming
ThemeContext.
- Components might not be correctly consuming
- State Initialization Issues
- Themes may not be loaded before components try to access them.
- Malformed
-
Solutions:
- Validate
themes.json- Ensure JSON is well-formed and complete.
- Check Context Providers and Consumers
- Verify that all components accessing
ThemeContextare wrapped within the provider.
- Verify that all components accessing
- Implement Loading States
- Ensure the application waits for themes to load before rendering dependent components.
- Validate
-
Issue: Changing themes does not update the UI as expected.
-
Possible Causes:
- Inline Styles Not Applied Correctly
- Ensure that components are applying styles based on the current theme.
- Incorrect State Updates
- The state might not be updating correctly when a new theme is selected.
- Inline Styles Not Applied Correctly
-
Solutions:
- Review Style Bindings
- Check that styles are dynamically bound to
themes[currentTheme].colors.
- Check that styles are dynamically bound to
- Ensure Proper State Management
- Verify that
setCurrentThemecorrectly updates the state and triggers re-renders.
- Verify that
- Review Style Bindings
- Meaningful Component Names
- Use descriptive names for components to enhance readability.
- Follow React Hooks Rules
- Adhere to the rules of hooks to prevent unexpected behavior.
- Consistent Formatting
- Maintain uniform code formatting using tools like Prettier.
- Comment Complex Logic
- Add comments to explain non-trivial parts of the code.
- Optimize Re-Renders
- Use
React.memooruseMemowhere appropriate to prevent unnecessary re-renders.
- Use
- Lazy Load Components
- Implement code-splitting for large components to improve initial load times.
- Memoization
- Utilize
useCallbackanduseMemoto memoize functions and values.
- Utilize
- Minimize Bundle Size
- Remove unused dependencies and optimize imports.
- Semantic HTML
- Use appropriate HTML elements to convey meaning.
- Color Contrast
- Ensure sufficient contrast between text and background colors for readability.
- Keyboard Navigation
- Make interactive elements accessible via keyboard.
- ARIA Labels
- Implement ARIA attributes to enhance screen reader compatibility.
- Fork the Repository
- Create a Feature Branch
git checkout -b feature/your-feature-name
- Make Changes
- Commit Changes
git commit -m "Add your descriptive commit message" - Push to Forked Repository
git push origin feature/your-feature-name
- Submit a Pull Request
- Provide a clear description of the changes and reference any relevant issues.
- Follow ESLint Configuration
- Adhere to the project's ESLint rules for consistent code quality.
- Meaningful Commit Messages
- Write clear and concise commit messages that describe the changes.
- Include Tests for New Features
- Ensure new features are accompanied by relevant tests.
- Update Documentation
- Reflect any changes or additions in the project documentation.
# Run tests
npm test
# Run linter
npm run lint
# Check formatting
npm run formatManage configuration settings using environment variables.
REACT_APP_THEME_STORAGE_KEY=theme_preference
REACT_APP_DEFAULT_THEME=light1Customize build processes for different environments.
# Development build
npm run build:dev
# Production build
npm run build:prod
# Generate stats
npm run build:statsDeploy the production build to your chosen hosting service.
# Build for production
npm run build
# Deploy to hosting service
npm run deployDefine additional scripts in package.json for specialized build tasks.
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build:dev": "react-scripts build --mode development",
"build:prod": "react-scripts build --mode production",
"build:stats": "react-scripts build --stats",
"deploy": "your-deployment-script-here",
"test": "react-scripts test",
"lint": "eslint ./src",
"format": "prettier --write ./src"
}- React Documentation
- Tailwind CSS Documentation
- Lucide React Icons
- JSONLint - JSON Validator
- ESLint Documentation
- Prettier Documentation
Ensure that your themes.json file is correctly formatted and placed inside the public/ directory. Below is the complete themes.json based on your initial theme definitions, along with examples of how to add new themes.
{
"light1": {
"name": "Classic Light",
"colors": {
"primary": "#2563eb",
"background": "#ffffff",
"text": "#1f2937",
"secondary": "#f3f4f6",
"accent": "#3b82f6"
}
},
"light2": {
"name": "Warm Light",
"colors": {
"primary": "#d97706",
"background": "#fffbeb",
"text": "#292524",
"secondary": "#fef3c7",
"accent": "#f59e0b"
}
},
"light3": {
"name": "Cool Light",
"colors": {
"primary": "#0891b2",
"background": "#f0fdfa",
"text": "#134e4a",
"secondary": "#ccfbf1",
"accent": "#06b6d4"
}
},
"light4": {
"name": "Forest Light",
"colors": {
"primary": "#059669",
"background": "#f0fdf4",
"text": "#064e3b",
"secondary": "#dcfce7",
"accent": "#10b981"
}
},
"light5": {
"name": "Rose Light",
"colors": {
"primary": "#e11d48",
"background": "#fff1f2",
"text": "#881337",
"secondary": "#ffe4e6",
"accent": "#f43f5e"
}
},
"dark1": {
"name": "Classic Dark",
"colors": {
"primary": "#60a5fa",
"background": "#1f2937",
"text": "#f3f4f6",
"secondary": "#374151",
"accent": "#3b82f6"
}
},
"dark2": {
"name": "Deep Dark",
"colors": {
"primary": "#8b5cf6",
"background": "#18181b",
"text": "#fafafa",
"secondary": "#27272a",
"accent": "#a78bfa"
}
},
"dark3": {
"name": "Midnight",
"colors": {
"primary": "#6366f1",
"background": "#020617",
"text": "#e2e8f0",
"secondary": "#1e293b",
"accent": "#818cf8"
}
},
"dark4": {
"name": "Forest Dark",
"colors": {
"primary": "#10b981",
"background": "#022c22",
"text": "#ecfdf5",
"secondary": "#064e3b",
"accent": "#34d399"
}
},
"dark5": {
"name": "Ruby Dark",
"colors": {
"primary": "#f43f5e",
"background": "#1c1917",
"text": "#fecdd3",
"secondary": "#292524",
"accent": "#fb7185"
}
},
"light6": {
"name": "Ocean Light",
"colors": {
"primary": "#0ea5e9",
"background": "#e0f2fe",
"text": "#034e7b",
"secondary": "#bae6fd",
"accent": "#0284c7"
}
},
"dark6": {
"name": "Ocean Dark",
"colors": {
"primary": "#0284c7",
"background": "#0e7490",
"text": "#f0fdfa",
"secondary": "#075985",
"accent": "#0369a1"
}
},
"light7": {
"name": "Sunset Light",
"colors": {
"primary": "#fb923c",
"background": "#fff7ed",
"text": "#7c2d12",
"secondary": "#fef3c7",
"accent": "#ea580c"
}
},
"dark7": {
"name": "Sunset Dark",
"colors": {
"primary": "#ea580c",
"background": "#7c2d12",
"text": "#fef3c7",
"secondary": "#a16207",
"accent": "#c2410c"
}
}
}- Unique Keys: Ensure each theme has a unique key (e.g.,
light1,dark1,light6, etc.). - Complete Properties: Every theme must define all required color properties (
primary,background,text,secondary,accent). - JSON Syntax: Maintain correct JSON syntax to prevent parsing errors.
After setting up themes.json and updating App.jsx, follow these steps to ensure everything works correctly.
-
Navigate to Project Directory
cd path/to/theme-switcher -
Install Dependencies
npm install
-
Start Development Server
npm start
- Open http://localhost:3000 in your browser.
- Ensure that the application loads without 404 errors.
- Verify that themes are correctly loaded and selectable from the carousel.
-
Build for Production
npm run build
- This creates an optimized build in the
build/directory. - Deploy the contents of
build/to your preferred hosting service.
- This creates an optimized build in the
-
404 Errors for
themes.json- Ensure Correct Placement:
themes.jsonmust be inside thepublic/directory. - Check File Naming: The file should be named exactly
themes.json. - Validate JSON: Use a JSON validator to ensure there are no syntax errors.
- Restart Server: Sometimes, restarting the development server can resolve path-related issues.
npm start
- Ensure Correct Placement:
-
Styles Not Applying Correctly
- Verify Theme Keys: Ensure the theme keys used in the application match those in
themes.json. - Inspect Elements: Use browser developer tools to check if styles are correctly applied based on the selected theme.
- Clear Cache: Perform a hard refresh to eliminate cached data.
- Windows/Linux:
Ctrl + F5 - Mac:
Cmd + Shift + R
- Windows/Linux:
- Verify Theme Keys: Ensure the theme keys used in the application match those in
-
Application Crashes or Fails to Render
- Check Console for Errors: Look for any runtime errors in the browser console.
- Ensure Proper Context Usage: All components relying on
ThemeContextshould be correctly wrapped within the provider. - Validate Theme Data: Ensure all themes have the necessary properties to prevent undefined errors.
By transitioning to JSON-based theme management, the Theme Switcher project has become more scalable and maintainable. This setup allows you to effortlessly add, modify, or remove themes by simply updating the themes.json file without delving into the core application code. Additionally, leveraging React Context ensures seamless propagation of theme data across the component tree, enhancing the application's responsiveness and user experience.
Key Benefits:
- Scalability: Easily manage a growing number of themes.
- Maintainability: Centralized theme definitions simplify updates.
- Flexibility: Supports complex and nested theme properties.
- User Experience: Persistent preferences and real-time customization enhance usability.