feat: Bootstrap 5 block renderer, widget cards, and sidebar post layout (v1.1.0)
All checks were successful
Create Release Package / PHP Lint (push) Successful in 1m7s
Create Release Package / Build Release (push) Successful in 1m41s

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>
This commit is contained in:
2026-02-28 23:43:43 +01:00
parent 9904bf508a
commit 3165e60639
16 changed files with 955 additions and 49 deletions

View File

@@ -34,7 +34,7 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
**Note for AI Assistants:** Clean this section after the specific features are done or new releases are made. Effective changes are tracked in `CHANGELOG.md`. Do not add completed versions here - document them in the Session History section at the end of this file.
Current version is **v1.0.11**. See `PLAN.md` for details.
Current version is **v1.1.0**. See `PLAN.md` for details.
## Technical Stack
@@ -234,6 +234,60 @@ Build steps (in order):
## Session History
### Session 19 — v1.1.0 Block Renderer, Widget Renderer & Sidebar Post Layout (2026-02-28)
**Completed:** Bootstrap 5 class injection for core blocks, sidebar widget card wrappers, widget SCSS styling, and sidebar-default post template.
**What was built:**
- **BlockRenderer** (`inc/Block/BlockRenderer.php`): Per-block `render_block_{$name}` filters inject Bootstrap classes into 8 core block types using `WP_HTML_Tag_Processor`. Supports button color mapping (WP preset slugs → Bootstrap btn-{variant}), striped tables, responsive images, input-group search forms, blockquote styling, and list-group block style. Extensible via `wp_bootstrap_block_renderer_blocks` filter.
- **WidgetRenderer** (`inc/Block/WidgetRenderer.php`): `dynamic_sidebar_params` filter wraps sidebar widgets in Bootstrap `.card > .card-body` structure. `widget_block_content` filter downgrades block widget `<h2 class="wp-block-heading">` to `<h4>` via preg_replace. Widget ID and type-specific classes extracted from the already-processed `before_widget` string.
- **Widget SCSS** (`src/scss/_widgets.scss`): Comprehensive widget styling — list items with border separators, flush-to-card-edge positioning (negating card-body padding), form-control selects, input-group search forms, pill-badge tag cloud, and secondary-color post dates. Handles both legacy and block widget nesting (`.wp-block-group > ul`).
- **List Group block style** (`functions.php`): `register_block_style('core/list', ...)` for the `is-style-list-group` style option in the editor.
- **Single post sidebar template** (`views/pages/single-sidebar.html.twig`): Two-column Bootstrap layout (8/4 split) with all single post features. "More posts" section uses `row-cols-md-2` for the narrower column.
- **Post template selection** (`inc/Template/TemplateController.php`): Posts default to sidebar layout; `page-full-width` template slug maps to full-width.
- **Sidebar data always loaded for posts** (`inc/Template/ContextBuilder.php`): `getSidebarData()` called unconditionally for `is_singular('post')`.
**Architecture decisions:**
- **Per-block filters over generic `render_block`**: `render_block_{$blockName}` only fires for the targeted block type, avoiding callback overhead on every block render. More maintainable — each handler is scoped to one block type.
- **`WP_HTML_Tag_Processor` over regex**: WordPress 6.7+ class provides safe, forward-only HTML manipulation. `add_class()` is idempotent (no duplicate classes). Handles malformed HTML gracefully.
- **Card-body with card-title, not card-header**: WordPress omits `before_title`/`after_title` entirely when a widget has no title. Using `card-header` for the title would leave an empty `<div class="card-header"></div>` for titleless widgets — broken HTML. Card-body with card-title inside works correctly in both cases.
- **`dynamic_sidebar_params` + regex extraction**: WordPress runs `sprintf` on `before_widget` BEFORE the `dynamic_sidebar_params` filter fires, so placeholder strings (`%1$s`, `%2$s`) are already replaced. Widget ID comes from `$params[0]['widget_id']`, type classes extracted via regex from the processed HTML.
- **Sidebar default for posts**: Blog posts are content-centric and benefit from sidebar context (recent posts, search, tags). Full-width is opt-in via "Full Width" template assignment.
**Key findings:**
- `WP_Block $instance` type hint is too strict for manual `apply_filters()` calls — WordPress passes `null` when filters are invoked outside the block rendering pipeline. Use `?WP_Block $instance = null`.
- Block widgets nest content inside `.wp-block-group` wrappers. CSS selectors like `.card-body > ul` won't match — need `.card-body > .wp-block-group > ul` for flush list positioning.
- `widget_block_content` filter (WordPress 5.8+) fires for block-based widgets only, allowing inner HTML modification without affecting legacy widgets.
- WordPress search block uses `.wp-block-search__inside-wrapper` as the input+button container — adding `.input-group` to this element creates a proper Bootstrap input-group.
- `@extend .btn; @extend .btn-primary` in SCSS works for search submit buttons because Bootstrap is imported before the widgets partial in the SCSS cascade.
**Files created:**
- `inc/Block/BlockRenderer.php` — 8 block handlers
- `inc/Block/WidgetRenderer.php` — card wrapper + heading downgrade
- `src/scss/_widgets.scss` — widget Bootstrap styling
- `views/pages/single-sidebar.html.twig` — two-column post template
**Files modified:**
- `functions.php` — init hooks for both renderers, list-group block style
- `src/scss/style.scss` — widgets import
- `inc/Template/TemplateController.php` — post template selection logic
- `inc/Template/ContextBuilder.php` — always load sidebar for posts
- `style.css` — version bump to 1.1.0
- `CHANGELOG.md`, `README.md`, `CLAUDE.md` — documentation
### Session 18 — v1.0.12 Admin Bar Offcanvas Fix (2026-02-28)
**Completed:** Scoped admin bar offcanvas padding to mobile viewports only.
**What was fixed:**
- `functions.php`: Added `@media (max-width: 991.98px)` wrapper to the admin bar offcanvas padding CSS so the extra padding does not appear on desktop where the offcanvas renders inline as a regular navbar.
### Session 17 — v1.0.11 Offcanvas Navigation & User Context (2026-02-28)
**Completed:** Switched mobile navigation from Bootstrap collapse to offcanvas, added logged-in user context to the header, and fixed admin bar overlap.