Add theme picker dropdown with swatches for theme selection
This commit is contained in:
1 parent
142dcbdf0a
commit
1e2bcb18f1
3 files changed
+144
-22
No files matched your search
@@ -548,6 +548,86 @@ img.nav-svg-icon {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------
|
||||
THEME PICKER DROPDOWN
|
||||
---------------------------------------- */
|
||||
.theme-picker-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.theme-picker {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: calc(100% + 10px);
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
padding: 10px 8px;
|
||||
background: #111111;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: 0 8px 28px rgba(0, 0, 0, 0.5);
|
||||
z-index: 1051;
|
||||
/* hidden by default */
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transform: translateY(-6px) scale(0.95);
|
||||
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0.15s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.theme-picker.is-open {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0s linear 0s;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.swatch {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 6px 7px;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
transition: background 0.12s;
|
||||
}
|
||||
|
||||
.swatch:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
/* --sb = swatch background (bg-body of that theme)
|
||||
--sa = swatch accent colour */
|
||||
.swatch-dot {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border-radius: 50%;
|
||||
background: var(--sb);
|
||||
border: 2px solid var(--sa);
|
||||
transition: box-shadow 0.15s;
|
||||
}
|
||||
|
||||
.swatch.is-active .swatch-dot {
|
||||
box-shadow: 0 0 0 2px var(--sa), 0 0 0 4px rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
.swatch-label {
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
transition: color 0.12s;
|
||||
}
|
||||
|
||||
.swatch.is-active .swatch-label {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
/* ========================================
|
||||
PROFILE HERO (Blowfish-style homepage)
|
||||
======================================== */
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
echo Theme::jsBootstrap();
|
||||
?>
|
||||
|
||||
<!-- Theme rotation: light -> dark -> nord -> dracula -->
|
||||
<!-- Theme picker -->
|
||||
<script>
|
||||
(function () {
|
||||
var STORAGE_KEY = 'blowdit-theme';
|
||||
@@ -169,8 +169,9 @@
|
||||
dracula: 'bi-droplet',
|
||||
catppuccin: 'bi-cup'
|
||||
};
|
||||
var root = document.documentElement;
|
||||
var btn = document.getElementById('theme-toggle');
|
||||
var root = document.documentElement;
|
||||
var btn = document.getElementById('theme-toggle');
|
||||
var picker = document.getElementById('theme-picker');
|
||||
|
||||
function syncIcon(theme) {
|
||||
if (!btn) return;
|
||||
@@ -179,38 +180,72 @@
|
||||
icon.className = 'bi ' + (ICONS[theme] || 'bi-circle-half');
|
||||
}
|
||||
|
||||
function syncSwatches(theme) {
|
||||
if (!picker) return;
|
||||
Array.prototype.forEach.call(picker.querySelectorAll('.swatch'), function (s) {
|
||||
s.classList.toggle('is-active', s.getAttribute('data-pick') === theme);
|
||||
});
|
||||
}
|
||||
|
||||
function apply(theme) {
|
||||
root.setAttribute('data-theme', theme);
|
||||
try { localStorage.setItem(STORAGE_KEY, theme); } catch (e) {}
|
||||
// Persist to a cookie so PHP pre-colours the next page server-side
|
||||
// (prevents the white flash between navigations).
|
||||
document.cookie = 'blowdit-theme=' + theme + '; path=/; max-age=31536000; SameSite=Lax';
|
||||
// Keep the inline html background/color-scheme in sync.
|
||||
var bg = window.BLOWDIT_THEME_BG || {};
|
||||
if (bg[theme]) { root.style.backgroundColor = bg[theme]; }
|
||||
root.style.colorScheme = (theme === 'light') ? 'light' : 'dark';
|
||||
syncIcon(theme);
|
||||
syncSwatches(theme);
|
||||
}
|
||||
|
||||
// Reflect the theme set by the inline head script.
|
||||
var current = root.getAttribute('data-theme') || 'light';
|
||||
if (THEMES.indexOf(current) === -1) current = 'light';
|
||||
syncIcon(current);
|
||||
function openPicker() {
|
||||
if (!picker) return;
|
||||
picker.classList.add('is-open');
|
||||
if (btn) btn.setAttribute('aria-expanded', 'true');
|
||||
}
|
||||
|
||||
if (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var cur = root.getAttribute('data-theme') || 'light';
|
||||
var i = THEMES.indexOf(cur);
|
||||
apply(THEMES[(i + 1) % THEMES.length]);
|
||||
function closePicker() {
|
||||
if (!picker) return;
|
||||
picker.classList.remove('is-open');
|
||||
if (btn) btn.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
|
||||
// Swatch clicks
|
||||
if (picker) {
|
||||
Array.prototype.forEach.call(picker.querySelectorAll('.swatch'), function (s) {
|
||||
s.addEventListener('click', function () {
|
||||
apply(s.getAttribute('data-pick'));
|
||||
closePicker();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Follow the OS preference unless the visitor chose explicitly.
|
||||
// Toggle button: open / close picker
|
||||
if (btn) {
|
||||
btn.addEventListener('click', function (e) {
|
||||
e.stopPropagation();
|
||||
picker && picker.classList.contains('is-open') ? closePicker() : openPicker();
|
||||
});
|
||||
}
|
||||
|
||||
// Close on outside click
|
||||
document.addEventListener('click', function (e) {
|
||||
if (!picker || !picker.classList.contains('is-open')) return;
|
||||
if (btn && btn.contains(e.target)) return;
|
||||
if (picker.contains(e.target)) return;
|
||||
closePicker();
|
||||
});
|
||||
|
||||
// Initialise icons + active swatch
|
||||
var current = root.getAttribute('data-theme') || 'light';
|
||||
if (THEMES.indexOf(current) === -1) current = 'light';
|
||||
syncIcon(current);
|
||||
syncSwatches(current);
|
||||
|
||||
// Follow OS preference unless the visitor chose explicitly
|
||||
if (window.matchMedia) {
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function (e) {
|
||||
try {
|
||||
if (localStorage.getItem(STORAGE_KEY)) return;
|
||||
} catch (err) {}
|
||||
try { if (localStorage.getItem(STORAGE_KEY)) return; } catch (err) {}
|
||||
apply(e.matches ? 'dark' : 'light');
|
||||
});
|
||||
}
|
||||
|
||||
+10
-3
@@ -33,11 +33,18 @@
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<!-- Light / Dark mode toggle -->
|
||||
<li class="nav-item ml-md-2 mt-2 mt-md-0">
|
||||
<button type="button" id="theme-toggle" class="theme-toggle" aria-label="<?php echo $L->get('Toggle theme') ?>" title="<?php echo $L->get('Toggle theme') ?>">
|
||||
<!-- Theme picker -->
|
||||
<li class="nav-item ml-md-2 mt-2 mt-md-0 theme-picker-wrap">
|
||||
<button type="button" id="theme-toggle" class="theme-toggle" aria-label="<?php echo $L->get('Toggle theme') ?>" title="<?php echo $L->get('Toggle theme') ?>" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="bi bi-moon-stars" aria-hidden="true"></i>
|
||||
</button>
|
||||
<div id="theme-picker" class="theme-picker" role="menu">
|
||||
<button class="swatch" data-pick="light" title="Light" style="--sb:#ffffff;--sa:#1f1f1f"><span class="swatch-dot"></span><span class="swatch-label">Light</span></button>
|
||||
<button class="swatch" data-pick="dark" title="Dark" style="--sb:#171717;--sa:#e5e5e5"><span class="swatch-dot"></span><span class="swatch-label">Dark</span></button>
|
||||
<button class="swatch" data-pick="nord" title="Nord" style="--sb:#2e3440;--sa:#88c0d0"><span class="swatch-dot"></span><span class="swatch-label">Nord</span></button>
|
||||
<button class="swatch" data-pick="dracula" title="Dracula" style="--sb:#282a36;--sa:#bd93f9"><span class="swatch-dot"></span><span class="swatch-label">Dracula</span></button>
|
||||
<button class="swatch" data-pick="catppuccin" title="Catppuccin" style="--sb:#1e1e2e;--sa:#cba6f7"><span class="swatch-dot"></span><span class="swatch-label">Cat</span></button>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
Reference in new issue
Block a user