Dark Mode
Pro featureBuddyX 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
- Appearance → Customize → Site Skin
- Set Color Mode to Light or Dark.
- 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.
- Optionally pick a Toggle Position (Floating, Header, Mobile, or Both).
- Optionally customize the dark palette (the "(Dark Mode)" color controls in Site Skin).
- 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:
- Appearance → Customize → Site Identity → Logo
- Upload an alternate logo to Dark Mode Logo
- Use a light-colored version of your brand mark
- 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)"
- Pick your dark accent color → set Dark Brand Color (
dark_site_primary_color). This drives buttons, accent, primary, and accent in block patterns. - Pick your dark page background → set Dark Body Background (
dark_body_background_color). This drives the page bg +--wp--preset--color--basefor blocks. - Pick your dark elevated surface → set Dark Card / Box Background (
dark_box_background_color). This drives cards +--wp--preset--color--base-3for blocks. - 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)"
- Set Color Mode to Dark.
- Set Show color-mode toggle to Off.
- Customize the dark palette to taste.
- 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"
- Set Color Mode to the look you want new visitors to land on (Light or Dark).
- Set Show color-mode toggle to On.
- 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:
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 classbuddyx-dark-themeships in the initial HTML; dark CSS rules apply on the first paint.Synchronous HTML attribute bootstrap. Even before any stylesheet parses, a tiny inline script in
<head>sets<html data-bx-mode="dark|light">by reading thebx-color-modelocalStorage 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"]andbody.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