Initial theme scaffolding (v0.0.1)
- Bootstrap 5 CSS/JS integration via Yarn (served locally)
- Dart Sass build pipeline with PostCSS, Autoprefixer, cssnano
- Twig 3.0 via Composer with PSR-4 autoloading
- FSE block theme templates (index, home, single, page, archive, search, 404)
- Template parts (header, footer) and block patterns
- theme.json with Bootstrap 5-aligned design tokens
- Gitea CI/CD workflow for automated release packages
- WordPress i18n support (en_US base, de_CH translation)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 02:25:33 +01:00
|
|
|
|
<?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 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' );
|
2026-02-08 15:11:00 +01:00
|
|
|
|
|
|
|
|
|
|
// Register navigation menu locations.
|
|
|
|
|
|
register_nav_menus( array(
|
|
|
|
|
|
'primary' => __( 'Primary Navigation', 'wp-bootstrap' ),
|
|
|
|
|
|
'footer' => __( 'Footer Navigation', 'wp-bootstrap' ),
|
|
|
|
|
|
) );
|
Initial theme scaffolding (v0.0.1)
- Bootstrap 5 CSS/JS integration via Yarn (served locally)
- Dart Sass build pipeline with PostCSS, Autoprefixer, cssnano
- Twig 3.0 via Composer with PSR-4 autoloading
- FSE block theme templates (index, home, single, page, archive, search, 404)
- Template parts (header, footer) and block patterns
- theme.json with Bootstrap 5-aligned design tokens
- Gitea CI/CD workflow for automated release packages
- WordPress i18n support (en_US base, de_CH translation)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 02:25:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
endif;
|
|
|
|
|
|
add_action( 'after_setup_theme', 'wp_bootstrap_setup' );
|
|
|
|
|
|
|
2026-02-08 18:43:09 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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' );
|
|
|
|
|
|
|
Initial theme scaffolding (v0.0.1)
- Bootstrap 5 CSS/JS integration via Yarn (served locally)
- Dart Sass build pipeline with PostCSS, Autoprefixer, cssnano
- Twig 3.0 via Composer with PSR-4 autoloading
- FSE block theme templates (index, home, single, page, archive, search, 404)
- Template parts (header, footer) and block patterns
- theme.json with Bootstrap 5-aligned design tokens
- Gitea CI/CD workflow for automated release packages
- WordPress i18n support (en_US base, de_CH translation)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 02:25:33 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
);
|
v0.1.0 - Core Theme: patterns, dark mode, block styles, style variations
- Add 16 block patterns across 7 new categories (hero, features, CTA,
testimonials, pricing, contact, text)
- Add dark mode toggle with localStorage persistence and system preference
detection (Bootstrap 5.3 data-bs-theme)
- Register 17 custom block styles mapping Bootstrap components to WordPress
blocks (cards, alerts, tables, buttons, etc.)
- Add 4 style variations: Ocean, Forest, Sunset, Midnight
- Add sidebar template part and "Blog with Sidebar" custom template
- Add Inter and Lora variable fonts with fontFace declarations
- Add Display font size (fluid 2.5rem-3.5rem)
- Update translations (en_US .pot, de_CH .po) with all new strings
- Bump version to 0.1.0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 03:07:16 +01:00
|
|
|
|
|
|
|
|
|
|
// 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'
|
|
|
|
|
|
);
|
Initial theme scaffolding (v0.0.1)
- Bootstrap 5 CSS/JS integration via Yarn (served locally)
- Dart Sass build pipeline with PostCSS, Autoprefixer, cssnano
- Twig 3.0 via Composer with PSR-4 autoloading
- FSE block theme templates (index, home, single, page, archive, search, 404)
- Template parts (header, footer) and block patterns
- theme.json with Bootstrap 5-aligned design tokens
- Gitea CI/CD workflow for automated release packages
- WordPress i18n support (en_US base, de_CH translation)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 02:25:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
endif;
|
|
|
|
|
|
add_action( 'wp_enqueue_scripts', 'wp_bootstrap_enqueue_scripts' );
|
|
|
|
|
|
|
2026-02-08 16:27:13 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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 );
|
|
|
|
|
|
|
2026-02-08 17:33:12 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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() {
|
2026-02-08 18:12:52 +01:00
|
|
|
|
// 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'];
|
2026-02-08 17:33:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-08 18:12:52 +01:00
|
|
|
|
// 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',
|
|
|
|
|
|
);
|
2026-02-08 17:33:12 +01:00
|
|
|
|
|
2026-02-08 18:12:52 +01:00
|
|
|
|
$is_default = true;
|
|
|
|
|
|
foreach ( $base_defaults as $slug => $default_color ) {
|
|
|
|
|
|
if ( ! empty( $colors[ $slug ] ) && strtolower( $colors[ $slug ] ) !== $default_color ) {
|
|
|
|
|
|
$is_default = false;
|
|
|
|
|
|
break;
|
2026-02-08 17:33:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-08 18:12:52 +01:00
|
|
|
|
// No variation active — let Bootstrap's compiled CSS handle both modes.
|
|
|
|
|
|
if ( $is_default ) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-08 17:33:12 +01:00
|
|
|
|
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;
|
|
|
|
|
|
|
2026-02-08 16:05:29 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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 );
|
|
|
|
|
|
|
Initial theme scaffolding (v0.0.1)
- Bootstrap 5 CSS/JS integration via Yarn (served locally)
- Dart Sass build pipeline with PostCSS, Autoprefixer, cssnano
- Twig 3.0 via Composer with PSR-4 autoloading
- FSE block theme templates (index, home, single, page, archive, search, 404)
- Template parts (header, footer) and block patterns
- theme.json with Bootstrap 5-aligned design tokens
- Gitea CI/CD workflow for automated release packages
- WordPress i18n support (en_US base, de_CH translation)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 02:25:33 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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' ),
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
v0.1.0 - Core Theme: patterns, dark mode, block styles, style variations
- Add 16 block patterns across 7 new categories (hero, features, CTA,
testimonials, pricing, contact, text)
- Add dark mode toggle with localStorage persistence and system preference
detection (Bootstrap 5.3 data-bs-theme)
- Register 17 custom block styles mapping Bootstrap components to WordPress
blocks (cards, alerts, tables, buttons, etc.)
- Add 4 style variations: Ocean, Forest, Sunset, Midnight
- Add sidebar template part and "Blog with Sidebar" custom template
- Add Inter and Lora variable fonts with fontFace declarations
- Add Display font size (fluid 2.5rem-3.5rem)
- Update translations (en_US .pot, de_CH .po) with all new strings
- Bump version to 0.1.0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 03:07:16 +01:00
|
|
|
|
|
|
|
|
|
|
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' ),
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
2026-02-08 16:05:29 +01:00
|
|
|
|
|
|
|
|
|
|
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' ),
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
Initial theme scaffolding (v0.0.1)
- Bootstrap 5 CSS/JS integration via Yarn (served locally)
- Dart Sass build pipeline with PostCSS, Autoprefixer, cssnano
- Twig 3.0 via Composer with PSR-4 autoloading
- FSE block theme templates (index, home, single, page, archive, search, 404)
- Template parts (header, footer) and block patterns
- theme.json with Bootstrap 5-aligned design tokens
- Gitea CI/CD workflow for automated release packages
- WordPress i18n support (en_US base, de_CH translation)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 02:25:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
endif;
|
|
|
|
|
|
add_action( 'init', 'wp_bootstrap_pattern_categories' );
|
|
|
|
|
|
|
v0.1.0 - Core Theme: patterns, dark mode, block styles, style variations
- Add 16 block patterns across 7 new categories (hero, features, CTA,
testimonials, pricing, contact, text)
- Add dark mode toggle with localStorage persistence and system preference
detection (Bootstrap 5.3 data-bs-theme)
- Register 17 custom block styles mapping Bootstrap components to WordPress
blocks (cards, alerts, tables, buttons, etc.)
- Add 4 style variations: Ocean, Forest, Sunset, Midnight
- Add sidebar template part and "Blog with Sidebar" custom template
- Add Inter and Lora variable fonts with fontFace declarations
- Add Display font size (fluid 2.5rem-3.5rem)
- Update translations (en_US .pot, de_CH .po) with all new strings
- Bump version to 0.1.0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 03:07:16 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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' => '
|
2026-02-08 16:27:13 +01:00
|
|
|
|
ul.is-style-list-unstyled, ol.is-style-list-unstyled { list-style: none; padding-inline-start: 0; }',
|
v0.1.0 - Core Theme: patterns, dark mode, block styles, style variations
- Add 16 block patterns across 7 new categories (hero, features, CTA,
testimonials, pricing, contact, text)
- Add dark mode toggle with localStorage persistence and system preference
detection (Bootstrap 5.3 data-bs-theme)
- Register 17 custom block styles mapping Bootstrap components to WordPress
blocks (cards, alerts, tables, buttons, etc.)
- Add 4 style variations: Ocean, Forest, Sunset, Midnight
- Add sidebar template part and "Blog with Sidebar" custom template
- Add Inter and Lora variable fonts with fontFace declarations
- Add Display font size (fluid 2.5rem-3.5rem)
- Update translations (en_US .pot, de_CH .po) with all new strings
- Bump version to 0.1.0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 03:07:16 +01:00
|
|
|
|
) );
|
|
|
|
|
|
|
|
|
|
|
|
// 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' => '
|
2026-02-08 16:27:13 +01:00
|
|
|
|
.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; }',
|
v0.1.0 - Core Theme: patterns, dark mode, block styles, style variations
- Add 16 block patterns across 7 new categories (hero, features, CTA,
testimonials, pricing, contact, text)
- Add dark mode toggle with localStorage persistence and system preference
detection (Bootstrap 5.3 data-bs-theme)
- Register 17 custom block styles mapping Bootstrap components to WordPress
blocks (cards, alerts, tables, buttons, etc.)
- Add 4 style variations: Ocean, Forest, Sunset, Midnight
- Add sidebar template part and "Blog with Sidebar" custom template
- Add Inter and Lora variable fonts with fontFace declarations
- Add Display font size (fluid 2.5rem-3.5rem)
- Update translations (en_US .pot, de_CH .po) with all new strings
- Bump version to 0.1.0
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 03:07:16 +01:00
|
|
|
|
) );
|
|
|
|
|
|
|
|
|
|
|
|
// 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; }',
|
|
|
|
|
|
) );
|
|
|
|
|
|
}
|
|
|
|
|
|
endif;
|
|
|
|
|
|
add_action( 'init', 'wp_bootstrap_block_styles' );
|
|
|
|
|
|
|
Initial theme scaffolding (v0.0.1)
- Bootstrap 5 CSS/JS integration via Yarn (served locally)
- Dart Sass build pipeline with PostCSS, Autoprefixer, cssnano
- Twig 3.0 via Composer with PSR-4 autoloading
- FSE block theme templates (index, home, single, page, archive, search, 404)
- Template parts (header, footer) and block patterns
- theme.json with Bootstrap 5-aligned design tokens
- Gitea CI/CD workflow for automated release packages
- WordPress i18n support (en_US base, de_CH translation)
Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-08 02:25:33 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* 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' );
|
2026-02-08 15:11:00 +01:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 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' );
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 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' );
|