Commit 02d81493 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix: sidebar to right side (RTL) + add 20 UI customization features

- Moved sidebar from left to right for proper RTL layout
- Added full customization panel at /branding with 20 live UI preferences:
  sidebar width, font size, card style, animation speed, accent color,
  table density, toast position, content width, sidebar style, border radius,
  time format, number format, reduce motion, high contrast, color blind modes,
  focus mode, sounds, auto save, page transitions, compact header
- All preferences persist in localStorage and apply instantly
- Added customization.css + customization.js engine
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent b0303129
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
<link rel="stylesheet" href="/public/css/animations.css"> <link rel="stylesheet" href="/public/css/animations.css">
<link rel="stylesheet" href="/public/css/utilities.css"> <link rel="stylesheet" href="/public/css/utilities.css">
<link rel="stylesheet" href="/public/css/enhancements.css"> <link rel="stylesheet" href="/public/css/enhancements.css">
<link rel="stylesheet" href="/public/css/customization.css">
<?php if (isset($moduleCSS)): ?> <?php if (isset($moduleCSS)): ?>
<link rel="stylesheet" href="/modules/<?= $moduleCSS ?>/assets/<?= basename($moduleCSS) ?>.css"> <link rel="stylesheet" href="/modules/<?= $moduleCSS ?>/assets/<?= basename($moduleCSS) ?>.css">
<?php endif; ?> <?php endif; ?>
...@@ -34,6 +35,7 @@ ...@@ -34,6 +35,7 @@
<?php require LAYOUTS_PATH . '/partials/modal.php'; ?> <?php require LAYOUTS_PATH . '/partials/modal.php'; ?>
<?php require LAYOUTS_PATH . '/partials/confirm-dialog.php'; ?> <?php require LAYOUTS_PATH . '/partials/confirm-dialog.php'; ?>
<script src="/public/js/customization.js"></script>
<script src="/public/js/app.js"></script> <script src="/public/js/app.js"></script>
<script src="/public/js/sidebar.js"></script> <script src="/public/js/sidebar.js"></script>
<script src="/public/js/enhancements.js"></script> <script src="/public/js/enhancements.js"></script>
......
This diff is collapsed.
/* Customization Engine Styles */
/* Card Styles */
[data-card-style="flat"] .card,
[data-card-style="flat"] .stat-card {
box-shadow: none;
border: 1px solid var(--border);
}
[data-card-style="glass"] .card,
[data-card-style="glass"] .stat-card {
background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.08);
}
/* Toast Position */
[data-toast-position="top-left"] .toast-container { top: var(--space-5); left: var(--space-5); right: auto; bottom: auto; }
[data-toast-position="top-right"] .toast-container { top: var(--space-5); right: var(--space-5); left: auto; bottom: auto; }
[data-toast-position="bottom-left"] .toast-container { bottom: var(--space-5); left: var(--space-5); right: auto; top: auto; }
[data-toast-position="bottom-right"] .toast-container { bottom: var(--space-5); right: var(--space-5); left: auto; top: auto; }
/* Sidebar Style */
[data-sidebar-style="transparent"] .sidebar {
background: rgba(17, 17, 35, 0.7);
backdrop-filter: blur(16px);
}
[data-sidebar-style="gradient"] .sidebar {
background: linear-gradient(180deg, var(--bg-secondary) 0%, rgba(32, 130, 240, 0.05) 100%);
}
/* High Contrast */
.high-contrast {
--text-primary: #ffffff;
--text-secondary: #e0e0e0;
--text-muted: #b0b0b0;
--border: rgba(255, 255, 255, 0.25);
--bg-hover: rgba(255, 255, 255, 0.12);
}
.high-contrast .card,
.high-contrast .stat-card {
border: 1px solid rgba(255, 255, 255, 0.2);
}
.high-contrast .nav-item.active {
background: rgba(32, 130, 240, 0.2);
}
/* Color Blind Modes */
[data-color-blind="protanopia"] {
--success: #3b82f6;
--danger: #f59e0b;
--warning: #8b5cf6;
}
[data-color-blind="deuteranopia"] {
--success: #06b6d4;
--danger: #f97316;
--warning: #a855f7;
}
[data-color-blind="tritanopia"] {
--success: #ec4899;
--danger: #14b8a6;
--warning: #f43f5e;
}
/* Focus Mode */
.focus-mode .sidebar .nav-section:not(:has(.nav-item.active)) .nav-section-items {
opacity: 0.4;
transition: opacity var(--duration-fast);
}
.focus-mode .sidebar .nav-section:not(:has(.nav-item.active)):hover .nav-section-items {
opacity: 1;
}
.focus-mode .topbar {
opacity: 0.7;
transition: opacity var(--duration-fast);
}
.focus-mode .topbar:hover {
opacity: 1;
}
/* Page transitions */
.no-page-transitions * {
animation-duration: 0ms !important;
transition-duration: 0ms !important;
}
/* Compact header */
.compact-header .topbar {
height: 48px;
min-height: 48px;
}
.compact-header .content-header {
margin-bottom: var(--space-4);
}
.compact-header .content-header h1 {
font-size: var(--font-size-lg);
}
/* Table density via custom property */
.data-table td,
.data-table th {
padding: var(--table-cell-padding, 10px 14px);
}
/* Customization Panel */
.customize-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: var(--space-4);
}
.customize-card {
background: var(--bg-elevated);
border: 1px solid var(--border);
border-radius: var(--radius-lg);
padding: var(--space-5);
transition: border-color var(--duration-fast);
}
.customize-card:hover {
border-color: var(--border-hover);
}
.customize-card-header {
display: flex;
align-items: center;
gap: var(--space-3);
margin-bottom: var(--space-4);
}
.customize-card-icon {
width: 36px;
height: 36px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.customize-card-icon.blue { background: rgba(32, 130, 240, 0.15); color: var(--brand-blue); }
.customize-card-icon.purple { background: rgba(139, 92, 246, 0.15); color: var(--brand-purple); }
.customize-card-icon.gold { background: rgba(228, 172, 56, 0.15); color: var(--brand-gold); }
.customize-card-icon.green { background: rgba(16, 185, 129, 0.15); color: var(--success); }
.customize-card-icon.orange { background: rgba(232, 77, 30, 0.15); color: var(--brand-orange); }
.customize-card-icon.cyan { background: rgba(6, 182, 212, 0.15); color: var(--brand-cyan); }
.customize-card-title {
font-weight: var(--font-weight-semibold);
font-size: var(--font-size-sm);
}
.customize-card-desc {
font-size: var(--font-size-xs);
color: var(--text-muted);
}
.customize-options {
display: flex;
gap: var(--space-2);
flex-wrap: wrap;
}
.customize-option {
padding: var(--space-2) var(--space-3);
border: 1px solid var(--border);
border-radius: var(--radius-md);
font-size: var(--font-size-xs);
cursor: pointer;
transition: all var(--duration-fast);
background: transparent;
color: var(--text-secondary);
}
.customize-option:hover {
border-color: var(--brand-blue);
color: var(--text-primary);
}
.customize-option.active {
background: rgba(32, 130, 240, 0.15);
border-color: var(--brand-blue);
color: var(--brand-blue);
font-weight: var(--font-weight-medium);
}
.customize-toggle {
display: flex;
align-items: center;
justify-content: space-between;
}
.customize-color-input {
width: 100%;
height: 36px;
border: 1px solid var(--border);
border-radius: var(--radius-md);
cursor: pointer;
padding: 2px;
}
.customize-reset {
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-5);
border: 1px dashed var(--border);
border-radius: var(--radius-lg);
color: var(--text-muted);
cursor: pointer;
transition: all var(--duration-fast);
font-size: var(--font-size-sm);
}
.customize-reset:hover {
border-color: var(--danger);
color: var(--danger);
}
.app-layout { .app-layout {
display: grid; display: grid;
grid-template-columns: var(--sidebar-width) 1fr; grid-template-columns: 1fr var(--sidebar-width);
grid-template-rows: var(--topbar-height) 1fr; grid-template-rows: var(--topbar-height) 1fr;
grid-template-areas: grid-template-areas:
"sidebar topbar" "topbar sidebar"
"sidebar content"; "content sidebar";
min-height: 100vh; min-height: 100vh;
} }
.sidebar { .sidebar {
grid-area: sidebar; grid-area: sidebar;
background: var(--bg-secondary); background: var(--bg-secondary);
border-inline-start: 1px solid var(--border); border-inline-end: 1px solid var(--border);
position: fixed; position: fixed;
inset-inline-end: 0; inset-inline-start: 0;
top: 0; top: 0;
bottom: 0; bottom: 0;
width: var(--sidebar-width); width: var(--sidebar-width);
...@@ -108,7 +108,7 @@ ...@@ -108,7 +108,7 @@
.nav-item.active::before { .nav-item.active::before {
content: ''; content: '';
position: absolute; position: absolute;
inset-inline-end: 0; inset-inline-start: 0;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
width: 3px; width: 3px;
...@@ -148,7 +148,7 @@ ...@@ -148,7 +148,7 @@
position: sticky; position: sticky;
top: 0; top: 0;
z-index: 90; z-index: 90;
margin-inline-end: var(--sidebar-width); margin-inline-start: var(--sidebar-width);
} }
.topbar-right { .topbar-right {
...@@ -196,7 +196,7 @@ ...@@ -196,7 +196,7 @@
.content { .content {
grid-area: content; grid-area: content;
padding: var(--space-6); padding: var(--space-6);
margin-inline-end: var(--sidebar-width); margin-inline-start: var(--sidebar-width);
min-height: calc(100vh - var(--topbar-height)); min-height: calc(100vh - var(--topbar-height));
max-width: var(--content-max-width); max-width: var(--content-max-width);
} }
...@@ -234,7 +234,7 @@ ...@@ -234,7 +234,7 @@
@media (max-width: 1200px) { @media (max-width: 1200px) {
.app-layout { .app-layout {
grid-template-columns: var(--sidebar-collapsed) 1fr; grid-template-columns: 1fr var(--sidebar-collapsed);
} }
.sidebar { .sidebar {
...@@ -258,7 +258,7 @@ ...@@ -258,7 +258,7 @@
} }
.topbar, .content { .topbar, .content {
margin-inline-end: var(--sidebar-collapsed); margin-inline-start: var(--sidebar-collapsed);
} }
} }
...@@ -271,7 +271,7 @@ ...@@ -271,7 +271,7 @@
} }
.sidebar { .sidebar {
transform: translateX(100%); transform: translateX(-100%);
width: var(--sidebar-width); width: var(--sidebar-width);
} }
...@@ -296,7 +296,7 @@ ...@@ -296,7 +296,7 @@
} }
.topbar, .content { .topbar, .content {
margin-inline-end: 0; margin-inline-start: 0;
} }
.mobile-toggle { .mobile-toggle {
......
/**
* El3ab UI Customization Engine
* All preferences stored in localStorage, applied via CSS custom property overrides
*/
(function() {
'use strict';
const STORAGE_KEY = 'el3ab-ui-prefs';
const DEFAULTS = {
sidebarWidth: 'normal', // compact | normal | wide
fontSize: 'medium', // small | medium | large
cardStyle: 'elevated', // flat | elevated | glass
animationSpeed: 'normal', // off | fast | normal
accentColor: '#2082F0', // hex
tableDensity: 'comfortable', // compact | comfortable | spacious
toastPosition: 'top-left', // top-left | top-right | bottom-left | bottom-right
contentWidth: 'standard', // narrow | standard | full
sidebarStyle: 'solid', // solid | transparent | gradient
borderRadius: 'rounded', // sharp | rounded | pill
timeFormat: '24h', // 12h | 24h
numberFormat: 'western', // western | arabic
reduceMotion: false,
highContrast: false,
colorBlindMode: 'none', // none | protanopia | deuteranopia | tritanopia
focusMode: false,
soundEnabled: false,
autoSave: true,
pageTransitions: true,
compactHeader: false,
};
function getPrefs() {
try {
const saved = localStorage.getItem(STORAGE_KEY);
return saved ? { ...DEFAULTS, ...JSON.parse(saved) } : { ...DEFAULTS };
} catch { return { ...DEFAULTS }; }
}
function savePrefs(prefs) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
}
function applyPrefs(prefs) {
const root = document.documentElement;
// 1. Sidebar width
const sidebarWidths = { compact: '200px', normal: '260px', wide: '300px' };
root.style.setProperty('--sidebar-width', sidebarWidths[prefs.sidebarWidth] || '260px');
// 2. Font size
const fontScales = { small: '13px', medium: '14px', large: '16px' };
root.style.setProperty('--font-size-base', fontScales[prefs.fontSize] || '14px');
document.body.style.fontSize = fontScales[prefs.fontSize] || '14px';
// 3. Card style
document.body.setAttribute('data-card-style', prefs.cardStyle);
// 4. Animation speed
const durations = { off: '0ms', fast: '100ms', normal: '200ms' };
root.style.setProperty('--duration-fast', durations[prefs.animationSpeed] || '150ms');
root.style.setProperty('--duration-normal', prefs.animationSpeed === 'off' ? '0ms' : '250ms');
// 5. Accent color
root.style.setProperty('--brand-blue', prefs.accentColor);
// 6. Table density
const densities = { compact: '4px 8px', comfortable: '10px 14px', spacious: '14px 18px' };
root.style.setProperty('--table-cell-padding', densities[prefs.tableDensity] || '10px 14px');
// 7. Toast position
document.body.setAttribute('data-toast-position', prefs.toastPosition);
// 8. Content width
const widths = { narrow: '900px', standard: '1200px', full: '100%' };
root.style.setProperty('--content-max-width', widths[prefs.contentWidth] || '1200px');
// 9. Sidebar style
document.body.setAttribute('data-sidebar-style', prefs.sidebarStyle);
// 10. Border radius
const radii = { sharp: '4px', rounded: '8px', pill: '16px' };
root.style.setProperty('--radius-md', radii[prefs.borderRadius] || '8px');
root.style.setProperty('--radius-lg', prefs.borderRadius === 'sharp' ? '6px' : prefs.borderRadius === 'pill' ? '20px' : '12px');
// 11-12. Time/Number format stored for use by formatters
// 13. Reduce motion
if (prefs.reduceMotion) {
root.style.setProperty('--duration-fast', '0ms');
root.style.setProperty('--duration-normal', '0ms');
root.style.setProperty('--duration-slow', '0ms');
}
// 14. High contrast
document.body.classList.toggle('high-contrast', prefs.highContrast);
// 15. Color blind mode
document.body.setAttribute('data-color-blind', prefs.colorBlindMode);
// 16. Focus mode
document.body.classList.toggle('focus-mode', prefs.focusMode);
// 17. Page transitions
document.body.classList.toggle('no-page-transitions', !prefs.pageTransitions);
// 18. Compact header
document.body.classList.toggle('compact-header', prefs.compactHeader);
}
// Expose globally
window.UIPrefs = {
get: getPrefs,
set: function(key, value) {
const prefs = getPrefs();
prefs[key] = value;
savePrefs(prefs);
applyPrefs(prefs);
},
reset: function() {
localStorage.removeItem(STORAGE_KEY);
applyPrefs(DEFAULTS);
},
apply: function() {
applyPrefs(getPrefs());
},
defaults: DEFAULTS,
};
// Apply on load
document.addEventListener('DOMContentLoaded', () => applyPrefs(getPrefs()));
// Also apply immediately for above-fold
applyPrefs(getPrefs());
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment