diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5d71791 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +*.zip +*.log +.idea/ +.vscode/ diff --git a/assets/css/dark-fallback.css b/assets/css/dark-fallback.css new file mode 100644 index 0000000..ff64815 --- /dev/null +++ b/assets/css/dark-fallback.css @@ -0,0 +1,56 @@ +/** + * Fallback dark palette when Blocksy Colour Mode Switch is not active. + * Matches Beatwise Customizer tokens from test2.beatwise.nl. + */ + +html.theme-dark { + color-scheme: dark; + + --blc-beatwise-purple: #442b79; + --blc-beatwise-purple-hover: #5a3d9a; + --blc-beatwise-purple-light: #9b8fd4; + --blc-beatwise-purple-soft: #c4b5fd; + + --theme-palette-color-1: var(--blc-beatwise-purple); + --theme-palette-color-2: var(--blc-beatwise-purple-hover); + --theme-palette-color-3: #333; + --theme-palette-color-4: #f5f5f5; + --theme-palette-color-5: #404040; + --theme-palette-color-6: #1e1e1e; + --theme-palette-color-7: #121212; + --theme-palette-color-8: #ffffff; + + --theme-text-color: var(--theme-palette-color-3); + --theme-link-initial-color: var(--blc-beatwise-purple-light); + --theme-link-hover-color: var(--blc-beatwise-purple-soft); + --theme-headings-color: var(--theme-palette-color-4); + --theme-border-color: var(--theme-palette-color-5); + --theme-selection-text-color: #ffffff; + --theme-selection-background-color: var(--blc-beatwise-purple); + --theme-button-background-initial-color: var(--blc-beatwise-purple); + --theme-button-background-hover-color: var(--blc-beatwise-purple-hover); +} + +html.theme-dark body, +html.theme-dark #main-container, +html.theme-dark .site { + background-color: var(--theme-palette-color-7); + color: var(--theme-text-color); +} + +html.theme-dark input:not([type="submit"]):not([type="button"]):not([type="reset"]), +html.theme-dark textarea, +html.theme-dark select { + background-color: var(--theme-palette-color-6); + color: var(--theme-palette-color-3); + border-color: var(--theme-palette-color-5); +} + +html.blc-ldt-animate, +html.blc-ldt-animate body { + transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease; +} + +html.theme.dark .bwam-day-header { + color: #000 !important; +} \ No newline at end of file diff --git a/assets/css/dark-overrides.css b/assets/css/dark-overrides.css new file mode 100644 index 0000000..fe430dc --- /dev/null +++ b/assets/css/dark-overrides.css @@ -0,0 +1,210 @@ +/** + * Beatwise (test2.beatwise.nl) dark-mode fixes. + * Tuned to Blocksy 2.1.38 global palette from the Customizer. + */ + +html.theme-dark { + /* Beatwise brand — keep lavender purple, never blue */ + --blc-beatwise-purple: #442b79; + --blc-beatwise-purple-hover: #5a3d9a; + --blc-beatwise-purple-light: #9b8fd4; + --blc-beatwise-purple-soft: #c4b5fd; + + --theme-palette-color-1: var(--blc-beatwise-purple); + --theme-palette-color-2: var(--blc-beatwise-purple-hover); + --theme-palette-color-3: #e0e0e0; + --theme-palette-color-4: #f5f5f5; + --theme-palette-color-5: #404040; + --theme-palette-color-6: #1e1e1e; + --theme-palette-color-7: #121212; + --theme-palette-color-8: #ffffff; + + --theme-text-color: var(--theme-palette-color-3); + --theme-headings-color: var(--theme-palette-color-4); + --theme-link-initial-color: var(--blc-beatwise-purple-light); + --theme-link-hover-color: var(--blc-beatwise-purple-soft); + --theme-button-background-initial-color: var(--blc-beatwise-purple); + --theme-button-background-hover-color: var(--blc-beatwise-purple-hover); + --theme-selection-background-color: var(--blc-beatwise-purple); + --theme-selection-text-color: #ffffff; +} + +html.theme-dark body { + background-color: var(--theme-palette-color-7); + color: var(--theme-text-color); +} + +/* ------------------------------------------------------------------------- + 1 & 3 — Header menu + dropdown (Blocksy header type-1) + ------------------------------------------------------------------------- */ +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] > ul > li > a, +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] .ct-menu-link { + --theme-link-initial-color: #ffffff !important; + color: #ffffff !important; +} + +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] > ul > li > a:hover, +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] .ct-menu-link:hover { + --theme-link-initial-color: var(--blc-beatwise-purple-soft) !important; + color: var(--blc-beatwise-purple-soft) !important; +} + +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] .sub-menu { + background-color: #2b2b2b !important; + --dropdown-background-color: #2b2b2b !important; +} + +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] .sub-menu .ct-menu-link, +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] .sub-menu a { + --theme-link-initial-color: #ffffff !important; + color: #ffffff !important; +} + +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] .sub-menu .ct-menu-link:hover, +html.theme-dark [data-header*="type-1"] .ct-header [data-id="menu"] .sub-menu a:hover { + color: var(--blc-beatwise-purple-soft) !important; + background-color: rgba(255, 255, 255, 0.06) !important; +} + +html.theme-dark [data-header*="type-1"] [data-id="mobile-menu"] a, +html.theme-dark [data-header*="type-1"] #offcanvas .ct-menu-link, +html.theme-dark [data-header*="type-1"] #offcanvas a { + --theme-link-initial-color: #ffffff !important; + color: #ffffff !important; +} + +/* Account dropdown */ +html.theme-dark [data-header*="type-1"] [data-id="account"] .ct-header-account-dropdown { + --theme-text-color: #ffffff !important; + --dropdown-background-color: #2b2b2b !important; +} + +/* ------------------------------------------------------------------------- + 5 — Footer: purple background + white text (same as light mode) + ------------------------------------------------------------------------- */ +html.theme-dark [data-footer*="type-1"] .ct-footer { + background-color: var(--blc-beatwise-purple) !important; + color: #ffffff !important; +} + +html.theme-dark [data-footer*="type-1"] .ct-footer, +html.theme-dark [data-footer*="type-1"] .ct-footer p, +html.theme-dark [data-footer*="type-1"] .ct-footer li, +html.theme-dark [data-footer*="type-1"] .ct-footer span, +html.theme-dark [data-footer*="type-1"] .ct-footer div, +html.theme-dark [data-footer*="type-1"] .ct-footer h1, +html.theme-dark [data-footer*="type-1"] .ct-footer h2, +html.theme-dark [data-footer*="type-1"] .ct-footer h3, +html.theme-dark [data-footer*="type-1"] .ct-footer h4, +html.theme-dark [data-footer*="type-1"] .ct-footer h5, +html.theme-dark [data-footer*="type-1"] .ct-footer h6, +html.theme-dark [data-footer*="type-1"] .ct-footer .widget-title, +html.theme-dark [data-footer*="type-1"] .ct-footer .ct-widget, +html.theme-dark [data-footer*="type-1"] [data-id="copyright"] { + --theme-text-color: #ffffff !important; + --theme-heading-color: #ffffff !important; + --theme-link-initial-color: #ffffff !important; + --theme-link-hover-color: var(--blc-beatwise-purple-soft) !important; + color: #ffffff !important; +} + +html.theme-dark [data-footer*="type-1"] .ct-footer a:hover, +html.theme-dark [data-footer*="type-1"] [data-id="copyright"] a:hover { + color: var(--blc-beatwise-purple-soft) !important; +} + +/* ------------------------------------------------------------------------- + 6 — /codes page: dark text on light hero/header area + ------------------------------------------------------------------------- */ +html.theme-dark.page-codes [data-header*="type-1"] .ct-header [data-row*="middle"], +html.theme-dark.blc-ldt-page-codes [data-header*="type-1"] .ct-header [data-row*="middle"] { + background-color: rgba(255, 255, 255, 0.94) !important; +} + +html.theme-dark.page-codes [data-header*="type-1"] .ct-header [data-id="menu"] > ul > li > a, +html.theme-dark.page-codes [data-header*="type-1"] .ct-header [data-id="menu"] .ct-menu-link, +html.theme-dark.page-codes [data-header*="type-1"] .ct-header a, +html.theme-dark.blc-ldt-page-codes [data-header*="type-1"] .ct-header [data-id="menu"] > ul > li > a, +html.theme-dark.blc-ldt-page-codes [data-header*="type-1"] .ct-header [data-id="menu"] .ct-menu-link { + --theme-link-initial-color: #181818 !important; + color: #181818 !important; +} + +html.theme-dark.page-codes [data-header*="type-1"] .ct-header [data-id="menu"] > ul > li > a:hover, +html.theme-dark.blc-ldt-page-codes [data-header*="type-1"] .ct-header [data-id="menu"] > ul > li > a:hover { + color: var(--blc-beatwise-purple) !important; +} + +html.theme-dark.page-codes .hero-section, +html.theme-dark.page-codes .entry-header, +html.theme-dark.page-codes .wp-block-cover, +html.theme-dark.blc-ldt-page-codes .hero-section, +html.theme-dark.blc-ldt-page-codes .entry-header, +html.theme-dark.blc-ldt-page-codes .wp-block-cover { + color: #181818 !important; +} + +html.theme-dark.page-codes .hero-section h1, +html.theme-dark.page-codes .hero-section p, +html.theme-dark.page-codes .entry-header h1, +html.theme-dark.page-codes .entry-header p, +html.theme-dark.blc-ldt-page-codes .hero-section h1, +html.theme-dark.blc-ldt-page-codes .hero-section p { + color: #181818 !important; +} + +/* ------------------------------------------------------------------------- + 2 — /reservations page: dark date labels on light schedule cards + ------------------------------------------------------------------------- */ +html.theme-dark.page-reservations .has-white-background-color, +html.theme-dark.page-reservations .has-base-background-color, +html.theme-dark.page-reservations .has-palette-color-8-background-color, +html.theme-dark.page-reservations .has-palette-color-6-background-color, +html.theme-dark.blc-ldt-page-reservations .has-white-background-color, +html.theme-dark.blc-ldt-page-reservations .has-base-background-color, +html.theme-dark.blc-ldt-page-reservations .has-palette-color-8-background-color, +html.theme-dark.blc-ldt-page-reservations .has-palette-color-6-background-color { + color: #181818 !important; +} + +html.theme-dark.page-reservations .has-white-background-color strong, +html.theme-dark.page-reservations .has-white-background-color span, +html.theme-dark.page-reservations .has-base-background-color strong, +html.theme-dark.page-reservations .has-base-background-color span, +html.theme-dark.blc-ldt-page-reservations .has-white-background-color strong, +html.theme-dark.blc-ldt-page-reservations .has-white-background-color span, +html.theme-dark.blc-ldt-page-reservations .has-base-background-color strong, +html.theme-dark.blc-ldt-page-reservations .has-base-background-color span { + color: #181818 !important; +} + +/* Reservation schedule grid (day headers like "Zondag 7 jun") */ +html.theme-dark.page-reservations article strong, +html.theme-dark.page-reservations article .wp-block-group strong, +html.theme-dark.blc-ldt-page-reservations article strong, +html.theme-dark.blc-ldt-page-reservations article .wp-block-group strong { + color: #181818 !important; +} + +html.theme-dark.page-reservations article .wp-block-group span, +html.theme-dark.blc-ldt-page-reservations article .wp-block-group span { + color: #181818 !important; +} + +/* Buttons stay Beatwise purple */ +html.theme-dark .wp-block-button__link, +html.theme-dark .ct-button { + background-color: var(--blc-beatwise-purple) !important; + border-color: var(--blc-beatwise-purple) !important; + color: #ffffff !important; +} + +html.theme-dark .wp-block-button__link:hover, +html.theme-dark .ct-button:hover { + background-color: var(--blc-beatwise-purple-hover) !important; + border-color: var(--blc-beatwise-purple-hover) !important; +} + +html.theme.dark .bwam-day-header { + color: #000 !important; +} \ No newline at end of file diff --git a/assets/css/toggle.css b/assets/css/toggle.css new file mode 100644 index 0000000..ddb0be6 --- /dev/null +++ b/assets/css/toggle.css @@ -0,0 +1,90 @@ +.blc-ldt-toggle { + position: fixed; + /* Position, offsets, and z-index are set via inline settings CSS. */ + right: 1.25rem; + bottom: 1.25rem; + z-index: 999999; + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 0.65rem 0.5rem 0.5rem; + border: 1px solid color-mix(in srgb, currentColor 18%, transparent); + border-radius: 999px; + background: var(--theme-palette-color-8, #ffffff); + color: var(--theme-palette-color-1, #1a1a1a); + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12); + font: 600 0.8125rem/1 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + cursor: pointer; + transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.2s ease, color 0.2s ease; +} + +.blc-ldt-toggle:hover { + transform: translateY(-2px); + box-shadow: 0 8px 28px rgba(0, 0, 0, 0.18); +} + +.blc-ldt-toggle:focus-visible { + outline: 2px solid var(--theme-palette-color-1, #442b79); + outline-offset: 3px; +} + +.blc-ldt-toggle__track { + position: relative; + width: 2.5rem; + height: 1.375rem; + border-radius: 999px; + background: color-mix(in srgb, currentColor 12%, transparent); + flex-shrink: 0; +} + +.blc-ldt-toggle__thumb { + position: absolute; + top: 2px; + left: 2px; + width: calc(1.375rem - 4px); + height: calc(1.375rem - 4px); + border-radius: 50%; + background: var(--theme-palette-color-1, #442b79); + transition: transform 0.25s ease; +} + +.blc-ldt-toggle--dark .blc-ldt-toggle__thumb { + transform: translateX(1.125rem); +} + +.blc-ldt-toggle__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1rem; + height: 1rem; +} + +html:not(.theme-dark) .blc-ldt-toggle__icon--moon, +html.theme-dark .blc-ldt-toggle__icon--sun { + display: none; +} + +html.theme-dark .blc-ldt-toggle { + background: var(--theme-palette-color-7, #1a1a1a); + color: var(--theme-palette-color-4, #f5f5f5); + border-color: rgba(255, 255, 255, 0.12); +} + +/* Avoid overlap with Blocksy back-to-top button */ +.ct-back-to-top ~ .blc-ldt-toggle, +#ct-back-to-top ~ .blc-ldt-toggle { + bottom: 4.5rem; +} + +@media (max-width: 480px) { + .blc-ldt-toggle { + right: 1rem; + bottom: 1rem; + padding: 0.45rem; + } + + .blc-ldt-toggle__icon { + display: none; + } +} diff --git a/assets/js/toggle.js b/assets/js/toggle.js new file mode 100644 index 0000000..d2d7228 --- /dev/null +++ b/assets/js/toggle.js @@ -0,0 +1,169 @@ +(function () { + 'use strict'; + + var config = window.blcLdt || {}; + var btn = document.getElementById('blc-ldt-toggle'); + if (!btn) return; + + var storageKey = config.storageKey || 'blc_ldt_scheme'; + var cookieName = config.cookieName || 'blc_ldt_scheme'; + var blocksyKeys = config.blocksyStorageKeys || ['blocksyColorMode', 'blocksy_color_mode', 'theme']; + var allKeys = [storageKey].concat(blocksyKeys); + var respectOs = config.respectOsPreference !== false; + var enableAnimation = config.enableAnimation !== false; + var prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); + + function getStoredScheme() { + var i, stored; + + try { + for (i = 0; i < allKeys.length; i++) { + stored = localStorage.getItem(allKeys[i]); + if (stored === 'light' || stored === 'dark') { + return stored; + } + } + } catch (e) {} + + var match = document.cookie.match( + new RegExp('(?:^|; )' + cookieName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '=([^;]*)') + ); + if (match) { + var cookieVal = decodeURIComponent(match[1]); + if (cookieVal === 'light' || cookieVal === 'dark') { + return cookieVal; + } + } + + return null; + } + + function getScheme() { + var stored = getStoredScheme(); + if (stored) { + return stored; + } + return document.documentElement.classList.contains('theme-dark') ? 'dark' : 'light'; + } + + function isDark() { + return getScheme() === 'dark'; + } + + function setCookie(scheme) { + var path = config.cookiePath || '/'; + var domain = config.cookieDomain ? '; domain=' + config.cookieDomain : ''; + var maxAge = 60 * 60 * 24 * 365; + document.cookie = + cookieName + + '=' + + encodeURIComponent(scheme) + + ';path=' + + path + + domain + + ';max-age=' + + maxAge + + ';SameSite=Lax'; + } + + function persistScheme(scheme) { + var i; + + try { + localStorage.setItem(storageKey, scheme); + for (i = 0; i < blocksyKeys.length; i++) { + localStorage.setItem(blocksyKeys[i], scheme); + } + } catch (e) {} + + setCookie(scheme); + } + + function applyScheme(scheme, animate) { + var root = document.documentElement; + var isDarkMode = scheme === 'dark'; + var shouldAnimate = animate && enableAnimation; + + if (shouldAnimate) { + root.classList.add('blc-ldt-animate'); + window.setTimeout(function () { + root.classList.remove('blc-ldt-animate'); + }, 300); + } + + root.classList.remove('theme-dark', 'theme-light'); + root.classList.add(isDarkMode ? 'theme-dark' : 'theme-light'); + root.setAttribute('data-blc-scheme', scheme); + root.style.colorScheme = scheme; + + persistScheme(scheme); + updateButton(isDarkMode); + + try { + document.dispatchEvent( + new CustomEvent('ct:color-mode:changed', { + bubbles: true, + detail: { mode: scheme }, + }) + ); + } catch (e) {} + + if (config.isLoggedIn && config.ajaxUrl && config.nonce) { + var body = new FormData(); + body.append('action', 'blc_ldt_save_scheme'); + body.append('nonce', config.nonce); + body.append('scheme', scheme); + fetch(config.ajaxUrl, { method: 'POST', body: body, credentials: 'same-origin' }); + } + } + + function updateButton(dark) { + var i18n = config.i18n || {}; + btn.setAttribute('aria-pressed', dark ? 'true' : 'false'); + btn.setAttribute( + 'aria-label', + dark ? i18n.light || 'Switch to light mode' : i18n.dark || 'Switch to dark mode' + ); + btn.setAttribute( + 'title', + dark ? i18n.lightLabel || 'Light mode' : i18n.darkLabel || 'Dark mode' + ); + btn.classList.toggle('blc-ldt-toggle--dark', dark); + } + + function defaultScheme() { + if (respectOs && prefersDark.matches) { + return 'dark'; + } + return 'light'; + } + + btn.addEventListener('click', function () { + applyScheme(isDark() ? 'light' : 'dark', true); + }); + + document.addEventListener('ct:color-mode:changed', function (event) { + if (!event.detail || !event.detail.mode) { + return; + } + if (event.detail.mode !== 'light' && event.detail.mode !== 'dark') { + return; + } + updateButton(event.detail.mode === 'dark'); + }); + + if (!getStoredScheme() && !document.documentElement.getAttribute('data-blc-scheme')) { + applyScheme(defaultScheme(), false); + } else { + updateButton(document.documentElement.classList.contains('theme-dark')); + } + + if (respectOs) { + prefersDark.addEventListener('change', function (event) { + if (getStoredScheme()) { + return; + } + applyScheme(event.matches ? 'dark' : 'light', true); + }); + } +})(); diff --git a/blocksy-light-dark-toggle.php b/blocksy-light-dark-toggle.php new file mode 100644 index 0000000..e86671e --- /dev/null +++ b/blocksy-light-dark-toggle.php @@ -0,0 +1,30 @@ + + */ + public static function defaults() { + return array( + 'position' => 'bottom-right', + 'offset_x' => '1.25rem', + 'offset_y' => '1.25rem', + 'z_index' => 999999, + 'hide_header_switch' => true, + 'respect_os_preference' => true, + 'enable_animation' => true, + 'show_on_mobile' => true, + 'show_on_desktop' => true, + ); + } + + /** + * Initialize admin settings. + */ + public static function init() { + add_action( 'admin_init', array( __CLASS__, 'register' ) ); + add_action( 'admin_menu', array( __CLASS__, 'add_menu' ) ); + add_filter( 'plugin_action_links_' . plugin_basename( BLC_LDT_FILE ), array( __CLASS__, 'plugin_links' ) ); + } + + /** + * Get merged settings. + * + * @return array + */ + public static function get() { + $stored = get_option( self::OPTION, array() ); + if ( ! is_array( $stored ) ) { + $stored = array(); + } + + $settings = wp_parse_args( $stored, self::defaults() ); + + return apply_filters( 'blc_ldt_settings', $settings ); + } + + /** + * Get a single setting. + * + * @param string $key Setting key. + * @return mixed + */ + public static function get_value( $key ) { + $settings = self::get(); + return isset( $settings[ $key ] ) ? $settings[ $key ] : null; + } + + /** + * Register settings with the Settings API. + */ + public static function register() { + register_setting( + 'blc_ldt_settings_group', + self::OPTION, + array( + 'type' => 'array', + 'sanitize_callback' => array( __CLASS__, 'sanitize' ), + 'default' => self::defaults(), + ) + ); + + add_settings_section( + 'blc_ldt_main', + __( 'Toggle appearance', 'blocksy-light-dark-toggle' ), + '__return_false', + 'blc-ldt-settings' + ); + + self::add_field( 'position', __( 'Position', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_position' ) ); + self::add_field( 'offset_x', __( 'Horizontal offset', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_offset_x' ) ); + self::add_field( 'offset_y', __( 'Vertical offset', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_offset_y' ) ); + self::add_field( 'z_index', __( 'Z-index', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_z_index' ) ); + + add_settings_section( + 'blc_ldt_behavior', + __( 'Behavior', 'blocksy-light-dark-toggle' ), + array( __CLASS__, 'section_behavior' ), + 'blc-ldt-settings' + ); + + self::add_field( 'hide_header_switch', __( 'Hide header colour switch', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_hide_header_switch' ), 'blc_ldt_behavior' ); + self::add_field( 'respect_os_preference', __( 'Follow system preference', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_respect_os_preference' ), 'blc_ldt_behavior' ); + self::add_field( 'enable_animation', __( 'Animate transitions', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_enable_animation' ), 'blc_ldt_behavior' ); + self::add_field( 'show_on_mobile', __( 'Show on mobile', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_show_on_mobile' ), 'blc_ldt_behavior' ); + self::add_field( 'show_on_desktop', __( 'Show on desktop', 'blocksy-light-dark-toggle' ), array( __CLASS__, 'field_show_on_desktop' ), 'blc_ldt_behavior' ); + } + + /** + * Register a settings field. + * + * @param string $id Field ID. + * @param string $title Field title. + * @param callable $callback Render callback. + * @param string $section Settings section. + */ + private static function add_field( $id, $title, $callback, $section = 'blc_ldt_main' ) { + add_settings_field( + 'blc_ldt_' . $id, + $title, + $callback, + 'blc-ldt-settings', + $section, + array( 'id' => $id ) + ); + } + + /** + * Sanitize submitted settings. + * + * @param array $input Raw input. + * @return array + */ + public static function sanitize( $input ) { + $defaults = self::defaults(); + $output = self::defaults(); + + if ( ! is_array( $input ) ) { + return $output; + } + + $positions = array( 'bottom-right', 'bottom-left', 'top-right', 'top-left' ); + $output['position'] = in_array( $input['position'] ?? '', $positions, true ) + ? $input['position'] + : $defaults['position']; + + $output['offset_x'] = self::sanitize_css_length( $input['offset_x'] ?? $defaults['offset_x'], $defaults['offset_x'] ); + $output['offset_y'] = self::sanitize_css_length( $input['offset_y'] ?? $defaults['offset_y'], $defaults['offset_y'] ); + $output['z_index'] = max( 1, min( 9999999, absint( $input['z_index'] ?? $defaults['z_index'] ) ) ); + + $output['hide_header_switch'] = ! empty( $input['hide_header_switch'] ); + $output['respect_os_preference'] = ! empty( $input['respect_os_preference'] ); + $output['enable_animation'] = ! empty( $input['enable_animation'] ); + $output['show_on_mobile'] = ! empty( $input['show_on_mobile'] ); + $output['show_on_desktop'] = ! empty( $input['show_on_desktop'] ); + + return $output; + } + + /** + * Sanitize a CSS length value. + * + * @param string $value Raw value. + * @param string $default Default value. + * @return string + */ + private static function sanitize_css_length( $value, $default ) { + $value = trim( (string) $value ); + if ( preg_match( '/^\d+(\.\d+)?(px|rem|em|vh|vw|%)$/', $value ) ) { + return $value; + } + return $default; + } + + /** + * Add settings page. + */ + public static function add_menu() { + add_options_page( + __( 'Light/Dark Toggle', 'blocksy-light-dark-toggle' ), + __( 'Light/Dark Toggle', 'blocksy-light-dark-toggle' ), + 'manage_options', + 'blc-ldt-settings', + array( __CLASS__, 'render_page' ) + ); + } + + /** + * Settings link on plugins list. + * + * @param string[] $links Plugin action links. + * @return string[] + */ + public static function plugin_links( $links ) { + $links[] = '' . esc_html__( 'Settings', 'blocksy-light-dark-toggle' ) . ''; + return $links; + } + + /** + * Behavior section description. + */ + public static function section_behavior() { + echo '

' . esc_html__( 'These options control how the toggle interacts with Blocksy and visitor preferences.', 'blocksy-light-dark-toggle' ) . '

'; + } + + /** + * Render settings page. + */ + public static function render_page() { + if ( ! current_user_can( 'manage_options' ) ) { + return; + } + + $has_color_mode = BLC_LDT::has_blocksy_color_mode(); + ?> +
+

+ + +

+ +

+ +

+ Colour Mode Switch extension and configure your dark palette in the Customizer. The plugin will then use your custom dark colors instead of the built-in fallback palette.', 'blocksy-light-dark-toggle' ) + ); + ?> +

+ +

+ +

+ + +
+ +
+
+ __( 'Bottom right', 'blocksy-light-dark-toggle' ), + 'bottom-left' => __( 'Bottom left', 'blocksy-light-dark-toggle' ), + 'top-right' => __( 'Top right', 'blocksy-light-dark-toggle' ), + 'top-left' => __( 'Top left', 'blocksy-light-dark-toggle' ), + ); + ?> + + + +

