Files
peptide-starter-theme/assets/js/theme.js
peptiderepo 65b9f7a13c fix: resolve PHPCS coding standard violations for CI
- Convert all PHP files from CRLF to LF line endings
- Reformat multi-line function calls per WPCS standard
  (opening paren last on line, closing paren on own line)
- Fix assignment-in-conditional in single-peptide.php
- Change double quotes to single quotes in content-none.php
- Exclude InvalidClassFileName sniff (standard WP functions.php pattern)
- Fix archive-peptide.php opening PHP tag placement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-12 22:54:51 +07:00

211 lines
5.8 KiB
JavaScript

/**
* Theme Utilities
* Peptide Starter Theme
*
* Handles:
* - Dark mode toggle functionality
* - Dark mode preference persistence
* - Plugin dark mode coordination
* - Smooth transitions
*/
(function() {
'use strict';
const html = document.documentElement;
const darkModeToggle = document.querySelector('.dark-mode-toggle');
const sunIcon = document.querySelector('.sun-icon');
const moonIcon = document.querySelector('.moon-icon');
if (!darkModeToggle) {
return;
}
/**
* Get current theme preference
* @returns {string} 'dark' or 'light'
*/
function getCurrentTheme() {
const stored = localStorage.getItem('peptide-starter-theme');
if (stored) {
return stored;
}
const currentAttr = html.getAttribute('data-theme');
if (currentAttr) {
return currentAttr;
}
const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
return systemDark ? 'dark' : 'light';
}
/**
* Set theme preference
* @param {string} theme - 'dark' or 'light'
*/
function setTheme(theme) {
html.setAttribute('data-theme', theme);
localStorage.setItem('peptide-starter-theme', theme);
// Update icon visibility
updateIcons(theme);
// Notify plugins about theme change via custom event
const event = new CustomEvent('themechange', {
detail: { theme: theme }
});
document.dispatchEvent(event);
// Also update body class for CSS hooks
document.body.classList.remove('dark-mode', 'light-mode');
document.body.classList.add(theme === 'dark' ? 'dark-mode' : 'light-mode');
}
/**
* Update icon visibility based on theme
* @param {string} theme - 'dark' or 'light'
*/
function updateIcons(theme) {
if (theme === 'dark') {
sunIcon.style.display = 'block';
moonIcon.style.display = 'none';
} else {
sunIcon.style.display = 'none';
moonIcon.style.display = 'block';
}
}
/**
* Toggle between dark and light mode
*/
function toggleDarkMode() {
const currentTheme = getCurrentTheme();
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
setTheme(newTheme);
}
// Initialize theme on page load
const initialTheme = getCurrentTheme();
setTheme(initialTheme);
// Dark mode toggle button click
darkModeToggle.addEventListener('click', toggleDarkMode);
// Listen for system preference changes
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
mediaQuery.addEventListener('change', (e) => {
const stored = localStorage.getItem('peptide-starter-theme');
if (!stored) {
setTheme(e.matches ? 'dark' : 'light');
}
});
// Expose API for plugins to read/listen to theme changes
window.peptideStarterTheme = {
getCurrentTheme: getCurrentTheme,
setTheme: setTheme,
toggleDarkMode: toggleDarkMode,
};
/**
* Plugin Integration Example:
* Plugins can listen to theme changes via:
*
* 1. Custom event listener:
* document.addEventListener('themechange', function(e) {
* console.log('Theme changed to:', e.detail.theme);
* });
*
* 2. MutationObserver on data-theme:
* const observer = new MutationObserver((mutations) => {
* const theme = html.getAttribute('data-theme');
* console.log('Theme is now:', theme);
* });
* observer.observe(html, { attributes: true });
*
* 3. Direct access to CSS custom properties which theme controls
*/
/**
* Smooth scroll behavior
*/
function initSmoothScroll() {
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!prefersReducedMotion) {
document.documentElement.style.scrollBehavior = 'smooth';
}
}
initSmoothScroll();
/**
* Add focus-visible polyfill for older browsers
*/
function initFocusVisible() {
const isKeyboardEvent = (e) => {
return e.mozInputSource === MOZ_SOURCE_KEYBOARD ||
e.detail === 0 ||
(e.sourceCapabilities && e.sourceCapabilities.firesTouchEvents === false);
};
document.addEventListener('mousedown', (e) => {
document.body.classList.remove('keyboard-focus');
}, true);
document.addEventListener('keydown', () => {
document.body.classList.add('keyboard-focus');
}, true);
}
// Search overlay functionality (Peptide Search AI integration)
const searchToggle = document.querySelector('.search-toggle');
const searchOverlay = document.getElementById('search-overlay');
const searchOverlayClose = document.querySelector('.search-overlay-close');
function openSearchOverlay() {
if (!searchOverlay) return;
searchOverlay.classList.add('active');
searchOverlay.setAttribute('aria-hidden', 'false');
document.body.style.overflow = 'hidden';
// Focus the search input inside the overlay
const input = searchOverlay.querySelector('.psa-search-input, .ps-search-input, input[type="search"], input[type="text"]');
if (input) {
setTimeout(() => input.focus(), 100);
}
}
function closeSearchOverlay() {
if (!searchOverlay) return;
searchOverlay.classList.remove('active');
searchOverlay.setAttribute('aria-hidden', 'true');
document.body.style.overflow = '';
// Return focus to search toggle
if (searchToggle) searchToggle.focus();
}
if (searchToggle) {
searchToggle.addEventListener('click', openSearchOverlay);
}
if (searchOverlayClose) {
searchOverlayClose.addEventListener('click', closeSearchOverlay);
}
if (searchOverlay) {
// Close overlay when clicking the backdrop
searchOverlay.addEventListener('click', (e) => {
if (e.target === searchOverlay) {
closeSearchOverlay();
}
});
// Close on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && searchOverlay.classList.contains('active')) {
closeSearchOverlay();
}
});
}
})();