diff --git a/acf/group_6867eaa8c0001.json b/acf/group_6867eaa8c0001.json new file mode 100644 index 0000000..84b6d68 --- /dev/null +++ b/acf/group_6867eaa8c0001.json @@ -0,0 +1,118 @@ +{ + "key": "group_6867eaa8c0001", + "title": "Contact Info", + "fields": [ + { + "key": "field_6a4997f2d7b1f", + "label": "Heading", + "name": "heading", + "aria-label": "", + "type": "text", + "instructions": "", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "", + "class": "", + "id": "" + }, + "default_value": "", + "maxlength": "", + "allow_in_bindings": 0, + "placeholder": "", + "prepend": "", + "append": "" + }, + { + "key": "field_6867eaa8c0002", + "label": "Map Latitude", + "name": "map_latitude", + "aria-label": "", + "type": "number", + "instructions": "Latitude for the Leaflet map center.", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "100", + "class": "", + "id": "" + }, + "default_value": "49.8951", + "min": -90, + "max": 90, + "allow_in_bindings": 1, + "placeholder": "49.8951", + "step": "0.000001", + "prepend": "", + "append": "" + }, + { + "key": "field_6867eaa8c0003", + "label": "Map Longitude", + "name": "map_longitude", + "aria-label": "", + "type": "number", + "instructions": "Longitude for the Leaflet map center.", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "100", + "class": "", + "id": "" + }, + "default_value": "-97.1384", + "min": -180, + "max": 180, + "allow_in_bindings": 1, + "placeholder": "-97.1384", + "step": "0.000001", + "prepend": "", + "append": "" + }, + { + "key": "field_6867eaa8c0004", + "label": "Map Zoom", + "name": "map_zoom", + "aria-label": "", + "type": "number", + "instructions": "Initial map zoom level (1-19).", + "required": 0, + "conditional_logic": 0, + "wrapper": { + "width": "100", + "class": "", + "id": "" + }, + "default_value": 14, + "min": 1, + "max": 19, + "allow_in_bindings": 1, + "placeholder": 14, + "step": 1, + "prepend": "", + "append": "" + } + ], + "location": [ + [ + { + "param": "block", + "operator": "==", + "value": "acf\/contact-info" + } + ] + ], + "menu_order": 0, + "position": "side", + "style": "default", + "label_placement": "top", + "instruction_placement": "label", + "hide_on_screen": "", + "active": true, + "description": "heading and map fields for the Contact Info block.", + "show_in_rest": 0, + "display_title": "", + "allow_ai_access": false, + "ai_description": "", + "modified": 1783208109 +} diff --git a/docs/superpowers/plans/2026-07-04-contact-page.md b/docs/superpowers/plans/2026-07-04-contact-page.md new file mode 100644 index 0000000..2405e22 --- /dev/null +++ b/docs/superpowers/plans/2026-07-04-contact-page.md @@ -0,0 +1,880 @@ +# Contact Page + Hero Re-Scope Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build the contact page layout (two-column 50/50 with contact info, Gravity Forms, and a Leaflet placeholder) and re-scope the existing inner-page hero to render only on the Services page, its children, and its grandchildren. + +**Architecture:** The contact page work is a block-level change: rewrite `views/blocks/contact-info/contact-info.php` and `contact-info.css`, add three SVG icons, and add a Playwright test. The hero re-scope is a single PHP helper in `lib/extras.php` plus a one-line change in `header.php`. Both are independently testable. A test bypass constant lets the existing `inner-page.spec.js` fixtures keep working without moving them under the Services page. + +**Tech Stack:** WordPress 6.x, ACF Pro, PHP 8.x, Tailwind CSS v4, Playwright, axe-core, PHPCS (WordPress standard). + +## Global Constraints + +- Tabs for PHP indentation (project standard, see `composer.json` and existing files). +- The theme's Tailwind build outputs `static/dist/theme.css`; that file is gitignored but committed with `git add -f` per project convention. +- PHPCS uses the WordPress coding standard; `composer lint` must pass. +- All Playwright tests in `tests/*.spec.js` must pass; the existing `mobile-homepage.spec.js` has 4 known pre-existing failures that are out of scope. +- The `acf/contact-info` block currently has an `InnerBlocks` call. Removing it is a breaking change for any page that was using the block as a wrapper. A grep at task start confirms there are zero such pages. +- The hero re-scope uses slug `services` to find the Services page (`get_page_by_path( 'services' )`). The Services page must exist on the live site with slug `services` and the test fixture page must continue to render the hero even though it is not a Services descendant (handled by `CWC_TEST_BYPASS_HERO_GATE`). +- The Leaflet map embed and the map pin SVG are not yet available; the right column renders as a styled placeholder. + +--- + +## File Structure + +| File | Responsibility | Created/Modified | +| --- | --- | --- | +| `views/blocks/contact-info/contact-info.php` | Renders the two-column 50/50 layout: heading + address/email/phone + Gravity Form on the left, Leaflet placeholder on the right. | Modify | +| `views/blocks/contact-info/contact-info.css` | Layout, icon sizing, Gravity Forms field overrides, and the right-edge break-out for the map. | Modify | +| `static/img/contact/icon-address.svg` | Map pin outline, 24×24, `stroke="currentColor" fill="none"`. | Create | +| `static/img/contact/icon-email.svg` | Envelope outline, 24×24, `stroke="currentColor" fill="none"`. | Create | +| `static/img/contact/icon-phone.svg` | Phone outline, 24×24, `stroke="currentColor" fill="none"`. | Create | +| `lib/extras.php` | Add `isServicesDescendant()` and `CWC_TEST_BYPASS_HERO_GATE` constant. | Modify | +| `header.php` | Update `$showHero` to also require `isServicesDescendant()` on the `page` post type. | Modify | +| `static/dist/theme.css` | Rebuilt via `npm run build`. | Modified (committed with `git add -f`). | +| `tests/contact-page.spec.js` | New Playwright spec for the contact page layout. | Create | +| `tests/services-hero-scope.spec.js` | New Playwright spec for the hero re-scope. | Create | + +--- + +### Task 1: Add the three contact icon SVGs + +**Files:** +- Create: `static/img/contact/icon-address.svg` +- Create: `static/img/contact/icon-email.svg` +- Create: `static/img/contact/icon-phone.svg` + +**Interfaces:** +- Consumes: nothing +- Produces: three static SVG assets that the `contact-info` block references via `get_theme_file_uri( '/static/img/contact/...svg' )`. + +The user is providing the final SVGs. Until they arrive, use these placeholders (24×24 viewBox, `stroke="currentColor" fill="none" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"`). The CSS colors them via `currentColor`. + +- [ ] **Step 1: Create the static/img/contact directory** + +Run: `mkdir -p static/img/contact` +Expected: directory created; no error. + +- [ ] **Step 2: Write `static/img/contact/icon-address.svg`** + +```xml + +``` + +- [ ] **Step 3: Write `static/img/contact/icon-email.svg`** + +```xml + +``` + +- [ ] **Step 4: Write `static/img/contact/icon-phone.svg`** + +```xml + +``` + +- [ ] **Step 5: Verify all three files exist and are valid XML** + +Run: `ls -la static/img/contact/ && for f in static/img/contact/*.svg; do echo "== $f =="; head -1 "$f"; done` +Expected: three files, each starting with `` with `.contact-info__grid` > `.contact-info__details` and `.contact-info__map`. The details column has the heading, an `