Fix 10 known bugs: catalog, single product, and account pages (v0.1.5)
All checks were successful
Create Release Package / PHP Lint (push) Successful in 44s
Create Release Package / Build Release (push) Successful in 52s

Catalog: page title via woocommerce_page_title(), breadcrumbs, category
template rename (underscore), 3-column grid, single chevron on sort.

Single product: variable form data attributes + disabled CSS class fix
(WC JS only toggles CSS classes, not HTML disabled attribute), dark mode
select specificity (0,5,1) to beat WC's (0,4,3) background shorthand,
gallery main image in thumbnail strip with empty URL guard, related/
upsells setup_postdata for correct global $product, grouped product
loop logic rewrite.

Account: downloads via wc_get_customer_available_downloads().

New: product-gallery.js, sanitize_title filter, wc_setup_product_data()
and wp_reset_postdata() Twig functions, product-thumbnails.html.twig
suppressor. Removed obsolete PLAN.md and SETUP.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 03:33:31 +01:00
parent 98359d4cfb
commit 784b400c46
23 changed files with 340 additions and 922 deletions

View File

@@ -237,6 +237,24 @@ Recurring bugs and non-obvious behaviours discovered across sessions. **Read thi
- **WooCommerce float layout fights Bootstrap grid** -- `div.product div.images/summary` have `float:left/right; width:48%` in `woocommerce-layout.css`. Override with `float: none; width: 100%`.
- **Bootstrap `g-*` gutters add negative top margin** -- `g-4` sets both `--bs-gutter-x` and `--bs-gutter-y`; the `.row` gets `margin-top: calc(-1 * var(--bs-gutter-y))` pulling it upward. Use `gx-*` for horizontal-only gutters when vertical gap isn't desired.
### WooCommerce Variation JS
- **NEVER use HTML `disabled` attribute on the add-to-cart button.** WC's `add-to-cart-variation.js` only manages CSS classes (`disabled`, `wc-variation-selection-needed`) via jQuery `.addClass()`/`.removeClass()`. It never removes the HTML `disabled` attribute. The HTML attribute prevents click events from firing, making the button permanently unclickable.
- Use CSS classes `disabled wc-variation-selection-needed` instead. WC's `onAddToCart` handler checks `$button.is('.disabled')` (CSS class), not the HTML property.
- WC variation form requires `data-product_id` and `data-product_variations` on `<form class="variations_form">`. Without these, the JS doesn't initialize.
- Attribute select `name` and `data-attribute_name` must use `sanitize_title()` on the attribute name (lowercase, hyphens) to match the keys in the variation JSON data.
### WooCommerce Variation Select CSS Specificity
- WC's `.woocommerce div.product form.cart .variations select` at specificity `(0,4,3)` uses `background` shorthand — this resets BOTH `background-color` and `background-image`. A dark mode override must exceed `(0,4,3)` AND override `background-image` with `var(--bs-form-select-bg-img)` for Bootstrap's dark-mode-aware chevron SVG.
- Previous selector `[data-bs-theme="dark"] .variations .form-select` at `(0,3,0)` lost the specificity battle.
### WooCommerce Gallery Data Integrity
- `_product_image_gallery` post meta may contain IDs pointing to product variations or other non-attachment posts instead of image attachments. `wp_get_attachment_url()` returns empty/false for these.
- Always guard thumbnail rendering with `{% if thumb_url %}` after calling `wp_get_attachment_url(image_id)`.
- The main product image should be prepended to the gallery strip so users can switch back to it after viewing gallery images.
### Double Heading Prevention
- Parent theme (`wp-bootstrap`) conditionally skips its `<h1>` when `post.title` is empty.
@@ -327,10 +345,50 @@ The child theme inherits from `wp-bootstrap` via WordPress `Template: wp-bootstr
## Version History
Current version: **v0.1.4**
Current version: **v0.1.5**
## Session History
### 2026-03-01 — v0.1.5 Fix 10 Known Bugs
**Scope:** Fixed all 10 bugs from KNOWN_BUGS.md — catalog page features (title, breadcrumbs, categories, filters, sort dropdown), single product fixes (variable form, gallery, related products, grouped products), and downloads page.
**Files changed (17):**
- `inc/Twig/WooCommerceExtension.php` — Added `woocommerce_page_title` and `wc_get_customer_available_downloads` to `ALLOWED_FUNCTIONS`; added `wc_setup_product_data()` method, `wp_reset_postdata` Twig function, and `sanitize_title` Twig filter
- `templates/single-product/add-to-cart/variable.html.twig` — Added `data-product_id` and `data-product_variations` attributes to form; applied `sanitize_title` filter on attribute names for variation matching
- `templates/single-product/add-to-cart/variation-add-to-cart-button.html.twig` — Removed HTML `disabled` attribute, replaced with CSS classes `disabled wc-variation-selection-needed` (WC JS only manages CSS classes, never the HTML attribute)
- `templates/single-product/product-image.html.twig` — Prepend main image to gallery thumbnail strip; added `{% if thumb_url %}` guard for invalid attachment IDs
- `templates/single-product/product-thumbnails.html.twig` — New empty override to suppress WC's default full-size gallery images
- `templates/single-product/related.html.twig` — Added `wc_setup_product_data()` before each product render + `wp_reset_postdata()` after loop
- `templates/single-product/up-sells.html.twig` — Same setup_postdata fix as related.html.twig
- `templates/single-product/add-to-cart/grouped.html.twig` — Rewrote to compute `quantites_required`/`show_add_to_cart_button` in loop (matching WC PHP); moved hidden input outside conditional; added `has_options()` and `is_sold_individually()` checks
- `templates/loop/header.html.twig` — Replaced `page_title` context variable with direct `fn('woocommerce_page_title', false)` call
- `woocommerce/archive-product.php` — Added `woocommerce_breadcrumb()` call before shop loop header
- `functions.php` — Changed loop columns from 4 to 3; added product gallery JS conditional enqueue
- `templates/loop/loop-start.html.twig` — Changed default columns from 4 to 3
- `assets/css/wc-bootstrap.css` — Replaced duplicate ordering select rules with `appearance: none`; increased dark mode variation select specificity to `(0,5,1)` to beat WC's `(0,4,3)` background shorthand
- `templates/content-product-cat.html.twig` — Renamed to `content-product_cat.html.twig` (WC uses underscore)
- `assets/js/product-gallery.js` — New: thumbnail click-to-swap, gallery fade-in, active state highlighting
- `templates/myaccount/downloads.html.twig` — Replaced `fn('WC').customer.get_downloadable_products()` with direct `fn('wc_get_customer_available_downloads', get_current_user_id())`
- `style.css` — Version bump 0.1.4 → 0.1.5
**Key decisions:**
- **`wc_setup_product_data()` over PHP bridge:** Adding a Twig function that sets `$GLOBALS['product']` + `setup_postdata()` is simpler and more maintainable than creating PHP bridge files for related/upsells rendering
- **`json_encode|esc_attr` over `wc_esc_json`:** Avoids adding a custom filter; `esc_attr` performs the same HTML entity encoding as `wc_esc_json`
- **3 columns default:** With sidebar taking `col-lg-3`, 3 product columns in `col-lg-9` gives better card proportions than 4
- **Vanilla JS gallery:** Lightweight click-to-swap handler instead of WC's built-in flexslider/photoswipe (which requires specific PHP setup for `wp_get_attachment_image_src` data attributes)
- **CSS class `disabled` over HTML `disabled` attribute:** WC's `add-to-cart-variation.js` `onShow`/`onHide` only toggle CSS classes (`disabled`, `wc-variation-selection-needed`). The `onAddToCart` handler checks `.is('.disabled')`. Using the HTML `disabled` attribute prevents click events entirely and WC JS never removes it.
- **CSS specificity `(0,5,1)` for dark mode selects:** WC's `.woocommerce div.product form.cart .variations select` at `(0,4,3)` uses `background` shorthand which resets `background-color` and `background-image`. Must exceed this specificity AND override `background-image` for Bootstrap's dark-mode SVG chevron.
**Key findings (WC variation JS):**
- WC's `add-to-cart-variation.js` event flow: `onChange``check_variations``findMatchingVariations``found_variation``onFoundVariation` (300ms setTimeout) → `show_variation``onShow` (removes CSS class only)
- `onShow` calls `$button.removeClass('disabled wc-variation-selection-needed')` — never touches the HTML `disabled` attribute/property
- `onAddToCart` checks `$button.is('.disabled')` — CSS class, not HTML attribute
- Gallery `_product_image_gallery` meta may contain IDs of product variations or posts instead of image attachments — always guard with `{% if thumb_url %}` after `wp_get_attachment_url()`
### 2026-03-01 — v0.1.4 Security Audit & Performance Fixes
**Scope:** Cross-theme security audit (12 findings), all fixed. Covers fn() whitelist, notice data escaping, search query escaping, per-request context caching, shared render helper, and unused constant removal.