You've already forked wp-bootstrap
v0.3.2 - Fix dark mode conflicts with WordPress global styles
Fix dark mode body colors overridden by WordPress theme.json styles.color, add broad dark mode rules for plugin form elements, fix footer-columns template, and add style variation bridge function. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
275
functions.php
275
functions.php
@@ -134,6 +134,281 @@ if ( ! function_exists( 'wp_bootstrap_rtl_styles' ) ) :
|
||||
endif;
|
||||
add_action( 'wp_enqueue_scripts', 'wp_bootstrap_rtl_styles', 20 );
|
||||
|
||||
/**
|
||||
* Bridge WordPress style variation colors to Bootstrap CSS custom properties.
|
||||
*
|
||||
* Reads the active color palette (theme.json + variation + user overrides)
|
||||
* and outputs inline CSS for both light and dark modes so the dark-mode toggle
|
||||
* switches between two variation-aware color schemes.
|
||||
*
|
||||
* Theme colors (primary–info) go into :root and apply in both modes.
|
||||
* Surface colors (body-bg, tertiary-bg, etc.) are computed separately for
|
||||
* [data-bs-theme=light] and [data-bs-theme=dark].
|
||||
*
|
||||
* For light palettes: light mode uses base/contrast; dark mode uses dark/light slugs.
|
||||
* For dark palettes: dark mode uses base/contrast; light mode swaps them.
|
||||
*
|
||||
* @since 0.3.2
|
||||
*/
|
||||
if ( ! function_exists( 'wp_bootstrap_variation_colors' ) ) :
|
||||
function wp_bootstrap_variation_colors() {
|
||||
// Check if user has customized colors (selected a variation or edited
|
||||
// colors in the Site Editor). The custom origin palette will contain
|
||||
// our theme slugs (base, contrast, primary, etc.) only when a variation
|
||||
// or manual color override has been saved.
|
||||
$custom_palette = wp_get_global_settings( array( 'color', 'palette', 'custom' ) );
|
||||
$has_variation = false;
|
||||
if ( ! empty( $custom_palette ) && is_array( $custom_palette ) ) {
|
||||
foreach ( $custom_palette as $entry ) {
|
||||
if ( isset( $entry['slug'] ) && in_array( $entry['slug'], array( 'base', 'contrast', 'primary' ), true ) ) {
|
||||
$has_variation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No variation active — let Bootstrap's compiled CSS handle both modes.
|
||||
if ( ! $has_variation ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Merge palette origins: default < theme < custom.
|
||||
// Query each origin separately because the combined API omits 'custom'.
|
||||
$colors = array();
|
||||
foreach ( array( 'default', 'theme', 'custom' ) as $origin ) {
|
||||
$origin_palette = wp_get_global_settings( array( 'color', 'palette', $origin ) );
|
||||
if ( ! empty( $origin_palette ) && is_array( $origin_palette ) ) {
|
||||
foreach ( $origin_palette as $entry ) {
|
||||
if ( ! empty( $entry['slug'] ) && ! empty( $entry['color'] ) ) {
|
||||
$colors[ $entry['slug'] ] = $entry['color'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $colors['base'] ) || empty( $colors['contrast'] ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Theme colors apply in both modes via :root.
|
||||
$theme_slugs = array(
|
||||
'primary' => '--bs-primary',
|
||||
'secondary' => '--bs-secondary',
|
||||
'success' => '--bs-success',
|
||||
'danger' => '--bs-danger',
|
||||
'warning' => '--bs-warning',
|
||||
'info' => '--bs-info',
|
||||
);
|
||||
|
||||
$root_css = '';
|
||||
foreach ( $theme_slugs as $slug => $var ) {
|
||||
if ( ! empty( $colors[ $slug ] ) ) {
|
||||
$hex = esc_attr( $colors[ $slug ] );
|
||||
$rgb = wp_bootstrap_hex_to_rgb( $colors[ $slug ] );
|
||||
$root_css .= "{$var}:{$hex};";
|
||||
if ( $rgb ) {
|
||||
$root_css .= "{$var}-rgb:{$rgb};";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Link colors from primary (both modes).
|
||||
if ( ! empty( $colors['primary'] ) ) {
|
||||
$primary_rgb = wp_bootstrap_hex_to_rgb( $colors['primary'] );
|
||||
$root_css .= '--bs-link-color:' . esc_attr( $colors['primary'] ) . ';';
|
||||
$root_css .= '--bs-link-color-rgb:' . $primary_rgb . ';';
|
||||
$root_css .= '--bs-link-hover-color:' . esc_attr( $colors['primary'] ) . ';';
|
||||
$root_css .= '--bs-link-hover-color-rgb:' . $primary_rgb . ';';
|
||||
}
|
||||
|
||||
// Determine if this is a dark palette (base luminance < contrast luminance).
|
||||
$is_dark = wp_bootstrap_relative_luminance( $colors['base'] )
|
||||
< wp_bootstrap_relative_luminance( $colors['contrast'] );
|
||||
|
||||
// Resolve light-mode and dark-mode base colors.
|
||||
if ( $is_dark ) {
|
||||
// Dark palette: dark mode is native, light mode swaps base↔contrast.
|
||||
$light_bg = $colors['contrast'];
|
||||
$light_fg = $colors['base'];
|
||||
$dark_bg = $colors['base'];
|
||||
$dark_fg = $colors['contrast'];
|
||||
} else {
|
||||
// Light palette: light mode is native, dark mode uses dark/light slugs.
|
||||
$light_bg = $colors['base'];
|
||||
$light_fg = $colors['contrast'];
|
||||
$dark_bg = $colors['dark'] ?? '#212529';
|
||||
$dark_fg = $colors['light'] ?? '#f8f9fa';
|
||||
}
|
||||
|
||||
// Also set light/dark slug variables for utilities like bg-light, bg-dark.
|
||||
if ( ! empty( $colors['light'] ) ) {
|
||||
$root_css .= '--bs-light:' . esc_attr( $colors['light'] ) . ';';
|
||||
$root_css .= '--bs-light-rgb:' . wp_bootstrap_hex_to_rgb( $colors['light'] ) . ';';
|
||||
}
|
||||
if ( ! empty( $colors['dark'] ) ) {
|
||||
$root_css .= '--bs-dark:' . esc_attr( $colors['dark'] ) . ';';
|
||||
$root_css .= '--bs-dark-rgb:' . wp_bootstrap_hex_to_rgb( $colors['dark'] ) . ';';
|
||||
}
|
||||
|
||||
// Build surface CSS for a given bg/fg pair.
|
||||
$light_css = wp_bootstrap_build_surface_css( $light_bg, $light_fg, false );
|
||||
$dark_css = wp_bootstrap_build_surface_css( $dark_bg, $dark_fg, true );
|
||||
|
||||
$css = ':root{' . $root_css . '}'
|
||||
. '[data-bs-theme=light]{' . $light_css . '}'
|
||||
. '[data-bs-theme=dark]{' . $dark_css . '}';
|
||||
|
||||
// Attach after the compiled stylesheet so variation values override
|
||||
// Bootstrap's hardcoded dark-mode defaults via source order.
|
||||
wp_add_inline_style( 'wp-bootstrap-style', $css );
|
||||
}
|
||||
endif;
|
||||
add_action( 'wp_enqueue_scripts', 'wp_bootstrap_variation_colors', 30 );
|
||||
|
||||
/**
|
||||
* Build Bootstrap surface CSS variables for a given background/foreground pair.
|
||||
*
|
||||
* Computes body-bg, body-color, tertiary-bg, secondary-bg, secondary-color,
|
||||
* emphasis-color, and border-color — mirroring how Bootstrap derives them.
|
||||
*
|
||||
* @since 0.3.2
|
||||
*
|
||||
* @param string $bg Background hex color.
|
||||
* @param string $fg Foreground (text) hex color.
|
||||
* @param bool $is_dark Whether this is for dark mode.
|
||||
* @return string CSS declarations (no selector).
|
||||
*/
|
||||
if ( ! function_exists( 'wp_bootstrap_build_surface_css' ) ) :
|
||||
function wp_bootstrap_build_surface_css( $bg, $fg, $is_dark ) {
|
||||
$bg_rgb = wp_bootstrap_hex_to_rgb( $bg );
|
||||
$fg_rgb = wp_bootstrap_hex_to_rgb( $fg );
|
||||
|
||||
$css = '--bs-body-bg:' . esc_attr( $bg ) . ';';
|
||||
$css .= '--bs-body-bg-rgb:' . $bg_rgb . ';';
|
||||
$css .= '--bs-body-color:' . esc_attr( $fg ) . ';';
|
||||
$css .= '--bs-body-color-rgb:' . $fg_rgb . ';';
|
||||
|
||||
if ( $is_dark ) {
|
||||
$secondary_bg = wp_bootstrap_mix_hex( $fg, $bg, 0.16 );
|
||||
$tertiary_bg = wp_bootstrap_mix_hex( $fg, $bg, 0.08 );
|
||||
$border_color = wp_bootstrap_mix_hex( $fg, $bg, 0.24 );
|
||||
$emphasis = '#FFFFFF';
|
||||
} else {
|
||||
$secondary_bg = wp_bootstrap_mix_hex( $fg, $bg, 0.08 );
|
||||
$tertiary_bg = wp_bootstrap_mix_hex( $fg, $bg, 0.04 );
|
||||
$border_color = wp_bootstrap_mix_hex( $fg, $bg, 0.16 );
|
||||
$emphasis = '#000000';
|
||||
}
|
||||
|
||||
$css .= '--bs-secondary-color:rgba(' . $fg_rgb . ',0.75);';
|
||||
$css .= '--bs-secondary-bg:' . $secondary_bg . ';';
|
||||
$css .= '--bs-secondary-bg-rgb:' . wp_bootstrap_hex_to_rgb( $secondary_bg ) . ';';
|
||||
$css .= '--bs-tertiary-color:rgba(' . $fg_rgb . ',0.5);';
|
||||
$css .= '--bs-tertiary-bg:' . $tertiary_bg . ';';
|
||||
$css .= '--bs-tertiary-bg-rgb:' . wp_bootstrap_hex_to_rgb( $tertiary_bg ) . ';';
|
||||
$css .= '--bs-emphasis-color:' . $emphasis . ';';
|
||||
$css .= '--bs-emphasis-color-rgb:' . wp_bootstrap_hex_to_rgb( $emphasis ) . ';';
|
||||
$css .= '--bs-border-color:' . $border_color . ';';
|
||||
|
||||
return $css;
|
||||
}
|
||||
endif;
|
||||
|
||||
/**
|
||||
* Convert a hex color string to an RGB triplet string.
|
||||
*
|
||||
* @since 0.3.2
|
||||
*
|
||||
* @param string $hex Hex color (e.g. "#0d6efd" or "0d6efd").
|
||||
* @return string RGB triplet (e.g. "13,110,253") or empty string on failure.
|
||||
*/
|
||||
if ( ! function_exists( 'wp_bootstrap_hex_to_rgb' ) ) :
|
||||
function wp_bootstrap_hex_to_rgb( $hex ) {
|
||||
$hex = ltrim( $hex, '#' );
|
||||
if ( strlen( $hex ) === 3 ) {
|
||||
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
||||
}
|
||||
if ( strlen( $hex ) !== 6 ) {
|
||||
return '';
|
||||
}
|
||||
return hexdec( substr( $hex, 0, 2 ) ) . ','
|
||||
. hexdec( substr( $hex, 2, 2 ) ) . ','
|
||||
. hexdec( substr( $hex, 4, 2 ) );
|
||||
}
|
||||
endif;
|
||||
|
||||
/**
|
||||
* Mix two hex colors by a given weight.
|
||||
*
|
||||
* @since 0.3.2
|
||||
*
|
||||
* @param string $color1 Hex color to mix in.
|
||||
* @param string $color2 Base hex color.
|
||||
* @param float $weight Weight of color1 (0.0 to 1.0).
|
||||
* @return string Resulting hex color.
|
||||
*/
|
||||
if ( ! function_exists( 'wp_bootstrap_mix_hex' ) ) :
|
||||
function wp_bootstrap_mix_hex( $color1, $color2, $weight ) {
|
||||
$c1 = wp_bootstrap_hex_to_rgb_array( $color1 );
|
||||
$c2 = wp_bootstrap_hex_to_rgb_array( $color2 );
|
||||
if ( ! $c1 || ! $c2 ) {
|
||||
return $color2;
|
||||
}
|
||||
$r = (int) round( $c1[0] * $weight + $c2[0] * ( 1 - $weight ) );
|
||||
$g = (int) round( $c1[1] * $weight + $c2[1] * ( 1 - $weight ) );
|
||||
$b = (int) round( $c1[2] * $weight + $c2[2] * ( 1 - $weight ) );
|
||||
return sprintf( '#%02x%02x%02x', max( 0, min( 255, $r ) ), max( 0, min( 255, $g ) ), max( 0, min( 255, $b ) ) );
|
||||
}
|
||||
endif;
|
||||
|
||||
/**
|
||||
* Convert a hex color to an array of [r, g, b] integers.
|
||||
*
|
||||
* @since 0.3.2
|
||||
*
|
||||
* @param string $hex Hex color.
|
||||
* @return array|false Array of [r, g, b] or false on failure.
|
||||
*/
|
||||
if ( ! function_exists( 'wp_bootstrap_hex_to_rgb_array' ) ) :
|
||||
function wp_bootstrap_hex_to_rgb_array( $hex ) {
|
||||
$hex = ltrim( $hex, '#' );
|
||||
if ( strlen( $hex ) === 3 ) {
|
||||
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
||||
}
|
||||
if ( strlen( $hex ) !== 6 ) {
|
||||
return false;
|
||||
}
|
||||
return array(
|
||||
hexdec( substr( $hex, 0, 2 ) ),
|
||||
hexdec( substr( $hex, 2, 2 ) ),
|
||||
hexdec( substr( $hex, 4, 2 ) ),
|
||||
);
|
||||
}
|
||||
endif;
|
||||
|
||||
/**
|
||||
* Compute relative luminance of a hex color (0.0 = black, 1.0 = white).
|
||||
*
|
||||
* @since 0.3.2
|
||||
*
|
||||
* @param string $hex Hex color.
|
||||
* @return float Relative luminance.
|
||||
*/
|
||||
if ( ! function_exists( 'wp_bootstrap_relative_luminance' ) ) :
|
||||
function wp_bootstrap_relative_luminance( $hex ) {
|
||||
$rgb = wp_bootstrap_hex_to_rgb_array( $hex );
|
||||
if ( ! $rgb ) {
|
||||
return 0.0;
|
||||
}
|
||||
$channels = array();
|
||||
foreach ( $rgb as $val ) {
|
||||
$val /= 255;
|
||||
$channels[] = ( $val <= 0.03928 ) ? $val / 12.92 : pow( ( $val + 0.055 ) / 1.055, 2.4 );
|
||||
}
|
||||
return 0.2126 * $channels[0] + 0.7152 * $channels[1] + 0.0722 * $channels[2];
|
||||
}
|
||||
endif;
|
||||
|
||||
/**
|
||||
* Enqueue Bootstrap JS in the block editor for interactive previews.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user