Files
wp-bootstrap/functions.php
magdev 3165e60639
All checks were successful
Create Release Package / PHP Lint (push) Successful in 1m7s
Create Release Package / Build Release (push) Successful in 1m41s
feat: Bootstrap 5 block renderer, widget cards, and sidebar post layout (v1.1.0)
Add BlockRenderer class injecting Bootstrap classes into 8 core block types
(table, button, buttons, image, search, quote, pullquote, list) via per-block
render_block filters using WP_HTML_Tag_Processor.

Add WidgetRenderer class wrapping sidebar widgets in Bootstrap card components
with h4 heading hierarchy via dynamic_sidebar_params and widget_block_content
filters.

Add widget SCSS stylesheet for list styling, search input-group, tag cloud
pills, and card-flush list positioning.

Add single-sidebar.html.twig as the default post template with two-column
Bootstrap layout (col-lg-8 content, col-lg-4 sidebar). Full-width available
via template selection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 23:43:43 +01:00

874 lines
33 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* WP Bootstrap functions and definitions.
*
* @link https://developer.wordpress.org/themes/basics/theme-functions/
*
* @package WPBootstrap
* @since 0.0.1
*/
// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Load Composer autoloader.
if ( file_exists( get_template_directory() . '/vendor/autoload.php' ) ) {
require_once get_template_directory() . '/vendor/autoload.php';
}
/**
* Sets up theme defaults and registers support for various WordPress features.
*/
if ( ! function_exists( 'wp_bootstrap_setup' ) ) :
function wp_bootstrap_setup() {
// Add support for automatic document title tag.
add_theme_support( 'title-tag' );
// Add support for post formats.
add_theme_support( 'post-formats', array(
'aside', 'audio', 'chat', 'gallery', 'image',
'link', 'quote', 'status', 'video',
) );
// Make theme available for translation.
load_theme_textdomain( 'wp-bootstrap', get_template_directory() . '/languages' );
// Add editor styles.
add_editor_style( 'assets/css/editor-style.css' );
// Register navigation menu locations.
register_nav_menus( array(
'primary' => __( 'Primary Navigation', 'wp-bootstrap' ),
'footer' => __( 'Footer Navigation', 'wp-bootstrap' ),
) );
}
endif;
add_action( 'after_setup_theme', 'wp_bootstrap_setup' );
/**
* Register widget areas.
*
* @since 1.0.0
*/
if ( ! function_exists( 'wp_bootstrap_register_sidebars' ) ) :
function wp_bootstrap_register_sidebars() {
register_sidebar( array(
'name' => __( 'Sidebar', 'wp-bootstrap' ),
'id' => 'primary-sidebar',
'description' => __( 'Add widgets here to appear in the sidebar.', 'wp-bootstrap' ),
'before_widget' => '<div id="%1$s" class="widget mb-4 %2$s">',
'after_widget' => '</div>',
'before_title' => '<h3 class="sidebar-heading h6 text-uppercase fw-semibold">',
'after_title' => '</h3>',
) );
}
endif;
add_action( 'widgets_init', 'wp_bootstrap_register_sidebars' );
/**
* Enqueue theme scripts and styles.
*/
if ( ! function_exists( 'wp_bootstrap_enqueue_scripts' ) ) :
function wp_bootstrap_enqueue_scripts() {
$theme_version = wp_get_theme()->get( 'Version' );
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
// Enqueue the compiled Bootstrap + custom CSS.
wp_enqueue_style(
'wp-bootstrap-style',
get_template_directory_uri() . '/assets/css/style' . $suffix . '.css',
array(),
$theme_version
);
// Push offcanvas below the WP admin bar when logged in.
if ( is_admin_bar_showing() ) {
wp_add_inline_style( 'wp-bootstrap-style',
'@media (max-width: 991.98px) { .offcanvas { padding-top: var(--wp-admin--admin-bar--height, 32px); } }'
);
}
// Enqueue Bootstrap JS bundle (includes Popper).
wp_enqueue_script(
'wp-bootstrap-js',
get_template_directory_uri() . '/assets/js/bootstrap.bundle.min.js',
array(),
$theme_version,
true
);
// Enqueue dark mode toggle script.
wp_enqueue_script(
'wp-bootstrap-dark-mode',
get_template_directory_uri() . '/assets/js/dark-mode.js',
array(),
$theme_version,
array(
'strategy' => 'defer',
'in_footer' => false,
)
);
// Inline script to prevent flash of wrong theme.
wp_add_inline_script(
'wp-bootstrap-dark-mode',
'(function(){var t=localStorage.getItem("wp-bootstrap-theme")||(window.matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light");document.documentElement.setAttribute("data-bs-theme",t)})();',
'before'
);
}
endif;
add_action( 'wp_enqueue_scripts', 'wp_bootstrap_enqueue_scripts' );
/**
* Preload critical fonts for performance.
*
* @since 0.3.0
*/
if ( ! function_exists( 'wp_bootstrap_preload_fonts' ) ) :
function wp_bootstrap_preload_fonts() {
$fonts = array(
'inter/InterVariable.woff2',
'lora/Lora-VariableFont.woff2',
);
$base = get_template_directory_uri() . '/assets/fonts/';
foreach ( $fonts as $font ) {
printf(
'<link rel="preload" href="%s" as="font" type="font/woff2" crossorigin>' . "\n",
esc_url( $base . $font )
);
}
}
endif;
add_action( 'wp_head', 'wp_bootstrap_preload_fonts', 1 );
/**
* Enqueue RTL stylesheet for right-to-left languages.
*
* @since 0.3.0
*/
if ( ! function_exists( 'wp_bootstrap_rtl_styles' ) ) :
function wp_bootstrap_rtl_styles() {
if ( ! is_rtl() ) {
return;
}
$theme_version = wp_get_theme()->get( 'Version' );
wp_enqueue_style(
'wp-bootstrap-rtl',
get_template_directory_uri() . '/assets/css/rtl.css',
array( 'wp-bootstrap-style' ),
$theme_version
);
}
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 (primaryinfo) 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() {
$transient_key = 'wp_bootstrap_variation_css_' . md5( get_stylesheet() );
$cached_css = get_transient( $transient_key );
if ( false !== $cached_css ) {
// '' means default palette (no inline CSS needed); non-empty string is the computed CSS.
if ( '' !== $cached_css ) {
wp_add_inline_style( 'wp-bootstrap-style', $cached_css );
}
return;
}
// Read the theme origin palette — this contains the base theme.json
// colors merged with the active style variation (if any).
$theme_palette = wp_get_global_settings( array( 'color', 'palette', 'theme' ) );
$colors = array();
if ( ! empty( $theme_palette ) && is_array( $theme_palette ) ) {
foreach ( $theme_palette as $entry ) {
if ( ! empty( $entry['slug'] ) && ! empty( $entry['color'] ) ) {
$colors[ $entry['slug'] ] = $entry['color'];
}
}
}
// Compare against base theme.json defaults to detect an active variation.
// WordPress puts variation colors in the 'theme' origin, not 'custom'.
$base_defaults = array(
'base' => '#ffffff',
'contrast' => '#212529',
'primary' => '#0d6efd',
);
$is_default = true;
foreach ( $base_defaults as $slug => $default_color ) {
if ( ! empty( $colors[ $slug ] ) && strtolower( $colors[ $slug ] ) !== $default_color ) {
$is_default = false;
break;
}
}
// No variation active — let Bootstrap's compiled CSS handle both modes.
if ( $is_default ) {
set_transient( $transient_key, '', DAY_IN_SECONDS );
return;
}
if ( empty( $colors['base'] ) || empty( $colors['contrast'] ) ) {
set_transient( $transient_key, '', DAY_IN_SECONDS );
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 . '}';
// Cache for 24 hours; invalidated on theme switch or global-styles save.
set_transient( $transient_key, $css, DAY_IN_SECONDS );
// 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 );
/**
* Invalidate the color variation CSS transient when global styles or theme change.
*/
add_action( 'switch_theme', function () {
delete_transient( 'wp_bootstrap_variation_css_' . md5( get_stylesheet() ) );
} );
add_action( 'save_post_wp_global_styles', function () {
delete_transient( 'wp_bootstrap_variation_css_' . md5( get_stylesheet() ) );
} );
/**
* 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.
*
* @since 0.2.0
*/
if ( ! function_exists( 'wp_bootstrap_enqueue_editor_assets' ) ) :
function wp_bootstrap_enqueue_editor_assets() {
$theme_version = wp_get_theme()->get( 'Version' );
wp_enqueue_script(
'wp-bootstrap-editor-js',
get_template_directory_uri() . '/assets/js/bootstrap.bundle.min.js',
array(),
$theme_version,
true
);
}
endif;
add_action( 'enqueue_block_editor_assets', 'wp_bootstrap_enqueue_editor_assets' );
/**
* Register custom block categories for Bootstrap components.
*
* @since 0.2.0
*
* @param array $categories Existing block categories.
* @param WP_Block_Editor_Context $context Block editor context.
* @return array Modified categories.
*/
if ( ! function_exists( 'wp_bootstrap_block_categories' ) ) :
function wp_bootstrap_block_categories( array $categories, $context ): array {
$bootstrap_categories = array(
array(
'slug' => 'wp-bootstrap-layout',
'title' => __( 'Bootstrap Layout', 'wp-bootstrap' ),
'icon' => 'layout',
),
array(
'slug' => 'wp-bootstrap-components',
'title' => __( 'Bootstrap Components', 'wp-bootstrap' ),
'icon' => 'grid-view',
),
array(
'slug' => 'wp-bootstrap-navigation',
'title' => __( 'Bootstrap Navigation', 'wp-bootstrap' ),
'icon' => 'menu',
),
);
return array_merge( $bootstrap_categories, $categories );
}
endif;
add_filter( 'block_categories_all', 'wp_bootstrap_block_categories', 10, 2 );
/**
* Register block pattern categories.
*/
if ( ! function_exists( 'wp_bootstrap_pattern_categories' ) ) :
function wp_bootstrap_pattern_categories() {
register_block_pattern_category(
'wp-bootstrap_page',
array(
'label' => __( 'Pages', 'wp-bootstrap' ),
'description' => __( 'A collection of full page layouts.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap_hero',
array(
'label' => __( 'Hero Sections', 'wp-bootstrap' ),
'description' => __( 'Large hero and banner sections.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap_cta',
array(
'label' => __( 'Call to Action', 'wp-bootstrap' ),
'description' => __( 'Call to action sections.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap_features',
array(
'label' => __( 'Features', 'wp-bootstrap' ),
'description' => __( 'Feature and service showcase sections.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap_testimonials',
array(
'label' => __( 'Testimonials', 'wp-bootstrap' ),
'description' => __( 'Testimonial and review sections.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap_pricing',
array(
'label' => __( 'Pricing', 'wp-bootstrap' ),
'description' => __( 'Pricing table sections.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap_contact',
array(
'label' => __( 'Contact', 'wp-bootstrap' ),
'description' => __( 'Contact information sections.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap_text',
array(
'label' => __( 'Text & Content', 'wp-bootstrap' ),
'description' => __( 'Text-focused content sections.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap-layout',
array(
'label' => __( 'Layout', 'wp-bootstrap' ),
'description' => __( 'Layout building blocks for page structure.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap-components',
array(
'label' => __( 'Components', 'wp-bootstrap' ),
'description' => __( 'Reusable Bootstrap component patterns.', 'wp-bootstrap' ),
)
);
register_block_pattern_category(
'wp-bootstrap-navigation',
array(
'label' => __( 'Navigation', 'wp-bootstrap' ),
'description' => __( 'Navigation and header patterns.', 'wp-bootstrap' ),
)
);
}
endif;
add_action( 'init', 'wp_bootstrap_pattern_categories' );
/**
* Register custom block styles for Bootstrap components.
*
* @since 0.1.0
*/
if ( ! function_exists( 'wp_bootstrap_block_styles' ) ) :
function wp_bootstrap_block_styles() {
// core/list - Checkmark style.
register_block_style( 'core/list', array(
'name' => 'checkmark-list',
'label' => __( 'Checkmark', 'wp-bootstrap' ),
'inline_style' => '
ul.is-style-checkmark-list { list-style-type: "\2713"; }
ul.is-style-checkmark-list li { padding-inline-start: 1ch; }',
) );
// core/list - Unstyled.
register_block_style( 'core/list', array(
'name' => 'list-unstyled',
'label' => __( 'Unstyled', 'wp-bootstrap' ),
'inline_style' => '
ul.is-style-list-unstyled, ol.is-style-list-unstyled { list-style: none; padding-inline-start: 0; }',
) );
// core/group - Card.
register_block_style( 'core/group', array(
'name' => 'card',
'label' => __( 'Card', 'wp-bootstrap' ),
'inline_style' => '
.is-style-card { border: 1px solid var(--wp--preset--color--light, #dee2e6); border-radius: 0.375rem; padding: var(--wp--preset--spacing--40); background: var(--wp--preset--color--base); }',
) );
// core/group - Card with Shadow.
register_block_style( 'core/group', array(
'name' => 'card-shadow',
'label' => __( 'Card with Shadow', 'wp-bootstrap' ),
'inline_style' => '
.is-style-card-shadow { border: 1px solid var(--wp--preset--color--light, #dee2e6); border-radius: 0.375rem; padding: var(--wp--preset--spacing--40); background: var(--wp--preset--color--base); box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); }',
) );
// core/group - Alert Info.
register_block_style( 'core/group', array(
'name' => 'alert-info',
'label' => __( 'Alert - Info', 'wp-bootstrap' ),
'inline_style' => '
.is-style-alert-info { background-color: #cff4fc; border: 1px solid #9eeaf9; color: #055160; border-radius: 0.375rem; padding: var(--wp--preset--spacing--30); }',
) );
// core/group - Alert Success.
register_block_style( 'core/group', array(
'name' => 'alert-success',
'label' => __( 'Alert - Success', 'wp-bootstrap' ),
'inline_style' => '
.is-style-alert-success { background-color: #d1e7dd; border: 1px solid #a3cfbb; color: #0a3622; border-radius: 0.375rem; padding: var(--wp--preset--spacing--30); }',
) );
// core/group - Alert Warning.
register_block_style( 'core/group', array(
'name' => 'alert-warning',
'label' => __( 'Alert - Warning', 'wp-bootstrap' ),
'inline_style' => '
.is-style-alert-warning { background-color: #fff3cd; border: 1px solid #ffe69c; color: #664d03; border-radius: 0.375rem; padding: var(--wp--preset--spacing--30); }',
) );
// core/group - Alert Danger.
register_block_style( 'core/group', array(
'name' => 'alert-danger',
'label' => __( 'Alert - Danger', 'wp-bootstrap' ),
'inline_style' => '
.is-style-alert-danger { background-color: #f8d7da; border: 1px solid #f1aeb5; color: #58151c; border-radius: 0.375rem; padding: var(--wp--preset--spacing--30); }',
) );
// core/table - Striped Rows.
register_block_style( 'core/table', array(
'name' => 'table-striped',
'label' => __( 'Striped Rows', 'wp-bootstrap' ),
'inline_style' => '
.is-style-table-striped tbody tr:nth-of-type(odd) > * { background-color: rgba(0, 0, 0, 0.05); }',
) );
// core/table - Hover Rows.
register_block_style( 'core/table', array(
'name' => 'table-hover',
'label' => __( 'Hover Rows', 'wp-bootstrap' ),
'inline_style' => '
.is-style-table-hover tbody tr:hover > * { background-color: rgba(0, 0, 0, 0.075); }',
) );
// core/table - Bordered.
register_block_style( 'core/table', array(
'name' => 'table-bordered',
'label' => __( 'Bordered', 'wp-bootstrap' ),
'inline_style' => '
.is-style-table-bordered, .is-style-table-bordered td, .is-style-table-bordered th { border: 1px solid var(--wp--preset--color--light, #dee2e6); }',
) );
// core/quote - Accent Border.
register_block_style( 'core/quote', array(
'name' => 'blockquote-accent',
'label' => __( 'Accent Border', 'wp-bootstrap' ),
'inline_style' => '
.is-style-blockquote-accent { border-inline-start: 4px solid var(--wp--preset--color--primary); background: var(--wp--preset--color--light); padding: var(--wp--preset--spacing--30); border-start-start-radius: 0; border-start-end-radius: 0.375rem; border-end-end-radius: 0.375rem; border-end-start-radius: 0; }',
) );
// core/image - Shadow.
register_block_style( 'core/image', array(
'name' => 'shadow',
'label' => __( 'Shadow', 'wp-bootstrap' ),
'inline_style' => '
.is-style-shadow img { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); }',
) );
// core/image - Large Rounded.
register_block_style( 'core/image', array(
'name' => 'rounded-lg',
'label' => __( 'Large Rounded', 'wp-bootstrap' ),
'inline_style' => '
.is-style-rounded-lg img { border-radius: 0.75rem; }',
) );
// core/button - Large.
register_block_style( 'core/button', array(
'name' => 'btn-lg',
'label' => __( 'Large', 'wp-bootstrap' ),
'inline_style' => '
.is-style-btn-lg .wp-block-button__link { padding: 0.75rem 1.5rem; font-size: 1.25rem; border-radius: 0.5rem; }',
) );
// core/button - Small.
register_block_style( 'core/button', array(
'name' => 'btn-sm',
'label' => __( 'Small', 'wp-bootstrap' ),
'inline_style' => '
.is-style-btn-sm .wp-block-button__link { padding: 0.25rem 0.5rem; font-size: 0.875rem; border-radius: 0.25rem; }',
) );
// core/separator - Wide.
register_block_style( 'core/separator', array(
'name' => 'separator-wide',
'label' => __( 'Wide', 'wp-bootstrap' ),
'inline_style' => '
.is-style-separator-wide { max-width: none; }',
) );
// core/list - List Group (Bootstrap).
register_block_style( 'core/list', array(
'name' => 'list-group',
'label' => __( 'List Group', 'wp-bootstrap' ),
) );
}
endif;
add_action( 'init', 'wp_bootstrap_block_styles' );
/**
* Initialize Twig template engine.
*/
if ( ! function_exists( 'wp_bootstrap_init_twig' ) ) :
function wp_bootstrap_init_twig() {
if ( class_exists( '\\WPBootstrap\\Twig\\TwigService' ) ) {
\WPBootstrap\Twig\TwigService::getInstance();
}
}
endif;
add_action( 'after_setup_theme', 'wp_bootstrap_init_twig' );
/**
* Initialize Twig template controller for frontend rendering.
*
* Hooks into template_redirect to render Bootstrap 5 HTML
* via Twig templates instead of FSE block markup on the frontend.
*
* @since 0.1.1
*/
if ( ! function_exists( 'wp_bootstrap_init_templates' ) ) :
function wp_bootstrap_init_templates() {
if ( class_exists( '\\WPBootstrap\\Template\\TemplateController' ) ) {
new \WPBootstrap\Template\TemplateController();
}
}
endif;
add_action( 'after_setup_theme', 'wp_bootstrap_init_templates' );
/**
* Initialize block renderer for Bootstrap 5 class injection.
*
* Hooks per-block render_block filters to inject Bootstrap classes
* (e.g. .table, .btn, .img-fluid) into core block HTML on the frontend.
*
* @since 1.1.0
*/
if ( ! function_exists( 'wp_bootstrap_init_block_renderer' ) ) :
function wp_bootstrap_init_block_renderer() {
if ( class_exists( '\\WPBootstrap\\Block\\BlockRenderer' ) ) {
new \WPBootstrap\Block\BlockRenderer();
}
}
endif;
add_action( 'after_setup_theme', 'wp_bootstrap_init_block_renderer' );
/**
* Initialize widget renderer for Bootstrap 5 card wrappers.
*
* Hooks dynamic_sidebar_params to wrap sidebar widgets in Bootstrap
* card components with proper heading structure.
*
* @since 1.1.0
*/
if ( ! function_exists( 'wp_bootstrap_init_widget_renderer' ) ) :
function wp_bootstrap_init_widget_renderer() {
if ( class_exists( '\\WPBootstrap\\Block\\WidgetRenderer' ) ) {
new \WPBootstrap\Block\WidgetRenderer();
}
}
endif;
add_action( 'after_setup_theme', 'wp_bootstrap_init_widget_renderer' );
/**
* Customize comment form fields with Bootstrap classes.
*
* @since 0.1.1
*
* @param array $fields Default comment form fields.
* @return array Modified fields with Bootstrap classes.
*/
if ( ! function_exists( 'wp_bootstrap_comment_form_fields' ) ) :
function wp_bootstrap_comment_form_fields( array $fields ): array {
$commenter = wp_get_current_commenter();
$required = get_option( 'require_name_email' );
$req_attr = $required ? ' required' : '';
$fields['author'] = '<div class="mb-3">'
. '<label for="author" class="form-label">' . __( 'Name', 'wp-bootstrap' ) . ( $required ? ' <span class="text-danger">*</span>' : '' ) . '</label>'
. '<input id="author" name="author" type="text" class="form-control" value="' . esc_attr( $commenter['comment_author'] ) . '"' . $req_attr . '>'
. '</div>';
$fields['email'] = '<div class="mb-3">'
. '<label for="email" class="form-label">' . __( 'Email', 'wp-bootstrap' ) . ( $required ? ' <span class="text-danger">*</span>' : '' ) . '</label>'
. '<input id="email" name="email" type="email" class="form-control" value="' . esc_attr( $commenter['comment_author_email'] ) . '"' . $req_attr . '>'
. '</div>';
$fields['url'] = '<div class="mb-3">'
. '<label for="url" class="form-label">' . __( 'Website', 'wp-bootstrap' ) . '</label>'
. '<input id="url" name="url" type="url" class="form-control" value="' . esc_attr( $commenter['comment_author_url'] ) . '">'
. '</div>';
if ( isset( $fields['cookies'] ) ) {
$fields['cookies'] = '<div class="mb-3 form-check">'
. '<input id="wp-comment-cookies-consent" name="wp-comment-cookies-consent" type="checkbox" class="form-check-input" value="yes">'
. '<label for="wp-comment-cookies-consent" class="form-check-label">'
. __( 'Save my name, email, and website in this browser for the next time I comment.', 'wp-bootstrap' )
. '</label></div>';
}
return $fields;
}
endif;
add_filter( 'comment_form_default_fields', 'wp_bootstrap_comment_form_fields' );