diff --git a/CHANGELOG.md b/CHANGELOG.md index b9240c1..2b8385b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,119 @@ # Changelog +## [2.6.0] - 2025-01-21 + +### 🌙 Dark Mode Release + +This release introduces a comprehensive dark mode system with seamless theme switching and component integration. + +### ✨ New Features + +**🎨 Complete Dark Mode System:** +- **Smart Theme Toggle**: Bootstrap-based switch with sun/moon icons and "Light/Dark" labels +- **OS Preference Detection**: Automatically detects and applies user's preferred color scheme +- **Persistent Theme Storage**: Remembers user's theme choice across sessions +- **Instant Theme Switching**: Real-time theme updates without page reload + +**🎯 Theme-Aware Components:** +- **Chart.js Integration**: Dynamic color schemes for all chart types with proper contrast +- **FullCalendar Support**: Dark-mode aware calendar with proper border and text colors +- **Vector Maps**: Custom color palettes for both light and dark themes +- **Google Maps**: Theme-specific styling for landscapes, highways, and POI markers +- **Sparkline Charts**: Optimized color sets for dark mode visibility +- **Skycons Weather Icons**: Adaptive colors for better dark mode contrast + +**🎛️ CSS Architecture:** +- **CSS Custom Properties**: Comprehensive variable system for consistent theming +- **Semantic Color Naming**: Intuitive color variables (--c-text-base, --c-bkg-card, etc.) +- **Component Isolation**: Each component respects global theme variables +- **Responsive Design**: Theme switching works seamlessly across all screen sizes + +**🖼️ Visual Enhancements:** +- **Adaptive Logo**: SVG logo automatically adjusts colors based on theme +- **Smart Contrast**: Proper text/background contrast ratios in both themes +- **Border Consistency**: Unified border colors throughout the interface +- **Loading States**: Theme-aware loaders and progress indicators + +### 🔧 Technical Improvements + +**🏗️ Architecture Updates:** +- **Theme Utility Module**: New `src/assets/scripts/utils/theme.js` with comprehensive theme management +- **CSS Variables File**: New `src/assets/styles/utils/theme.css` with light/dark color schemes +- **Component Integration**: Updated all major components to support theme switching +- **Event System**: Custom events for theme change notifications + +**⚡ Performance Optimizations:** +- **Efficient Switching**: Minimal DOM manipulation for theme changes +- **CSS Variable Updates**: Leverages browser-native CSS custom properties +- **Memory Management**: Proper cleanup of theme-related event listeners +- **Build Integration**: Theme assets are properly bundled and optimized + +### 🎮 User Experience + +**💡 Intuitive Controls:** +- **Accessible Toggle**: Proper ARIA labels and keyboard navigation support +- **Visual Feedback**: Clear indication of current theme state +- **Smooth Transitions**: CSS transitions for theme switching (where appropriate) +- **Consistent Placement**: Theme toggle integrated into header navigation + +**🔄 Smart Behavior:** +- **First-Time Detection**: Respects OS dark mode preference on first visit +- **Cross-Session Persistence**: Theme choice remembered across browser sessions +- **Fallback Handling**: Graceful degradation when localStorage is unavailable +- **Dynamic Updates**: All components update immediately when theme changes + +### 🛠️ Development Experience + +**📝 Documentation:** +- **Theme API**: Comprehensive methods for theme management +- **Color Guidelines**: Standardized color usage across components +- **Component Examples**: Updated examples showing theme-aware components +- **Migration Guide**: Instructions for theme integration in custom components + +### 🔍 Enhanced Components + +**📊 Charts & Data Visualization:** +- Chart.js with dynamic color schemes +- Sparkline charts with theme-optimized colors +- Easy Pie Charts with adaptive styling +- Vector maps with custom dark mode palettes + +**🗓️ Interactive Elements:** +- FullCalendar with proper dark mode borders +- DataTables with theme-consistent styling +- Date pickers with adaptive colors +- Form elements with dark mode support + +**🗺️ Maps & Location:** +- Google Maps with custom dark mode styling +- Vector maps with region-specific color schemes +- Marker and overlay theme integration + +### ⚠️ Breaking Changes + +None. This release is fully backward compatible. + +### 🏁 Migration Guide + +Existing projects will automatically inherit dark mode capabilities. No code changes required. + +**Optional Enhancements:** +- Add `data-theme` attribute handling for custom components +- Use CSS variables from `theme.css` for consistent coloring +- Listen for `adminator:themeChanged` events for custom theme handling + +### 📋 Files Added/Modified + +**New Files:** +- `src/assets/scripts/utils/theme.js` - Theme management utility +- `src/assets/styles/utils/theme.css` - CSS variables and color schemes + +**Enhanced Files:** +- All HTML pages updated with theme-aware components +- Component JavaScript files updated for theme integration +- SCSS files enhanced with CSS variable usage +- Logo SVG updated for theme compatibility + ## [2.5.0] - 2025-06-16 ### 🎉 Major Modernization Release diff --git a/README.md b/README.md index 9145cb3..4b653d4 100755 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -# Adminator Bootstrap 5 Admin Template v2.5.0 +# Adminator Bootstrap 5 Admin Template v2.6.0 **Adminator** is a responsive Bootstrap 5 Admin Template built with modern development tools. It provides you with a collection of ready to use code snippets and utilities, custom pages, a collection of applications and some useful widgets. -✨ **Latest Update (v2.5.0)**: Completely modernized build system with latest dependencies, ESLint 9.x flat config, and enhanced development experience. +✨ **Latest Update (v2.6.0)**: Complete **Dark Mode System** with smart theme switching, OS preference detection, and seamless component integration. + +🌙 **Dark Mode Features**: Automatic theme detection, persistent user preferences, theme-aware components (charts, calendars, maps), and a beautiful toggle switch. Preview of this awesome admin template available here: https://colorlib.com/polygon/adminator/index.html @@ -27,18 +29,31 @@ Preview of this awesome admin template available here: https://colorlib.com/poly - [Authors](#authors) - [License](#license) -## What's New in v2.5.0 +## What's New in v2.6.0 + +🌙 **Dark Mode Release** - Complete dark mode system with seamless theme switching: -🎉 **Major Modernization Release** - Complete overhaul of the development stack: +### 🎨 Dark Mode Features +- **🌗 Smart Theme Toggle**: Bootstrap-based switch with sun/moon icons and intuitive labels +- **🔄 OS Preference Detection**: Automatically detects and applies your preferred color scheme +- **💾 Persistent Storage**: Remembers your theme choice across browser sessions +- **⚡ Instant Switching**: Real-time theme updates without page reload +- **🎯 Component Integration**: All charts, calendars, maps, and UI elements are theme-aware -- **🚀 Latest Dependencies**: All 22+ dependencies updated to latest versions -- **⚡ Modern Build Tools**: webpack 5.99.9, webpack-dev-server 5.2.2 -- **🔧 ESLint 9.x**: Migrated to modern flat config format -- **🎨 Enhanced CSS**: Latest Sass (1.89.2), PostCSS (8.5.5), Bootstrap (5.3.6) -- **📊 Updated Components**: Chart.js 4.5.0, FullCalendar 6.1.17 -- **🛡️ Zero Vulnerabilities**: Complete security audit with all packages secure -- **🔄 Modern Tooling**: babel-loader 10.x, copy-webpack-plugin 13.x, webpack-cli 6.x -- **📱 Enhanced Experience**: Better development server, faster builds, improved linting +### 🛠️ Technical Implementation +- **🎨 CSS Variables Architecture**: Comprehensive color system with semantic naming +- **📊 Chart.js Integration**: Dynamic color schemes for all chart types +- **🗓️ FullCalendar Support**: Dark-mode aware calendar with proper contrast +- **🗺️ Vector Maps**: Custom color palettes for both light and dark themes +- **🎪 Component Compatibility**: Theme support across all interactive elements + +### 🚀 Previous Updates (v2.5.0) +- **Latest Dependencies**: All 22+ dependencies updated to latest versions +- **Modern Build Tools**: webpack 5.99.9, webpack-dev-server 5.2.2 +- **ESLint 9.x**: Migrated to modern flat config format +- **Enhanced CSS**: Latest Sass (1.89.2), PostCSS (8.5.5), Bootstrap (5.3.6) +- **Updated Components**: Chart.js 4.5.0, FullCalendar 6.1.17 +- **Zero Vulnerabilities**: Complete security audit with all packages secure ## Getting Started @@ -101,6 +116,56 @@ npm run lint:scss npm run lint ``` +## 🌙 Dark Mode Usage + +Adminator now includes a comprehensive dark mode system that works out of the box: + +### **Automatic Setup** +- Dark mode is automatically initialized on page load +- Detects your OS preference (light/dark) on first visit +- Remembers your choice across browser sessions + +### **Theme Toggle** +- Look for the **Light/Dark** toggle switch in the header navigation +- Click to instantly switch between light and dark themes +- Visual feedback with sun ☀️ and moon 🌙 icons + +### **For Developers** + +**Using the Theme API:** +```javascript +// Get current theme +const currentTheme = Theme.current(); // 'light' or 'dark' + +// Switch themes programmatically +Theme.toggle(); + +// Set specific theme +Theme.apply('dark'); + +// Listen for theme changes +window.addEventListener('adminator:themeChanged', (event) => { + console.log('Theme changed to:', event.detail.theme); +}); +``` + +**CSS Variables for Custom Styling:** +```css +.my-component { + background: var(--c-bkg-card); + color: var(--c-text-base); + border: 1px solid var(--c-border); +} +``` + +**Available CSS Variables:** +- `--c-bkg-body` - Main background +- `--c-bkg-card` - Card backgrounds +- `--c-text-base` - Primary text color +- `--c-text-muted` - Secondary text color +- `--c-border` - Border colors +- `--c-primary` - Primary brand color + ## Adminator for other platforms and frameworks * [Adminator right to left](https://github.com/mortezakarimi/Adminator-admin-dashboard-rtl) - Adminator modified to work with right to left languages like Persian and Arabic diff --git a/package.json b/package.json index c077f83..865e925 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "adminator", - "version": "2.5.0", + "version": "2.6.0", "private": true, "description": "HTML Admin Template", "scripts": { diff --git a/src/assets/scripts/app.js b/src/assets/scripts/app.js index ec7b205..1359a94 100644 --- a/src/assets/scripts/app.js +++ b/src/assets/scripts/app.js @@ -6,6 +6,7 @@ import bootstrap from 'bootstrap'; import DOM from './utils/dom'; import DateUtils from './utils/date'; +import Theme from './utils/theme'; import Sidebar from './components/Sidebar'; import ChartComponent from './components/Chart'; @@ -50,6 +51,7 @@ class AdminatorApp { this.initCharts(); this.initDataTables(); this.initDatePickers(); + this.initTheme(); // Setup global event listeners this.setupGlobalEvents(); @@ -250,6 +252,49 @@ class AdminatorApp { }); } + /** + * Initialize theme + */ + initTheme() { + Theme.init(); + + // inject toggle switch if missing + const navRight = document.querySelector('.nav-right'); + if (navRight && !document.getElementById('theme-toggle')) { + const li = document.createElement('li'); + li.className = 'theme-toggle d-flex ai-c'; + li.innerHTML = ` +
+ + + +
+ `; + navRight.insertBefore(li, navRight.firstElementChild); + + const toggleInput = li.querySelector('#theme-toggle'); + + const updateSwitch = () => { + toggleInput.checked = Theme.current() === 'dark'; + }; + updateSwitch(); + + toggleInput.addEventListener('change', (e) => { + Theme.apply(e.target.checked ? 'dark' : 'light'); + }); + + window.addEventListener('adminator:themeChanged', () => { + updateSwitch(); + const charts = this.components.get('charts'); + if (charts) charts.redrawCharts(); + }); + } + } + /** * Setup global event listeners */ diff --git a/src/assets/scripts/charts/sparkline/index.js b/src/assets/scripts/charts/sparkline/index.js index 6ef99d3..4023034 100755 --- a/src/assets/scripts/charts/sparkline/index.js +++ b/src/assets/scripts/charts/sparkline/index.js @@ -2,6 +2,7 @@ import * as $ from 'jquery'; import 'jquery-sparkline'; import { debounce } from 'lodash'; import { COLORS } from '../../constants/colors'; +import Theme from '../../utils/theme.js'; export default (function () { // ------------------------------------------------------ @@ -9,6 +10,8 @@ export default (function () { // ------------------------------------------------------ const drawSparklines = () => { + const sparkColors = Theme.getSparklineColors(); + if ($('#sparklinedash').length > 0) { $('#sparklinedash').sparkline([0, 5, 6, 10, 9, 12, 4, 9], { type: 'bar', @@ -16,7 +19,7 @@ export default (function () { barWidth: '3', resize: true, barSpacing: '3', - barColor: '#4caf50', + barColor: sparkColors.success, }); } @@ -27,7 +30,7 @@ export default (function () { barWidth: '3', resize: true, barSpacing: '3', - barColor: '#9675ce', + barColor: sparkColors.purple, }); } @@ -38,7 +41,7 @@ export default (function () { barWidth: '3', resize: true, barSpacing: '3', - barColor: '#03a9f3', + barColor: sparkColors.info, }); } @@ -49,7 +52,7 @@ export default (function () { barWidth: '3', resize: true, barSpacing: '3', - barColor: '#f96262', + barColor: sparkColors.danger, }); } }; @@ -58,6 +61,9 @@ export default (function () { // Redraw sparklines on resize $(window).resize(debounce(drawSparklines, 150)); + + // Listen for theme changes + window.addEventListener('adminator:themeChanged', debounce(drawSparklines, 150)); // ------------------------------------------------------ // @Other Sparklines @@ -77,7 +83,7 @@ export default (function () { { type: 'bar', resize: true, - barColor: '#aaf', + barColor: Theme.getSparklineColors().light, height: '20', } ); diff --git a/src/assets/scripts/components/Chart.js b/src/assets/scripts/components/Chart.js index 214d7cd..c575a1a 100644 --- a/src/assets/scripts/components/Chart.js +++ b/src/assets/scripts/components/Chart.js @@ -249,17 +249,11 @@ class ChartComponent { font: { size: 12, weight: '600' - }, - color: '#333' + } } }, tooltip: { enabled: true, - backgroundColor: 'rgba(255, 255, 255, 0.95)', - titleColor: '#333', - bodyColor: '#666', - borderColor: '#ddd', - borderWidth: 1, cornerRadius: 8, displayColors: true, intersect: false, @@ -277,7 +271,6 @@ class ChartComponent { display: false }, ticks: { - color: '#666', font: { size: 11 } @@ -286,11 +279,9 @@ class ChartComponent { y: { beginAtZero: true, grid: { - color: 'rgba(0, 0, 0, 0.05)', borderDash: [5, 5] }, ticks: { - color: '#666', font: { size: 11 }, @@ -938,16 +929,10 @@ class ChartComponent { size: 12, weight: '600' }, - color: '#333' } }, tooltip: { enabled: true, - backgroundColor: 'rgba(255, 255, 255, 0.95)', - titleColor: '#333', - bodyColor: '#666', - borderColor: '#ddd', - borderWidth: 1, cornerRadius: 8, displayColors: true } @@ -985,7 +970,6 @@ class ChartComponent { } }, grid: { - color: 'rgba(0, 0, 0, 0.1)' } } } @@ -997,21 +981,17 @@ class ChartComponent { scales: { r: { angleLines: { - display: true, - color: 'rgba(0, 0, 0, 0.1)' + display: true }, grid: { - color: 'rgba(0, 0, 0, 0.1)' }, pointLabels: { font: { size: 11 - }, - color: '#666' + } }, ticks: { display: true, - color: '#666', font: { size: 10 } @@ -1028,11 +1008,9 @@ class ChartComponent { type: 'linear', position: 'bottom', grid: { - color: 'rgba(0, 0, 0, 0.05)', borderDash: [5, 5] }, ticks: { - color: '#666', font: { size: 11 } @@ -1041,11 +1019,9 @@ class ChartComponent { y: { beginAtZero: true, grid: { - color: 'rgba(0, 0, 0, 0.05)', borderDash: [5, 5] }, ticks: { - color: '#666', font: { size: 11 } @@ -1073,11 +1049,9 @@ class ChartComponent { type: 'linear', position: 'bottom', grid: { - color: 'rgba(0, 0, 0, 0.05)', borderDash: [5, 5] }, ticks: { - color: '#666', font: { size: 11 } @@ -1085,11 +1059,9 @@ class ChartComponent { }, y: { grid: { - color: 'rgba(0, 0, 0, 0.05)', borderDash: [5, 5] }, ticks: { - color: '#666', font: { size: 11 } @@ -1105,11 +1077,9 @@ class ChartComponent { scales: { x: { grid: { - color: 'rgba(0, 0, 0, 0.05)', borderDash: [5, 5] }, ticks: { - color: '#666', font: { size: 11 } @@ -1118,11 +1088,9 @@ class ChartComponent { y: { beginAtZero: true, grid: { - color: 'rgba(0, 0, 0, 0.05)', borderDash: [5, 5] }, ticks: { - color: '#666', font: { size: 11 } diff --git a/src/assets/scripts/components/Sidebar.js b/src/assets/scripts/components/Sidebar.js index 58d2bf3..de9ac4f 100644 --- a/src/assets/scripts/components/Sidebar.js +++ b/src/assets/scripts/components/Sidebar.js @@ -36,8 +36,13 @@ class Sidebar { const listItem = link.parentElement; const dropdownMenu = listItem.querySelector('.dropdown-menu'); - if (!dropdownMenu) return; + // If this is a regular navigation link (not dropdown), allow normal navigation + if (!dropdownMenu) { + // Don't prevent default for regular navigation links + return; + } + // Only prevent default for dropdown toggles e.preventDefault(); if (listItem.classList.contains('open')) { @@ -109,6 +114,9 @@ class Sidebar { if (dropdownMenu) { this.closeDropdown(item, dropdownMenu); } + + // Also remove the has-active-child class + item.classList.remove('has-active-child'); }); } @@ -161,21 +169,62 @@ class Sidebar { * Set active link based on current URL */ setActiveLink() { - const sidebarLinks = this.sidebar.querySelectorAll('.sidebar-link'); - const currentPath = window.location.pathname.substr(1); + // Remove active class from all nav items (including dropdown items) + const allNavItems = this.sidebar.querySelectorAll('.nav-item'); + allNavItems.forEach(item => { + item.classList.remove('actived'); + }); + + // Close all dropdowns first + this.closeAllDropdowns(); + + // Get current page filename + const currentPath = window.location.pathname; + const currentPage = currentPath.split('/').pop() || 'index.html'; - sidebarLinks.forEach(link => { - link.classList.remove('active'); - + let activeItemFound = false; + + // Find and activate the correct nav item + const allLinks = this.sidebar.querySelectorAll('a[href]'); + + allLinks.forEach(link => { const href = link.getAttribute('href'); - if (!href) return; + if (!href || href === 'javascript:void(0);' || href === 'javascript:void(0)') return; - const pattern = href.startsWith('/') ? href.substr(1) : href; + // Extract filename from href + const linkPage = href.split('/').pop(); - if (pattern === currentPath) { - link.classList.add('active'); + if (linkPage === currentPage) { + const navItem = link.closest('.nav-item'); + if (navItem) { + navItem.classList.add('actived'); + activeItemFound = true; + + // If this is inside a dropdown, handle parent dropdown specially + const parentDropdown = navItem.closest('.dropdown-menu'); + if (parentDropdown) { + const parentDropdownItem = parentDropdown.closest('.nav-item.dropdown'); + if (parentDropdownItem) { + // Open the parent dropdown + parentDropdownItem.classList.add('open'); + parentDropdown.style.display = 'block'; + + // Add special styling to indicate parent has active child + parentDropdownItem.classList.add('has-active-child'); + + console.log('Active dropdown child set for:', currentPage); + } + } else { + console.log('Active navigation set for:', currentPage); + } + } } }); + + // If no specific item was found, log it for debugging + if (!activeItemFound) { + console.log('No matching navigation item found for:', currentPage); + } } /** diff --git a/src/assets/scripts/googleMaps/index.js b/src/assets/scripts/googleMaps/index.js index 0974c6a..8d9ea11 100755 --- a/src/assets/scripts/googleMaps/index.js +++ b/src/assets/scripts/googleMaps/index.js @@ -1,11 +1,15 @@ import * as $ from 'jquery'; import loadGoogleMapsAPI from 'load-google-maps-api'; +import Theme from '../utils/theme.js'; export default (function () { - if ($('#google-map').length > 0) { - loadGoogleMapsAPI({ - key: 'AIzaSyDW8td30_gj6sGXjiMU0ALeMu1SDEwUnEA', - }).then(() => { + let map, marker; + + const initGoogleMap = () => { + if ($('#google-map').length > 0) { + loadGoogleMapsAPI({ + key: 'AIzaSyDW8td30_gj6sGXjiMU0ALeMu1SDEwUnEA', + }).then(() => { const latitude = 26.8206; const longitude = 30.8025; const mapZoom = 5; @@ -18,7 +22,7 @@ export default (function () { styles: [{ 'featureType': 'landscape', 'stylers': [ - { 'hue' : '#FFBB00' }, + { 'hue' : Theme.getCSSVar('--gmap-landscape-hue') }, { 'saturation' : 43.400000000000006 }, { 'lightness' : 37.599999999999994 }, { 'gamma' : 1 }, @@ -26,7 +30,7 @@ export default (function () { }, { 'featureType': 'road.highway', 'stylers': [ - { 'hue' : '#FFC200' }, + { 'hue' : Theme.getCSSVar('--gmap-highway-hue') }, { 'saturation' : -61.8 }, { 'lightness' : 45.599999999999994 }, { 'gamma' : 1 }, @@ -34,7 +38,7 @@ export default (function () { }, { 'featureType': 'road.arterial', 'stylers': [ - { 'hue' : '#FF0300' }, + { 'hue' : Theme.getCSSVar('--gmap-road-hue') }, { 'saturation' : -100 }, { 'lightness' : 51.19999999999999 }, { 'gamma' : 1 }, @@ -42,7 +46,7 @@ export default (function () { }, { 'featureType': 'road.local', 'stylers': [ - { 'hue' : '#FF0300' }, + { 'hue' : Theme.getCSSVar('--gmap-road-hue') }, { 'saturation' : -100 }, { 'lightness' : 52 }, { 'gamma' : 1 }, @@ -50,7 +54,7 @@ export default (function () { }, { 'featureType': 'water', 'stylers': [ - { 'hue' : '#0078FF' }, + { 'hue' : Theme.getCSSVar('--gmap-water-hue') }, { 'saturation' : -13.200000000000003 }, { 'lightness' : 2.4000000000000057 }, { 'gamma' : 1 }, @@ -58,7 +62,7 @@ export default (function () { }, { 'featureType': 'poi', 'stylers': [ - { 'hue' : '#00FF6A' }, + { 'hue' : Theme.getCSSVar('--gmap-poi-hue') }, { 'saturation' : -1.0989010989011234 }, { 'lightness' : 11.200000000000017 }, { 'gamma' : 1 }, @@ -66,13 +70,24 @@ export default (function () { }], }; - const map = new google.maps.Map(document.getElementById('google-map'), mapOptions); + map = new google.maps.Map(document.getElementById('google-map'), mapOptions); - new google.maps.Marker({ - map, - position : new google.maps.LatLng(latitude, longitude), - visible : true, + if (marker) { + marker.setMap(null); + } + + marker = new google.maps.Marker({ + map, + position : new google.maps.LatLng(latitude, longitude), + visible : true, + }); }); - }); - } + } + }; + + // Initialize Google Maps + initGoogleMap(); + + // Listen for theme changes + window.addEventListener('adminator:themeChanged', initGoogleMap); }()) diff --git a/src/assets/scripts/skycons/index.js b/src/assets/scripts/skycons/index.js index f2cce43..541d965 100755 --- a/src/assets/scripts/skycons/index.js +++ b/src/assets/scripts/skycons/index.js @@ -1,33 +1,52 @@ import SkyconsInit from 'skycons'; +import Theme from '../utils/theme.js'; const Skycons = SkyconsInit(window); export default (function () { - const icons = new Skycons({ 'color': '#ff6849' }); - const list = [ - 'clear-day', - 'clear-night', - 'partly-cloudy-day', - 'partly-cloudy-night', - 'cloudy', - 'rain', - 'sleet', - 'snow', - 'wind', - 'fog', - ]; - let i = list.length; + let icons; + + const initSkycons = () => { + const skyconsColor = Theme.getCSSVar('--skycons-color'); + + if (icons) { + icons.pause(); + icons.remove('all'); + } + + icons = new Skycons({ 'color': skyconsColor }); + + const list = [ + 'clear-day', + 'clear-night', + 'partly-cloudy-day', + 'partly-cloudy-night', + 'cloudy', + 'rain', + 'sleet', + 'snow', + 'wind', + 'fog', + ]; + let i = list.length; - while (i--) { - const - weatherType = list[i], - elements = document.getElementsByClassName(weatherType); - let j = elements.length; + while (i--) { + const + weatherType = list[i], + elements = document.getElementsByClassName(weatherType); + let j = elements.length; - while (j--) { - icons.set(elements[j], weatherType); + while (j--) { + icons.set(elements[j], weatherType); + } } - } - icons.play(); + icons.play(); + }; + + // Initialize skycons + initSkycons(); + + // Listen for theme changes + window.addEventListener('adminator:themeChanged', initSkycons); }()); diff --git a/src/assets/scripts/utils/theme.js b/src/assets/scripts/utils/theme.js new file mode 100644 index 0000000..7b17ba6 --- /dev/null +++ b/src/assets/scripts/utils/theme.js @@ -0,0 +1,104 @@ +const THEME_KEY = 'adminator-theme'; + +const Theme = { + apply(theme) { + document.documentElement.setAttribute('data-theme', theme); + if (window.Chart && Chart.defaults) { + const isDark = theme === 'dark'; + const textColor = isDark ? '#FFFFFF' : '#212529'; + const mutedColor = isDark ? '#D1D5DB' : '#6C757D'; + const borderColor = isDark ? '#374151' : '#E2E5E8'; + const gridColor = isDark ? 'rgba(209, 213, 219, 0.15)' : 'rgba(0, 0, 0, 0.05)'; + const tooltipBg = isDark ? '#1F2937' : 'rgba(255, 255, 255, 0.95)'; + + // Set global defaults + Chart.defaults.color = textColor; + Chart.defaults.borderColor = borderColor; + Chart.defaults.backgroundColor = tooltipBg; + + // Set plugin defaults + Chart.defaults.plugins.legend.labels.color = textColor; + Chart.defaults.plugins.tooltip.backgroundColor = tooltipBg; + Chart.defaults.plugins.tooltip.titleColor = textColor; + Chart.defaults.plugins.tooltip.bodyColor = textColor; + Chart.defaults.plugins.tooltip.borderColor = borderColor; + + // Set scale defaults + Chart.defaults.scales.category.ticks.color = mutedColor; + Chart.defaults.scales.category.grid.color = gridColor; + Chart.defaults.scales.linear.ticks.color = mutedColor; + Chart.defaults.scales.linear.grid.color = gridColor; + Chart.defaults.scales.logarithmic.ticks.color = mutedColor; + Chart.defaults.scales.logarithmic.grid.color = gridColor; + Chart.defaults.scales.time.ticks.color = mutedColor; + Chart.defaults.scales.time.grid.color = gridColor; + Chart.defaults.scales.radialLinear.ticks.color = mutedColor; + Chart.defaults.scales.radialLinear.grid.color = gridColor; + Chart.defaults.scales.radialLinear.pointLabels.color = mutedColor; + Chart.defaults.scales.radialLinear.angleLines.color = gridColor; + } + try { + localStorage.setItem(THEME_KEY, theme); + } catch (_) {} + window.dispatchEvent(new CustomEvent('adminator:themeChanged', { detail: { theme } })); + }, + toggle() { + const next = this.current() === 'dark' ? 'light' : 'dark'; + this.apply(next); + }, + current() { + try { + return localStorage.getItem(THEME_KEY) || 'light'; + } catch (_) { + return 'light'; + } + }, + init() { + // Detect OS preference first time + if (!localStorage.getItem(THEME_KEY)) { + const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; + this.apply(prefersDark ? 'dark' : 'light'); + } else { + this.apply(this.current()); + } + }, + getCSSVar(varName) { + return getComputedStyle(document.documentElement).getPropertyValue(varName).trim(); + }, + getVectorMapColors() { + return { + backgroundColor: this.getCSSVar('--vmap-bg-color'), + borderColor: this.getCSSVar('--vmap-border-color'), + regionColor: this.getCSSVar('--vmap-region-color'), + markerFill: this.getCSSVar('--vmap-marker-fill'), + markerStroke: this.getCSSVar('--vmap-marker-stroke'), + hoverColor: this.getCSSVar('--vmap-hover-color'), + selectedColor: this.getCSSVar('--vmap-selected-color'), + scaleStart: this.getCSSVar('--vmap-scale-start'), + scaleEnd: this.getCSSVar('--vmap-scale-end'), + scaleLight: this.getCSSVar('--vmap-scale-light'), + scaleDark: this.getCSSVar('--vmap-scale-dark') + }; + }, + getSparklineColors() { + return { + success: this.getCSSVar('--sparkline-success'), + purple: this.getCSSVar('--sparkline-purple'), + info: this.getCSSVar('--sparkline-info'), + danger: this.getCSSVar('--sparkline-danger'), + light: this.getCSSVar('--sparkline-light') + }; + }, + getChartColors() { + const isDark = this.current() === 'dark'; + return { + textColor: isDark ? '#FFFFFF' : '#212529', + mutedColor: isDark ? '#D1D5DB' : '#6C757D', + borderColor: isDark ? '#374151' : '#E2E5E8', + gridColor: isDark ? 'rgba(209, 213, 219, 0.15)' : 'rgba(0, 0, 0, 0.05)', + tooltipBg: isDark ? '#1F2937' : 'rgba(255, 255, 255, 0.95)' + }; + } +}; + +export default Theme; \ No newline at end of file diff --git a/src/assets/scripts/vectorMaps/index.js b/src/assets/scripts/vectorMaps/index.js index f8d4426..7f19d49 100755 --- a/src/assets/scripts/vectorMaps/index.js +++ b/src/assets/scripts/vectorMaps/index.js @@ -3,6 +3,7 @@ import 'jvectormap'; import 'jvectormap/jquery-jvectormap.css'; import './jquery-jvectormap-world-mill.js'; import { debounce } from 'lodash'; +import Theme from '../utils/theme.js'; export default (function () { const vectorMapInit = () => { @@ -24,25 +25,28 @@ export default (function () { `); + // Get current theme colors + const colors = Theme.getVectorMapColors(); + $('#vmap').vectorMap({ map: 'world_mill', - backgroundColor: '#fff', - borderColor: '#fff', + backgroundColor: colors.backgroundColor, + borderColor: colors.borderColor, borderOpacity: 0.25, borderWidth: 0, - color: '#e6e6e6', + color: colors.regionColor, regionStyle : { initial : { - fill : '#e4ecef', + fill : colors.regionColor, }, }, markerStyle: { initial: { r: 7, - 'fill': '#fff', + 'fill': colors.markerFill, 'fill-opacity':1, - 'stroke': '#000', + 'stroke': colors.markerStroke, 'stroke-width' : 2, 'stroke-opacity': 0.4, }, @@ -73,22 +77,25 @@ export default (function () { 'IN': 200, 'GB': 120, }, - scale: ['#03a9f3', '#02a7f1'], + scale: [colors.scaleStart, colors.scaleEnd], normalizeFunction: 'polynomial', }], }, hoverOpacity: null, normalizeFunction: 'linear', zoomOnScroll: false, - scaleColors: ['#b6d6ff', '#005ace'], - selectedColor: '#c9dfaf', + scaleColors: [colors.scaleLight, colors.scaleDark], + selectedColor: colors.selectedColor, selectedRegions: [], enableZoom: false, - hoverColor: '#fff', + hoverColor: colors.hoverColor, }); } }; vectorMapInit(); $(window).resize(debounce(vectorMapInit, 150)); + + // Listen for theme changes and reinitialize the vector map + window.addEventListener('adminator:themeChanged', debounce(vectorMapInit, 150)); })(); diff --git a/src/assets/static/images/logo.svg b/src/assets/static/images/logo.svg index 9c3a8d8..0ad04f1 100755 --- a/src/assets/static/images/logo.svg +++ b/src/assets/static/images/logo.svg @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index 72eba90..dee00cf 100755 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -3,4 +3,686 @@ @use "bootstrap/scss/bootstrap" as *; @use 'spec/index' as *; @use 'vendor/index' as *; +@import "utils/theme.css"; + +body { + background: var(--c-bkg-body); + color: var(--c-text-base); +} + +.sidebar { + background: var(--c-bkg-sidebar); +} + +.bgc-white { + background: var(--c-bkg-card) !important; +} + +// Dark-mode aware Header & Dropdown -------------------------------- +.header { + background: var(--c-bkg-card); + border-bottom: 1px solid var(--c-border); + + .dropdown-menu { + background: var(--c-bkg-card); + border: 1px solid var(--c-border); + } + + .nav-left > li > a, + .nav-right > li > a { + color: var(--c-text-base); + + &:hover, + &:focus { + color: var(--c-primary); + } + } + + .notifications .counter { + background: var(--c-danger); + color: #fff; + } + + // Theme toggle switch styling + .theme-toggle { + display: flex; + align-items: center; + height: 65px; // Match header height + padding: 0 15px; + + .form-check { + margin: 0; + + .form-check-label { + color: var(--c-text-muted); + font-size: 11px; + font-weight: 500; + text-transform: uppercase; + letter-spacing: 0.5px; + + i { + font-size: 12px; + } + } + + .form-check-input { + width: 2.5rem; + height: 1.25rem; + background-color: var(--c-border); + border: 1px solid var(--c-border); + cursor: pointer; + + &:checked { + background-color: var(--c-primary); + border-color: var(--c-primary); + } + + &:focus { + box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--c-primary) 25%, transparent); + border-color: var(--c-primary); + } + } + } + } +} + +// Tables ------------------------------------------------- +.table { + background: var(--c-bkg-card); + color: var(--c-text-base); + + thead th { + background: var(--c-bkg-card); + color: var(--c-text-base); + border-color: var(--c-border); + } + + tbody { + td, th { + border-color: var(--c-border); + color: var(--c-text-base); + background: var(--c-bkg-card); + } + + tr:nth-child(even) { + td, th { + background: color-mix(in srgb, var(--c-bkg-card) 95%, var(--c-border)); + } + } + } + + tfoot th { + background: var(--c-bkg-card); + color: var(--c-text-base); + border-color: var(--c-border); + } + + // Bootstrap table variants + &.table-striped { + tbody tr:nth-child(odd) { + td, th { + background: var(--c-bkg-card); + } + } + + tbody tr:nth-child(even) { + td, th { + background: color-mix(in srgb, var(--c-bkg-card) 95%, var(--c-border)); + } + } + } + + &.table-hover { + tbody tr:hover { + td, th { + background: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-border)) !important; + color: var(--c-text-base); + } + } + } + + &.table-bordered { + border: 1px solid var(--c-border); + + th, td { + border: 1px solid var(--c-border); + } + } + + // Table head variants + .table-dark { + background: color-mix(in srgb, var(--c-bkg-card) 80%, #000); + + th { + background: color-mix(in srgb, var(--c-bkg-card) 80%, #000); + color: var(--c-text-base); + border-color: var(--c-border); + } + } + + .table-light { + background: var(--c-bkg-card); + + th { + background: var(--c-bkg-card); + color: var(--c-text-base); + border-color: var(--c-border); + } + } +} + +// Forms -------------------------------------------------- +.form-control, +.form-select { + background: var(--c-bkg-card); + color: var(--c-text-base); + border: 1px solid var(--c-border); + + &:focus { + border-color: var(--c-primary); + box-shadow: 0 0 0 0.1rem rgba(75, 124, 243, .25); + } +} + +input::placeholder { + color: var(--c-text-muted); +} + +// Cards -------------------------------------------------- +.card, +.bgc-white.bd, +.bgc-white.bdT, +.bgc-white.bdB { + background: var(--c-bkg-card); + border-color: var(--c-border) !important; + color: var(--c-text-base); +} + +// Alerts ------------------------------------------------- +.alert { + color: var(--c-text-base); + border-color: var(--c-border); + background: color-mix(in srgb, var(--c-bkg-card) 85%, var(--c-border)); + + &.alert-primary { + background: color-mix(in srgb, var(--c-primary) 20%, var(--c-bkg-card)); + border-color: var(--c-primary); + color: var(--c-primary); + } +} + +// Modals ------------------------------------------------- +.modal-content { + background: var(--c-bkg-card); + color: var(--c-text-base); + border: 1px solid var(--c-border); +} + +// Logo colours ------------------------------------------ +.logo img, +.logo-auth { + // Light mode: invert the "A" to make it white (if default is dark) + filter: invert(1) hue-rotate(180deg) brightness(1.2); +} + +[data-theme="dark"] .logo img, +[data-theme="dark"] .logo-auth { + // Dark mode: keep original colors (dark "A" on blue background) + filter: brightness(1) contrast(1); +} + +.logo-text { + color: var(--c-text-base); +} + +// Ensure auth page logos are properly sized +.logo-auth { + max-width: 60px !important; + max-height: 60px !important; + width: auto; + height: auto; +} + +// Generic border utility override ----------------------- +.bd, +.bdT, +.bdB, +.bdL, +.bdR { + border-color: var(--c-border) !important; +} + +// Sidebar logo border ----------------------------------- +.sidebar-logo { + border-color: var(--c-border) !important; +} + +// Grey-100 utility override ----------------------------- +.bgc-grey-100 { background: color-mix(in srgb, var(--c-bkg-body) 90%, #000) !important; } + +// Sales Report widget styling ------------------------------- +.sales-report-header { + background-color: var(--c-primary) !important; + color: #ffffff !important; + + h5, h3, p { + color: #ffffff !important; + } +} + +// Hover background utilities for dark mode ---------------- +[data-theme="dark"] .bgcH-grey-100:hover { + background: color-mix(in srgb, var(--c-bkg-card) 85%, var(--c-border)) !important; +} + +// Sidebar right border ---------------------------------- +.sidebar, +.sidebar-menu { + border-right: 1px solid var(--c-border); +} + +// Dark mode text color overrides for better visibility ------- +[data-theme="dark"] .c-grey-900 { + color: var(--c-text-base) !important; +} + +[data-theme="dark"] .c-grey-800 { + color: var(--c-text-base) !important; +} + +[data-theme="dark"] .c-grey-700 { + color: var(--c-text-muted) !important; +} + +[data-theme="dark"] .c-grey-600 { + color: var(--c-text-muted) !important; +} + +[data-theme="dark"] .text-dark { + color: var(--c-text-base) !important; +} + +// Ensure all headings are theme-aware ---------------------- +h1, h2, h3, h4, h5, h6 { + color: var(--c-text-base); +} + +// Email & Compose dark mode fixes --------------------------- +.email-app { + .email-side-nav { + background: var(--c-bkg-card); + border-color: var(--c-border); + } + + .email-list, + .email-content, + .email-wrapper { + background: var(--c-bkg-card) !important; + color: var(--c-text-base); + } + + .email-list-item { + border-color: var(--c-border) !important; + + &:hover { + background: color-mix(in srgb, var(--c-bkg-card) 85%, var(--c-border)) !important; + } + } +} + +// Badge colors for dark mode --------------------------------- +[data-theme="dark"] .badge { + &.bgc-deep-purple-50 { + background: #8b5cf6 !important; + color: #fff !important; + } + + &.c-deep-purple-700 { + color: #fff !important; + } + + &.bgc-green-50 { + background: var(--c-success) !important; + color: #fff !important; + } + + &.c-green-700 { + color: #fff !important; + } + + &.bgc-blue-50 { + background: var(--c-primary) !important; + color: #fff !important; + } + + &.c-blue-700 { + color: #fff !important; + } + + &.bgc-amber-50 { + background: #f59e0b !important; + color: #000 !important; + } + + &.c-amber-700 { + color: #000 !important; + } + + &.bgc-red-50 { + background: var(--c-danger) !important; + color: #fff !important; + } + + &.c-red-700 { + color: #fff !important; + } +} + +// Email buttons in dark mode --------------------------------- +[data-theme="dark"] .email-app { + .btn.bgc-white { + background: var(--c-bkg-card) !important; + color: var(--c-text-base) !important; + border: 1px solid var(--c-border) !important; + + &:hover { + background: color-mix(in srgb, var(--c-bkg-card) 85%, var(--c-border)) !important; + } + } +} + +// Additional table styling for consistency ---------------- +.table-responsive { + border: 1px solid var(--c-border); + border-radius: 3px; +} + +// Table inside cards should blend seamlessly +.bgc-white .table { + background: var(--c-bkg-card); + border: none; + + thead th { + border-top: none; + } +} + +// Status badges in tables need proper theming +.table .badge { + &.bgc-red-50.c-red-700 { + background: var(--c-danger) !important; + color: #fff !important; + } + + &.bgc-deep-purple-50.c-deep-purple-700 { + background: #8b5cf6 !important; + color: #fff !important; + } + + &.bgc-pink-50.c-pink-700 { + background: #ec4899 !important; + color: #fff !important; + } +} + +// Chat page specific dark mode fixes ---------------------- +[data-theme="dark"] { + // Chat page loader + #loader { + background: var(--c-bkg-body) !important; + } + + // Chat message bubbles - different styling for sent vs received + .ai-fs .pY-3.pX-10.bgc-white { + background: var(--c-bkg-card) !important; + border: 1px solid var(--c-border); + } + + .ai-fe .pY-3.pX-10.bgc-white { + background: var(--c-primary) !important; + border: 1px solid var(--c-primary); + color: white !important; + + small { + color: rgba(255, 255, 255, 0.8) !important; + } + + span { + color: white !important; + } + } + + // Chat status indicators (preserve their semantic colors) + .c-green-500 { + color: var(--c-success) !important; + } + + .c-amber-500 { + color: #f59e0b !important; + } + + .c-red-500 { + color: var(--c-danger) !important; + } + + // Chat typing indicator + .lh-1 i { + color: var(--c-text-muted); + } + + // Chat backgrounds + .bgc-grey-200 { + background: var(--c-bkg-body) !important; + } +} + +// Todo List dark mode fixes --------------------------------- +[data-theme="dark"] { + .list-task { + background: var(--c-bkg-card); + border: 1px solid var(--c-border); + + .list-group-item { + background: var(--c-bkg-card); + border-color: var(--c-border); + color: var(--c-text-base); + + &:first-child { + border-top-color: var(--c-border); + } + + &:last-child { + border-bottom-color: var(--c-border); + } + + .form-label { + color: var(--c-text-base); + } + + // Checkbox styling for dark mode + .checkbox { + input[type="checkbox"] { + &:checked + label::before { + background: var(--c-primary); + border-color: var(--c-primary); + } + + &:focus + label::before { + border-color: var(--c-primary); + box-shadow: 0 0 0 0.2rem rgba(var(--c-primary-rgb), 0.25); + } + } + + label::before { + background: var(--c-bkg-card); + border-color: var(--c-border); + } + } + + // Todo badges + .badge { + &.bg-success { + background: var(--c-success) !important; + color: white !important; + } + + &.bg-danger { + background: var(--c-danger) !important; + color: white !important; + } + + &.bg-warning { + background: #f59e0b !important; + color: #000 !important; + } + + &.bg-info { + background: var(--c-primary) !important; + color: white !important; + } + } + } + } +} + +// Calendar page dark mode fixes ----------------------------- +[data-theme="dark"] { + // Calendar event sidebar + .bgc-white.bd { + background: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + + .bdB { + border-bottom-color: var(--c-border) !important; + } + + // Calendar event items + .peers.ai-c.jc-sb.fxw-nw { + border-bottom-color: var(--c-border) !important; + + .c-grey-900 { + color: var(--c-text-base) !important; + } + + .c-grey-600 { + color: var(--c-text-muted) !important; + } + + .c-grey-700 { + color: var(--c-text-muted) !important; + } + + // Action buttons (edit, delete) + .c-deep-purple-500 { + &:hover.cH-blue-500 { + color: var(--c-primary) !important; + } + } + + .c-red-500 { + &:hover.cH-blue-500 { + color: var(--c-danger) !important; + } + } + } + + // Add event button + .btn-warning { + background: #f59e0b; + border-color: #f59e0b; + color: #000; + + &:hover { + background: #d97706; + border-color: #d97706; + } + } + } + + // Calendar modal + .modal-content { + background: var(--c-bkg-card); + border: 1px solid var(--c-border); + color: var(--c-text-base); + + .modal-body { + .form-label { + color: var(--c-text-base); + } + + .form-control { + background: var(--c-bkg-body); + border-color: var(--c-border); + color: var(--c-text-base); + + &::placeholder { + color: var(--c-text-muted); + } + + &:focus { + background: var(--c-bkg-body); + border-color: var(--c-primary); + box-shadow: 0 0 0 0.2rem rgba(var(--c-primary-rgb), 0.25); + } + } + + .input-group-text { + background: var(--c-bkg-card) !important; + border-color: var(--c-border); + color: var(--c-text-base); + + &.bgc-white { + background: var(--c-bkg-card) !important; + } + } + + .btn-primary { + background: var(--c-primary); + border-color: var(--c-primary); + + &:hover { + background: var(--c-primary-hover); + border-color: var(--c-primary-hover); + } + } + } + } + + // Calendar grid improvements + .fc { + // Calendar day cells + .fc-day { + background: var(--c-bkg-card); + + &:hover { + background: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-border)); + } + } + + // Calendar header + .fc-head { + background: var(--c-bkg-card); + } + + // Weekend styling + .fc-sun, + .fc-sat { + background: color-mix(in srgb, var(--c-bkg-card) 95%, var(--c-border)); + } + + // Other days + .fc-other-month { + .fc-day-number { + color: var(--c-text-muted) !important; + } + } + + // Event hover effects + .fc-event { + &:hover { + opacity: 0.9; + } + } + } +} diff --git a/src/assets/styles/spec/components/forms.scss b/src/assets/styles/spec/components/forms.scss index b62847a..5dedffe 100755 --- a/src/assets/styles/spec/components/forms.scss +++ b/src/assets/styles/spec/components/forms.scss @@ -142,7 +142,7 @@ margin-left: -20px; border: 1px solid #cccccc; border-radius: 50%; - background-color: #fff; + background-color: var(--c-bkg-card); -webkit-transition: border 0.15s ease-in-out; -o-transition: border 0.15s ease-in-out; transition: border 0.15s ease-in-out; @@ -158,7 +158,7 @@ top: 3px; margin-left: -20px; border-radius: 50%; - background-color: #555555; + background-color: var(--c-text-base); -webkit-transform: scale(0, 0); -ms-transform: scale(0, 0); -o-transform: scale(0, 0); @@ -258,3 +258,31 @@ .radio-success input[type="radio"]:checked + label::after { background-color: #5cb85c; } + +// Dark mode specific fixes for birthdate field only +[data-theme="dark"] { + // Date picker specific styling (birthdate field) + .timepicker-input { + .input-group-text { + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + color: var(--c-text-base) !important; + + // Override specific background classes + &.bgc-white { + background-color: var(--c-bkg-card) !important; + } + + // Calendar icon styling + .ti-calendar { + color: var(--c-text-base) !important; + } + } + + .form-control { + &.bdc-grey-200 { + border-color: var(--c-border) !important; + } + } + } +} diff --git a/src/assets/styles/spec/components/loader.scss b/src/assets/styles/spec/components/loader.scss index f97b167..0e9b993 100755 --- a/src/assets/styles/spec/components/loader.scss +++ b/src/assets/styles/spec/components/loader.scss @@ -1,23 +1,26 @@ #loader { transition: all 0.3s ease-in-out; opacity: 1; - display: default; + visibility: visible; + position: fixed; + height: 100vh; + width: 100%; + background: var(--loader-bg); + z-index: 90000; } #loader.fadeOut { opacity: 0; - display: none; + visibility: hidden; } - - .spinner { width: 40px; height: 40px; position: absolute; top: calc(50% - 20px); left: calc(50% - 20px); - background-color: #333; + background-color: var(--spinner-bg); border-radius: 100%; -webkit-animation: sk-scaleout 1.0s infinite ease-in-out; animation: sk-scaleout 1.0s infinite ease-in-out; diff --git a/src/assets/styles/spec/components/progressBar.scss b/src/assets/styles/spec/components/progressBar.scss index 87a8e67..ef2a63e 100755 --- a/src/assets/styles/spec/components/progressBar.scss +++ b/src/assets/styles/spec/components/progressBar.scss @@ -1,6 +1,6 @@ .progress { height: 4px; - background-color: #eaeef3; + background-color: var(--c-border); border-radius: 4px; margin-bottom: 10px; } diff --git a/src/assets/styles/spec/components/sidebar.scss b/src/assets/styles/spec/components/sidebar.scss index 11c4d3f..a5a9ab7 100755 --- a/src/assets/styles/spec/components/sidebar.scss +++ b/src/assets/styles/spec/components/sidebar.scss @@ -458,3 +458,185 @@ } } } + +// Dark mode sidebar improvements - proper active states for both themes +[data-theme="dark"] { + .sidebar { + background-color: var(--c-bkg-card); + + .sidebar-menu { + border-right-color: var(--c-border); + + li { + a { + color: var(--c-text-base); + + &:hover, + &:focus { + color: var(--c-text-base); + background-color: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-primary)); + + .icon-holder { + color: var(--c-primary); + background-color: color-mix(in srgb, var(--c-primary) 10%, transparent); + } + } + } + + &.dropdown { + &.open { + > a { + color: var(--c-text-base); + background-color: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-primary)); + + .icon-holder { + color: var(--c-primary); + background-color: color-mix(in srgb, var(--c-primary) 10%, transparent); + } + } + } + } + + // Active menu item styling for dark mode - lighter shade approach + &.actived { + > a { + color: var(--c-text-base) !important; + background-color: color-mix(in srgb, var(--c-bkg-card) 92%, #ffffff) !important; + + .icon-holder { + color: var(--c-text-base) !important; + background-color: color-mix(in srgb, var(--c-bkg-card) 85%, #ffffff) !important; + } + } + } + + // Styling for dropdown parent when it has an active child - lighter shade approach + &.dropdown.has-active-child { + > a { + color: var(--c-text-base) !important; + background-color: color-mix(in srgb, var(--c-bkg-card) 95%, #ffffff) !important; + + .icon-holder { + color: var(--c-text-base) !important; + background-color: color-mix(in srgb, var(--c-bkg-card) 90%, #ffffff) !important; + } + } + } + } + + // Dropdown menu items + > li { + &.dropdown { + ul.dropdown-menu { + > li { + > a { + color: var(--c-text-muted); + + &:hover, + &:focus { + color: var(--c-text-base); + background-color: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-primary)); + } + } + + &.actived { + a { + color: var(--c-text-base) !important; + background-color: color-mix(in srgb, var(--c-bkg-card) 92%, #ffffff) !important; + } + } + } + } + } + } + } + } + + .sidebar-logo { + border-bottom-color: var(--c-border); + border-right-color: var(--c-border); + + a { + .logo-text { + color: var(--c-text-base); + } + } + + .mobile-toggle { + a { + color: var(--c-text-base); + } + } + } +} + +// Light mode active states (ensure proper visibility) +[data-theme="light"], :root { + .sidebar { + .sidebar-menu { + li { + &.actived { + > a { + background-color: color-mix(in srgb, #ffffff 85%, var(--c-primary)) !important; + color: var(--c-primary) !important; + + .icon-holder { + color: var(--c-primary) !important; + background-color: color-mix(in srgb, var(--c-primary) 10%, transparent) !important; + } + } + } + + // Styling for dropdown parent when it has an active child (light mode) + &.dropdown.has-active-child { + > a { + background-color: color-mix(in srgb, #ffffff 90%, var(--c-primary)) !important; + color: var(--c-primary) !important; + + .icon-holder { + color: var(--c-primary) !important; + background-color: color-mix(in srgb, var(--c-primary) 8%, transparent) !important; + } + } + } + + a { + &:hover, + &:focus { + background-color: color-mix(in srgb, #ffffff 90%, var(--c-primary)); + color: var(--c-primary); + + .icon-holder { + color: var(--c-primary); + background-color: color-mix(in srgb, var(--c-primary) 8%, transparent); + } + } + } + } + + // Dropdown items for light mode + > li { + &.dropdown { + ul.dropdown-menu { + > li { + &.actived { + a { + color: var(--c-primary) !important; + background-color: color-mix(in srgb, #ffffff 85%, var(--c-primary)) !important; + } + } + + > a { + &:hover, + &:focus { + background-color: color-mix(in srgb, #ffffff 90%, var(--c-primary)); + color: var(--c-primary); + } + } + } + } + } + } + } + } +} diff --git a/src/assets/styles/spec/screens/chat.scss b/src/assets/styles/spec/screens/chat.scss index 92a714e..e8cbe90 100755 --- a/src/assets/styles/spec/screens/chat.scss +++ b/src/assets/styles/spec/screens/chat.scss @@ -21,3 +21,127 @@ height: calc(100vh - #{$header-height} - 60px); overflow: auto; } + +// Dark mode chat styles +[data-theme="dark"] { + // Chat sidebar search input + input[name="chatSearch"] { + background: var(--c-bkg-card); + color: var(--c-text-base); + border-color: var(--c-border); + + &::placeholder { + color: var(--c-text-muted); + } + + &:focus { + background: var(--c-bkg-card); + border-color: var(--c-primary); + box-shadow: 0 0 0 0.2rem rgba(var(--c-primary-rgb), 0.25); + } + } + + // Contact list items + .peers.bgc-white { + background: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + + &:hover, + &.bgcH-grey-50:hover { + background: var(--c-bkg-hover) !important; + } + + h6 { + color: var(--c-text-base); + } + + small { + // Status colors remain as they are for visual indication + &.c-grey-500 { + color: var(--c-text-muted) !important; + } + } + } + + // Chat header + .peers.bgc-white { + background: var(--c-bkg-card) !important; + + h6 { + color: var(--c-text-base); + } + + i { + color: var(--c-text-muted); + } + + .c-grey-900 { + color: var(--c-text-base) !important; + + &.cH-blue-500:hover { + color: var(--c-primary) !important; + } + } + } + + // Chat background area + .bgc-grey-200 { + background: var(--c-bkg-body) !important; + } + + .bgc-grey-100 { + background: var(--c-bkg-body) !important; + } + + // Chat messages + .pY-3.pX-10.bgc-white { + background: var(--c-bkg-card) !important; + color: var(--c-text-base); + border: 1px solid var(--c-border); + + small { + color: var(--c-text-muted); + } + + span { + color: var(--c-text-base); + } + } + + // Chat input area + .bdT.bgc-white { + background: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + + .form-control { + background: var(--c-bkg-body); + border-color: var(--c-border); + color: var(--c-text-base); + + &::placeholder { + color: var(--c-text-muted); + } + + &:focus { + background: var(--c-bkg-body); + border-color: var(--c-primary); + color: var(--c-text-base); + box-shadow: 0 0 0 0.2rem rgba(var(--c-primary-rgb), 0.25); + } + } + + .btn-primary { + background: var(--c-primary); + border-color: var(--c-primary); + + &:hover { + background: var(--c-primary-hover); + border-color: var(--c-primary-hover); + } + + &:focus { + box-shadow: 0 0 0 0.2rem rgba(var(--c-primary-rgb), 0.25); + } + } + } +} diff --git a/src/assets/styles/spec/screens/email.scss b/src/assets/styles/spec/screens/email.scss index a885f0a..81a63d5 100755 --- a/src/assets/styles/spec/screens/email.scss +++ b/src/assets/styles/spec/screens/email.scss @@ -4,9 +4,9 @@ .email-app { .email-side-nav { - background: $default-white; + background: var(--c-bkg-card); position: fixed; - border-right: 1px solid $border-color; + border-right: 1px solid var(--c-border); float: left; width: 250px; transition: all 0.3s ease-in-out; @@ -40,7 +40,7 @@ padding: 0; width: 100%; overflow-y: hidden; - background-color: $default-white; + background-color: var(--c-bkg-card); height: calc(100vh - #{$header-height}); @include to($breakpoint-md) { @@ -49,7 +49,7 @@ @include from($breakpoint-md) { width: 40%; - border-right: 1px solid $border-color; + border-right: 1px solid var(--c-border); float: left; } } @@ -59,7 +59,7 @@ width: 60%; position: relative; padding: 0; - background-color: $default-white; + background-color: var(--c-bkg-card); // min-height: calc(100vh - #{$header-height}); &.no-inbox-view { diff --git a/src/assets/styles/utils/theme.css b/src/assets/styles/utils/theme.css new file mode 100644 index 0000000..abc7272 --- /dev/null +++ b/src/assets/styles/utils/theme.css @@ -0,0 +1,97 @@ +:root { + --c-bkg-body: #f8f9fa; + --c-bkg-card: #ffffff; + --c-bkg-sidebar: #ffffff; + + --c-text-base: #212529; + --c-text-muted: #6c757d; + + --c-border: #e2e5e8; + + --c-primary: #4b7cf3; + --c-success: #2ecc71; + --c-danger: #e74c3c; + + /* Vector map colors */ + --vmap-bg-color: #ffffff; + --vmap-border-color: #ffffff; + --vmap-region-color: #e4ecef; + --vmap-marker-fill: #ffffff; + --vmap-marker-stroke: #000000; + --vmap-hover-color: #ffffff; + --vmap-selected-color: #c9dfaf; + --vmap-scale-start: #03a9f3; + --vmap-scale-end: #02a7f1; + --vmap-scale-light: #b6d6ff; + --vmap-scale-dark: #005ace; + + /* Skycons */ + --skycons-color: #ff6849; + + /* Sparkline colors */ + --sparkline-success: #4caf50; + --sparkline-purple: #9675ce; + --sparkline-info: #03a9f3; + --sparkline-danger: #f96262; + --sparkline-light: #aaf; + + /* Loader */ + --loader-bg: #ffffff; + --spinner-bg: #333333; + + /* Google Maps */ + --gmap-landscape-hue: #FFBB00; + --gmap-highway-hue: #FFC200; + --gmap-road-hue: #FF0300; + --gmap-water-hue: #0078FF; + --gmap-poi-hue: #00FF6A; +} + +[data-theme="dark"] { + --c-bkg-body: #181a1f; + --c-bkg-card: #20232a; + --c-bkg-sidebar: #20232a; + + --c-text-base: #e8eaed; + --c-text-muted: #9ca3af; + + --c-border: #2b2f36; + + --c-primary: #4b7cf3; + --c-success: #32d48e; + --c-danger: #ff6b6b; + + /* Vector map colors - dark theme */ + --vmap-bg-color: #20232a; + --vmap-border-color: #2b2f36; + --vmap-region-color: #2b2f36; + --vmap-marker-fill: #e8eaed; + --vmap-marker-stroke: #9ca3af; + --vmap-hover-color: #181a1f; + --vmap-selected-color: #4b5563; + --vmap-scale-start: #3b82f6; + --vmap-scale-end: #1d4ed8; + --vmap-scale-light: #1e40af; + --vmap-scale-dark: #60a5fa; + + /* Skycons - dark theme */ + --skycons-color: #f97316; + + /* Sparkline colors - dark theme */ + --sparkline-success: #10b981; + --sparkline-purple: #a855f7; + --sparkline-info: #3b82f6; + --sparkline-danger: #ef4444; + --sparkline-light: #60a5fa; + + /* Loader - dark theme */ + --loader-bg: #181a1f; + --spinner-bg: #e8eaed; + + /* Google Maps - dark theme */ + --gmap-landscape-hue: #D4AC0D; + --gmap-highway-hue: #E6AC00; + --gmap-road-hue: #CC2900; + --gmap-water-hue: #1B4F72; + --gmap-poi-hue: #148F77; +} \ No newline at end of file diff --git a/src/assets/styles/vendor/fullcalendar.scss b/src/assets/styles/vendor/fullcalendar.scss index abf7de8..ce3d034 100755 --- a/src/assets/styles/vendor/fullcalendar.scss +++ b/src/assets/styles/vendor/fullcalendar.scss @@ -1,54 +1,124 @@ @use '../spec/settings/baseColors' as *; .fc { - background-color: $default-white; - border: 1px solid $border-color; + background-color: var(--c-bkg-card) !important; + border: 1px solid var(--c-border) !important; + color: var(--c-text-base) !important; + + // All table elements within the calendar + table { + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + } th { text-align: center; padding: 15px; - background-color: transparent; - color: $default-text-color; + background-color: var(--c-bkg-card) !important; + color: var(--c-text-base) !important; font-size: 12px; text-transform: uppercase; border-right-width: 0; border-left-width: 0; + border-color: var(--c-border) !important; + } + + td { + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + color: var(--c-text-base) !important; } button { - background-color: $default-white; + background-color: var(--c-bkg-card) !important; background-image: none; height: 37px; padding: 0 15px; - color: darken($default-text-color, 10%); + color: var(--c-text-base) !important; + border-color: var(--c-border) !important; &.fc-state-default { - border-color: $border-color; + border-color: var(--c-border) !important; box-shadow: none; + background-color: var(--c-bkg-card) !important; + color: var(--c-text-base) !important; } &.fc-state-active { box-shadow: none; - background-color: $border-color; + background-color: var(--c-primary) !important; + color: white !important; + border-color: var(--c-primary) !important; + } + + &:hover { + background-color: color-mix(in srgb, var(--c-bkg-card) 85%, var(--c-border)) !important; + border-color: var(--c-border) !important; + color: var(--c-text-base) !important; + } + } + + // Calendar header/toolbar + .fc-head { + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + } + + .fc-head-container { + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + } + + // Calendar body + .fc-body { + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + } + + // Individual day cells + .fc-day { + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + + &:hover { + background-color: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-border)) !important; } } + + // Week cells + .fc-week { + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; + } + + // All grid elements + .fc-widget-header, + .fc-widget-content { + border-color: var(--c-border) !important; + background-color: var(--c-bkg-card) !important; + } } .fc-toolbar { padding: 20px 20px 0; + background-color: var(--c-bkg-card) !important; } .fc-view, .fc-view > table { - background-color: $default-white; + background-color: var(--c-bkg-card) !important; + border-color: var(--c-border) !important; } .fc-basic-view td.fc-day-number, .fc-basic-view td.fc-week-number span { padding: 7px 15px; + color: var(--c-text-base) !important; } .fc-unthemed { + background-color: var(--c-bkg-card) !important; + .fc-content, .fc-divider, .fc-popover, @@ -57,19 +127,43 @@ td, th, thead { - border-color: $border-color; + border-color: var(--c-border) !important; + background-color: var(--c-bkg-card) !important; } .fc-today { - background-color: transparent; + background-color: color-mix(in srgb, var(--c-primary) 10%, var(--c-bkg-card)) !important; + } + + .fc-popover { + background: var(--c-bkg-card) !important; + color: var(--c-text-base) !important; + border-color: var(--c-border) !important; + } + + // Other month dates (faded) + .fc-other-month { + .fc-day-number { + color: var(--c-text-muted) !important; + } + } + + // Weekend styling + .fc-sun, + .fc-sat { + background-color: color-mix(in srgb, var(--c-bkg-card) 95%, var(--c-border)) !important; } } .fc-basic-view { + background-color: var(--c-bkg-card) !important; + .fc-day-number { + color: var(--c-text-base) !important; + &.fc-today { - background-color: $default-info; - color: $default-white; + background-color: var(--c-primary) !important; + color: white !important; display: inline-block; float: right; border-radius: 50%; @@ -78,20 +172,46 @@ margin: 4px 4px 0 0; } } + + .fc-day-header { + background-color: var(--c-bkg-card) !important; + color: var(--c-text-base) !important; + border-color: var(--c-border) !important; + } } .fc-event-container { .fc-event { - border-radius: 0; + border-radius: 3px; border: 0; - background-color: $inverse-info; - color: $default-info !important; + background-color: var(--c-primary) !important; + color: white !important; font-size: 12px; line-height: 2.5; padding: 0 15px; + + &:hover { + background-color: var(--c-primary-hover) !important; + opacity: 0.9; + } } .fc-day-grid-event { margin: 1px 5px 5px; } } + +// Comprehensive border fix for all calendar elements +.fc * { + border-color: var(--c-border) !important; +} + +// Fix for any remaining border inconsistencies +.fc .fc-grid, +.fc .fc-grid table, +.fc .fc-grid tr, +.fc .fc-grid td, +.fc .fc-grid th { + border-color: var(--c-border) !important; + background-color: var(--c-bkg-card) !important; +} diff --git a/src/assets/styles/vendor/jquery.datatables.scss b/src/assets/styles/vendor/jquery.datatables.scss index 847d0a5..d57f3e9 100755 --- a/src/assets/styles/vendor/jquery.datatables.scss +++ b/src/assets/styles/vendor/jquery.datatables.scss @@ -4,10 +4,33 @@ table { &.dataTable { + background: var(--c-bkg-card); + color: var(--c-text-base); + &.no-footer { - border-bottom: 1px solid $border-color; + border-bottom: 1px solid var(--c-border); margin-bottom: 20px; } + + thead th { + background: var(--c-bkg-card); + color: var(--c-text-base); + border-color: var(--c-border); + } + + tbody td { + background: var(--c-bkg-card); + color: var(--c-text-base); + border-color: var(--c-border); + } + + tbody tr:nth-child(even) td { + background: color-mix(in srgb, var(--c-bkg-card) 95%, var(--c-border)); + } + + tbody tr:hover td { + background: color-mix(in srgb, var(--c-bkg-card) 90%, var(--c-border)) !important; + } } } @@ -22,7 +45,7 @@ table { padding-bottom: 5px; .dataTables_length{ - color: $default-dark; + color: var(--c-text-base); float: left; @include to($breakpoint-sm) { @@ -30,7 +53,7 @@ table { } select { - border: 1px solid $border-color; + border: 1px solid var(--c-border); border-radius: 2px; box-shadow: none; height: 35px; @@ -38,13 +61,14 @@ table { padding: 5px; margin-left: 5px; margin-right: 5px; - color: $default-text-color; + color: var(--c-text-base); + background: var(--c-bkg-card); transition: all 0.2s ease-in; } } .dataTables_filter { - color: $default-dark; + color: var(--c-text-base); float: right; @include to($breakpoint-sm) { @@ -52,38 +76,45 @@ table { } input { - border: 1px solid $border-color; + border: 1px solid var(--c-border); border-radius: 2px; box-shadow: none; height: 35px; font-size: 14px; margin-left: 15px; padding: 5px; - color: $default-text-color; + color: var(--c-text-base); + background: var(--c-bkg-card); transition: all 0.2s ease-in; + + &::placeholder { + color: var(--c-text-muted); + } } } .dataTables_info { - color: $default-text-color; + color: var(--c-text-base); float: left; } .dataTables_processing { - color: $default-dark; + color: var(--c-text-base); } .dataTables_paginate { - color: $default-text-color; + color: var(--c-text-base); float: right; .paginate_button { - color: $default-text-color !important; + color: var(--c-text-base) !important; padding: 6px 12px; border-radius: 2px; margin-right: 10px; transition: all 0.2s ease-in-out; text-decoration: none; + background: var(--c-bkg-card); + border: 1px solid var(--c-border); &.next, &.previous, @@ -95,6 +126,7 @@ table { &:hover, &:focus { color: #fff !important; + background: var(--c-primary); } &.disabled { @@ -105,16 +137,18 @@ table { &:hover { color: #fff !important; - background: $default-primary; + background: var(--c-primary); + border-color: var(--c-primary); } &.current { color: #fff !important; - background: $default-primary; + background: var(--c-primary); + border-color: var(--c-primary); &:hover { - color: $default-white !important; - background: $default-primary; + color: #fff !important; + background: var(--c-primary); } } } diff --git a/src/basic-table.html b/src/basic-table.html index 2af53a6..57c89fd 100755 --- a/src/basic-table.html +++ b/src/basic-table.html @@ -120,7 +120,7 @@