mirror of
https://github.com/shadoll/theme-switcher.git
synced 2025-12-20 02:26:12 +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