# WooCommerce Bootstrap **Author:** Marco Grätsch **Author URL:** **Author Email:** **Repository URL:** **Issues URL:** ## Project Overview This child theme of wp-bootstrap (`../wp-bootstrap/` or ) extends wp-bootstrap and adds overrides for all theme files of the WooCommerce plugin (`../../plugins/woocommerce/` or ). 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 **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 - `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 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. ### 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. ### Double Heading Prevention - Parent theme (`wp-bootstrap`) conditionally skips its `

` when `post.title` is empty. - Plugin passes empty `post.title` to parent theme's Twig system. - Child theme layout templates always render their own `

` 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, `

`) 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/` | | | wp-bootstrap | Parent Theme | `wp-content/themes/wp-bootstrap/` | | | wc-bootstrap | Child Theme | `wp-content/themes/wc-bootstrap/` | | ### 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 is not yet set up.** Commands referencing `docker exec` (e.g., in the translation workflow) are not currently available. Local alternatives or manual steps must be used until the container is configured. ### 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 - **Edit forms** use 3+9 column layout: `col-lg-3` sticky sidebar (progress indicator, section nav) + `col-lg-9` card-based sections. - **Detail pages** use 8+4 column layout with sticky sidebar. - **Cards** use `
` 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.0.1** ## Session History