226 lines
5.9 KiB
PHP
226 lines
5.9 KiB
PHP
<?php
|
|
$themeSection = $themeSection ?? null;
|
|
|
|
if ($themeSection === 'head'):
|
|
?>
|
|
<script>
|
|
(function () {
|
|
function readThemeCookie() {
|
|
var cookieParts = document.cookie ? document.cookie.split(';') : [];
|
|
|
|
for (var i = 0; i < cookieParts.length; i++) {
|
|
var part = cookieParts[i].trim();
|
|
if (part.indexOf('theme=') === 0) {
|
|
var value = decodeURIComponent(part.substring(6));
|
|
if (value === 'dark' || value === 'light') {
|
|
return value;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function resolveTheme() {
|
|
var cookieTheme = readThemeCookie();
|
|
if (cookieTheme) {
|
|
return cookieTheme;
|
|
}
|
|
|
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
return 'dark';
|
|
}
|
|
|
|
return 'light';
|
|
}
|
|
|
|
var resolvedTheme = resolveTheme();
|
|
var html = document.documentElement;
|
|
|
|
html.classList.toggle('dark', resolvedTheme === 'dark');
|
|
html.setAttribute('data-theme', resolvedTheme);
|
|
})();
|
|
</script>
|
|
|
|
<style>
|
|
:root {
|
|
--theme-bg: #f3f4f6;
|
|
--theme-surface: #ffffff;
|
|
--theme-surface-alt: #f9fafb;
|
|
--theme-text: #111827;
|
|
--theme-text-muted: #6b7280;
|
|
--theme-border: #d1d5db;
|
|
}
|
|
|
|
html.dark {
|
|
--theme-bg: #111827;
|
|
--theme-surface: #1f2937;
|
|
--theme-surface-alt: #111827;
|
|
--theme-text: #f3f4f6;
|
|
--theme-text-muted: #9ca3af;
|
|
--theme-border: #374151;
|
|
}
|
|
|
|
.theme-page {
|
|
background-color: var(--theme-bg);
|
|
color: var(--theme-text);
|
|
}
|
|
|
|
.theme-surface {
|
|
background-color: var(--theme-surface);
|
|
color: var(--theme-text);
|
|
}
|
|
|
|
.theme-surface-alt {
|
|
background-color: var(--theme-surface-alt);
|
|
}
|
|
|
|
.theme-text {
|
|
color: var(--theme-text);
|
|
}
|
|
|
|
.theme-text-muted {
|
|
color: var(--theme-text-muted);
|
|
}
|
|
|
|
.theme-border {
|
|
border-color: var(--theme-border);
|
|
}
|
|
|
|
.theme-row-dividers > tr {
|
|
border-bottom: 1px solid var(--theme-border);
|
|
}
|
|
|
|
.theme-row-dividers > tr:last-child {
|
|
border-bottom-width: 0;
|
|
}
|
|
|
|
.theme-input {
|
|
background-color: var(--theme-surface);
|
|
border-color: var(--theme-border);
|
|
color: var(--theme-text);
|
|
}
|
|
|
|
.theme-input::placeholder {
|
|
color: var(--theme-text-muted);
|
|
}
|
|
|
|
#themeToggle {
|
|
position: fixed;
|
|
top: calc(1rem + env(safe-area-inset-top));
|
|
right: 1rem;
|
|
z-index: 50;
|
|
}
|
|
|
|
@media (max-width: 640px) {
|
|
#themeToggle {
|
|
top: calc(4.75rem + env(safe-area-inset-top));
|
|
}
|
|
}
|
|
|
|
#themeToggle:focus-visible {
|
|
outline: 3px solid #2563eb;
|
|
outline-offset: 2px;
|
|
}
|
|
</style>
|
|
<?php
|
|
endif;
|
|
|
|
if ($themeSection === 'body'):
|
|
?>
|
|
<button
|
|
id="themeToggle"
|
|
type="button"
|
|
class="bg-blue-600 hover:bg-blue-700 text-white p-3 rounded-full shadow-md transition"
|
|
aria-label="Switch to dark mode"
|
|
title="Switch to dark mode"
|
|
>
|
|
<i id="themeToggleIcon" class="bi bi-moon-stars-fill" aria-hidden="true"></i>
|
|
</button>
|
|
|
|
<noscript>
|
|
<style>
|
|
#themeToggle {
|
|
display: none !important;
|
|
}
|
|
</style>
|
|
</noscript>
|
|
|
|
<script>
|
|
(function () {
|
|
var html = document.documentElement;
|
|
var toggleButton = document.getElementById('themeToggle');
|
|
var toggleIcon = document.getElementById('themeToggleIcon');
|
|
|
|
if (!toggleButton || !toggleIcon) {
|
|
return;
|
|
}
|
|
|
|
function readThemeCookie() {
|
|
var cookieParts = document.cookie ? document.cookie.split(';') : [];
|
|
|
|
for (var i = 0; i < cookieParts.length; i++) {
|
|
var part = cookieParts[i].trim();
|
|
if (part.indexOf('theme=') === 0) {
|
|
var value = decodeURIComponent(part.substring(6));
|
|
if (value === 'dark' || value === 'light') {
|
|
return value;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function writeThemeCookie(theme) {
|
|
document.cookie = 'theme=' + encodeURIComponent(theme) + '; max-age=31536000; path=/; SameSite=Lax';
|
|
}
|
|
|
|
function applyTheme(theme) {
|
|
var validTheme = theme === 'dark' ? 'dark' : 'light';
|
|
html.classList.toggle('dark', validTheme === 'dark');
|
|
html.setAttribute('data-theme', validTheme);
|
|
return validTheme;
|
|
}
|
|
|
|
function syncToggle(theme) {
|
|
var isDark = theme === 'dark';
|
|
var label = isDark ? 'Switch to light mode' : 'Switch to dark mode';
|
|
var iconClass = isDark ? 'bi-sun-fill' : 'bi-moon-stars-fill';
|
|
|
|
toggleButton.setAttribute('aria-label', label);
|
|
toggleButton.setAttribute('title', label);
|
|
toggleButton.setAttribute('aria-pressed', isDark ? 'true' : 'false');
|
|
toggleIcon.className = 'bi ' + iconClass;
|
|
}
|
|
|
|
var initialTheme;
|
|
if (html.classList.contains('dark')) {
|
|
initialTheme = 'dark';
|
|
} else {
|
|
initialTheme = readThemeCookie() || 'light';
|
|
}
|
|
|
|
var currentTheme = applyTheme(initialTheme);
|
|
syncToggle(currentTheme);
|
|
|
|
toggleButton.addEventListener('click', function () {
|
|
var nextTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
|
|
currentTheme = applyTheme(nextTheme);
|
|
|
|
try {
|
|
writeThemeCookie(currentTheme);
|
|
} catch (error) {
|
|
// Visual switch remains active if cookie storage is restricted.
|
|
}
|
|
|
|
syncToggle(currentTheme);
|
|
});
|
|
})();
|
|
</script>
|
|
<?php
|
|
endif;
|