CSS Design System
BuddyPress Statistics 2.0.x uses a theme-aware token system on the front end. The plugin no longer ships with saturated brand defaults; instead, every visible surface, text color, border, and accent is driven by a chain of CSS custom properties that read the active theme’s variables first and fall back to quiet neutral defaults.
The design philosophy is simple: inherit the theme. A bright indigo default looks AI-generated next to a neutral block theme. A muted neutral default looks intentional next to everything, and themes that DO expose their palette will override it for free.
Source File
Section titled “Source File”Front-end tokens are defined in public/css/bp-stats-variables.css. It is not a separate style handle: public/css/bp-stats-public.css pulls it in with @import url('bp-stats-variables.css'), and the minified build (public/css/min/bp-stats-public.min.css) inlines it. The theme compat stylesheets declare the public handle (bp-stats) as their dependency so they load after the tokens.
The wp-admin screens use a separate --bpstats-admin-* token set; see Admin Tokens below.
Token Chain
Section titled “Token Chain”Each token has the same structure: theme tokens first, neutral fallback last.
1. BuddyX → --bx-color-*2. Reign → --reign-*3. BuddyBoss → --bb-*4. WP core themes → --wp--preset--color--*5. Neutral default (gray-scale, NOT indigo / purple / brand)Not every token has all five links. Some (--bp-stats-bg-tertiary, --bp-stats-border-light) only chain a BuddyBoss token, and a few (--bp-stats-text-light, --bp-stats-border-medium) are plain neutrals.
The comment block at the top of bp-stats-variables.css still lists BuddyBoss first; the declarations below it are the source of truth.
Example - page background:
--bp-stats-bg-primary: var(--bx-color-bg-page, var(--reign-site-body-bg-color, var(--bb-content-background-color, var(--wp--preset--color--base, #ffffff))));If the active theme is BuddyX, --bx-color-bg-page resolves and the rest of the chain is never read. If it is a neutral WP block theme that publishes --wp--preset--color--base, that wins. If nothing in the chain is published, the final #ffffff keeps the plugin from rendering invisibly.
Token Catalogue
Section titled “Token Catalogue”Surfaces
Section titled “Surfaces”| Token | Purpose |
|---|---|
--bp-stats-bg-primary |
Paper / page background |
--bp-stats-bg-elevated |
Elevated card surface |
--bp-stats-bg-secondary |
Recessed background / section divider |
--bp-stats-bg-tertiary |
Sub-recessed background (table headers, etc.) |
| Token | Purpose |
|---|---|
--bp-stats-text-primary |
Headlines, primary copy |
--bp-stats-text-secondary |
Body copy |
--bp-stats-text-muted |
Helper text, legends |
--bp-stats-text-light |
Disabled / placeholder |
--bp-stats-text-inverse |
Text on accent-filled backgrounds |
Borders
Section titled “Borders”| Token | Purpose |
|---|---|
--bp-stats-border-color |
Standard divider |
--bp-stats-border-light |
Subtle separator |
--bp-stats-border-medium |
Form field border |
Accent
Section titled “Accent”--bp-stats-primary / --bp-stats-accent is the one color the plugin uses for emphasis (focus rings, active tab, primary CTA stroke, hero band, streak ring). It chains --bx-color-accent → --reign-colors-theme → --bb-primary-button-background-regular → --bb-primary-color → --wp--preset--color--primary, and falls back to currentColor so it picks up the surrounding text color.
--bp-stats-link / --bp-stats-link-hover follow the theme’s link colors (BuddyX, Reign, BuddyBoss) and fall back to the accent.
Status hues
Section titled “Status hues”Restrained colors used only to communicate state, never for decoration:
| Token | Default | Bg variant |
|---|---|---|
--bp-stats-success |
--bb-success-color, else #15803d |
--bp-stats-success-bg (10% mix) |
--bp-stats-warning |
--bb-warning-color, else #b45309 |
--bp-stats-warning-bg |
--bp-stats-danger |
--bb-error-color, else #b91c1c |
--bp-stats-danger-bg |
--bp-stats-info |
--bb-info-color, else #1d4ed8 |
--bp-stats-info-bg |
Chart palette
Section titled “Chart palette”Derived from the accent + neutrals so charts read as part of the theme:
| Token | Source |
|---|---|
--bp-stats-chart-1 |
accent |
--bp-stats-chart-2 |
65% accent + 35% muted text |
--bp-stats-chart-3 |
info |
--bp-stats-chart-3-alt |
info @ 60% opacity |
--bp-stats-chart-4 |
success |
--bp-stats-chart-5 |
warning |
--bp-stats-chart-6 |
danger |
--bp-stats-chart-7 |
50% accent + 50% info |
--bp-stats-chart-8 |
70% info + 30% success |
Shadows + tints + overlays
Section titled “Shadows + tints + overlays”Token names follow --bp-stats-shadow*, --bp-stats-tint-*, --bp-stats-overlay-*. Tints are produced with color-mix( in srgb, … ) so they re-tint automatically when the accent or surface changes.
Per-Theme Compat Layer
Section titled “Per-Theme Compat Layer”When a theme’s token namespace does not match the chain (BuddyX / Reign / BuddyBoss / WP core), the plugin ships a per-theme stylesheet under public/css/themes/ that remaps --bp-stats-* onto whatever tokens that theme does expose.
Each compat stylesheet is registered for every handle in the map and conditionally enqueued by Bp_Stats_Public::enqueue_styles() on group, member activity and Statistics pages:
- The plugin reads
get_template()(the active parent theme’s slug). - It looks the slug up in a filterable map (default ships
'buddyx'and'buddyx-pro'both pointing atbp-stats-theme-buddyx-family). - If a match is found, the matching stylesheet is enqueued with the public
bp-statsstylesheet (which carries the tokens) as its dependency, so the remapping wins.
Default map (in public/class-bp-stats-public.php:167):
array( 'buddyx' => 'bp-stats-theme-buddyx-family', 'buddyx-pro' => 'bp-stats-theme-buddyx-family',)The shipped stylesheet public/css/themes/bp-stats-theme-buddyx-family.css is shared by both buddyx and buddyx-pro (same token namespace) and covers light + dark mode.
Add support for a new theme
Section titled “Add support for a new theme”Three steps:
- Write the compat stylesheet at
public/css/themes/bp-stats-theme-{handle}.css. Override--bp-stats-*tokens onto the theme’s real variables. - Register the mapping via
bp_stats_theme_compat_map:
add_filter( 'bp_stats_theme_compat_map', function ( $map ) { $map['my-theme-slug'] = 'bp-stats-theme-my-theme'; return $map;} );- (Optional) Override the URL if the stylesheet ships from your own plugin / child theme:
add_filter( 'bp_stats_theme_compat_url', function ( $url, $handle ) { if ( 'bp-stats-theme-my-theme' === $handle ) { return get_stylesheet_directory_uri() . '/bp-stats-compat.css'; } return $url;}, 10, 2 );(Optional) Use the bp_stats_after_theme_compat_enqueue action to enqueue companion JS / fonts for your theme.
Full hook reference: Hooks & Filters → Theme Compatibility Hooks.
Dark Mode
Section titled “Dark Mode”There is no prefers-color-scheme query; dark mode follows the host theme’s own switch. bp-stats-variables.css has one root override for these triggers:
html.dark-mode,body.dark-mode,body.dark-scheme,html.dark,[data-theme="dark"],.buddyx-dark-mode,.bb-dark-mode { ... }Inside it, surface, text and border tokens are set to explicit dark hex values rather than chained through theme tokens, because --bx-color-* / --reign-* / --bb-* stay light unless the theme itself is in dark mode. BuddyX and Reign native dark mode is already covered by the light :root chain, since those themes flip their own tokens. Accent and status hues are not overridden, so they keep inheriting the theme’s colors.
Because this single override flips the tokens for the whole plugin, components should not add per-component .buddyx-dark-mode .bp-stats-card { ... } color rules; use the tokens instead.
Admin Tokens
Section titled “Admin Tokens”The wp-admin screens (?page=bp-stats-settings) do not use --bp-stats-*. They use a fixed --bpstats-admin-* palette (accent, surfaces, text scale, semantic colors, radius, shadow, spacing) declared at the top of assets/css/admin.css, which Bp_Stats_Admin_Panel::enqueue_assets() enqueues as the bpstats-admin handle.
- Token roots. The tokens are declared on
.bpstats-admin(the page wrapper, also.wbcom-bpstats) and, since 2.0.1, on.bpstats-toast-hostand.bpstats-confirm-backdrop.assets/js/admin.jsappends toasts and confirm dialogs to<body>, outside the wrapper, so without their own token roots everyvar()resolved empty and the dialog rendered transparent. Any new body-appended surface needs to be added to that selector list. - Dark mode. Admin dark tokens are overridden only under theme body classes (
body.dark-mode,body.bb-dark-mode,body.bx-dark-theme,body.buddyx-dark-mode) scoped to.wbcom-bpstats,.bpstats-toast-hostand.bpstats-confirm-backdrop. Admin CSS must never use@media (prefers-color-scheme: dark): wp-admin itself does not follow the OS setting. - Links vs buttons. Link color is applied with
.bpstats-admin a:not(.bpstats-btn)(since 2.0.1), so<a>elements styled as.bpstats-btn*buttons keep their button colors. - Toolbar.
.bpstats-toolbar(filter bars above charts and tables) is defined once inadmin.css. Fields are capped at 320px (.bpstats-toolbar__field--growremoves the cap) and selects, date inputs and action buttons share a 40px control height. Analytics filter bars (.bpstats-toolbar.bp-stats-dashboard-filter,.bpstats-toolbar.bp-stats-filter-bar) only add a bottom divider; do not redeclare layout, labels or controls there. - Settings cards. Form-table and save-bar alignment inside settings cards (
.bpstats-settings-main .bpstats-card ...) lives inassets/css/admin-pulse.css, the shared panel stylesheet (bp-stats-admin-pulsehandle), including the 640px stacked-row rule.
After editing any admin or public stylesheet, regenerate the min/ and rtl/ variants with grunt (the rtlcss + cssmin tasks).
Customization
Section titled “Customization”Override a single token at the site level
Section titled “Override a single token at the site level”:root { --bp-stats-primary: #0ea5e9; --bp-stats-accent: #0ea5e9;}Match an existing theme variable
Section titled “Match an existing theme variable”:root { --bp-stats-bg-elevated: var(--my-theme-card-bg); --bp-stats-border-color: var(--my-theme-border);}Plain CSS - components remain class-based
Section titled “Plain CSS - components remain class-based”The token chain is how things color, not how things layout. Components still use familiar BEM-ish classes:
<div class="bp-stats-card"> <div class="bp-stats-card__head">...</div> <div class="bp-stats-card__body">...</div></div>If you need to restyle a specific component, target its class with normal CSS specificity - the tokens give you a clean override path for color decisions without having to fight the class selector.
Design Philosophy
Section titled “Design Philosophy”- Inherit the theme. The default visual identity is whatever the active theme says it is - no plugin brand color forced on the page.
- Quiet neutrals as defaults. When a theme exposes nothing, the plugin reads as a calm gray UI, not a bright AI-default brand.
- One accent for emphasis. Active tab, primary CTA, focus rings, the hero band, the streak ring - all read from one accent so the plugin reads as a single product across surfaces.
- Status hues communicate state. Green / red / yellow / blue only appear when they mean something (active / dormant / at-risk / info). They never decorate.
- Charts derive from accent + neutrals. The chart palette is built with
color-mix()from the same accent that drives the rest of the UI, so a theme swap re-tints every chart for free.

