mirror of
https://github.com/shadoll/theme-switcher.git
synced 2025-12-20 01:26:08 +00:00
initial commit
This commit is contained in:
115
README.md
Normal file
115
README.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# Theme Switcher Demo
|
||||||
|
|
||||||
|
A lightweight, elegant theme switcher for web applications that allows users to toggle between light, dark, and system themes, along with custom accent colors.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Multiple theme options**:
|
||||||
|
- Light mode
|
||||||
|
- Dark mode
|
||||||
|
- System preference detection
|
||||||
|
- **Custom accent color** selection
|
||||||
|
- **Persistent settings** using localStorage
|
||||||
|
- **Responsive design** that works on all screen sizes
|
||||||
|
- **No flash of unstyled content** when loading in dark mode
|
||||||
|
- **Modern UI** with accessible controls
|
||||||
|
- **Smooth transitions** between themes
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
The project includes a demo page with various UI elements to showcase the theme switching capabilities:
|
||||||
|
- Text elements (headings, paragraphs)
|
||||||
|
- Cards with different backgrounds
|
||||||
|
- Buttons
|
||||||
|
- Form elements
|
||||||
|
- Footer
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
### Theme Switching
|
||||||
|
|
||||||
|
The theme switcher allows users to select between three theme options:
|
||||||
|
- **Light**: Forces light theme regardless of system settings
|
||||||
|
- **Dark**: Forces dark theme regardless of system settings
|
||||||
|
- **System**: Automatically follows the user's system preference
|
||||||
|
|
||||||
|
### Accent Color
|
||||||
|
|
||||||
|
Users can customize the accent color used throughout the interface:
|
||||||
|
1. Click on the color preview in the theme menu
|
||||||
|
2. Select a color using the color picker
|
||||||
|
3. The accent color is applied across buttons, headings, and UI accents
|
||||||
|
|
||||||
|
### Persistence
|
||||||
|
|
||||||
|
User preferences are stored in the browser's localStorage, so theme settings persist between visits.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
1. Include the CSS and JavaScript files in your project
|
||||||
|
2. Add the theme toggle HTML markup
|
||||||
|
3. Initialize the theme switcher with JavaScript
|
||||||
|
|
||||||
|
```html
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
<script src="script.js"></script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### Preventing Flash
|
||||||
|
|
||||||
|
To prevent flash of unstyled content when loading in dark mode, the project includes an inline script in the head that sets the theme before the page renders:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<script>
|
||||||
|
// Immediately set theme before page renders
|
||||||
|
const savedTheme = localStorage.getItem('preferred-theme') || 'system';
|
||||||
|
if (savedTheme === 'dark') {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark');
|
||||||
|
} else if (savedTheme === 'system') {
|
||||||
|
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
document.documentElement.setAttribute('data-theme', prefersDarkScheme ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSS Variables
|
||||||
|
|
||||||
|
The theme switcher uses CSS custom properties (variables) to define theme colors:
|
||||||
|
|
||||||
|
```css
|
||||||
|
:root {
|
||||||
|
--background-color: #ffffff;
|
||||||
|
--text-color: #333333;
|
||||||
|
--accent-color: #4a90e2;
|
||||||
|
/* more variables... */
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--background-color: #1a1a1a;
|
||||||
|
--text-color: #f5f5f5;
|
||||||
|
/* more variables... */
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Browser Support
|
||||||
|
|
||||||
|
This theme switcher works in all modern browsers that support:
|
||||||
|
- CSS Custom Properties (variables)
|
||||||
|
- localStorage API
|
||||||
|
- matchMedia API
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This project is available for use under the MIT License.
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
Created by sHa.
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
1. Clone the repository
|
||||||
|
2. Open index.html in your browser
|
||||||
|
3. Try switching between themes and selecting custom accent colors
|
||||||
95
index.html
Normal file
95
index.html
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Theme Switcher Demo</title>
|
||||||
|
<script>
|
||||||
|
// Immediately set theme before page renders
|
||||||
|
const savedTheme = localStorage.getItem('preferred-theme') || 'system';
|
||||||
|
if (savedTheme === 'dark') {
|
||||||
|
document.documentElement.setAttribute('data-theme', 'dark');
|
||||||
|
} else if (savedTheme === 'system') {
|
||||||
|
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
document.documentElement.setAttribute('data-theme', prefersDarkScheme ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<link rel="stylesheet" href="styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>Theme Switcher Demo</h1>
|
||||||
|
<div class="theme-controls">
|
||||||
|
<button id="theme-menu-toggle" class="theme-menu-toggle" aria-label="Theme settings">
|
||||||
|
<img id="toggle-icon" src="system.svg" alt="Theme settings" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div id="theme-menu" class="theme-menu">
|
||||||
|
<div class="menu-section">
|
||||||
|
<h4>Theme</h4>
|
||||||
|
<div class="theme-button-group">
|
||||||
|
<button class="theme-button" data-theme="light" aria-label="Light theme">
|
||||||
|
<img src="sun.svg" alt="Light theme" class="theme-icon" />
|
||||||
|
<span>Light</span>
|
||||||
|
</button>
|
||||||
|
<button class="theme-button" data-theme="dark" aria-label="Dark theme">
|
||||||
|
<img src="moon.svg" alt="Dark theme" class="theme-icon" />
|
||||||
|
<span>Dark</span>
|
||||||
|
</button>
|
||||||
|
<button class="theme-button" data-theme="system" aria-label="System theme">
|
||||||
|
<img src="system.svg" alt="System theme" class="theme-icon" />
|
||||||
|
<span>System</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="menu-section">
|
||||||
|
<h4>Accent Color</h4>
|
||||||
|
<div class="accent-color-control">
|
||||||
|
<input type="color" id="accent-color" value="#4a90e2">
|
||||||
|
<span class="accent-color-preview" id="accent-color-preview"></span>
|
||||||
|
<label for="accent-color">Select accent color</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<section class="content">
|
||||||
|
<h2>Welcome to the Theme Switcher</h2>
|
||||||
|
<p>This is a demonstration of a theme switcher that allows you to choose between light, dark, or system theme preferences.</p>
|
||||||
|
<p>Your theme preference will be stored in local storage and remembered when you return.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="demo-elements">
|
||||||
|
<h3>Demo Elements</h3>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<h4>Sample Card</h4>
|
||||||
|
<p>This is a sample card element to demonstrate theming capabilities.</p>
|
||||||
|
<button class="btn">Sample Button</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="demo-form">
|
||||||
|
<h4>Sample Form</h4>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Name:</label>
|
||||||
|
<input type="text" id="name" placeholder="Enter your name">
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">Email:</label>
|
||||||
|
<input type="email" id="email" placeholder="Enter your email">
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn">Submit</button>
|
||||||
|
</form>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>Developed by sHa. Theme Switcher Demo</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="script.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
5
moon.svg
Normal file
5
moon.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
|
||||||
|
<path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 356 B |
138
script.js
Normal file
138
script.js
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const themeMenuToggle = document.getElementById('theme-menu-toggle');
|
||||||
|
const toggleIcon = document.getElementById('toggle-icon');
|
||||||
|
const themeMenu = document.getElementById('theme-menu');
|
||||||
|
const themeButtons = document.querySelectorAll('.theme-button');
|
||||||
|
const accentColorInput = document.getElementById('accent-color');
|
||||||
|
const accentColorPreview = document.getElementById('accent-color-preview');
|
||||||
|
|
||||||
|
console.log('Theme switcher initialized');
|
||||||
|
|
||||||
|
// Function to update toggle icon based on theme
|
||||||
|
const updateToggleIcon = (theme) => {
|
||||||
|
const iconPath = theme === 'light' ? 'sun.svg' :
|
||||||
|
theme === 'dark' ? 'moon.svg' : 'system.svg';
|
||||||
|
toggleIcon.src = iconPath;
|
||||||
|
console.log(`Updated toggle icon to: ${iconPath}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to set theme
|
||||||
|
const setTheme = (theme) => {
|
||||||
|
console.log(`Setting theme preference to: ${theme}`);
|
||||||
|
let appliedTheme;
|
||||||
|
|
||||||
|
if (theme === 'system') {
|
||||||
|
// Check system preference
|
||||||
|
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
appliedTheme = prefersDarkScheme ? 'dark' : 'light';
|
||||||
|
console.log(`System preference detected: ${appliedTheme}`);
|
||||||
|
} else {
|
||||||
|
// Set specific theme
|
||||||
|
appliedTheme = theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the determined theme
|
||||||
|
document.documentElement.setAttribute('data-theme', appliedTheme);
|
||||||
|
|
||||||
|
// Update toggle icon to reflect current theme
|
||||||
|
updateToggleIcon(theme);
|
||||||
|
|
||||||
|
// Update active state in button group
|
||||||
|
themeButtons.forEach(button => {
|
||||||
|
if (button.getAttribute('data-theme') === theme) {
|
||||||
|
button.classList.add('active');
|
||||||
|
|
||||||
|
// Apply accent color dynamically to active button
|
||||||
|
button.style.backgroundColor = getComputedStyle(document.documentElement).getPropertyValue('--accent-color');
|
||||||
|
} else {
|
||||||
|
button.classList.remove('active');
|
||||||
|
button.style.backgroundColor = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Applied theme: ${appliedTheme}`);
|
||||||
|
|
||||||
|
// Save to localStorage
|
||||||
|
localStorage.setItem('preferred-theme', theme);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to set accent color
|
||||||
|
const setAccentColor = (color) => {
|
||||||
|
console.log(`Setting accent color to: ${color}`);
|
||||||
|
document.documentElement.style.setProperty('--accent-color', color);
|
||||||
|
|
||||||
|
// Convert hex to RGB for the accent-color-rgb variable
|
||||||
|
const r = parseInt(color.slice(1, 3), 16);
|
||||||
|
const g = parseInt(color.slice(3, 5), 16);
|
||||||
|
const b = parseInt(color.slice(5, 7), 16);
|
||||||
|
document.documentElement.style.setProperty('--accent-color-rgb', `${r}, ${g}, ${b}`);
|
||||||
|
|
||||||
|
// Update active button with new accent color
|
||||||
|
const activeButton = document.querySelector('.theme-button.active');
|
||||||
|
if (activeButton) {
|
||||||
|
activeButton.style.backgroundColor = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
accentColorInput.value = color;
|
||||||
|
accentColorPreview.style.backgroundColor = color;
|
||||||
|
localStorage.setItem('accent-color', color);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Toggle theme menu
|
||||||
|
themeMenuToggle.addEventListener('click', (e) => {
|
||||||
|
themeMenu.classList.toggle('active');
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close menu when clicking outside
|
||||||
|
document.addEventListener('click', (e) => {
|
||||||
|
if (!themeMenu.contains(e.target) && e.target !== themeMenuToggle) {
|
||||||
|
themeMenu.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Theme button selection
|
||||||
|
themeButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
const theme = button.getAttribute('data-theme');
|
||||||
|
console.log(`Theme changed by user to: ${theme}`);
|
||||||
|
setTheme(theme);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Accent color preview click opens color picker
|
||||||
|
accentColorPreview.addEventListener('click', () => {
|
||||||
|
accentColorInput.click();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Event listener for accent color change
|
||||||
|
accentColorInput.addEventListener('change', (e) => {
|
||||||
|
console.log(`Accent color changed by user to: ${e.target.value}`);
|
||||||
|
setAccentColor(e.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load saved theme or use system default
|
||||||
|
const savedTheme = localStorage.getItem('preferred-theme') || 'system';
|
||||||
|
console.log(`Loading saved theme from localStorage: ${savedTheme}`);
|
||||||
|
setTheme(savedTheme);
|
||||||
|
|
||||||
|
// Load saved accent color or use default
|
||||||
|
const savedAccentColor = localStorage.getItem('accent-color') || '#4a90e2';
|
||||||
|
console.log(`Loading saved accent color from localStorage: ${savedAccentColor}`);
|
||||||
|
setAccentColor(savedAccentColor);
|
||||||
|
|
||||||
|
// Listen for system theme changes when in "system" mode
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
|
||||||
|
console.log(`System theme preference changed. Dark mode: ${e.matches}`);
|
||||||
|
const currentTheme = localStorage.getItem('preferred-theme');
|
||||||
|
if (currentTheme === 'system') {
|
||||||
|
setTheme('system');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Enable transitions after initial load (prevents flash of white)
|
||||||
|
setTimeout(() => {
|
||||||
|
document.documentElement.classList.add('transitions-enabled');
|
||||||
|
console.log('Theme transitions enabled');
|
||||||
|
}, 100);
|
||||||
|
});
|
||||||
555
styles.css
Normal file
555
styles.css
Normal file
@@ -0,0 +1,555 @@
|
|||||||
|
html {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
/* Light theme variables (default) */
|
||||||
|
--background-color: #ffffff;
|
||||||
|
--text-color: #333333;
|
||||||
|
--text-invert-color: #f5f5f5;
|
||||||
|
--header-bg: #f5f5f5;
|
||||||
|
--footer-bg: #f5f5f5;
|
||||||
|
--card-bg: #ffffff;
|
||||||
|
--border-color: #dddddd;
|
||||||
|
--accent-color: #4a90e2; /* Default accent color */
|
||||||
|
--accent-color-rgb: 74, 144, 226; /* RGB values of accent color */
|
||||||
|
--button-bg: var(--accent-color);
|
||||||
|
--button-text: #ffffff;
|
||||||
|
--input-bg: #ffffff;
|
||||||
|
--input-border: #cccccc;
|
||||||
|
--form-bg: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark theme variables */
|
||||||
|
[data-theme="dark"] {
|
||||||
|
--background-color: #1a1a1a;
|
||||||
|
--text-color: #f5f5f5;
|
||||||
|
--text-invert-color: #33333;
|
||||||
|
--header-bg: #2c2c2c;
|
||||||
|
--footer-bg: #2c2c2c;
|
||||||
|
--card-bg: #2c2c2c;
|
||||||
|
--border-color: #444444;
|
||||||
|
--accent-color: #4a90e2; /* Default accent color */
|
||||||
|
--accent-color-rgb: 74, 144, 226; /* RGB values of accent color */
|
||||||
|
--button-bg: var(--accent-color);
|
||||||
|
--button-text: #ffffff;
|
||||||
|
--input-bg: #333333;
|
||||||
|
--input-border: #555555;
|
||||||
|
--form-bg: #252525;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add a class to re-enable transitions after page load */
|
||||||
|
html.transitions-enabled {
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global styles */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Global layout structure - using grid */
|
||||||
|
body {
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
color: var(--text-color);
|
||||||
|
line-height: 1.6;
|
||||||
|
transition: background-color 0.3s ease, color 0.3s ease;
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas:
|
||||||
|
"header"
|
||||||
|
"main"
|
||||||
|
"footer";
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
grid-area: header;
|
||||||
|
background-color: var(--header-bg);
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid var(--border-color);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-controls {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-menu-toggle {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 8px;
|
||||||
|
transition: background-color 0.3s, box-shadow 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-menu-toggle:hover {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-menu-toggle svg {
|
||||||
|
fill: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-menu-toggle img,
|
||||||
|
.theme-button img {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
fill: var(--accent-color);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-menu-toggle img,
|
||||||
|
.theme-button img {
|
||||||
|
filter: invert(50%) sepia(50%) saturate(1000%) hue-rotate(190deg) brightness(90%) contrast(95%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-button.active img {
|
||||||
|
filter: invert(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 8px);
|
||||||
|
right: 0;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||||
|
width: 320px;
|
||||||
|
padding: 20px;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
transition: opacity 0.3s, transform 0.3s, visibility 0s linear 0.3s;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-menu.active {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transform: translateY(0);
|
||||||
|
transition: opacity 0.3s, transform 0.3s, visibility 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-section {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-section:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-section h4 {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 14px;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-button-group {
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--background-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px 12px; /* Slightly more vertical padding */
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
color: var(--text-color);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-button svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
fill: var(--text-color);
|
||||||
|
transition: fill 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Active button uses accent color */
|
||||||
|
.theme-button.active {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override active button styling */
|
||||||
|
.theme-button.active {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-button.active svg {
|
||||||
|
fill: white; /* White icon when button is active */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Theme-specific icon styling for better visual cues */
|
||||||
|
.theme-button[data-theme="light"] svg {
|
||||||
|
color: #f9a825; /* Sunny yellow for light theme */
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-button[data-theme="dark"] svg {
|
||||||
|
color: #9fa8da; /* Soft blue for dark theme */
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-button[data-theme="system"] svg {
|
||||||
|
color: #78909c; /* Neutral blue-grey for system */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Override fill when active */
|
||||||
|
.theme-button.active[data-theme="light"] svg,
|
||||||
|
.theme-button.active[data-theme="dark"] svg,
|
||||||
|
.theme-button.active[data-theme="system"] svg {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accent Color Styles */
|
||||||
|
.accent-color-control {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
width: 100%; /* Ensure full width */
|
||||||
|
}
|
||||||
|
|
||||||
|
#accent-color {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-color-preview {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
border: 2px solid var(--border-color);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-color-preview:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.accent-color-control label {
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main content styles - using grid */
|
||||||
|
main {
|
||||||
|
grid-area: main;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 2rem auto;
|
||||||
|
padding: 0 2rem;
|
||||||
|
width: 100%;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For wider screens, use two columns for content sections */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
main {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make some sections span full width */
|
||||||
|
.content {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Section styles - using flexbox */
|
||||||
|
section {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--accent-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Card styles - using flexbox */
|
||||||
|
.card {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Button styles */
|
||||||
|
.btn {
|
||||||
|
background-color: var(--button-bg);
|
||||||
|
color: var(--button-text);
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: var(--button-bg);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Demo form - using grid */
|
||||||
|
.demo-form {
|
||||||
|
background-color: var(--form-bg);
|
||||||
|
padding: 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 576px) {
|
||||||
|
.demo-form {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
grid-column: span 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-form h4 {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-form button {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
justify-self: start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form group styles - using flexbox */
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border: 1px solid var(--input-border);
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: var(--input-bg);
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer styles - using flexbox */
|
||||||
|
footer {
|
||||||
|
grid-area: footer;
|
||||||
|
background-color: var(--footer-bg);
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
border-top: 1px solid var(--border-color);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer p {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Theme Dropdown Styles */
|
||||||
|
.theme-dropdown {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle {
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 8px;
|
||||||
|
transition: background-color 0.3s, box-shadow 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle:hover {
|
||||||
|
background-color: var(--background-color);
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-icon svg {
|
||||||
|
fill: var(--text-color);
|
||||||
|
transition: fill 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide all icons by default */
|
||||||
|
.theme-toggle .theme-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show only the current theme icon */
|
||||||
|
[data-theme="light"] .theme-toggle .light-icon,
|
||||||
|
.theme-toggle .light-icon {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .theme-toggle .light-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .theme-toggle .dark-icon {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="system"] .theme-toggle .light-icon,
|
||||||
|
[data-theme="system"] .theme-toggle .dark-icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="system"] .theme-toggle .system-icon {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-button[data-theme="dark"]{
|
||||||
|
color: var(--text-invert-color) !important;
|
||||||
|
}
|
||||||
|
[data-theme="light"] .theme-button[data-theme="dark"]>svg {
|
||||||
|
fill: var(--text-invert-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-menu {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 8px);
|
||||||
|
right: 0;
|
||||||
|
background-color: var(--card-bg);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
transform: translateY(-10px);
|
||||||
|
transition: opacity 0.3s, transform 0.3s, visibility 0s linear 0.3s;
|
||||||
|
z-index: 100;
|
||||||
|
color: var(--text-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-dropdown.active .theme-menu {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
transform: translateY(0);
|
||||||
|
transition: opacity 0.3s, transform 0.3s, visibility 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clean slate for theme option styling */
|
||||||
|
.theme-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
|
||||||
|
/* Force the text color to match the parent document theme */
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make icons match text color */
|
||||||
|
.theme-option .theme-icon svg {
|
||||||
|
fill: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover states */
|
||||||
|
.theme-option:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .theme-option:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Active state styling */
|
||||||
|
.theme-option.active {
|
||||||
|
background-color: var(--accent-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-option.active,
|
||||||
|
.theme-option.active .theme-icon svg {
|
||||||
|
/* Force white for active option regardless of theme */
|
||||||
|
color: white;
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
5
sun.svg
Normal file
5
sun.svg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
|
||||||
|
<path fill="none" d="M0 0h24v24H0z" />
|
||||||
|
<path
|
||||||
|
d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 522 B |
11
system.svg
Normal file
11
system.svg
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
|
||||||
|
<rect x="0" y="-3.059" width="26.969" height="26.969" style="fill:none;" />
|
||||||
|
<rect x="12.361" y="19.415" width="2.247" height="3.371" />
|
||||||
|
<path d="M3.95,2.48l1.589,-1.589l2.383,2.383l-1.589,1.589l-2.383,-2.383Z" />
|
||||||
|
<path d="M19.047,17.577l1.589,-1.589l2.384,2.384l-1.589,1.588l-2.384,-2.383Z" />
|
||||||
|
<path d="M6.333,15.988l1.589,1.589l-2.383,2.383l-1.589,-1.588l2.383,-2.384Z" />
|
||||||
|
<rect x="1.124" y="9.302" width="3.371" height="2.247" />
|
||||||
|
<path
|
||||||
|
d="M12.158,7.063c0,-0 0,0 0,0c0,2.586 2.128,4.714 4.713,4.714c1.267,-0 2.481,-0.511 3.367,-1.415l-0,0.067c-0,3.719 -3.015,6.734 -6.733,6.734c-3.719,-0 -6.734,-3.015 -6.734,-6.734c0,-3.718 3.015,-6.733 6.734,-6.733l0.067,0c-0.906,0.885 -1.416,2.1 -1.414,3.367Zm-4.04,3.366c0,0.001 0,0.002 0,0.003c0,2.955 2.432,5.386 5.387,5.386c1.988,0 3.821,-1.101 4.755,-2.856c-0.456,0.108 -0.922,0.162 -1.39,0.162c-3.324,-0 -6.06,-2.736 -6.06,-6.06c0,-0.468 0.054,-0.934 0.162,-1.39c-1.755,0.935 -2.854,2.768 -2.854,4.755Z"
|
||||||
|
style="fill-rule:nonzero;" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
Reference in New Issue
Block a user