diff --git a/public/click.ogg b/public/click.ogg new file mode 100644 index 0000000..f9c6dc0 Binary files /dev/null and b/public/click.ogg differ diff --git a/public/css/style.css b/public/css/style.css new file mode 100644 index 0000000..1affa22 --- /dev/null +++ b/public/css/style.css @@ -0,0 +1,125 @@ +:root { + --icon-size: 1.3rem; + --icon-color: var(--text); +} + +#nav-bar { + padding: 0.625rem 0 0; + display: flex; + flex-wrap: wrap; + gap: 0.25rem; + justify-content: flex-end; + align-items: center; +} + +#footer-container { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; +} + +.accent-data { + color: var(--accent); +} + +.theme-transition { + transition: color 0.3s ease, background-color 0.3s ease; +} + +.tags-data { + display: flex; + flex-direction: column; + flex-wrap: wrap; + justify-content: flex-end; + align-items: flex-start; + align-content: flex-end +} + +.title-list li { + margin-bottom: .375rem; +} + +/* icons settings */ +.icon { + width: var(--icon-size); + height: var(--icon-size); + display: inline-block; + vertical-align: middle; + color: var(--icon-color); + fill: currentColor; + transition: color 0.3s ease; + cursor: pointer; +} + +.icon:hover { + --icon-color: var(--accent); +} + +/* Theme toggle specific styles */ +.theme-toggle { + cursor: pointer; + padding: 0; + margin: 0; + background: none; + border: none; + display: inline-flex; +} + +.theme-toggle:hover .icon { + --icon-color: var(--accent); +} + +.theme-toggle:active { + transform: scale(0.95); +} + +/* footnotes */ +.footnote-definition { + margin: 0 0 0 .125rem; +} + +.footnote-definition-label { + color: var(--accent); +} + +.footnote-definition p { + display: inline; + margin: .625rem 0 0 .625rem; +} + +/* general classes */ +/* Cleaned no-style class */ +.no-style { + all: unset; + background: none !important; + border: none !important; +} + +.no-style:hover { + background: transparent; + color: inherit; +} + +/* Modern float replacement */ +.float-right { + margin-inline-start: auto; +} + +.float-left { + margin-inline-end: auto; +} + +.center { + text-align: center; +} + +.center img { + display: block; + margin: 1rem auto; +} + +/* shortcodes css */ +.webring { + margin: .375rem; +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..7eb16ea Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/icons.svg b/public/icons.svg new file mode 100644 index 0000000..6fe3989 --- /dev/null +++ b/public/icons.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/js/script.js b/public/js/script.js new file mode 100644 index 0000000..70d5970 --- /dev/null +++ b/public/js/script.js @@ -0,0 +1,72 @@ +class ThemeManager { + constructor() { + this.toggle = document.getElementById('theme-toggle'); + if (!this.toggle) return; + + this.icon = document.getElementById('theme-icon'); + const { iconBase, iconDark, iconLight, soundSrc } = this.toggle.dataset; + this.iconBase = iconBase; + this.iconDark = iconDark; + this.iconLight = iconLight; + + // Create audio element lazily only when needed + this.sound = null; + this.soundSrc = soundSrc; + + this.init(); + } + + init() { + this.setInitialTheme(); + this.toggle.addEventListener('click', () => this.toggleTheme()); + } + + setInitialTheme() { + const savedTheme = localStorage.getItem('theme'); + const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + const initialTheme = savedTheme || (systemDark ? 'dark' : 'light'); + + document.documentElement.setAttribute('data-theme', initialTheme); + this.updateIcon(initialTheme === 'dark'); + } + + toggleTheme() { + const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; + const newTheme = isDark ? 'light' : 'dark'; + + document.documentElement.setAttribute('data-theme', newTheme); + this.updateIcon(!isDark); + localStorage.setItem('theme', newTheme); + + // Lazy load sound only when needed + if (!this.sound && this.soundSrc) { + this.sound = new Audio(this.soundSrc); + } + + if (this.sound) { + this.sound.play().catch(() => {}); + } + + // Use requestAnimationFrame for better performance on transition + requestAnimationFrame(() => { + setTimeout(() => { + document.body.classList.remove('theme-transition'); + }, 300); + }); + } + + updateIcon(isDark) { + if (this.icon) { + this.icon.setAttribute('href', + `${this.iconBase}${isDark ? this.iconDark : this.iconLight}`); + } + } +} + + +// Initialize when content is loaded +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => new ThemeManager()); +} else { + new ThemeManager(); +} diff --git a/static/js/script.js b/static/js/script.js index a5433f7..70d5970 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -37,15 +37,22 @@ class ThemeManager { document.documentElement.setAttribute('data-theme', newTheme); this.updateIcon(!isDark); localStorage.setItem('theme', newTheme); - + // Lazy load sound only when needed if (!this.sound && this.soundSrc) { this.sound = new Audio(this.soundSrc); } - + if (this.sound) { this.sound.play().catch(() => {}); } + + // Use requestAnimationFrame for better performance on transition + requestAnimationFrame(() => { + setTimeout(() => { + document.body.classList.remove('theme-transition'); + }, 300); + }); } updateIcon(isDark) { @@ -56,6 +63,7 @@ class ThemeManager { } } + // Initialize when content is loaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => new ThemeManager());