*/ class DarkModeConflicts { public $options; public function __construct() { add_action('admin_enqueue_scripts', array($this, 'darkmode_scripts'), 100); add_action( 'enqueue_block_editor_assets', array($this, "gutenberg_block_editor_darkmode_assets")); // WP 6.3+/7.0: the block editor canvas runs in an iframe. The AdminifyDarkMode // script has no built-in iframe support, so inject the same script INTO the // iframe and call .enable() from inside — that way Darkreader runs over the // iframe DOM the same dynamic way it runs on the parent admin. // Use admin_footer (not enqueue_block_editor_assets — wp_add_inline_script // silently dropped the bridge) and direct-print via wp_print_inline_script_tag. add_action('admin_footer', array($this, 'inject_darkmode_into_editor_iframe'), 999); // Classic editor (TinyMCE) renders the Visual tab inside its own iframe // (#content_ifr). Same iframe-isolation problem as the block canvas: the // parent Darkreader run can't reach the iframe DOM, so the editor body // stays white in dark mode. Inject the same script into TinyMCE iframes. add_action('admin_footer', array($this, 'inject_darkmode_into_classic_editor_iframe'), 999); // customize.php controls: Adminify scripts are skipped there by design // (Assets::should_skip_adminify_scripts), so enqueue dark-mode only on this // dedicated hook to keep the customize panel dark without the rest of Adminify. add_action('customize_controls_enqueue_scripts', array($this, 'customize_controls_darkmode_enqueue')); // customize.php PREVIEW: the right-side theme preview renders inside its own // iframe (a full frontend render). The controls-panel enqueue above never // reaches it, so the previewed site stays light in dark mode. customize_preview_init // fires inside the preview frame context — enqueue dark-mode there to darken the // previewed theme the same dynamic Darkreader way, matching the classic editor. add_action('customize_preview_init', array($this, 'customize_preview_darkmode_enqueue')); } public function darkmode_scripts() { $this->options = (array) AdminSettings::get_instance()->get(); $this->options = !empty($this->options['light_dark_mode']['admin_ui_mode']) ? $this->options['light_dark_mode']['admin_ui_mode'] : ''; if ($this->options == 'light') { return; } $dark_mode_style = '.ReactModal__Content, .select2-selection, .select2-results__options, .adminify-dark-mode.toplevel_page_aio-contact-lite, .aio-contact-menu, .bsr-action-form input.regular-text, .select2-dropdown, .form-cancel-btn, .folders-tabs, .folder-tab-menu, .folder-tab-menu a, .custom-checkbox span, .preview-inner-box, .preview-inner-box .form-options, .media-buttons, .popup-form-content, .select2-results__option, div.tagsinput, .settings-tabs-list .nav-tab, input[type=number], .bulk-action-select .bulk-action-select__control, .bulk-action-select__menu, .wprf-section-fields, .wprf-section-title, .wprf-input-radio-option:not(.wprf-option-selected) .wprf-input-label, .wprf-control-field .components-button, .wprf-select__menu, .wprf-select__control, .wprf-tab-content, .squirrly-seo-settings #wpcontent .bg-light, .bootstrap-select, .bg-white, .wdt-addons-intro { background: white !important; } .el-checkbox.is-checked ~ div, .fluentcrm_header { background: none!important; } .folder-user-settings .pro-feature-popup { background: rgb(0, 0, 0, 0.4) } .big-pluspro-btn, .dokan-btn, .close_dp_help, .dup-btn, .folder-tab-menu a, .pro-feature-popup .pro-feature-content > a, .user-upgrade-inline-btn, .pro-feature-popup .pro-feature-content .pro-user-title, .pro-feature-popup .pro-feature-content .pro-user-desc, .folder-access, .add-new-folder, .media-select option, .popup-form-content, .nf-button:hover, .wp-react-form h1, .wp-react-form h2, .wp-react-form h3, .wp-react-form h4, .wprf-input-label, .wprf-tab-nav-item:not(:hover), .swift-btn, .wpcode-button, .wdt-constructor-type-selecter-block .card-body h4, .wdt-constructor-type-selecter-block .card-body h4 span, .yoast-button-upsell, .aioseo-button:hover, .el-button { color: white !important; } .folders-tabs .dashicons-editor-help, .folder-tab-menu a.active, .folder-list li a span, .select2-selection__rendered, .fp-item-name, .select2-results__option, .form-sample, input[type=number], .wprf-select__single-value, .wprf-code-viewer textarea, .wdt-constructor-type-selecter-block .card-body span, .mce-menu-item > span { color: gray !important; } .select2-selection, .bsr-action-form input.regular-text, #bsr-table-select, .custom-checkbox input+span, .preview-inner-box, div.tagsinput, input[type=number], .bulk-action-select .bulk-action-select__control, .wprf-tab-nav-item, .wprf-control-field .components-button, .wprf-select__control, .wprf-control-field input[type="checkbox"], .bootstrap-select { border-color: #D0D5DD !important; } .dokan-admin-header-logo img, .dup-header img { filter: grayscale(1) brightness(5); } .jetpack-logo, .it-ui-list div[direction="horizontal"] > svg { fill: black !important; } .jp-form-block-fade { opacity: 0.5; } '; // Elementor Style issues // if (Utils::is_plugin_active('elementor/elementor.php')) { // $dark_mode_style .= '.darkmode{ // background: white !important; // }'; // } // Better Links Style issues if (Utils::is_plugin_active('betterlinks/betterlinks.php')) { $dark_mode_style .= '#betterlinksbody .kpyoXL, .btl-react-select__menu, #betterlinksbody .jlDjrx { background: white !important; } #betterlinksbody .kpyoXL{ color: white !important; } '; } // Forminator Style issues if (Utils::is_plugin_active('forminator/forminator.php')) { $dark_mode_style .= '.sui-box, .sui-icon-plugin-2, .sui-vertical-tab a.current, .sui-wrap .fui-multi-answers, .sui-wrap .fui-multi-answers .fui-answers>li, .sui-dropdown.open ul, .sui-notice-content, .sui-wrap .sui-box-selectors, .sui-select-dropdown, .sui-accordion-item, .sui-tabs-menu, .forminator-addon-card--footer, .sui-vertical-tab.current, .sui-form-control, select#forminator-field-user_role, .forminator-save-field-settings { background: white !important; } .sui-upgrade-page, .sui-upgrade-page-header, .sui-upgrade-page-cta { background: none!important; } .sui-header-title, .sui-summary-large, .sui-list-label, .sui-box-title, .sui-wrap h1, .sui-wrap h2, .sui-wrap h3, .sui-button, .sui-vertical-tab a.current, .sui-table thead>tr>th, .sui-trim-text, .sui-message-content h2, .sui-tab-item.active, .sui-vertical-tab.current a, select#forminator-field-user_role { color: white !important; } .sui-upsell-list li, .sui-table-item-title, .sui-vertical-tab a, .sui-form-control, .sui-settings-label { color: gray !important; } .sui-list li, .sui-box-header, #sui-cross-sell-footer>div, .sui-box-settings-row, .sui-dropdown.open ul, .sui-box-footer, .sui-status-changes, .sui-table thead>tr>th, .sui-select-dropdown, .sui-tab-content, .forminator-addon-card--footer, .sui-table.fui-table--apps, .sui-table tbody>tr>td, .sui-tabs-overflow, .sui-form-control, .sui-button, select#forminator-field-user_role { border-color: #D0D5DD !important; } .sui-dropdown.open ul::after, .sui-dropdown.open ul::before { border-color: #D0D5DD rgba(0, 0, 0, 0)!important; } .sui-box, .sui-accordion-item, .sui-tabs-menu { box-shadow: none!important; } .sui-chartjs-message--empty { background-blend-mode: exclusion; } .sui-tag, .sui-tabs-menu .sui-tab-item { background: #6f6f6f!important; color: #fff!important; } .sui-tab-item.active { background: black!important; } '; } // Loginpress if (Utils::is_plugin_active('loginpress/loginpress.php')) { $dark_mode_style .= '.loginpress-help-page pre textarea, .loginpress-import-export-page .upload-file, .loginpress-extension { background: white !important; } .toplevel_page_loginpress-settings #wpcontent, .loginpress_page_loginpress-help #wpcontent, .loginpress_page_loginpress-import-export #wpcontent, .loginpress-header-wrapper, .loginpress-settings { background: none!important; } .loginpress-help-page h2, .loginpress-import-export-page h2, .loginpress-addons-wrap h2, .loginpress-extension h3 span, .loginpress-settings h3{ color: white !important; } .loginpress-help-page pre textarea, .loginpress-import-export-page > div, .loginpress-settings .form-table tr th, .loginpress-settings .form-table tr td fieldset label { color: gray !important } .loginpress-help-page pre textarea, .loginpress-import-export-page .upload-file, .loginpress-extension h3, .loginpress-extension { border-color: #D0D5DD !important; } .loginpress-header-logo img { filter: grayscale(1) brightness(5); } '; } // Notificationx Style issues if (Utils::is_plugin_active('notificationx/notificationx.php')) { $dark_mode_style .= '.nx-analytics-counter, #notificationx .notificationx-items .nx-admin-menu>ul li:not(.nx-active) a, .nx-admin-sidebar .sidebar-widget, .nx-settings-right, .nx-admin-block, .nx-sidebar, .nx-quick-builder-wrapper { background: white !important; } .nx-button:not(:hover) { background: none!important; } .nx-counter-number, .nx-admin-title { color: white !important; } #notificationx .notificationx-items .nx-admin-menu>ul li:not(.nx-active) a, #nx-title, #notification-template, .nx-admin-sidebar-cta a:not(:hover) { color: gray !important; } #notificationx .notificationx-items .nx-admin-menu>ul li:not(.nx-active) a, #nx-title, .nx-widget-title, #notification-template { border-color: #D0D5DD !important; } .nx-admin-sidebar-logo img { filter: grayscale(1) brightness(5); } .nx-admin-header svg g { fill: #fff!important; } '; } // Wpdatatables Style issues if (Utils::is_plugin_active('wpdatatables/wpdatatables.php')) { $dark_mode_style .= '.wpdt-c.toplevel_page_wpdatatables-dashboard, .wpdt-c .wdt-datatables-admin-wrap .plugin-dashboard, .wpdt-c.wpdatatables_page_wpdatatables-administration, .card-header.wdt-admin-card-header.ch-alt, #wdt-datatables-browse-table, .manage-column, .wpdt-c.wpdatatables_page_wpdatatables-constructor, .wdt-constructor-type-selecter-block .card-header, .wpdt-c, .chart-wizard-breadcrumb, .wpdt-c .chart-name, .wpdt-c .render-engine { background: white !important; } '; } $dark_mode_style = preg_replace('#/\*.*?\*/#s', '', $dark_mode_style); $dark_mode_style = preg_replace('/\s*([{}|:;,])\s+/', '$1', $dark_mode_style); $dark_mode_style = preg_replace('/\s\s+(.*)/', '$1', $dark_mode_style); wp_add_inline_style('adminify-admin', wp_strip_all_tags($dark_mode_style)); } /** * Gutenberg Block Editor Dark Mode CSS */ function gutenberg_block_editor_darkmode_assets () { wp_register_style('adminify-gutenberg-dark', false, array(), PXLBSADMINIFY_VER); wp_enqueue_style('adminify-gutenberg-dark'); // $parent_selector = 'body.wp-adminify.adminify-dark-mode'; $parent_selector = 'body.wp-adminify'; $dark_mode_style = "$parent_selector .components-modal__content .components-text, $parent_selector .components-popover__content .components-text { color: black!important; } $parent_selector .block-editor-block-inspector .components-tools-panel { border-top-color: #e0e0e0; } $parent_selector .admin-ui-navigable-region .components-panel__header > div > button { color: black; } $parent_selector .editor-sidebar__panel .editor-post-card-panel__header svg, $parent_selector .commands-command-menu__container .commands-command-menu__header svg { fill: black; } "; wp_add_inline_style('adminify-gutenberg-dark', wp_strip_all_tags($dark_mode_style)); } /** * Resolve the active light/dark mode for the current user. * Per-user `color_mode` meta takes precedence over the global setting. * Returns 'light' | 'dark' | 'auto' | ''. */ public function pxlbsadminify_resolve_color_mode() { $opts = (array) AdminSettings::get_instance()->get(); $global_mode = !empty($opts['light_dark_mode']['admin_ui_mode']) ? $opts['light_dark_mode']['admin_ui_mode'] : 'light'; $user_mode = get_user_meta(get_current_user_id(), 'color_mode', true); return !empty($user_mode) ? $user_mode : $global_mode; } /** * Inject the AdminifyDarkMode script INTO the WP 6.3+/7.0 block editor iframe * canvas. The script attaches a global `window.AdminifyDarkMode` and uses a * Darkreader-style dynamic theme that rewrites colors based on the running * document — so it must be loaded inside the iframe's window to darken the * canvas DOM, not just the parent admin chrome. * * The bridge below watches for `iframe[name="editor-canvas"]` (added/changed * across edits or full-site editor route changes), injects the dark-mode JS * into the iframe's document, and calls `AdminifyDarkMode.enable()` once it * loads. `__adminifyDarkInjected` guards against double-injection. */ public function inject_darkmode_into_editor_iframe() { $mode = $this->pxlbsadminify_resolve_color_mode(); if ( $mode === 'auto' ) { $mode = 'system'; } // 'dark' = always on; 'system' = follow OS prefers-color-scheme (decided client-side). if ( $mode !== 'dark' && $mode !== 'system' ) { return; } // Only run on block-editor pages (post.php / post-new.php / site editor). $screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; if ( ! $screen || ! method_exists( $screen, 'is_block_editor' ) || ! $screen->is_block_editor() ) { // also accept Site Editor screen ids $is_site_editor = $screen && in_array( $screen->id, array( 'site-editor', 'gutenberg_page_gutenberg-edit-site' ), true ); if ( ! $is_site_editor ) { return; } } $dark_js_url = PXLBSADMINIFY_ASSETS . 'admin/js/wp-adminify-dark-mode' . Utils::assets_ext( '.js' ); // For 'system' mode, only proceed when the OS currently prefers dark; otherwise bail // out of the bridge entirely so the iframe stays light (matches the parent admin). $apply_guard = ( $mode === 'system' ) ? 'if(!(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches)){return;}' : ''; $bridge = '(function(){' . $apply_guard . 'var URL=' . wp_json_encode( $dark_js_url ) . ';' . 'function looksLikeEditor(ifr){try{if(ifr.name==="editor-canvas")return true;var d=ifr.contentDocument;return !!(d&&d.body&&d.body.classList&&(d.body.classList.contains("block-editor-iframe__body")||d.body.classList.contains("editor-styles-wrapper")));}catch(e){return false;}}' . 'function activate(w){try{if(w&&w.AdminifyDarkMode){w.AdminifyDarkMode.enable({brightness:120});return true;}}catch(e){}return false;}' . 'function inject(ifr){try{var d=ifr.contentDocument,w=ifr.contentWindow;if(!d||!w)return;if(w.__adminifyDM){activate(w);return;}w.__adminifyDM=true;if(activate(w))return;var s=d.createElement("script");s.src=URL;s.onload=function(){if(!activate(w)){setTimeout(function(){activate(w);},100);setTimeout(function(){activate(w);},500);}};(d.head||d.documentElement||d.body).appendChild(s);}catch(e){}}' . 'var seen=new WeakSet();' . 'function watch(ifr){if(seen.has(ifr))return;seen.add(ifr);inject(ifr);ifr.addEventListener("load",function(){try{ifr.contentWindow.__adminifyDM=false;}catch(e){}inject(ifr);});}' . 'function scan(){var all=document.querySelectorAll("iframe");for(var i=0;i' . $bridge . ''; // phpcs:ignore WordPress.WP.EnqueuedResources } return; // sentinel — never reach legacy enqueue path below } /** * Inject the AdminifyDarkMode script INTO every classic-editor TinyMCE iframe. * * The TinyMCE Visual tab renders post content inside an iframe (`#content_ifr`, * generally `{editor_id}_ifr`) whose body carries the `mce-content-body` class. * As with the block-editor canvas, the parent Darkreader run never touches the * iframe document, so the editor background stays white in dark mode. The bridge * below watches for TinyMCE iframes (created lazily, and re-created when toggling * Visual/Text or initializing extra editors), injects the dark-mode JS into each * iframe's document and calls `AdminifyDarkMode.enable()` from inside. `__adminifyDM` * guards against double-injection per iframe window. */ public function inject_darkmode_into_classic_editor_iframe() { $mode = $this->pxlbsadminify_resolve_color_mode(); if ( $mode === 'auto' ) { $mode = 'system'; } // 'dark' = always on; 'system' = follow OS prefers-color-scheme (decided client-side). if ( $mode !== 'dark' && $mode !== 'system' ) { return; } $dark_js_url = PXLBSADMINIFY_ASSETS . 'admin/js/wp-adminify-dark-mode' . Utils::assets_ext( '.js' ); // For 'system' mode, only proceed when the OS currently prefers dark; otherwise bail // out of the bridge entirely so the iframe stays light (matches the parent admin). $apply_guard = ( $mode === 'system' ) ? 'if(!(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches)){return;}' : ''; $bridge = '(function(){' . $apply_guard . 'var URL=' . wp_json_encode( $dark_js_url ) . ';' . 'function looksLikeMCE(ifr){try{if(/_ifr$/.test(ifr.id||""))return true;var d=ifr.contentDocument;return !!(d&&d.body&&d.body.classList&&d.body.classList.contains("mce-content-body"));}catch(e){return false;}}' . 'function activate(w){try{if(w&&w.AdminifyDarkMode){w.AdminifyDarkMode.enable({brightness:120});return true;}}catch(e){}return false;}' . 'function inject(ifr){try{var d=ifr.contentDocument,w=ifr.contentWindow;if(!d||!w||!d.body)return;if(w.__adminifyDM){activate(w);return;}w.__adminifyDM=true;if(activate(w))return;var s=d.createElement("script");s.src=URL;s.onload=function(){if(!activate(w)){setTimeout(function(){activate(w);},100);setTimeout(function(){activate(w);},500);}};(d.head||d.documentElement||d.body).appendChild(s);}catch(e){}}' . 'var seen=new WeakSet();' . 'function watch(ifr){if(seen.has(ifr))return;seen.add(ifr);inject(ifr);ifr.addEventListener("load",function(){try{ifr.contentWindow.__adminifyDM=false;}catch(e){}inject(ifr);});}' . 'function scan(){var all=document.querySelectorAll("iframe");for(var i=0;i' . $bridge . ''; // phpcs:ignore WordPress.WP.EnqueuedResources } } /** * customize.php controls: Adminify's general scripts are deliberately skipped on * customize.php (Assets::should_skip_adminify_scripts), so dark-mode never reaches * the controls panel. Enqueue dark-mode standalone on the customize controls hook. */ public function customize_controls_darkmode_enqueue() { $mode = $this->pxlbsadminify_resolve_color_mode(); if ($mode === 'auto') { $mode = 'system'; } if ($mode !== 'dark' && $mode !== 'system') { return; } wp_enqueue_script( 'adminify--dark-mode', PXLBSADMINIFY_ASSETS . 'admin/js/wp-adminify-dark-mode' . Utils::assets_ext('.js'), array(), PXLBSADMINIFY_VER, false ); wp_add_inline_script('adminify--dark-mode', $this->darkmode_enable_inline($mode)); } /** * Build the inline enable() snippet. For 'dark' it always enables; for 'system' it * enables only when the OS currently prefers dark (prefers-color-scheme), matching * the parent-admin behavior in Assets::header_scripts. */ private function darkmode_enable_inline($mode) { $enable = ($mode === 'system') ? 'if(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches){window.AdminifyDarkMode.enable({brightness:120});}' : 'window.AdminifyDarkMode.enable({brightness:120});'; return 'if(window.AdminifyDarkMode){' . $enable . '}' . 'addEventListener("load",function(){if(window.AdminifyDarkMode){' . $enable . '}});'; } /** * customize.php preview: the previewed theme renders inside the preview iframe, * which the controls-panel enqueue never reaches. customize_preview_init runs in * the preview frame's frontend context, so enqueue the dark-mode JS here and call * .enable() to darken the previewed site dynamically — same as the classic editor. */ public function customize_preview_darkmode_enqueue() { $mode = $this->pxlbsadminify_resolve_color_mode(); if ($mode === 'auto') { $mode = 'system'; } if ($mode !== 'dark' && $mode !== 'system') { return; } wp_enqueue_script( 'adminify--dark-mode', PXLBSADMINIFY_ASSETS . 'admin/js/wp-adminify-dark-mode' . Utils::assets_ext('.js'), array(), PXLBSADMINIFY_VER, false ); wp_add_inline_script('adminify--dark-mode', $this->darkmode_enable_inline($mode)); } }