Files
wc-bootstrap/CLAUDE.md
magdev cb2d064441
All checks were successful
Create Release Package / PHP Lint (push) Successful in 59s
Create Release Package / Build Release (push) Successful in 1m11s
Prepare v0.1.1 bugfix release
Update CHANGELOG.md with v0.1.1 entry (dark mode selects, notice
styling, product card overflow). Bump version in style.css. Document
CSS specificity learnings in CLAUDE.md. Update README release example.

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

409 lines
24 KiB
Markdown

# WooCommerce Bootstrap
**Author:** Marco Grätsch
**Author URL:** <https://src.bundespruefstelle.ch/magdev>
**Author Email:** <magdev3.0@gmail.com>
**Repository URL:** <ssh://git@src.bundespruefstelle.ch:2022/magdev/wc-bootstrap.git>
**Issues URL:** <ssh://git@src.bundespruefstelle.ch:2022/magdev/wc-bootstrap.git/issues>
## Project Overview
This child theme of wp-bootstrap (`../wp-bootstrap/` or <https://src.bundespruefstelle.ch/magdev/wp-bootstrap>) extends wp-bootstrap and adds overrides for all theme files of the WooCommerce plugin (`../../plugins/woocommerce/` or <https://github.com/woocommerce/woocommerce.git>). All theme files from the plugin are converted to Bootstrap 5 structures and styles.
## Technical Stack
- **Language:** PHP 8.3.x
- **Framework:** Latest WordPress Theme API
- **Template Engine:** Twig 3.0 (via Composer)
- **WordPress Base Theme:** wp-bootstrap
- **Frontend:** Bootstrap 5 JavaScript & Vanilla JavaScript
- **Styling:** Bootstrap 5 & Custom CSS (if necessary)
- **Dependency Management:** Composer (PHP), npm (JS/CSS)
- **Internationalization:** WordPress i18n (.pot/.po/.mo files)
### Security Best Practices
- All user inputs are sanitized (integers for quantities/prices)
- Nonce verification on form submissions
- Output escaping in templates (`esc_attr`, `esc_html`, `esc_js`)
- Direct file access prevention via `ABSPATH` check
- XSS-safe DOM construction in JavaScript (no `innerHTML` with user data)
- `json_encode|raw` in JS context is correct (not XSS) -- Twig auto-escaping would break JSON
- `styles|raw` in PDF exports is backend-generated static CSS, documented with comments
### Translation Ready
All user-facing strings use:
```php
__('Text to translate', 'wc-bootstrap')
_e('Text to translate', 'wc-bootstrap')
```
Text domain: `wc-bootstrap`
#### Text Domain Strategy
<!-- Choose ONE of these strategies based on your project needs: -->
**Option A -- Plugin domain (recommended when plugin provides Twig functions):**
All strings in theme templates use the plugin's `'woocommerce'` domain. This is necessary when the plugin registers `__()` / `_n()` as Twig functions with a default domain, because `load_child_theme_textdomain()` does not resolve at Twig render time.
**Rule:** Always use `'woocommerce'` as the text domain in all Twig templates and PHP files.
**Option B -- Theme domain (when theme handles its own Twig rendering):**
Theme templates use the theme's own `'wc-bootstrap'` domain. Ensure `load_child_theme_textdomain()` is called before Twig rendering.
#### Available Translations
<!-- List your supported locales here -->
- `en_US` -- English (base language, .pot template)
- `de_DE` -- German (Germany, formal)
- `de_CH` -- German (Switzerland, formal)
- `fr_FR` -- French (France)
Translation file naming convention: `wc-bootstrap-{locale}.po` (e.g., `wc-bootstrap-de_CH.po`)
Compiled `.mo` files are built by the CI/CD pipeline during releases. For local development:
```bash
for po in languages/wc-bootstrap-*.po; do msgfmt -o "${po%.po}.mo" "$po"; done
```
#### Updating Translations (Fast JSON Workflow)
##### Step 1 -- Regenerate `.pot` files
wp-bootstrap (PHP sources only):
```bash
docker exec woocommerce-wordpress wp i18n make-pot \
/var/www/html/wp-content/themes/wp-bootstrap \
/var/www/html/wp-content/themes/wp-bootstrap/languages/wp-bootstrap.pot \
--allow-root
```
Child theme (PHP + Twig). Use a Twig extractor script that scans `.html.twig` files for `__()`, `_e()`, `_n()`, `_x()` calls, generates `twig-extracted.pot`, then merges with WP-CLI PHP extraction:
```bash
# Inside languages/ directory:
python3 twig-extractor.py # generates twig-extracted.pot
docker exec woocommerce-wordpress wp i18n make-pot \
/var/www/html/wp-content/themes/wc-bootstrap \
/var/www/html/wp-content/themes/wc-bootstrap/languages/php-extracted.pot \
--allow-root
msgcat --use-first php-extracted.pot twig-extracted.pot \
-o wc-bootstrap.pot
rm php-extracted.pot
```
##### Step 2 -- Merge new strings into all `.po` files
```bash
for locale in de_CH de_DE fr_FR; do
msgmerge --update --backup=none --no-fuzzy-matching \
wc-bootstrap-${locale}.po wc-bootstrap.pot
done
```
##### Step 3 -- Translate and apply
Extract untranslated strings per locale into JSON, translate (via AI or manual), then apply with `languages/patch-po.py`.
German variant derivation rules:
- `de_CH` from `de_DE`: replace `ss` where Swiss German uses `ss` instead of `ß`
- `de_DE_informal` from `de_DE`: `Sie` -> `du`, `Ihr` -> `dein`, etc.
- `de_CH_informal`: combine both transformations
##### Step 4 -- Validate and compile
```bash
for po in languages/wc-bootstrap-*.po; do msgfmt --statistics "$po" -o /dev/null; done
for po in languages/wc-bootstrap-*.po; do msgfmt -o "${po%.po}.mo" "$po"; done
```
### Important Git Notes
- Default branch while development is `dev`
- Create releases from branch `main` after merging branch `dev`
- Tags should use format `vX.X.X` (e.g., `v1.1.22`), start with v0.1.0
- Use annotated tags (`-a`) not lightweight tags
- **ALWAYS push tags to origin** -- CI/CD triggers on tag push
- Commit messages should follow the established format with Claude Code attribution
- `.claude/settings.local.json` changes are typically local-only (stash before rebasing)
### CRITICAL -- Release Workflow
On every new version, ALWAYS execute this complete workflow:
```bash
# 1. Commit changes to dev branch
git add <files>
git commit -m "Description of changes (vX.X.X)"
# 2. Merge dev to main
git checkout main
git merge dev --no-edit
# 3. Create annotated tag
git tag -a vX.X.X -m "Version X.X.X - Brief description"
# 4. Push everything to origin
git push origin dev main vX.X.X
# 5. Switch back to dev for continued development
git checkout dev
```
Never skip any of these steps. The release is not complete until all branches and the tag are pushed to origin.
### CRITICAL -- Cross-Project Release Order
When a release touches both the plugin and the theme, ALWAYS release in this order:
1. **Theme first** -- full release workflow (commit -> merge -> tag -> push)
2. **Plugin second** -- update plugin's Dockerfile/config to reference the new theme version, then full release workflow
Reason: the plugin's build references the theme version. If the plugin is released first with a new theme version number, the CI build will attempt to download a theme release that does not yet exist and fail.
---
## For AI Assistants
When starting a new session on this project:
1. Read this CLAUDE.md file first
2. Semantic versioning follows the `MAJOR.MINOR.BUGFIX` pattern
3. Check git log for recent changes
4. Verify you're on the `dev` branch before making changes
5. Run `git submodule update --init --recursive` if lib/ is empty (only if submodules present)
6. Run `composer install` if vendor/ is missing
7. Test changes before committing
8. Follow commit message format with Claude Code attribution
9. Update this session history section with learnings
10. Never commit backup files (`*.po~`, `*.bak`, etc.) - check `git status` before committing
11. Follow markdown linting rules (see below)
Always refer to this document when starting work on this project.
### Markdown Linting Rules
1. **MD031**: Blank lines before and after fenced code blocks
2. **MD056**: Table separators must have matching column counts
3. **MD009**: No trailing spaces
4. **MD012**: No multiple consecutive blank lines
5. **MD040**: Fenced code blocks should have a language specified
6. **MD032**: Lists surrounded by blank lines
7. **MD034**: Bare URLs in angle brackets or markdown link syntax
8. **MD013**: Disabled (line length)
9. **MD024**: `siblings_only` mode
## Known Pitfalls & Key Learnings
Recurring bugs and non-obvious behaviours discovered across sessions. **Read this before starting any task.**
### Template Override Mechanism
- Plugin's `Template` class uses Twig `FilesystemLoader`. The `inc/TemplateOverride.php` hooks `init` at priority 20 (after plugin init at 0) and uses `prependPath()` to give child theme templates priority.
- Plugin templates in `../../plugins/woocommerce/templates/` are overridden by matching files in `templates/`.
### Twig Compatibility
- **`|values` filter does NOT exist in Twig 3.x** -- use loop-based sum instead. Twig 4.x only.
- **Twig autoescape + WordPress escape filters = double encoding** -- register all `esc_*` filters with `['is_safe' => ['html']]` option in the plugin's `Template.php`.
- **`wp i18n make-pot` does NOT scan Twig templates** -- any string used exclusively in `.html.twig` files must be manually added to the `.pot` file.
- **`#, fuzzy` silently skips translations at runtime** -- always remove fuzzy flags after verifying translations.
- **`|nl2br|esc_html` is wrong filter order** -- `nl2br` outputs `<br>` tags, then `esc_html` escapes them to `&lt;br&gt;`. Correct: `|esc_html|nl2br`.
- **`function() is defined` is semantically wrong** -- always evaluates truthy since the function is called regardless. Use `{% if function() %}` directly.
- **HTML entities in translated strings get double-encoded** -- `&hellip;` in `__()` becomes `&amp;hellip;`. Use Unicode `…` directly or append `|raw` for trusted filter output.
- **Filter chains producing HTML need `|raw`** -- e.g., `term_description()|wptexturize|wpautop|do_shortcode|raw`.
### Bootstrap 5 vs Plugin CSS Conflicts
- Plugin CSS may define its own grid systems that override Bootstrap's flexbox `row row-cols-*`. Add theme CSS overrides as needed.
- **CSS dependency chain**: `woocommerce` -> child theme overrides. Ensures correct cascade.
- jQuery `.show()`/`.hide()` cannot override Bootstrap `!important` (`d-none`). Toggle both class and inline style.
- `overflow: visible !important` on `.wp-block-navigation__container` is essential for dropdowns inside block theme navigation.
- **WooCommerce `shop_table` borders conflict with Bootstrap `.table`** -- reset with `.woocommerce table.shop_table { border: 0 }` and cell `border-left/right: 0`.
- **WooCommerce gallery JS requires modifier classes** -- `--with-images`, `--without-images`, `--columns-N` on `.woocommerce-product-gallery` + `style="opacity: 0"` for fade-in. Without these, zoom and photoswipe won't initialize.
- **WooCommerce dark mode select backgrounds** -- `woocommerce.css` sets `select { background-color: var(--wc-form-color-background, #fff) }`. The custom property is never defined for dark mode, so it falls back to `#fff`. Override with `[data-bs-theme="dark"] .woocommerce select`.
- **Select2/SelectWoo dark mode** -- `select2.css` hardcodes `#fff` on selection containers and dropdowns. Needs overrides for `.select2-selection`, `.select2-dropdown`, `.select2-search__field`, and highlighted options.
- **WooCommerce notice CSS specificity** -- Uses `border-top: 3px solid`, `background-color: #f6f5f8`, and icon font `::before` at specificity `0,1,0`. Must use `.woocommerce .woocommerce-*` (specificity `0,2,0`) and explicitly reset `border-top` to override.
- **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.
### Double Heading Prevention
- Parent theme (`wp-bootstrap`) conditionally skips its `<h1>` when `post.title` is empty.
- Plugin passes empty `post.title` to parent theme's Twig system.
- Child theme layout templates always render their own `<h1>` with `page_title` context.
### Theme Wrapping (`_wrapped` / `_theme_wrapped`)
- `_wrapped` is set in `base.html.twig` when `_theme_wrapped` is true (parent theme provides page shell).
- **Structural blocks** (wrapper, breadcrumbs, sidebar, `<h1>`) are suppressed when wrapped.
- **Functional blocks** (`{% block scripts %}`) must ALWAYS render -- they contain AJAX handlers, JS initialization etc.
### Plugin JS Compatibility
- Theme must preserve plugin's JS-bound CSS class names (e.g., repeater fields, interactive widgets).
- Theme uses plugin's global JS objects for AJAX URLs, nonces, and i18n strings.
- Bootstrap 5 `data-bs-toggle` attributes replace plugin's custom dropdown mechanisms.
### Security
- Security audit checklist (OWASP Top-10): open redirect validation, `|esc_url`/`|esc_attr`/`|esc_js` on all dynamic output, `|wp_kses_post` for rich text, no `innerHTML` with dynamic data.
## Multi-Project Architecture
This child theme is part of a three-project ecosystem. Each project is a separate git repo with its own `CLAUDE.md` and should be worked on in **separate Claude Code sessions**.
### Projects
| Project | Type | Location | Repository |
| ------- | ---- | -------- | ---------- |
| woocommerce | Plugin (read-only) | `wp-content/plugins/woocommerce/` | <https://github.com/woocommerce/woocommerce.git> |
| wp-bootstrap | Parent Theme | `wp-content/themes/wp-bootstrap/` | <https://src.bundespruefstelle.ch/magdev/wp-bootstrap> |
| wc-bootstrap | Child Theme | `wp-content/themes/wc-bootstrap/` | <ssh://git@src.bundespruefstelle.ch:2022/magdev/wc-bootstrap.git> |
### Dependency Chain
```txt
wp-bootstrap (parent theme, Bootstrap 5 FSE + Twig rendering)
+-- wc-bootstrap (child theme, overrides plugin Twig templates with Bootstrap 5)
+-- woocommerce (plugin, provides post types, logic, base Twig templates)
```
### Important Constraints
- **WooCommerce plugin is read-only.** We have no control over its source code. All customizations happen in the child theme via template overrides and hooks.
- **Docker environment:** Container name is `woocommerce`. Use `docker exec woocommerce ...` for commands and `docker exec woocommerce apache2ctl graceful` to clear OPcache after PHP changes.
### Cross-Project Workflow
1. **One session per project.** Each session reads its own `CLAUDE.md` and stays within its own git repo.
2. **Plugin changes first.** When a change affects templates consumed by the theme, make the plugin change first, then switch to the theme session.
3. **Communicate changes explicitly.** When switching sessions, describe what changed upstream (new variables, renamed classes, new templates, etc.).
4. **Shared Docker environment.** All three projects will be bind-mounted into the same Docker container once it is set up, so changes will be visible live without rebuilds.
### Template Contract (Plugin -> Theme)
The child theme overrides plugin Twig templates by prepending its own `templates/` directory to the Twig `FilesystemLoader` via `TemplateOverride` class (hooks `init` priority 20, after plugin init at priority 0).
#### Twig Template Locations
- **Plugin base templates:** `wp-content/plugins/woocommerce/templates/`
- **Theme overrides:** `wp-content/themes/wc-bootstrap/templates/` (mirrors plugin structure)
- **Resolution order:** Theme templates take priority over plugin templates (Twig `prependPath`)
### Theme Contract (Parent -> Child)
The child theme inherits from `wp-bootstrap` via WordPress `Template: wp-bootstrap` declaration.
- **Bootstrap 5:** CSS framework and JS loaded by parent theme
- **Bootstrap Icons:** Web font available via parent theme's SCSS build
- **Twig rendering:** Parent theme's `TwigService` handles frontend rendering
- **Dark mode:** Parent handles via `data-bs-theme` attribute; child theme inherits
- **Style variations:** Color palettes available from parent, bridged to Bootstrap CSS custom properties
## Architecture Decisions
- **My Account layout** uses `col-lg-auto` (nav) + `col-lg` (content) so the nav auto-sizes to its content and the main area fills remaining space. Requires CSS resets for WooCommerce's float-based layout (`float: none; width: auto/100%`) and `max-width: none` on `.woocommerce-account main .woocommerce` to override the plugin's `max-width: 1000px`.
- **My Account navigation** uses `offcanvas-lg offcanvas-start` responsive pattern: full sidebar with icons on desktop (sticky via `position-sticky`), offcanvas slide-in on mobile with toggle button. Icons are mapped per endpoint via a Twig hash.
- **My Account dashboard** uses card-based quick actions (`row row-cols-1 row-cols-md-2 row-cols-xl-3 g-3`) with `bg-primary-subtle` icon containers that adapt to dark mode. Each action card is an `<a class="card">` for full-card clickability.
- **My Account forms** (edit-account, edit-address, lost-password, reset-password) are wrapped in `card shadow-sm` sections with icon headers for visual consistency.
- **Edit forms** use card-based section grouping (e.g., "Personal information" + "Password change" as separate cards) instead of `<fieldset>/<legend>`.
- **Detail pages** use 8+4 column layout with sticky sidebar.
- **Cards** use `<article class="card h-100">` with `stretched-link`.
- **Form layout** uses centered `col-lg-8 col-xl-7` with card + shadow for auth forms.
- **Mobile patterns**: offcanvas for search filters; navbar uses Bootstrap `offcanvas-lg` responsive pattern.
- **Email templates**: use custom CSS class prefix (not Bootstrap) to avoid email client conflicts.
- **PDF exports**: inline styles aligned with Bootstrap color palette (no external CSS in PDF renderers).
## Version History
Current version: **v0.1.1**
## Session History
### 2026-02-28 — My Account Bootstrap 5 Polish
**Scope:** Redesigned 8 my-account Twig templates + CSS overrides to feel like a polished Bootstrap 5 application.
**Files changed (10):**
- `templates/myaccount/navigation.html.twig` — Added endpoint icon map, `offcanvas-lg` responsive pattern, sticky sidebar
- `templates/myaccount/dashboard.html.twig` — Replaced plain `<p>` tags with welcome card (avatar + greeting) and 5 quick-action cards in responsive grid
- `templates/myaccount/view-order.html.twig` — Replaced `<mark>` tags with summary card using `list-group-flush` and `components/status-badge.html.twig`; notes wrapped in card
- `templates/myaccount/form-edit-account.html.twig` — Wrapped in two card sections (Personal info + Password change) with icons
- `templates/myaccount/form-edit-address.html.twig` — Wrapped in card with dynamic icon (`bi-receipt` billing / `bi-truck` shipping)
- `templates/myaccount/form-lost-password.html.twig` — Wrapped in card with `bi-key` icon
- `templates/myaccount/form-reset-password.html.twig` — Wrapped in card with `bi-shield-lock` icon
- `templates/myaccount/lost-password-confirmation.html.twig` — Added `text-body-secondary` styling and "Back to login" button
- `templates/myaccount/my-account.html.twig` — Changed grid from `col-lg-3`/`col-lg-9` to `col-lg-auto`/`col-lg`
- `assets/css/wc-bootstrap.css` — Reset WooCommerce float layout, override `max-width: 1000px`, avatar rounding, card hover lift
**Design decisions:**
- **Nav column auto-sizing (`col-lg-auto`)**: Fixed-width columns (e.g., `col-lg-3` = 285px) caused label overflow with icons. Auto-sizing lets the nav take exactly the space it needs across locales while the content fills the rest.
- **WooCommerce layout overrides require matching specificity**: The plugin uses `.woocommerce-account .woocommerce-MyAccount-navigation` (specificity `0,2,0`) — single-class selectors don't override. Also `.woocommerce-account main .woocommerce` sets `max-width: 1000px` (specificity `0,2,1`) which must be reset to `none`.
- **`offcanvas-lg` over `d-none d-lg-block`**: Bootstrap's responsive offcanvas natively handles the desktop/mobile switch without duplicating nav markup. The toggle button uses `d-lg-none` visibility.
- **`bg-primary-subtle` for icon containers**: These Bootstrap 5.3 contextual utilities automatically adapt to dark mode, unlike hardcoded colors.
- **Welcome message restructured**: Separated greeting from logout link instead of using WooCommerce's default inline-linked `__()` string. This gives full control over card layout and avoids translated strings containing HTML structure assumptions.
- **Templates NOT changed** (already well-done): `orders.html.twig`, `my-address.html.twig`, `form-login.html.twig`, `payment-methods.html.twig`, `form-add-payment-method.html.twig`, `downloads.html.twig`
### 2026-02-28 — Single Product Bootstrap 5 Layout + Template Quirks Audit
**Scope:** Created Bootstrap 5 two-column layout for single product pages. Then audited all ~90 Twig templates for WooCommerce CSS quirks, Twig escaping bugs, and missing Bootstrap styling.
**Single product layout (3 files):**
- `woocommerce/content-single-product.php` — Bridge file for `wc_get_template_part()` interception
- `templates/content-single-product.html.twig` — Two-column `row gx-4 gx-lg-5` grid (images left, summary right)
- `assets/css/wc-bootstrap.css` — Float/width reset, sale badge positioning, shop_table border reset, gallery opacity fallback
**Template quirks audit (14 files fixed):**
- `order/order-details.html.twig` — Fixed `|nl2br|esc_html` filter order (was escaping `<br>` tags)
- `single-product/product-image.html.twig` — Added WC gallery modifier classes + opacity:0 for JS init
- `brands/brand-description.html.twig` — Added `|raw` to HTML-producing filter chain
- `single-product/up-sells.html.twig`, `cart/cross-sells.html.twig`, `single-product/related.html.twig` — Fixed `&hellip;` double-encoding in headings
- `myaccount/dashboard.html.twig` — Removed duplicate deprecated hook fires
- `product-searchform.html.twig` — Replaced `&hellip;` entity with Unicode `…`
- `cart/cart-totals.html.twig`, `checkout/review-order.html.twig`, `checkout/form-login.html.twig`, `checkout/terms.html.twig` — Removed wrong `function() is defined` guards
- `wc-base.html.twig` — Added `|esc_attr` on notification type in class attribute
- `global/form-login.html.twig` — Added `is defined` guard on `hidden` variable
- `single-product/add-to-cart/variation.html.twig` — Added `role="alert"` for accessibility
- `cart/mini-cart.html.twig` — Changed remove link to Bootstrap `btn btn-sm btn-outline-danger`
- `cart/cart-shipping.html.twig` — Made `form-check` class conditional on multiple shipping methods
**Infrastructure:**
- Created `.claude/settings.json` with allowed commands (git, docker, composer, npm, etc.)
### 2026-02-28 — v0.1.0 Changelog & Re-Release
**Scope:** Added CHANGELOG.md entry for v0.1.0 and re-released the tag.
**Files changed (1):**
- `CHANGELOG.md` — Added `[0.1.0]` section documenting all features and fixes between v0.0.1 and v0.1.0
### 2026-02-28 — v0.1.1 Dark Mode & Notice CSS Bugfixes
**Scope:** Fixed dark mode select backgrounds, WooCommerce notice styling conflicts, and product card image overflow.
**Files changed (2):**
- `assets/css/wc-bootstrap.css` — Dark mode overrides for native `<select>` and Select2/SelectWoo widgets; bumped notice selectors to `.woocommerce .woocommerce-*` (specificity `0,2,0`) to beat `woocommerce.css`; suppressed WooCommerce icon font `::before` on notices; added checkout form focus color for dark mode
- `templates/content-product.html.twig` — Added `overflow-hidden` to product card `<article>` for border-radius clipping
**Key learnings:**
- WooCommerce CSS sets `select { background-color: var(--wc-form-color-background, #fff) }` — the custom property is never defined for dark mode, so it falls back to white. Override with `[data-bs-theme="dark"] .woocommerce select`.
- Select2/SelectWoo hardcodes `#fff` backgrounds in `select2.css` — needs comprehensive overrides for selection containers, dropdowns, search fields, and highlighted options.
- WooCommerce notice CSS uses `border-top: 3px solid`, `background-color: #f6f5f8`, and WooCommerce icon font `::before` at specificity `0,1,0`. Single-class overrides don't win — must use `.woocommerce .woocommerce-*` (specificity `0,2,0`) and explicitly reset `border-top`.
- Never set `background-image` without `background-repeat: no-repeat` / `background-size` / `background-position` — SVGs will tile.