BuddyX

Dark Mode

Pro feature

BuddyX Pro 5.1.0 ships a complete dark-mode system: two color modes (light / dark), a fully brandable dark palette (45 customizer controls), block-pattern dark coverage, and a first-paint architecture that eliminates the light → dark flash on initial page load.

This page covers everything from the basic "turn dark mode on" workflow through the developer-level architecture. Pick the section that matches your task.


Quick start

  1. Appearance → Customize → Site Skin
  2. Set Color Mode to Light or Dark.
  3. Show color-mode toggle is On by default - leave it on so visitors can switch modes themselves, or set it to Off to lock one look.
  4. Optionally pick a Toggle Position (Floating, Header, Mobile, or Both).
  5. Optionally customize the dark palette (the "(Dark Mode)" color controls in Site Skin).
  6. Click Publish.

That's the minimum. The rest of this page is the full reference.


The two color modes

Set with Site Skin → Color Mode (site_color_mode):

Mode What loads Visitor can override?
Light Site renders in light colors on every visit. Dark palette never applies. Only if Show Toggle is On - then visitors can toggle to dark.
Dark Site renders in dark colors on every visit. Dark palette is the default surface. Only if Show Toggle is On - then visitors can toggle to light.

Default: Light. New installs start with light mode because it's the safest assumption.

Visitor override behavior

When Show Toggle is On, every visitor can pick their own mode by clicking the sun/moon icon. Their choice persists across page loads via cookie + localStorage.

If the admin later changes the site default (e.g. flips from Light to Dark), stored visitor overrides whose stamp doesn't match the new default are discarded - so the new default reaches everyone, including visitors who had previously toggled. This avoids "stuck on the old default" complaints when a site rebrands its dark mode.


Color Mode Toggle controls

Setting Key Options Default Notes
Show color-mode toggle site_color_mode_toggle_show On / Off On When Off, no toggle UI renders - visitors get only the site default. On by default so the toggle matches BuddyX free out of the box.
Toggle position site_color_mode_toggle_position Floating / Header / Mobile / Both Floating Floating shows a small button on the side of the screen. Header places it next to the menu icons on desktop. Mobile shows it only on mobile (next to the hamburger). Both shows it in both the header AND mobile menu.

Hide the toggle on sites that need a single visual identity (one-mode-only). Use Floating when the header is already crowded with menu items, cart, and notifications.


Dark palette - every customizer control

The Site Skin section of the Customizer exposes 45 dark-mode color controls (each labeled "(Dark Mode)"). Each pairs with a light-mode control of the same name (e.g. body_background_color for light, dark_body_background_color for dark) so you can brand both modes independently.

These controls only render visibly when site_color_mode is Dark or when the visitor toggles to dark. They also require the Set Custom Colors? master switch (site_custom_colors, default Yes) to be on - the same switch that gates the light palette (see Color Scheme). Set it to No and every per-cluster picker, light and dark, is hidden.

Page surfaces

Setting Key Default Purpose
Page background dark_body_background_color #1A1A2E The body backdrop visitors see behind content (midnight blue baseline).
Content background dark_content_background_color #111 Inside content containers (post body, page body).
Secondary background dark_secondary_background_color #0F3460 Subtle elevation - tinted shade above the page bg.
Card / box background dark_box_background_color #16213E Cards, panels, widgets, elevated surfaces.

Text

Setting Key Default Purpose
Body text dark_body_text_color #bfc2c9 Default text color across the site.
Headings dark_headings_color #fcfcfc H1-H6.
Borders dark_site_border_color #1d3155 Dividers, table cell borders, input outlines.

Brand + accents

Setting Key Default Purpose
Brand color dark_site_primary_color #E94560 The brand accent for dark mode (a softer red than the light-mode #ee4036). Drives --bx-color-accent and the matching --wp--preset--color--primary / --wp--preset--color--accent block tokens.
Site title dark_site_title_color #fcfcfc Logo text color when no image logo is set.
Site title hover dark_site_title_hover_color #e94560 Hover state of the title link.
Links dark_site_links_color #fcfcfc All in-content <a> elements.
Link focus / hover dark_site_links_focus_hover_color #E94560 Hover + keyboard focus state.

Buttons

Setting Key Default Purpose
Button background dark_site_buttons_background_color #E94560 Default button bg.
Button background hover dark_site_buttons_background_hover_color #f83939 Hover state.
Button text dark_site_buttons_text_color #ffffff Text color on the button.
Button text hover dark_site_buttons_text_hover_color #ffffff Hover text.
Button border dark_site_buttons_border_color #E94560 Border color (matches bg by default).
Button border hover dark_site_buttons_border_hover_color #f83939 Hover border.

Header