+ + +

+ + + + + + + + + + + + + get_template(); + } + + /** + * Whether Blocksy's native Colour Mode Switch extension is active. + * + * @return bool + */ + public static function has_blocksy_color_mode() { + $extensions = get_option( 'blocksy_active_extensions', array() ); + if ( is_array( $extensions ) && in_array( 'color-mode-switch', $extensions, true ) ) { + return true; + } + + if ( function_exists( 'blc_get_extensions_status' ) ) { + $status = blc_get_extensions_status(); + if ( is_array( $status ) && ! empty( $status['color-mode-switch'] ) ) { + return true; + } + } + + return (bool) apply_filters( 'blc_ldt_has_blocksy_color_mode', false ); + } + + /** + * Whether the toggle should render on the current request. + * + * @return bool + */ + public static function should_render() { + if ( is_admin() || wp_doing_ajax() || is_customize_preview() ) { + return false; + } + + $settings = BLC_LDT_Settings::get(); + if ( empty( $settings['show_on_mobile'] ) && empty( $settings['show_on_desktop'] ) ) { + return false; + } + + return (bool) apply_filters( 'blc_ldt_should_render', true ); + } + + /** + * Warn in admin if the theme is not supported. + */ + public static function admin_theme_notice() { + if ( self::is_target_theme() || ! current_user_can( 'switch_themes' ) ) { + return; + } + + $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; + if ( $screen && 'plugins' !== $screen->id ) { + return; + } + + echo '

'; + echo esc_html__( + 'Blocksy Light/Dark Toggle is active but Blocksy is not the current theme. The toggle only appears with Blocksy.', + 'blocksy-light-dark-toggle' + ); + echo '

'; + } + + /** + * Prevent flash of wrong theme by applying Blocksy classes before paint. + */ + public static function inline_preference_script() { + if ( ! self::should_render() ) { + return; + } + + $scheme = self::get_initial_scheme(); + $settings = BLC_LDT_Settings::get(); + $storage_keys = array_merge( array( BLC_LDT_STORAGE ), self::BLOCKSY_STORAGE_KEYS ); + + ?> + + ' . esc_html( $css ) . ''; + } + + /** + * Resolve saved scheme for first paint (cookie or user meta only). + * + * @return string light|dark|empty string when none saved. + */ + public static function get_initial_scheme() { + if ( isset( $_COOKIE[ BLC_LDT_COOKIE ] ) ) { + $cookie = sanitize_key( wp_unslash( $_COOKIE[ BLC_LDT_COOKIE ] ) ); + if ( in_array( $cookie, array( 'light', 'dark' ), true ) ) { + return apply_filters( 'blc_ldt_initial_scheme', $cookie ); + } + } + + if ( is_user_logged_in() ) { + $meta = get_user_meta( get_current_user_id(), BLC_LDT_STORAGE, true ); + if ( in_array( $meta, array( 'light', 'dark' ), true ) ) { + return apply_filters( 'blc_ldt_initial_scheme', $meta ); + } + } + + return apply_filters( 'blc_ldt_initial_scheme', '' ); + } + + /** + * Enqueue styles and toggle script. + */ + public static function enqueue_assets() { + if ( ! self::should_render() ) { + return; + } + + if ( ! self::has_blocksy_color_mode() ) { + wp_enqueue_style( + 'blc-ldt-fallback', + BLC_LDT_URL . 'assets/css/dark-fallback.css', + array(), + BLC_LDT_VERSION + ); + } + + wp_enqueue_style( + 'blc-ldt-overrides', + BLC_LDT_URL . 'assets/css/dark-overrides.css', + self::has_blocksy_color_mode() ? array() : array( 'blc-ldt-fallback' ), + BLC_LDT_VERSION + ); + + wp_enqueue_style( + 'blc-ldt-toggle', + BLC_LDT_URL . 'assets/css/toggle.css', + array( 'blc-ldt-overrides' ), + BLC_LDT_VERSION + ); + + wp_enqueue_script( + 'blc-ldt-toggle', + BLC_LDT_URL . 'assets/js/toggle.js', + array(), + BLC_LDT_VERSION, + true + ); + + $settings = BLC_LDT_Settings::get(); + + wp_localize_script( + 'blc-ldt-toggle', + 'blcLdt', + array( + 'ajaxUrl' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'blc_ldt_save' ), + 'storageKey' => BLC_LDT_STORAGE, + 'cookieName' => BLC_LDT_COOKIE, + 'cookiePath' => COOKIEPATH ? COOKIEPATH : '/', + 'cookieDomain' => COOKIE_DOMAIN, + 'isLoggedIn' => is_user_logged_in(), + 'hasBlocksyColorMode' => self::has_blocksy_color_mode(), + 'blocksyStorageKeys' => self::BLOCKSY_STORAGE_KEYS, + 'respectOsPreference' => ! empty( $settings['respect_os_preference'] ), + 'enableAnimation' => ! empty( $settings['enable_animation'] ), + 'i18n' => array( + 'light' => __( 'Switch to light mode', 'blocksy-light-dark-toggle' ), + 'dark' => __( 'Switch to dark mode', 'blocksy-light-dark-toggle' ), + 'lightLabel' => __( 'Light', 'blocksy-light-dark-toggle' ), + 'darkLabel' => __( 'Dark', 'blocksy-light-dark-toggle' ), + ), + ) + ); + } + + /** + * Output floating toggle button. + */ + public static function render_toggle() { + if ( ! self::should_render() ) { + return; + } + + $saved = self::get_initial_scheme(); + $is_dark = 'dark' === $saved; + ?> + + 'Invalid scheme' ), 400 ); + } + + if ( is_user_logged_in() ) { + update_user_meta( get_current_user_id(), BLC_LDT_STORAGE, $scheme ); + } + + wp_send_json_success(); + } +} diff --git a/path/to/your/styles.css b/path/to/your/styles.css new file mode 100644 index 0000000..b069dfa --- /dev/null +++ b/path/to/your/styles.css @@ -0,0 +1,9 @@ +/* Change the font color of the text in the upper grey blocks */ +.upper-grey-blocks { + color: #333; /* Darker color */ +} + +/* If the text is inside a specific element, you can target it directly */ +.upper-grey-blocks p { + color: #333; /* Darker color */ +} \ No newline at end of file diff --git a/uninstall.php b/uninstall.php new file mode 100644 index 0000000..ec1cc75 --- /dev/null +++ b/uninstall.php @@ -0,0 +1,19 @@ +query( + $wpdb->prepare( + "DELETE FROM {$wpdb->usermeta} WHERE meta_key = %s", + 'blc_ldt_scheme' + ) +);