A collection of filters and actions for bootstrap-based themes
When integrating Bootstrap with Wordpress, it is not sufficient to just include assets and add some css-classes to templates. You will also need to inject bootstrap-compatible markup into programmatically generated sections, such as menus, widgets, comments etc. Bootstrap Hooks aims to cover most of these cases to make us immediately start implementing the individual application rather than getting hassled by markup incompatibilities.
Bootstrap Hooks consists of six separate modules for Navigation, Pagination, Forms, Widgets, Comments, Gallery and Blocks which can be used altogether or independently from each other. Every module is customizable by passing options to a central filter method.
Bootstrap Hooks has been optimized for Bootstrap 5, though it can be adjusted to also support older versions. See Recipes for more details on how to do this.
Install preferrably as a must-use plugin. It should be enabled by themes programmatically and updated by theme developers who maintain Bootstrap inside a certain themes.
Bootstrap Hooks is made aware that your theme actually supports Bootstrap by calling add_theme_support
in your functions.php
:
add_theme_support( 'bootstrap' );
With few exceptions, Bootstrap Hooks works out-of-the-box by injecting its magic via actions and filters. Read further to learn more about the distinct modules.
The Content-Hook takes care of your post content primarily. It sets proper markup and classes to images, captions, blockquotes and tables. It also handles the post thumbnail to add the responsive image class.
In Bootstrap 4, the Tag-component may break Wordpress` default taxonomy styles. See here for reference.
To avoid undesired effects, the tag
class is replaced with post-tag
in body_class
- or post_class
-methods and also when it's found in the content.
This module also provides a custom method to handle the edit-post-link.
Search your theme for occurrences of edit_post_link
and replace with wp_bootstrap_edit_post_link
:
// Edit post link
wp_bootstrap_edit_post_link(
sprintf(
/* translators: %s: Name of current post */
__( 'Edit<span class="screen-reader-text"> "%s"</span>', 'textdomain' ),
get_the_title()
),
'<span class="edit-link">',
'</span>'
) );
When using Bootstrap Hooks as a plugin, you should definitely check if the method has been loaded by wrapping function call into a conditional function_exists
-check. See the Recipes section for an example.
This module handles search- and password-forms.
A search-form is rendered as input-group. You can customize the button's icon by passing arguments from a filter. This example shows how to integrate font-awesome:
// Show Font-Awesome search icon in Searchform
function my_bootstrap_options($options) {
return array_merge($options, array(
'search_submit_label' => '<i class="fa fa-search"></i>'
));
}
add_filter( 'bootstrap_options', 'my_bootstrap_options' );
The gallery hook uses a grid of thumbnails in combination with a carousel inside a modal for zoom view.
In typical Bootstrap-driven layout, column sizes may differ from Wordpress default thumbnail size. You may update thumbnail size to your needs in order to fit thumbnail images into at least three columns:
// Adjust thumbnail size
update_option( 'thumbnail_size_w', 230 );
update_option( 'thumbnail_size_h', 230 );
update_option( 'thumbnail_crop', 1 );
The implementation does not handle different zoom-image heights. An easy way to fix this, is to register a custom image size with cropping enabled and apply to the gallery hook:
// Register custom gallery image size
add_image_size( 'gallery-zoom', 900, 500, true );
// Apply custom image size to gallery zoom
function my_bootstrap_options($options) {
return array_merge($options, array(
'gallery_zoom_size' => 'gallery-zoom'
));
}
add_filter( 'bootstrap_options', 'my_bootstrap_options' );
Bootstrap Hooks provides a custom MenuWalker based on the work by Edward McIntyre which is injected into menus per default.
For the menus being in primary or top location, the navbar-nav
-class will automatically be added.
Bootstrap Hooks also adds a script that handles links on dropdown-toggles which are prevented by default from Bootstrap.
Since there's no existing hook for posts-pagination as well as for post-navigation, we need to call custom methods from our templates:
Search your theme for occurrences of the_posts_pagination
and replace with wp_bootstrap_posts_pagination
:
// Previous/next page navigation.
wp_bootstrap_posts_pagination( array(
'prev_text' => __( 'Previous page', 'textdomain' ),
'next_text' => __( 'Next page', 'textdomain' ),
'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'textdomain' ) . ' </span>'
) );
Search your theme for occurrences of the_post_navigation
and replace with wp_bootstrap_post_navigation
:
// Previous/next post navigation.
wp_bootstrap_post_navigation( array(
'next_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Next', 'textdomain' ) . '</span> ' .
'<span class="screen-reader-text">' . __( 'Next post:', 'textdomain' ) . '</span> ' .
'<span class="post-title">%title</span>',
'prev_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Previous', 'textdomain' ) . '</span> ' .
'<span class="screen-reader-text">' . __( 'Previous post:', 'textdomain' ) . '</span> ' .
'<span class="post-title">%title</span>'
) );
Widgets are rendered as cards. Some manipulations take care of content that's common to widgets, such as applying list-groups to unordered lists.
Make sure that you registered any widget areas in your functions.php
:
// Register widget area.
register_sidebar( array(
'name' => __( 'Widget Area', 'textdomain' ),
'id' => 'sidebar-1',
'description' => __( 'Add widgets here to appear in your sidebar.', 'textdomain' )
) );
Comments are rendered as nested media-objects.
You can customize the label 'Comment' by making use of the bootstrap_options
-filter:
// Customize Comment Label
function my_bootstrap_options($args) {
return array_merge($args, array(
'comment_label' => ('Comment' , 'textdomain');
));
}
add_filter( 'bootstrap_options', 'my_bootstrap_options' );
When intended to use as plugin or must-use plugin, you should also take care of the situation where the plugin is unistalled and check if any of the functions exist first:
// Previous/next page navigation.
call_user_func_array(function_exists('wp_bootstrap_posts_pagination') ? 'wp_bootstrap_posts_pagination' : 'the_posts_pagination', array( array(
'prev_text' => __( 'Previous page', 'textdomain' ),
'next_text' => __( 'Next page', 'textdomain' ),
'before_page_number' => '<span class="meta-nav screen-reader-text">' . __( 'Page', 'textdomain' ) . ' </span>'
) ) );
// Previous/next post navigation.
call_user_func_array(function_exists('wp_bootstrap_post_navigation') ? 'wp_bootstrap_post_navigation' : 'the_post_navigation', array( array(
'next_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Next', 'textdomain' ) . '</span> ' .
'<span class="screen-reader-text">' . __( 'Next post:', 'textdomain' ) . '</span> ' .
'<span class="post-title">%title</span>',
'prev_text' => '<span class="meta-nav" aria-hidden="true">' . __( 'Previous', 'textdomain' ) . '</span> ' .
'<span class="screen-reader-text">' . __( 'Previous post:', 'textdomain' ) . '</span> ' .
'<span class="post-title">%title</span>'
) ) );
// Edit post link
call_user_func_array(function_exists('wp_bootstrap_edit_post_link') ? 'wp_bootstrap_edit_post_link' : 'edit_post_link', array(
sprintf(
/* translators: %s: Name of current post */
__( 'Edit<span class="screen-reader-text"> "%s"</span>', 'textdomain' ),
get_the_title()
),
'<span class="edit-link">',
'</span>'
) );
If you're still working with Bootstrap 3, you need to override at least some options.
// Bootstrap 3 Options
function my_bootstrap_options($options) {
return array_merge($options, array(
'img_class' => 'img-responsive',
'align_center_class' => 'center-block',
'edit_post_link_class' => 'btn btn-default btn-xs',
'search_submit_label' => '<i class="glyphicon glyphicon-search"></i>',
'gallery_thumbnail_class' => '',
'gallery_thumbnail_img_class' => 'img-thumbnail',
'close_button_class' => 'btn btn-default',
'carousel_item_class' => 'item',
'widget_class' => 'panel',
'widget_modifier_class' => 'panel-default',
'widget_header_class' => 'panel-heading',
'widget_content_class' => 'panel-block'
));
}
add_filter( 'bootstrap_options', 'my_bootstrap_options', 1 );
Bootstrap Hooks is highly customizable. That's mostly required because of managing different Bootstrap versions without splitting up the codebase. Normally there should be no need to change a lot.
edit_post_link( string $text = null, string $before = '', string $after = '', int $id, string $class = 'post-edit-link' )
Displays the edit post link for post. See edit_post_link for further details.
Displays a paginated navigation to next/previous set of posts, when applicable. See the_posts_pagination for further details.
Displays the navigation to next/previous post, when applicable. See the_post_navigation for further details.
Inject custom options.
Name | Description | Default |
---|---|---|
field_class | Sets the field class used in forms | form-group |
text_input_class | Sets the text input class used in forms | form-control |
submit_class | Sets submit button css class | btn btn-primary |
reply_link_class | Set reply link css class | btn btn-primary btn-xs |
comment_label | Sets the comment label | Comment |
img_class | Sets the general image class used in content and thumbnails | img-responsive |
align_left_class | Aligns an image to left | pull-left |
align_right_class | Aligns an image to right | pull-right |
align_center_class | Center an image | mx-auto |
img_caption_tag | Sets the tag for the img caption element | figure |
img_caption_class | Sets the css class for the img caption element | figure |
img_caption_text_tag | Sets the tag for the img caption text element | figcaption |
img_caption_text_class | Sets the css class for the img caption text element | figure-caption |
img_caption_img_class | Sets the css class for the image of the img element of the caption | figure-img |
table_class | Sets the table css class | table |
table_container_tag | Sets the table container tag | div |
table_container_class | Sets the table container class | table-responsive |
blockquote_class | Sets the blockquote css class | blockquote |
blockquote_footer_tag | Sets the blockquote footer tag | footer |
blockquote_footer_class | Sets the blockquote footer css class | blockquote-footer |
edit_post_link_class | Sets the edit post link css class | btn btn-secondary |
edit_post_link_container_class | Sets the edit post link container css class | form-group btn-group btn-group-sm |
search_submit_label | Sets the searchfield's submit label | 🔎 |
text_input_class | Sets the class of textfields used in search- and password-forms | form-control |
submit_button_class | Sets the class of submit buttons used in search- and password-forms | btn btn-primary |
gallery_thumbnail_size | Sets the default thumbnail size | thumbnail |
gallery_thumbnail_class | Sets the default thumbnail size | |
gallery_thumbnail_img_class | Sets the default thumbnail size | |
gallery_zoom_size | Sets the image size for the carousel view | large |
close_button_class | Sets the modal's close button class | btn btn-secondary |
close_button_label | Sets the modal's close button label | __('Close') |
carousel_item_class | Sets the carousel's item class | _item |
menu_item_class | Sets the menu item class | nav-item |
menu_item_link_class | Sets the menu item link class | nav-link |
sub_menu_tag | Sets the sub menu tag | ul |
sub_menu_class | Sets the sub menu class | dropdown-menu |
sub_menu_header_class | Sets the sub menu class | dropdown-header |
sub_menu_item_tag | Sets the sub menu item tag | li |
sub_menu_item_class | Sets the sub menu item class | |
sub_menu_item_link_class | Sets the sub menu header class | dropdown-item |
caret | Sets the menu item caret class (bs < 5) | <span class="caret"></span> |
pagination_class | Sets the pagination class | pagination |
page_item_class | Sets the page item class | page-item |
page_item_active_class | Sets the page item active class | active |
page_link_class | Sets the page link css class | page-link |
post_nav_class | Sets the post navigation class | nav |
post_nav_tag | Sets the post navigation tag | ul |
post_nav_item_class | Sets the post navigation item class | nav-item |
post_nav_item_tag | Sets the post navigation item tag | li |
post_nav_link_class | Sets the post navigation link class | nav-link |
paginated_class | Sets the paginated class | pagination pagination-sm |
paginated_tag | Sets the paginated tag | ul |
paginated_item_class | Sets the paginated item class | page-item |
paginated_item_tag | Sets the paginated item tag | li |
paginated_link_class | Sets the paginated link tag | page-link |
next_posts_link_class | Specify next posts link class | btn btn-sm btn-secondary |
previous_posts_link_class | Specify previous posts link class | btn btn-sm btn-secondary |
widget_class | Sets the widget class | card |
widget_modifier_class | Sets the widget modifier class | card-widget |
widget_header_class | Sets the widget header class | card-header |
widget_content_class | Sets the widget content class | card-body |
Download Docker CE for your OS. Download NodeJS for your OS.
Run container. Point your browser to http://localhost:8020.
docker-compose up -d
Import test data. Activate wordpress-importer if needed.
docker compose run wp plugin activate wordpress-importer
docker compose run wp import vendor/wptrt/theme-unit-test --authors=skip
Update composer dependencies
docker-compose run composer update
Stop all globally running docker containers
docker stop $(docker ps -a -q)
Install front-end dependencies
npm i
Watch front-end dependencies
npm run watch
Create a build for production
npm run build