Setting Key Default Purpose
Header background dark_site_header_bg_color #16213E The header strip behind the logo + menu.
Menu link dark_menu_color #fcfcfc Top-level nav links.
Menu link hover dark_menu_hover_color #E94560 Hover state.
Menu link active dark_menu_active_color #E94560 Active / current-page state.

Sub-header

Setting Key Default Purpose
Sub-header background dark_site_subheader_bg_color #0F3460 The breadcrumb / page-title strip.
Sub-header title dark_subheader_title_color #fcfcfc Title text in the sub-header.

Topbar

Setting Key Default Purpose
Topbar background dark_topbar_background_color #E94560 The utility bar above the header.
Topbar text dark_topbar_content_color #ffffff Plain text in the topbar.
Topbar links dark_topbar_links_color #ffffff Link color.
Topbar links hover dark_topbar_links_hover_color #E94560 Hover state.

Side panel (slide-out menu)

Setting Key Default Purpose
Side panel background dark_side_panel_background_color #16213E The slide-out panel surface.
Toggle icon dark_side_panel_toggle_color #E94560 The hamburger icon that opens the panel.
Menu link dark_side_panel_menu_color #fcfcfc Links inside the panel.
Menu link hover dark_side_panel_menu_hover_color #E94560 Hover state.
Menu link active dark_side_panel_menu_active_color #E94560 Active state.
Menu link bg hover dark_side_panel_menu_bg_hover_color #0F3460 Background of a hovered link row.
Menu link bg active dark_side_panel_menu_bg_active_color #0F3460 Background of the active link row.

Footer

Setting Key Default Purpose
Footer background dark_site_footer_background_color #16213E Footer area surface.
Footer title dark_site_footer_title_color #fcfcfc Widget titles.
Footer text dark_site_footer_content_color #bfc2c9 Widget body text.
Footer links dark_site_footer_links_color #fcfcfc In-footer links.
Footer links hover dark_site_footer_links_hover_color #E94560 Hover state.

Copyright bar

Setting Key Default Purpose
Copyright background dark_site_copyright_background_color #16213E Copyright strip below the footer.
Copyright text dark_site_copyright_content_color #bfc2c9 Text color.
Copyright links dark_site_copyright_links_color #fcfcfc Links.
Copyright links hover dark_site_copyright_links_hover_color #E94560 Hover state.

Loader

Setting Key Default Purpose
Loader background dark_site_loader_bg #E94560 The site-loader overlay background in dark mode.

