Add inner page layout design spec
Defines the inner page layout: dark hero with background image and decorative vector (driven by two new fields on the existing Page Heading ACF group), a centered narrow intro section underneath (driven by the existing Intro field), and the standard the_content body. The mockup's bottom dark block is the existing site footer, not a new CTA. No new ACF blocks are introduced; the layout reuses the existing Page Heading meta box and the page-hero partial that header.php already loads. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
# Inner Page Layout
|
||||
|
||||
## Goal
|
||||
|
||||
Build the inner page layout shown in `notes/inner-page-mockup.png` so that every WordPress page renders the same structure: a dark hero with a background image and a decorative vector on the right, a centered narrow intro section underneath, and the standard editor body below it. Different pages vary only by hero image, vector, intro text, and body content.
|
||||
|
||||
## Scope
|
||||
|
||||
- Applies to all `page` post-type views rendered by `page.php` (and through it, the `page-hero` partial that `header.php` already loads).
|
||||
- Does not change the front page, single posts, archives, search, or 404 templates.
|
||||
- Does not change the homepage-hero block.
|
||||
- Does not add a new contact block to the page body — the dark navy block with the orange rule in the mockup is the existing site footer, not a new CTA.
|
||||
|
||||
## Architecture
|
||||
|
||||
The layout is a fixed three-part composition:
|
||||
|
||||
1. **Hero** — `views/partials/page-hero.php` (existing), extended with two new ACF fields and two new visual slots.
|
||||
2. **Intro** — new `views/partials/page-intro.php`, rendered by `page.php` between the hero and `the_content` when the `intro` ACF field is non-empty.
|
||||
3. **Body** — `the_content`, unchanged.
|
||||
|
||||
`page.php` becomes the orchestrator: it decides whether to render the intro partial based on the `intro` ACF value, then renders the body. Sidebar behavior (already gated by `hasSidebar()` and the `Page Sidebar` field group) is preserved.
|
||||
|
||||
`hasPageHeader()` in `lib/extras.php` continues to gate the hero. The function's current behavior (hero shows when `hero_style !== 'none'`) is kept; no change to that filter is required.
|
||||
|
||||
## ACF Field Group Changes
|
||||
|
||||
Add two fields to the existing `Page Heading` group (`acf/group_60bfb84ae973c.json`):
|
||||
|
||||
- `hero_image` — type: `image`, return_format: `array`, library: `all`, preview_size: `medium`. Conditional: shown when `hero_style !== 'none'`. Optional.
|
||||
- `hero_vector` — type: `image`, return_format: `array`, library: `all`, preview_size: `medium`. Conditional: shown when `hero_style !== 'none'`. Optional.
|
||||
|
||||
No other ACF group changes. The existing `heading`, `intro`, and `call_to_actions` fields are reused as-is.
|
||||
|
||||
## Hero Partial — `views/partials/page-hero.php`
|
||||
|
||||
Changes:
|
||||
|
||||
- Read the two new fields (`hero_image`, `hero_vector`).
|
||||
- Render `hero_image` as an absolutely-positioned `<img>` inside a new `.page-hero__media` container, masked with a left-to-right fade so the text on the left remains readable.
|
||||
- Render `hero_vector` as an absolutely-positioned, right-anchored, bottom-anchored `<img>` inside a new `.page-hero__vector` container, with `aria-hidden="true"` and `role="presentation"`.
|
||||
- Stop rendering the `intro` paragraph inside the hero. The `intro` field is now rendered in the new page-intro partial. (The heading still renders inside the hero, so the page title and the optional heading override work as before.)
|
||||
- Keep the `heading`, `breadcrumbs`, and `call_to_actions` rendering unchanged.
|
||||
- When no `hero_image` is set, the existing `background_color` (or the default dark gradient) is the only background.
|
||||
|
||||
Both new images are `loading="lazy"` and decorative.
|
||||
|
||||
## Hero Partial CSS — `views/partials/page-hero.css` (new)
|
||||
|
||||
The partial needs a small CSS file for the absolute positioning and mask treatment that Tailwind utilities do not express cleanly. Inline styles in the partial are acceptable as a fallback if a CSS file is undesirable, but a dedicated file follows the project's existing pattern (`contact-block.css`, `homepage-hero.css`).
|
||||
|
||||
Rules:
|
||||
|
||||
- `.page-hero` — the existing wrapper. Add `position: relative` and `isolation: isolate` so the new media layers stack predictably.
|
||||
- `.page-hero__media` — `position: absolute; inset: 0; z-index: 0; mask-image: linear-gradient(to right, transparent 0%, black 30%);` (mirrored `-webkit-mask-image`). The image inside it is `width: 100%; height: 100%; object-fit: cover;`.
|
||||
- `.page-hero__vector` — `position: absolute; right: 0; bottom: 0; width: clamp(18rem, 38vw, 32rem); height: auto; z-index: 1; pointer-events: none;`. The mobile breakpoint (≤ 767px) repositions the vector to fill the lower portion of the hero: `width: 100vw; right: auto; left: 0; bottom: 0;` and the `__media` mask switches to a top-to-bottom gradient so the text remains readable.
|
||||
- `.page-hero__content` (or the existing `.content-wrapper` div inside the hero) — `position: relative; z-index: 10;` so text always sits above the media layers.
|
||||
|
||||
These rules are scoped to `.page-hero` and only affect the page-hero partial. They do not affect the homepage-hero block, which uses a different root class.
|
||||
|
||||
## Intro Partial — `views/partials/page-intro.php` (new)
|
||||
|
||||
A small PHP partial that renders a single centered column:
|
||||
|
||||
- Wrapper: `<section class="page-intro py-12 lg:py-20 text-center">`
|
||||
- Container: `<div class="container mx-auto max-w-3xl content-wrapper">`
|
||||
- Inner: a single `<p>` element with `text-lg lg:text-xl leading-relaxed` containing `wp_kses_post( $intro )`.
|
||||
|
||||
No new fields, no new CSS file — Tailwind utilities cover the layout.
|
||||
|
||||
## page.php Wiring
|
||||
|
||||
`page.php` changes from rendering the body directly to:
|
||||
|
||||
1. Output the article container (existing behavior).
|
||||
2. If `get_field( 'intro' )` is non-empty, call `get_template_part( 'views/partials/page-intro' )`.
|
||||
3. Render `the_content` (existing behavior).
|
||||
4. Render the sidebar if `hasSidebar()` (existing behavior).
|
||||
|
||||
The hero is still rendered by `header.php` before this template runs. No other behavior changes.
|
||||
|
||||
## Data Flow
|
||||
|
||||
- Editor experience: on every page (and post) edit screen, the existing Page Heading meta box shows the existing fields plus two new ones (Hero Image, Vector). The editor uploads the assets, sets a background color if desired, writes the optional heading override, intro, and CTAs, then writes the body in the standard block editor.
|
||||
- Render: `header.php` loads the page-hero partial → `page.php` loads the page-intro partial (if intro is set) → `the_content` renders the body blocks.
|
||||
- Per-page variation: hero image, vector, intro text, and body content. Everything else is shared.
|
||||
|
||||
## Error Handling and Edge Cases
|
||||
|
||||
- `hero_image` empty → hero uses the existing dark background color (or the default dark text-only style).
|
||||
- `hero_vector` empty → no vector renders; the right side of the hero is empty space.
|
||||
- `intro` empty → page-intro partial is not loaded; the page goes straight from hero to body.
|
||||
- `heading` empty → falls back to `getTheTitle()` (existing behavior, unchanged).
|
||||
- Background color set without a hero image → existing behavior; no change.
|
||||
- No sidebar field set → existing `hasSidebar()` filter returns false → no sidebar (existing behavior).
|
||||
- Lazy-loaded images must await `load`/`complete` before Playwright reads dimensions (existing project convention; see `tests/mobile-homepage.spec.js:641`).
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- New ACF blocks. This layout uses the existing Page Heading meta box, not the block editor.
|
||||
- Front page, single posts, archives, search, 404 changes.
|
||||
- Homepage-hero block changes.
|
||||
- The contact-block, pull-quote, our-work, or other existing blocks.
|
||||
- A new contact CTA block in the page body. The mockup's bottom dark block is the existing site footer.
|
||||
- Migration of existing pages' content.
|
||||
|
||||
## Verification
|
||||
|
||||
- Add `tests/inner-page.spec.js` covering:
|
||||
- Desktop (1280px): hero, intro, body render in that order. When hero image is set, it is visible behind the hero. When vector is set, it sits on the right of the hero.
|
||||
- Mobile (402px): same order. Vector fills the lower portion of the hero. Intro is centered and within the 48rem max-width. No clipped text.
|
||||
- 320px and 767px: nothing clips, vector scales proportionally, intro remains readable.
|
||||
- A page with no `intro` field renders hero + body with no empty intro wrapper.
|
||||
- A page with no `hero_image` and no `hero_vector` renders the existing dark text-only hero unchanged.
|
||||
- Add a test page fixture (or use an existing page) to drive the test. If no fixture exists, the test seeds a page in a `beforeAll` and tears it down in `afterAll`.
|
||||
- Run `npm run build` to regenerate `static/dist/`.
|
||||
- Run the full Playwright suite (`npx playwright test`) — including the existing `tests/mobile-homepage.spec.js` and `tests/site-a11y.spec.js` — to confirm no regressions.
|
||||
- Run `composer lint` to catch PHPCS issues.
|
||||
- Capture a 1280px and a 402px full-page screenshot of the test fixture for visual comparison against `notes/inner-page-mockup.png`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
1. Every WordPress page renders the new layout when the page-hero partial is active.
|
||||
2. The hero shows the page title (or heading override) and, when supplied, a background image and a decorative vector on the right.
|
||||
3. When the `intro` field is set, a centered narrow column appears under the hero. When empty, it is omitted entirely (no empty wrapper).
|
||||
4. The body renders `the_content` as before, with the standard block editor.
|
||||
5. The hero background image is masked so text on the left remains readable at all viewports.
|
||||
6. The vector scales proportionally from 320px through desktop and is repositioned below 768px to fill the lower portion of the hero.
|
||||
7. The page renders without console errors, axe violations, or PHPCS errors.
|
||||
8. Existing single posts, archives, search, and 404 pages render the page-hero partial unchanged.
|
||||
9. Existing tests (`mobile-homepage`, `site-a11y`) continue to pass.
|
||||
Reference in New Issue
Block a user