"Update login, new job, and settings templates with improved UI elements (feat)"
This commit is contained in:
@@ -400,7 +400,24 @@
|
||||
|
||||
// Sammle alle Einstellungen und speichere sie
|
||||
async function saveAllSettings() {
|
||||
const saveButton = document.getElementById('save-security-settings');
|
||||
const originalButtonText = saveButton.innerHTML;
|
||||
|
||||
try {
|
||||
// Show loading state
|
||||
saveButton.disabled = true;
|
||||
saveButton.innerHTML = `
|
||||
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white inline" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Speichern...
|
||||
`;
|
||||
|
||||
// Add loading state to settings cards
|
||||
const settingsCards = document.querySelectorAll('.glass-card');
|
||||
settingsCards.forEach(card => card.classList.add('settings-loading'));
|
||||
|
||||
// Erscheinungsbild-Einstellungen
|
||||
const theme = localStorage.getItem(STORAGE_KEY) === 'true' ? 'dark' :
|
||||
localStorage.getItem(STORAGE_KEY) === 'false' ? 'light' : 'system';
|
||||
@@ -418,6 +435,11 @@
|
||||
const twoFactor = document.getElementById('two-factor').checked;
|
||||
const autoLogout = document.getElementById('auto-logout').value;
|
||||
|
||||
// Validate settings
|
||||
if (!validateSettings({ theme, contrast, autoLogout })) {
|
||||
throw new Error('Ungültige Einstellungen erkannt');
|
||||
}
|
||||
|
||||
// Einstellungsobjekt erstellen
|
||||
const settings = {
|
||||
theme: theme,
|
||||
@@ -433,11 +455,19 @@
|
||||
activity_logs: activityLogs,
|
||||
two_factor: twoFactor,
|
||||
auto_logout: autoLogout
|
||||
}
|
||||
},
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
|
||||
// Apply reduced motion immediately if changed
|
||||
if (reducedMotion) {
|
||||
document.documentElement.style.setProperty('--tw-transition-duration', '0s');
|
||||
} else {
|
||||
document.documentElement.style.removeProperty('--tw-transition-duration');
|
||||
}
|
||||
|
||||
// Einstellungen an den Server senden
|
||||
const response = await fetch('/user/update-settings', {
|
||||
const response = await fetch('/api/user/settings', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -449,123 +479,484 @@
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
// Success animation
|
||||
settingsCards.forEach(card => {
|
||||
card.classList.add('settings-saved');
|
||||
setTimeout(() => card.classList.remove('settings-saved'), 600);
|
||||
});
|
||||
|
||||
showFlashMessage('Alle Einstellungen wurden erfolgreich gespeichert', 'success');
|
||||
|
||||
// Cache settings locally for faster access
|
||||
localStorage.setItem('myp-settings-cache', JSON.stringify(settings));
|
||||
|
||||
} else {
|
||||
throw new Error(result.error || 'Unbekannter Fehler');
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern der Einstellungen:', error);
|
||||
showFlashMessage('Fehler beim Speichern der Einstellungen: ' + error.message, 'error');
|
||||
} finally {
|
||||
// Restore button and remove loading states
|
||||
saveButton.disabled = false;
|
||||
saveButton.innerHTML = originalButtonText;
|
||||
|
||||
const settingsCards = document.querySelectorAll('.glass-card');
|
||||
settingsCards.forEach(card => card.classList.remove('settings-loading'));
|
||||
}
|
||||
}
|
||||
|
||||
// Einstellungen beim Laden der Seite abrufen
|
||||
// Validate settings before saving
|
||||
function validateSettings(settings) {
|
||||
const validThemes = ['light', 'dark', 'system'];
|
||||
const validContrast = ['normal', 'high'];
|
||||
const validLogoutValues = ['never', '30', '60', '120', '480'];
|
||||
|
||||
return validThemes.includes(settings.theme) &&
|
||||
validContrast.includes(settings.contrast) &&
|
||||
validLogoutValues.includes(settings.autoLogout);
|
||||
}
|
||||
|
||||
// Enhanced settings loading with caching
|
||||
async function loadUserSettings() {
|
||||
try {
|
||||
// Try to load from cache first for better performance
|
||||
const cachedSettings = localStorage.getItem('myp-settings-cache');
|
||||
if (cachedSettings) {
|
||||
try {
|
||||
const cached = JSON.parse(cachedSettings);
|
||||
// Use cached settings if they're less than 5 minutes old
|
||||
if (new Date() - new Date(cached.timestamp) < 5 * 60 * 1000) {
|
||||
applySettings(cached);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
localStorage.removeItem('myp-settings-cache');
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch('/api/user/settings');
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
const settings = result.settings;
|
||||
applySettings(settings);
|
||||
|
||||
// Theme-Einstellungen anwenden
|
||||
if (settings.theme === 'dark') {
|
||||
localStorage.setItem(STORAGE_KEY, 'true');
|
||||
document.documentElement.classList.add('dark');
|
||||
setActiveThemeButton(darkThemeBtn);
|
||||
} else if (settings.theme === 'light') {
|
||||
localStorage.setItem(STORAGE_KEY, 'false');
|
||||
document.documentElement.classList.remove('dark');
|
||||
setActiveThemeButton(lightThemeBtn);
|
||||
} else {
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
setActiveThemeButton(systemThemeBtn);
|
||||
}
|
||||
|
||||
// Weitere Einstellungen anwenden
|
||||
document.getElementById('reduced-motion').checked = settings.reduced_motion;
|
||||
document.getElementById('notify-new-jobs').checked = settings.notifications.new_jobs;
|
||||
document.getElementById('notify-job-updates').checked = settings.notifications.job_updates;
|
||||
document.getElementById('notify-system').checked = settings.notifications.system;
|
||||
document.getElementById('notify-email').checked = settings.notifications.email;
|
||||
document.getElementById('activity-logs').checked = settings.privacy.activity_logs;
|
||||
document.getElementById('two-factor').checked = settings.privacy.two_factor;
|
||||
document.getElementById('auto-logout').value = settings.privacy.auto_logout;
|
||||
|
||||
// Kontrast-Einstellungen
|
||||
if (settings.contrast === 'high') {
|
||||
localStorage.setItem('myp-contrast', 'high');
|
||||
document.documentElement.classList.add('high-contrast');
|
||||
setActiveContrastButton(highContrastBtn);
|
||||
} else {
|
||||
localStorage.setItem('myp-contrast', 'normal');
|
||||
document.documentElement.classList.remove('high-contrast');
|
||||
setActiveContrastButton(normalContrastBtn);
|
||||
}
|
||||
// Cache the loaded settings
|
||||
settings.timestamp = new Date().toISOString();
|
||||
localStorage.setItem('myp-settings-cache', JSON.stringify(settings));
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Einstellungen:', error);
|
||||
// Use fallback defaults if loading fails
|
||||
applyDefaultSettings();
|
||||
}
|
||||
}
|
||||
|
||||
// Einstellungen beim Laden der Seite abrufen
|
||||
loadUserSettings();
|
||||
function applySettings(settings) {
|
||||
// Theme-Einstellungen anwenden
|
||||
if (settings.theme === 'dark') {
|
||||
localStorage.setItem(STORAGE_KEY, 'true');
|
||||
document.documentElement.classList.add('dark');
|
||||
setActiveThemeButton(darkThemeBtn);
|
||||
} else if (settings.theme === 'light') {
|
||||
localStorage.setItem(STORAGE_KEY, 'false');
|
||||
document.documentElement.classList.remove('dark');
|
||||
setActiveThemeButton(lightThemeBtn);
|
||||
} else {
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
setActiveThemeButton(systemThemeBtn);
|
||||
}
|
||||
|
||||
// Apply reduced motion setting
|
||||
if (settings.reduced_motion) {
|
||||
document.documentElement.style.setProperty('--tw-transition-duration', '0s');
|
||||
document.getElementById('reduced-motion').checked = true;
|
||||
} else {
|
||||
document.documentElement.style.removeProperty('--tw-transition-duration');
|
||||
document.getElementById('reduced-motion').checked = false;
|
||||
}
|
||||
|
||||
// Weitere Einstellungen anwenden
|
||||
document.getElementById('notify-new-jobs').checked = settings.notifications?.new_jobs ?? true;
|
||||
document.getElementById('notify-job-updates').checked = settings.notifications?.job_updates ?? true;
|
||||
document.getElementById('notify-system').checked = settings.notifications?.system ?? true;
|
||||
document.getElementById('notify-email').checked = settings.notifications?.email ?? false;
|
||||
document.getElementById('activity-logs').checked = settings.privacy?.activity_logs ?? true;
|
||||
document.getElementById('two-factor').checked = settings.privacy?.two_factor ?? false;
|
||||
document.getElementById('auto-logout').value = settings.privacy?.auto_logout ?? '60';
|
||||
|
||||
// Kontrast-Einstellungen
|
||||
if (settings.contrast === 'high') {
|
||||
localStorage.setItem('myp-contrast', 'high');
|
||||
document.documentElement.classList.add('high-contrast');
|
||||
setActiveContrastButton(highContrastBtn);
|
||||
} else {
|
||||
localStorage.setItem('myp-contrast', 'normal');
|
||||
document.documentElement.classList.remove('high-contrast');
|
||||
setActiveContrastButton(normalContrastBtn);
|
||||
}
|
||||
}
|
||||
|
||||
function applyDefaultSettings() {
|
||||
// Apply safe defaults if loading fails
|
||||
setActiveThemeButton(systemThemeBtn);
|
||||
setActiveContrastButton(normalContrastBtn);
|
||||
|
||||
document.getElementById('reduced-motion').checked = false;
|
||||
document.getElementById('notify-new-jobs').checked = true;
|
||||
document.getElementById('notify-job-updates').checked = true;
|
||||
document.getElementById('notify-system').checked = true;
|
||||
document.getElementById('notify-email').checked = false;
|
||||
document.getElementById('activity-logs').checked = true;
|
||||
document.getElementById('two-factor').checked = false;
|
||||
document.getElementById('auto-logout').value = '60';
|
||||
}
|
||||
|
||||
// Auto-logout implementation
|
||||
let logoutTimer = null;
|
||||
|
||||
function setupAutoLogout() {
|
||||
const autoLogoutSelect = document.getElementById('auto-logout');
|
||||
|
||||
function resetLogoutTimer() {
|
||||
if (logoutTimer) {
|
||||
clearTimeout(logoutTimer);
|
||||
}
|
||||
|
||||
const minutes = parseInt(autoLogoutSelect.value);
|
||||
if (minutes && minutes !== 'never') {
|
||||
logoutTimer = setTimeout(() => {
|
||||
if (confirm('Sie werden aufgrund von Inaktivität abgemeldet. Möchten Sie angemeldet bleiben?')) {
|
||||
resetLogoutTimer();
|
||||
} else {
|
||||
window.location.href = '/logout';
|
||||
}
|
||||
}, minutes * 60 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Reset timer on any user activity
|
||||
['mousedown', 'mousemove', 'keypress', 'scroll', 'touchstart', 'click'].forEach(event => {
|
||||
document.addEventListener(event, resetLogoutTimer, { passive: true });
|
||||
});
|
||||
|
||||
// Initial setup
|
||||
resetLogoutTimer();
|
||||
|
||||
// Update timer when setting changes
|
||||
autoLogoutSelect.addEventListener('change', resetLogoutTimer);
|
||||
}
|
||||
|
||||
// Enhanced toggle switches with keyboard support
|
||||
function enhanceToggleSwitches() {
|
||||
document.querySelectorAll('.toggle-checkbox').forEach(checkbox => {
|
||||
// Add keyboard support
|
||||
checkbox.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
this.click();
|
||||
}
|
||||
});
|
||||
|
||||
// Enhanced change handler with debouncing
|
||||
let changeTimeout;
|
||||
checkbox.addEventListener('change', function() {
|
||||
clearTimeout(changeTimeout);
|
||||
changeTimeout = setTimeout(() => {
|
||||
const settingName = this.name.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
||||
const status = this.checked ? 'aktiviert' : 'deaktiviert';
|
||||
|
||||
// Visual feedback
|
||||
const label = this.nextElementSibling;
|
||||
if (label) {
|
||||
label.style.transform = 'scale(1.05)';
|
||||
setTimeout(() => {
|
||||
label.style.transform = '';
|
||||
}, 150);
|
||||
}
|
||||
|
||||
// Auto-save individual settings
|
||||
saveIndividualSetting(this.name, this.checked);
|
||||
|
||||
showFlashMessage(`${settingName} wurde ${status}`, 'info');
|
||||
}, 300); // Debounce to prevent spam
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Save individual settings for immediate feedback
|
||||
async function saveIndividualSetting(settingName, value) {
|
||||
try {
|
||||
const response = await fetch('/api/user/setting', {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
|
||||
},
|
||||
body: JSON.stringify({ [settingName]: value })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Fehler beim Speichern der Einstellung');
|
||||
}
|
||||
|
||||
// Update cache
|
||||
const cached = localStorage.getItem('myp-settings-cache');
|
||||
if (cached) {
|
||||
try {
|
||||
const settings = JSON.parse(cached);
|
||||
// Update the specific setting in cache
|
||||
const keys = settingName.split('.');
|
||||
let current = settings;
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
if (!current[keys[i]]) current[keys[i]] = {};
|
||||
current = current[keys[i]];
|
||||
}
|
||||
current[keys[keys.length - 1]] = value;
|
||||
settings.timestamp = new Date().toISOString();
|
||||
localStorage.setItem('myp-settings-cache', JSON.stringify(settings));
|
||||
} catch (e) {
|
||||
localStorage.removeItem('myp-settings-cache');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Speichern der Einzeleinstellung:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Performance optimization: Intersection Observer for animations
|
||||
function setupIntersectionObserver() {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('in-view');
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.1 });
|
||||
|
||||
document.querySelectorAll('.glass-card').forEach(card => {
|
||||
observer.observe(card);
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize all enhanced features
|
||||
function initializeEnhancedFeatures() {
|
||||
setupAutoLogout();
|
||||
enhanceToggleSwitches();
|
||||
setupIntersectionObserver();
|
||||
setupNavigationLinks();
|
||||
}
|
||||
|
||||
// Setup navigation links
|
||||
function setupNavigationLinks() {
|
||||
const navItems = document.querySelectorAll('.nav-item');
|
||||
navItems.forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
// If it's a real link to another page, don't preventDefault
|
||||
if (this.getAttribute('href').startsWith('#')) {
|
||||
e.preventDefault();
|
||||
|
||||
// Update active state
|
||||
navItems.forEach(navItem => {
|
||||
navItem.classList.remove('bg-blue-50', 'dark:bg-blue-900/20', 'text-blue-700', 'dark:text-blue-300');
|
||||
navItem.classList.add('text-slate-900', 'dark:text-white', 'hover:bg-gray-100', 'dark:hover:bg-slate-800');
|
||||
});
|
||||
|
||||
this.classList.add('bg-blue-50', 'dark:bg-blue-900/20', 'text-blue-700', 'dark:text-blue-300');
|
||||
this.classList.remove('text-slate-900', 'dark:text-white', 'hover:bg-gray-100', 'dark:hover:bg-slate-800');
|
||||
|
||||
// Scroll to section with offset for header
|
||||
const targetId = this.getAttribute('href').substring(1);
|
||||
const targetElement = document.querySelector(`[id="${targetId}"]`) ||
|
||||
document.querySelector(`h2:contains("${targetId}")`);
|
||||
|
||||
if (targetElement) {
|
||||
const offsetTop = targetElement.offsetTop - 100; // Account for header
|
||||
window.scrollTo({
|
||||
top: offsetTop,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to show flash messages
|
||||
function showFlashMessage(message, type = 'info') {
|
||||
// Use the global toast manager if available
|
||||
if (window.showToast) {
|
||||
window.showToast(message, type);
|
||||
} else if (window.MYP && window.MYP.UI && window.MYP.UI.ToastManager) {
|
||||
const toast = new window.MYP.UI.ToastManager();
|
||||
toast.show(message, type);
|
||||
} else {
|
||||
alert(message);
|
||||
// Fallback to simple notification
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `fixed top-4 right-4 p-4 rounded-lg text-white z-50 ${
|
||||
type === 'success' ? 'bg-green-500' :
|
||||
type === 'error' ? 'bg-red-500' :
|
||||
type === 'warning' ? 'bg-yellow-500' : 'bg-blue-500'
|
||||
}`;
|
||||
notification.textContent = message;
|
||||
document.body.appendChild(notification);
|
||||
|
||||
setTimeout(() => {
|
||||
notification.style.opacity = '0';
|
||||
setTimeout(() => document.body.removeChild(notification), 300);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation links
|
||||
const navItems = document.querySelectorAll('.nav-item');
|
||||
navItems.forEach(item => {
|
||||
item.addEventListener('click', function(e) {
|
||||
// If it's a real link to another page, don't preventDefault
|
||||
if (this.getAttribute('href').startsWith('#')) {
|
||||
e.preventDefault();
|
||||
|
||||
// Update active state
|
||||
navItems.forEach(navItem => {
|
||||
navItem.classList.remove('bg-blue-50', 'dark:bg-blue-900/20', 'text-blue-700', 'dark:text-blue-300');
|
||||
navItem.classList.add('text-slate-900', 'dark:text-white', 'hover:bg-gray-100', 'dark:hover:bg-slate-800');
|
||||
});
|
||||
|
||||
this.classList.add('bg-blue-50', 'dark:bg-blue-900/20', 'text-blue-700', 'dark:text-blue-300');
|
||||
this.classList.remove('text-slate-900', 'dark:text-white', 'hover:bg-gray-100', 'dark:hover:bg-slate-800');
|
||||
|
||||
// Scroll to section
|
||||
const targetId = this.getAttribute('href').substring(1);
|
||||
const targetElement = document.querySelector(`[id="${targetId}"]`) ||
|
||||
document.querySelector(`h2:contains("${targetId}")`);
|
||||
|
||||
if (targetElement) {
|
||||
targetElement.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
// Initialize everything
|
||||
loadUserSettings();
|
||||
initializeEnhancedFeatures();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* Glass Card Effect */
|
||||
.glass-card {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(229, 231, 235, 0.8);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||
padding: 1.5rem;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dark .glass-card {
|
||||
background: rgba(30, 41, 59, 0.8);
|
||||
border-color: rgba(100, 116, 139, 0.3);
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.glass-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.dark .glass-card:hover {
|
||||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.4), 0 4px 6px -2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Loading State Animations */
|
||||
.settings-loading {
|
||||
opacity: 0.6;
|
||||
pointer-events: none;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.settings-loading::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -10px 0 0 -10px;
|
||||
border: 2px solid #f3f3f3;
|
||||
border-top: 2px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* Enhanced Toggle Switches */
|
||||
.toggle-checkbox {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* Toggle Switch Styling */
|
||||
.toggle-checkbox:checked {
|
||||
right: 0;
|
||||
border-color: #60a5fa;
|
||||
@apply bg-blue-500;
|
||||
border-color: #3b82f6;
|
||||
background-color: #3b82f6;
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
.toggle-checkbox:checked + .toggle-label {
|
||||
@apply bg-blue-500 dark:bg-blue-600;
|
||||
background-color: #3b82f6;
|
||||
}
|
||||
|
||||
.dark .toggle-checkbox:checked + .toggle-label {
|
||||
background-color: #2563eb;
|
||||
}
|
||||
|
||||
.toggle-label {
|
||||
transition: background-color 0.3s ease;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.toggle-checkbox:focus + .toggle-label {
|
||||
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
|
||||
}
|
||||
|
||||
/* Button states */
|
||||
.theme-btn.active,
|
||||
.contrast-btn.active {
|
||||
box-shadow: 0 0 0 2px #3b82f6;
|
||||
}
|
||||
|
||||
/* High contrast mode styles */
|
||||
.high-contrast {
|
||||
--tw-text-opacity: 1;
|
||||
}
|
||||
|
||||
.high-contrast * {
|
||||
outline: 2px solid transparent;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.high-contrast button:focus,
|
||||
.high-contrast input:focus,
|
||||
.high-contrast select:focus {
|
||||
outline: 3px solid #000 !important;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.dark.high-contrast button:focus,
|
||||
.dark.high-contrast input:focus,
|
||||
.dark.high-contrast select:focus {
|
||||
outline: 3px solid #fff !important;
|
||||
}
|
||||
|
||||
/* Accessibility improvements */
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.glass-card,
|
||||
.toggle-checkbox,
|
||||
.toggle-label {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
.settings-loading::after {
|
||||
animation: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Success feedback animation */
|
||||
.settings-saved {
|
||||
animation: settingsSaved 0.6s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes settingsSaved {
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.02); background-color: rgba(34, 197, 94, 0.1); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
/* Smooth section transitions */
|
||||
.settings-section {
|
||||
scroll-margin-top: 2rem;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
Reference in New Issue
Block a user