The defaults above ship as a coordinated midnight-blue palette (deep navy surfaces, off-white text, a soft red-pink accent). When the customizer doesn't have a saved value AND the customer hasn't customized the field, the token cascade emits a separate set of fallback values from Tokens\Component::$dark_color_defaults (#0a0a0a / #161616 / #f5f5f5 / #ff6b6b) - those are the values that paint for installs that explicitly clear the customizer fields. Most customers will see and customize the midnight-blue defaults; only sites that deliberately reset customizer values to empty fall through to the lower fallback tier.


Block patterns + dark mode

5.1.0 ships dark equivalents for the WordPress block-library color presets that BuddyX Pro's theme.json declares. This means pattern-driven landing pages (home, About, Pricing, FAQ, etc.) flip to dark alongside the theme chrome.

Covered preset slugs

The dark mapping covers every semantic slug in the theme.json palette:

Slug Light value Dark value Driven by
base #ffffff #0a0a0a dark_body_background_color
base-2 #FAFAFA #101010 dark_secondary_background_color
base-3 #F4F4F4 #161616 dark_box_background_color
tertiary #F6F6F6 #161616 dark_box_background_color
contrast #1a1a1a #f5f5f5 dark_body_text_color
contrast-2 #3D3D3D #d0d0d0 static fallback
contrast-3 #6E6E6E #a0a0a0 static fallback
primary #ef5455 #ff6b6b dark_site_primary_color
accent #D83734 #ff6b6b dark_site_primary_color (same source)
secondary #41848f #5aa3ae static fallback
accent-2 #3A7882 #4a96a3 static fallback
accent-3 #F4D35E #f4d35e static fallback (yellow works on both modes)
neutral #666666 #888888 static fallback
surface-1 #FFF8F2 #1a1612 static fallback (warm-tinted dark)
surface-2 #F2F7F8 #121518 static fallback (cool-tinted dark)

When you customize dark_body_background_color (or any other "driven by" key above), both the theme chrome (--bx-color-bg) and the matching block preset (--wp--preset--color--base) update from the same source. This is the single source of truth guarantee: one customizer change → both color systems flip together.

Slugs deliberately not overridden

Slug Light value Why no dark override
surface-3 #1F2937 Already a dark color in light mode; doesn't need flipping.
transparent-accent color-mix(...) Mode-agnostic expression.
black, white, red, vivid-red, cyan-bluish-gray, named-hue presets literal hex These encode an absolute color the block author chose deliberately (e.g. has-black-background-color = "I want black background"). Flipping them would violate author intent.

If a child theme adds new palette slugs to its own theme.json, extend the dark mapping via the buddyxpro_dark_wp_preset_map filter (see For developers below).


No flash on load

Your visitors see the right color mode the moment the page appears - there's no jarring flash from light to dark on first load, even on the very first visit. We worked hard on this; you don't have to think about it.

(Developers and child-theme authors curious about how this works - see For developers at the bottom of this page for the architectural details.)


Per-entry override (page-settings)

For sites that want certain pages to ignore the site default (e.g. a "dark only" landing page on a light site), open the page in the WP editor and look for the Page Settings → Display → Color Mode field (_buddyxpro_page_color_mode):

Page setting Effect
Use site default (empty) Falls through to site_color_mode.
Force light This page renders in light regardless of visitor preference.
Force dark This page renders in dark regardless of visitor preference.

The toggle UI continues to render (if enabled site-wide) but the visitor's choice doesn't override the page-level force.


Dark mode logo

If your regular logo has dark colors that won't read on dark backgrounds:

  1. Appearance → Customize → Site Identity → Logo
  2. Upload an alternate logo to Dark Mode Logo
  3. Use a light-colored version of your brand mark
  4. Keep the dimensions identical to the regular logo (the swap is instant; the layout doesn't reflow)

The swap fires automatically when dark mode applies - no admin action needed beyond uploading the alternate file.


Common workflows

"I want to brand my dark mode (not use the defaults)"

  1. Pick your dark accent color → set Dark Brand Color (dark_site_primary_color). This drives buttons, accent, primary, and accent in block patterns.
  2. Pick your dark page background → set Dark Body Background (dark_body_background_color). This drives the page bg + --wp--preset--color--base for blocks.
  3. Pick your dark elevated surface → set Dark Card / Box Background (dark_box_background_color). This drives cards + --wp--preset--color--base-3 for blocks.
  4. Upload a dark-mode logo if needed.

That covers the 80% case - the rest of the dark palette can use the WCAG-AA defaults.

"I want my site to only ever be dark (no light mode)"

  1. Set Color Mode to Dark.
  2. Set Show color-mode toggle to Off.
  3. Customize the dark palette to taste.
  4. Publish.

Visitors land directly in dark mode with no toggle UI. The light palette controls still exist in the customizer (they're never made invisible), but they never paint because there's no path to light mode.

"I want maximum visitor control"

  1. Set Color Mode to the look you want new visitors to land on (Light or Dark).
  2. Set Show color-mode toggle to On.
  3. Set Toggle Position to Header (most discoverable) or Both.

Every visitor lands on your default and can switch to the other mode with one click. Their choice persists per-device via cookie + localStorage.


Plugin compatibility

The dark token system applies automatically to:

Plugin What's covered
BuddyPress / BuddyBoss Activity stream, member directory, member profile, group pages, messages, notifications
WooCommerce Shop archive, single product, cart, checkout, my-account
LearnDash Course archive, course/lesson/topic templates, focus mode (additional --ld-dark-mode-* tokens)
bbPress Forum index, single forum, single topic
Dokan / WCFM / MultiVendorX Vendor dashboards, store pages
FluentCart, SureCart Cart + checkout surfaces
Better Messages Conversation list, threads, chat header/footer, search + message inputs (maps the plugin's own dark palette)
GamiPress Points widgets, achievement lists, rank titles + descriptions
rtMedia Media galleries, media-option menus, upload controls, "no media" message
BP Verified Member Request-verification button (the verified badge + tooltip are dark-safe by design)

The add-on overrides (Better Messages, GamiPress, rtMedia, BP Verified Member) ship as small per-plugin stylesheets that load only when the plugin is active (added in 5.1.1).

If a third-party plugin renders content with hardcoded colors (instead of using BuddyX Pro's tokens), that plugin won't flip - file a support ticket and the team can usually add compatibility overrides.


For developers

First-paint architecture (no FOUC)

5.1.0 paints the correct color mode from the first rendered frame - no JavaScript-driven flash from light to dark on page load. Two independent first-paint pathways converge on the same outcome:

  1. Server-side resolution from cookie / user meta. When a visitor has previously toggled their preference, the server reads the cookie (buddyx_dark_mode) before rendering. The body class buddyx-dark-theme ships in the initial HTML; dark CSS rules apply on the first paint.

  2. Synchronous HTML attribute bootstrap. Even before any stylesheet parses, a tiny inline script in <head> sets <html data-bx-mode="dark|light"> by reading the bx-color-mode localStorage key. The CSS selector :root[data-bx-mode="dark"] then applies dark tokens on the first paint, covering the case where the cookie expired but localStorage still holds the preference.

Together these eliminate FOUC (Flash of Unstyled Content) in the scenarios that pre-5.1.0 caused a visible flash:

Scenario Pre-5.1.0 5.1.0
Cookie path (returning visitor toggled dark) No flash No flash
localStorage-only (cookie cleared / private mode) Light → dark flash on DOMContentLoaded No flash (HTML attribute path)

The token cascade

Every customizer color value flows through inc/Tokens/Component.php and emits as a CSS custom property:

  • Light mode: declared on :root (default).
  • Dark mode: declared on :root[data-bx-mode="dark"] and body.buddyx-dark-theme.

The two selectors are additive - either of them matching makes the dark tokens apply.

The token vocabulary itself is documented in docs/buddyx-pro-design-tokens.md.

Filter the dark block-preset mapping

Child themes that extend theme.json's palette can add their own dark equivalents via buddyxpro_dark_wp_preset_map:

add_filter( 'buddyxpro_dark_wp_preset_map', function ( $map ) {
    // Add a dark mapping for a child-theme palette slug 'forest'.
    $map['--wp--preset--color--forest'] = array(
        'key'      => null,         // No customizer key - static dark value.
        'fallback' => '#0e1f15',
    );
    // Drive a slug from an existing dark customizer key.
    $map['--wp--preset--color--ocean'] = array(
        'key'      => 'dark_site_primary_color',  // Existing customizer key.
        'fallback' => '#5aa3ae',
    );
    return $map;
} );

Each entry's key (if non-null) points at a dark_* customizer setting in $dark_color_defaults - the customer's customizer value drives the preset. When key is null, the fallback static value is used.

Filter the resolved color mode server-side

The buddyx_resolve_color_mode() function (in inc/Helpers/Dark_Mode.php) returns the canonical mode for the current request. Filter via body_class if you need to bolt-on additional behavior:

add_filter( 'body_class', function ( $classes ) {
    if ( in_array( 'buddyx-dark-theme', $classes, true ) ) {
        $classes[] = 'my-plugin-dark-extras';
    }
    return $classes;
} );

Defer dark CSS file (advanced)

The dark token CSS is inline in <head> (emitted by Tokens\Component::build_token_css()). For LearnDash-specific dark overrides, the separate stylesheet assets/css/dark-mode.min.css only loads when class_exists('SFWD_LMS') returns true (manifest-gated). See docs/developer/asset-manifest.md for the asset loading architecture.


Common questions

Can I hide the toggle? Yes - Appearance → Customize → Site Skin → Show color-mode toggle = Off. No CSS overrides required.

What are the toggle position options? Four: Floating (a small button on the side of the screen - the default), Header (next to the desktop menu icons), Mobile (in the mobile menu drawer only), and Both (header on desktop and mobile menu together). Exactly one placement renders per request based on this setting.

Does dark mode work with the Site Editor / Full Site Editing? Partially. BuddyX Pro is a classic theme (not a block theme). The theme.json palette is recognized by FSE-style block patterns, but template-level FSE workflows (Templates panel, Template Parts) aren't a primary surface. Dark mode applies to the same surfaces as light mode does on this theme.

Will my dark customizations persist across upgrades? Yes. All dark_* setting IDs are stable across releases. The 5.1.0 release introduced these IDs; subsequent releases (5.1.x, 5.2.x) will not rename or remove them. If a setting is ever retired, a migration runs in inc/Migration/Component.php to preserve customer values.

Does dark mode slow down my site? No. The dark CSS is the same payload as the light CSS - both are emitted inline by Tokens\Component. The mode-switch toggle JS is ~3 KB total. First-paint is dark or light depending on resolution; no flashing.

My logo looks washed out in dark mode. What do I do? Upload a dedicated dark-mode logo (a light-colored version of your brand mark) at Site Identity → Dark Mode Logo. Keep the dimensions identical to the regular logo to avoid layout shift.

Can I make some pages dark-only? Yes - use the page-settings Color Mode field (see Per-entry override above).

The dark mode looks ugly on my landing page (pattern blocks look wrong). You likely have a child theme that added custom theme.json palette slugs without dark equivalents. Add a filter on buddyxpro_dark_wp_preset_map (see developer section) to register dark equivalents for those slugs.


Related

  • Color Scheme - Light-mode palette settings (parallel to this page's dark palette)
  • Color Presets - Pre-built light + dark palette bundles
  • Border Radius - Skin shape customization
  • Site Identity - Logo (including dark-mode logo)
  • Design Tokens - Developer reference for the --bx-color-* token system
  • Asset Manifest - How dark CSS is loaded

Need help? support@wbcomdesigns.com