26 KiB
Mobile Homepage Fidelity 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: Make the homepage match the supplied 402px mobile section crops and remain proportional from 320px through 767px without changing layouts at 768px and above.
Architecture: Add narrowly scoped mobile overrides to the block or component that owns each visual region. Use the existing WordPress markup and content, with Playwright geometry assertions written before each correction. Treat 402px as the exact baseline, then verify the lower, upper, and desktop boundary widths.
Tech Stack: WordPress/PHP, ACF blocks, Tailwind CSS v4 with nested CSS, Playwright, axe-core, PHPCS, Prettier.
File Map
tests/mobile-homepage.spec.js: mobile geometry, ordering, breakpoint, and accessibility regression coverage.styles/components/site-header.css: hamburger appearance only.views/blocks/homepage-hero/homepage-hero.php: decorative location-label hook shown in the approved crop.views/blocks/homepage-hero/homepage-hero.css: hero height, content, CTAs, media crop, and linework.views/blocks/pull-quote/pull-quote.css: mobile type, block height, and vector crop.views/blocks/media-text/media-text.css: mobile text-first order, CTA row, collage size, and block spacing.views/blocks/contact-block/contact-block.css: mobile card dimensions, photo treatment, text, CTA, and linework.views/blocks/our-work/our-work.css: mobile content, vector density, and contained overlapping images.styles/components/site-footer.css: mobile footer grid and alignment.static/dist/theme.css: generated bynpm run build; do not hand-edit.
Reference Material
notes/header-hero.png— 402×874.notes/pullquote.png— 402×361.notes/image-text.png— 402×964.notes/contact.png— 343×502.notes/our-work.png— 402×888.notes/footer.png— 402×673.
Task 1: Add reusable mobile geometry assertions
Files:
-
Modify:
tests/mobile-homepage.spec.js -
Step 1: Add geometry helpers below the homepage constant
const getBox = async (locator) => {
const box = await locator.boundingBox();
expect(box).not.toBeNull();
return box;
};
const expectNear = (actual, expected, tolerance = 3) => {
expect(Math.abs(actual - expected)).toBeLessThanOrEqual(tolerance);
};
- Step 2: Replace the current hamburger size test with the approved target-only test
test("mobile header preserves its layout and uses the designed hamburger", async ({
page,
}) => {
await page.goto(homepage);
const header = await getBox(page.locator(".site-header"));
const toggle = page.locator(".nav-main__toggle");
const toggleBox = await getBox(toggle);
expectNear(header.height, 70, 2);
expectNear(toggleBox.width, 72, 2);
expectNear(toggleBox.height, 45, 2);
await expect(toggle).toHaveCSS(
"background-color",
"oklch(0.6353 0.1751 29.61)",
);
});
- Step 3: Run the test and verify it fails for the hamburger dimensions
Run:
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "designed hamburger" --reporter=line
Expected: FAIL because the current button is 36×36 rather than approximately 72×45.
Task 2: Correct the hamburger and homepage hero
Files:
-
Modify:
styles/components/site-header.css:73-96 -
Modify:
views/blocks/homepage-hero/homepage-hero.php:62-70 -
Modify:
views/blocks/homepage-hero/homepage-hero.css:55-114 -
Modify:
tests/mobile-homepage.spec.js -
Step 1: Add a failing hero geometry test
test("mobile hero matches the 402px reference composition", async ({ page }) => {
await page.goto(homepage);
const hero = await getBox(page.locator(".homepage-hero"));
const heading = await getBox(page.locator(".homepage-hero .intro h1"));
const buttons = page.locator(".homepage-hero .reset .button");
const firstButton = await getBox(buttons.nth(0));
const secondButton = await getBox(buttons.nth(1));
expectNear(hero.height, 752, 8);
expectNear(heading.x, 32, 3);
expect(heading.width).toBeGreaterThanOrEqual(330);
expectNear(firstButton.y, secondButton.y, 2);
expectNear(firstButton.width, 148, 4);
expectNear(secondButton.width, 156, 4);
await expect(page.locator(".homepage-hero__location")).toBeVisible();
});
- Step 2: Run the hero test and verify the current short composition fails
Run:
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "reference composition" --reporter=line
Expected: FAIL because the current hero is about 332px tall and the content/buttons are undersized.
- Step 3: Change only the hamburger button declarations inside the existing mobile query
.site-header .nav-main__toggle {
align-items: center;
background: var(--color-secondary);
border-radius: 1.4rem 0.35rem 1.4rem 0.35rem;
display: inline-flex;
height: 2.8125rem;
justify-content: center;
margin-right: 1rem;
padding: 0.65rem 1rem;
width: 4.5rem;
}
.site-header .nav-main__toggle svg {
height: 1.5rem;
width: 2rem;
}
Do not alter the header container, logo, padding, or height rules.
- Step 4: Add the decorative location label after
.heroVector
<div class="homepage-hero__location" aria-hidden="true">
<svg viewBox="0 0 24 24" focusable="false">
<path d="M12 21s7-5.6 7-12a7 7 0 1 0-14 0c0 6.4 7 12 7 12Z" />
<circle cx="12" cy="9" r="2.5" />
</svg>
<span><?php echo esc_html__( 'Location / Nation Information', 'cwc' ); ?></span>
</div>
The label is marked decorative because it reproduces a visual annotation from the approved design rather than page navigation or editorial content.
- Step 5: Replace the hero mobile query with a 402px-baseline composition
.homepage-hero__location {
display: none;
}
@media (max-width: 767px) {
.homepage-hero {
--hgtHero: clamp(37.5rem, 187vw, 47rem);
min-height: var(--hgtHero);
}
.homepage-hero .content-wrapper {
padding-inline: clamp(1.25rem, 8vw, 2rem);
}
.homepage-hero .content-wrapper > div {
max-width: 21.25rem;
padding-top: clamp(3rem, 14vw, 3.75rem);
text-align: left;
}
.homepage-hero .intro h1 {
font-size: clamp(1.75rem, 8vw, 2rem);
letter-spacing: 0;
line-height: 1.24;
margin-bottom: 1.75rem;
}
.homepage-hero .intro h1 strong {
font-size: inherit;
line-height: inherit;
}
.homepage-hero .intro p {
font-size: var(--text-14px);
line-height: 1.3;
margin: 0;
max-width: 18rem;
}
.homepage-hero .reset {
flex-wrap: nowrap;
gap: clamp(0.75rem, 5.5vw, 1.4rem);
justify-content: flex-start;
margin-top: 2rem;
}
.homepage-hero .button {
font-size: var(--text-18px);
min-height: 2.75rem;
min-width: 9.25rem;
padding: 0.65rem 1rem;
}
.homepage-hero .reset x-button:nth-child(2) .button {
min-width: 9.75rem;
}
.homepage-hero .heroVector .vector {
bottom: 6.25rem;
left: 50%;
transform: translateX(-50%);
width: clamp(43rem, 190vw, 48rem);
}
.homepage-hero .heroMedia img,
.homepage-hero .heroMedia video {
object-position: 58% center;
}
.homepage-hero .homepage-hero__location {
align-items: center;
bottom: 5.5rem;
color: var(--color-cwc-blue-03);
display: flex;
font-size: var(--text-14px);
gap: 0.5rem;
left: 2rem;
position: absolute;
z-index: 30;
}
.homepage-hero .homepage-hero__location svg {
fill: none;
height: 1.25rem;
stroke: currentColor;
stroke-width: 1.75;
width: 1.25rem;
}
}
- Step 6: Build and run the hamburger/hero tests
npm run build
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "designed hamburger|reference composition" --reporter=line
Expected: 2 passed.
- Step 7: Capture and inspect the 402px header/hero
npx playwright screenshot --viewport-size "402,874" --wait-for-timeout 1000 http://community-works-collaborative.test "$env:TEMP\cwc-header-hero.png"
Compare directly with notes/header-hero.png. Adjust only the numeric values in the mobile query until heading, buttons, media crop, and vector anchor match.
- Step 8: Commit the header and hero correction
git add styles/components/site-header.css views/blocks/homepage-hero/homepage-hero.php views/blocks/homepage-hero/homepage-hero.css tests/mobile-homepage.spec.js
git commit -m "fix: match mobile header and hero design"
Task 3: Correct the pull quote
Files:
-
Modify:
views/blocks/pull-quote/pull-quote.css:47-60 -
Modify:
tests/mobile-homepage.spec.js -
Step 1: Add the failing pull-quote test
test("mobile pull quote matches the reference scale", async ({ page }) => {
await page.goto(homepage);
const quote = await getBox(page.locator(".pull-quote"));
const text = await getBox(page.locator(".pull-quote__text"));
expectNear(quote.height, 361, 6);
expect(text.width).toBeGreaterThanOrEqual(330);
expect(text.height).toBeGreaterThanOrEqual(235);
});
- Step 2: Run the test and verify it fails
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "pull quote" --reporter=line
Expected: FAIL because the current text and block are substantially shorter than the reference.
- Step 3: Replace the mobile pull-quote overrides
@media (max-width: 767px) {
.pull-quote {
border-bottom-width: 0.75rem;
min-height: clamp(18rem, 89.8vw, 22.5625rem);
}
.pull-quote .pull-quote__vector img {
height: 118%;
object-fit: cover;
object-position: center 42%;
width: 118%;
}
.pull-quote .pull-quote__content {
align-items: center;
display: flex;
min-height: inherit;
padding: 3.5rem 2rem;
}
.pull-quote .pull-quote__text {
font-size: clamp(1.5rem, 6.75vw, 1.7rem);
line-height: 1.18;
width: 100%;
}
}
- Step 4: Build, test, and compare with the crop
npm run build
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "pull quote" --reporter=line
Expected: PASS. Use a 402px screenshot to compare the vector crop and text wrapping with notes/pullquote.png.
- Step 5: Commit the pull-quote correction
git add views/blocks/pull-quote/pull-quote.css tests/mobile-homepage.spec.js
git commit -m "fix: match mobile pull quote design"
Task 4: Reorder and resize the image/text block
Files:
-
Modify:
views/blocks/media-text/media-text.css -
Modify:
tests/mobile-homepage.spec.js -
Step 1: Add failing order and CTA-row tests
test("mobile media text places copy and CTAs before the complete collage", async ({
page,
}) => {
await page.goto(homepage);
const block = page.locator(".media-cols").first();
const content = await getBox(block.locator(".content-wrapper"));
const media = await getBox(block.locator(":scope > div").first());
const buttons = block.locator(".reset .button");
const firstButton = await getBox(buttons.nth(0));
const secondButton = await getBox(buttons.nth(1));
expect(content.y).toBeLessThan(media.y);
expectNear(firstButton.y, secondButton.y, 2);
expect(media.width).toBeGreaterThanOrEqual(335);
});
- Step 2: Run the test and verify it fails for media order
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "complete collage" --reporter=line
Expected: FAIL because the current order-first utility places the collage above the copy.
- Step 3: Populate the empty block stylesheet with scoped mobile rules
@media (max-width: 767px) {
.media-cols {
gap: 2.75rem;
margin-bottom: clamp(12rem, 55vw, 14rem);
padding-inline: clamp(1.25rem, 8vw, 2rem);
}
.media-cols > div:first-child {
order: 2;
width: 100%;
}
.media-cols > .content-wrapper {
order: 1;
padding-inline: 0;
}
.media-cols > div:first-child img {
display: block;
height: auto;
width: 100%;
}
.media-cols .content-wrapper > .font-light {
font-size: var(--text-16px);
line-height: 1.45;
}
.media-cols .reset {
flex-wrap: nowrap;
gap: 1.4rem;
justify-content: flex-start;
margin-top: 2rem;
}
.media-cols .reset .button {
font-size: var(--text-18px);
min-height: 2.75rem;
min-width: 0;
padding: 0.65rem 2rem;
}
}
- Step 4: Build, test, and compare with the reference
npm run build
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "complete collage" --reporter=line
Expected: PASS. Compare the full block with notes/image-text.png; tune the mobile margin-bottom so the contact block starts at the reference distance rather than the current excessive gap.
- Step 5: Commit the media-text correction
git add views/blocks/media-text/media-text.css tests/mobile-homepage.spec.js
git commit -m "fix: match mobile image text layout"
Task 5: Correct the contact card composition
Files:
-
Modify:
views/blocks/contact-block/contact-block.css:46-99 -
Modify:
tests/mobile-homepage.spec.js -
Step 1: Add a failing contact-card geometry test
test("mobile contact card matches the reference frame", async ({ page }) => {
await page.goto(homepage);
const card = await getBox(page.locator(".contact-block.home-contact"));
const content = await getBox(
page.locator(".contact-block.home-contact .contact-block__content"),
);
expectNear(card.width, 343, 4);
expectNear(card.height, 502, 8);
expectNear(content.x - card.x, 32, 4);
});
- Step 2: Run the test and verify it fails
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "contact card" --reporter=line
Expected: FAIL because the current card is wider, shorter, and top-pads the content by seven rem.
- Step 3: Replace the contact block mobile overrides
@media (max-width: 767px) {
.contact-block {
border-bottom-width: 0.75rem;
border-radius: 1rem 1rem 0 0;
min-height: clamp(25rem, 124.9vw, 31.375rem);
}
.contact-block .contact-block__media {
inset: 0;
mask-image: linear-gradient(to right, rgb(0 0 0 / 0.2) 0%, black 68%);
-webkit-mask-image: linear-gradient(to right, rgb(0 0 0 / 0.2) 0%, black 68%);
width: 100%;
}
.contact-block .contact-block__bg-image {
object-fit: cover;
object-position: 70% center;
}
.contact-block .contact-block__linework {
bottom: 0;
height: auto;
left: 0;
max-width: none;
right: auto;
top: auto;
width: 112%;
}
.contact-block .contact-block__content {
min-height: clamp(25rem, 124.9vw, 31.375rem);
padding: 2rem 2rem 2.75rem !important;
position: relative;
z-index: 2;
}
.contact-block .contact-block__text {
font-size: clamp(1.65rem, 7vw, 1.8rem);
line-height: 1.08;
max-width: 17rem !important;
}
.contact-block.home-contact {
bottom: auto;
left: 50%;
margin: 2.5rem auto -3.75rem;
max-width: 21.4375rem;
position: relative;
transform: translateX(-50%);
width: min(85.3vw, 21.4375rem);
}
}
- Step 4: Build, test, and visually tune photo/vector anchors
npm run build
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "contact card" --reporter=line
Expected: PASS. Compare with notes/contact.png; adjust only object-position, mask stop, and linework width/offset until the photo and lower vector match.
- Step 5: Commit the contact card correction
git add views/blocks/contact-block/contact-block.css tests/mobile-homepage.spec.js
git commit -m "fix: match mobile contact card design"
Task 6: Correct Our Work vector and image containment
Files:
-
Modify:
views/blocks/our-work/our-work.css:193-261 -
Modify:
tests/mobile-homepage.spec.js -
Step 1: Add failing block and image-containment tests
test("mobile Our Work contains both reference images", async ({ page }) => {
await page.goto(homepage);
const block = await getBox(page.locator(".our-work"));
const left = await getBox(page.locator(".our-work__media--left"));
const bottom = await getBox(page.locator(".our-work__media--bottom"));
expectNear(block.height, 888, 12);
expectNear(left.width, 189, 5);
expect(bottom.width).toBeGreaterThanOrEqual(335);
expect(bottom.x).toBeGreaterThanOrEqual(60);
});
- Step 2: Run the test and verify it fails
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "contains both" --reporter=line
Expected: FAIL because the existing left image is about 152px wide and the bottom image is undersized/clipped.
- Step 3: Change the mobile breakpoint to the approved 767px scope and replace its rules
@media (max-width: 767px) {
.our-work {
margin-bottom: 2rem;
min-height: clamp(44rem, 220.9vw, 55.5rem);
padding-top: 4.25rem;
}
.our-work__vector img {
height: 118%;
object-fit: cover;
object-position: 56% center;
width: 118%;
}
.our-work__inner {
display: block;
padding-inline: 2rem;
}
.our-work__content {
margin-inline: 0;
max-width: 21rem;
text-align: left;
}
.our-work__content .acf-innerblocks-container {
gap: 1.75rem;
}
.our-work__content h1,
.our-work__content h2,
.our-work__content h3,
.our-work__content h4,
.our-work__content h5,
.our-work__content h6 {
font-size: clamp(2.5rem, 11vw, 2.75rem);
}
.our-work__content p {
font-size: var(--text-16px);
line-height: 1.45;
}
.our-work__content .wp-block-acf-buttons > div {
flex-wrap: nowrap;
gap: 2rem;
justify-content: flex-start;
}
.our-work__content .button {
font-size: var(--text-18px);
min-height: 2.75rem;
min-width: 9.75rem;
padding: 0.65rem 1rem;
}
.our-work__media--left {
margin: 4rem 0 0;
max-width: 11.8125rem;
position: relative;
width: 55%;
z-index: 2;
}
.our-work__media--bottom {
margin: -3.5rem -2rem 0 auto;
max-width: none;
position: relative;
width: 100%;
z-index: 1;
}
.our-work__image {
object-fit: contain;
}
}
- Step 4: Build, test, and compare the overlap
npm run build
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "contains both" --reporter=line
Expected: PASS. Compare with notes/our-work.png; tune vector object-position and the two media margins without changing image containment.
- Step 5: Commit the Our Work correction
git add views/blocks/our-work/our-work.css tests/mobile-homepage.spec.js
git commit -m "fix: match mobile Our Work design"
Task 7: Rebuild the mobile footer hierarchy
Files:
-
Modify:
styles/components/site-footer.css:50-80 -
Modify:
tests/mobile-homepage.spec.js -
Step 1: Add a failing footer geometry test
test("mobile footer matches the reference hierarchy", async ({ page }) => {
await page.goto(homepage);
const footer = page.locator(".site-footer");
const footerBox = await getBox(footer);
const logo = await getBox(footer.locator(".site-footer__logo-image"));
const backToTop = await getBox(footer.locator("#backToTopBtn"));
const nav = await getBox(footer.locator("#footer-nav"));
const contact = await getBox(footer.locator(".contact"));
expectNear(footerBox.height, 673, 12);
expectNear(logo.y, backToTop.y, 8);
expect(nav.y).toBeGreaterThan(logo.y + logo.height);
expect(contact.y).toBeGreaterThan(nav.y + nav.height);
});
- Step 2: Run the test and verify the current four-cell grid fails
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "footer matches" --reporter=line
Expected: FAIL because the back-to-top, nav, contact details, social links, and copyright do not occupy the reference rows.
- Step 3: Replace the mobile footer query
@media (max-width: 767px) {
.site-footer {
min-height: clamp(34rem, 167.4vw, 42.0625rem);
padding-block: 4rem 2rem;
}
.site-footer > .container {
display: grid;
gap: 1.75rem 1rem;
grid-template-columns: minmax(0, 1fr) auto;
padding-inline: 2rem;
}
.site-footer > .container > div:first-child {
grid-column: 1;
grid-row: 1;
}
.site-footer #footer-header {
margin: 0;
max-width: 12.25rem;
}
.site-footer #footer-nav {
grid-column: 1 / -1;
grid-row: 2;
}
.site-footer .site-footer__nav-list {
display: grid;
gap: 0.75rem 2rem;
grid-auto-flow: column;
grid-template-rows: repeat(3, auto);
}
.site-footer #footer-nav + div {
grid-column: 1 / -1;
grid-row: 3;
}
.site-footer #footer-description {
font-size: var(--text-20px);
max-width: 21rem;
}
.site-footer .contact__item {
font-size: var(--text-16px);
}
.site-footer .text-right {
display: contents;
}
.site-footer x-back-to-top {
align-self: start;
grid-column: 2;
grid-row: 1;
}
.site-footer .social-links {
grid-column: 1 / -1;
grid-row: 4;
justify-content: flex-end;
margin-top: 0;
}
.site-footer .copyright {
grid-column: 1 / -1;
grid-row: 5;
padding-block: 0;
text-align: center;
}
}
- Step 4: Build, test, and compare against the footer crop
npm run build
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "footer matches" --reporter=line
Expected: PASS. Compare with notes/footer.png; tune only gaps, logo width, and row padding while retaining WordPress-driven content and link order.
- Step 5: Commit the footer correction
git add styles/components/site-footer.css tests/mobile-homepage.spec.js
git commit -m "fix: match mobile footer design"
Task 8: Verify responsive boundaries and desktop isolation
Files:
-
Modify:
tests/mobile-homepage.spec.js -
Step 1: Add a parameterized proportional-layout test
for (const width of [320, 402, 767]) {
test(`homepage remains contained at ${width}px`, async ({ page }) => {
await page.setViewportSize({ width, height: 900 });
await page.goto(homepage);
const overflow = await page.evaluate(
() => document.documentElement.scrollWidth - window.innerWidth,
);
expect(overflow).toBeLessThanOrEqual(1);
await expect(page.locator(".homepage-hero .reset")).toBeVisible();
await expect(page.locator(".contact-block.home-contact")).toBeVisible();
await expect(page.locator(".our-work__media--bottom")).toBeVisible();
});
}
- Step 2: Add the desktop-isolation assertion
test("mobile overrides stop below the 768px desktop boundary", async ({
page,
}) => {
await page.setViewportSize({ width: 768, height: 900 });
await page.goto(homepage);
const media = page.locator(".media-cols").first();
const content = await getBox(media.locator(".content-wrapper"));
const image = await getBox(media.locator(":scope > div").first());
expect(image.y).toBeLessThanOrEqual(content.y);
await expect(page.locator(".site-footer > .container")).toHaveCSS(
"grid-template-columns",
/.+ .+ .+ .+/,
);
});
- Step 3: Run the new boundary tests
npx playwright test tests/mobile-homepage.spec.js --project=chromium --grep "remains contained|desktop boundary" --reporter=line
Expected: 4 passed and no horizontal overflow.
- Step 4: Run the full production and quality gate
npm run build
npx playwright test --project=chromium --reporter=line
npx --yes prettier@3.6.2 --check static/js/modules/RecentPostsCarousel.js tests/mobile-homepage.spec.js tests/site-a11y.spec.js
composer validate --no-check-publish
vendor\bin\phpcs.bat --standard=.phpcs.xml footer.php views/blocks/recent-posts/recent-posts.php views/icons/facebook.php views/icons/linkedin.php views/icons/youtube.php
php -l footer.php
php -l views/blocks/recent-posts/recent-posts.php
git diff --check
Expected: build succeeds; all Playwright/axe tests pass; Prettier, Composer, PHPCS, PHP syntax, and whitespace checks exit 0.
- Step 5: Capture final comparison screenshots
npx playwright screenshot --viewport-size "402,874" --full-page --wait-for-timeout 1500 http://community-works-collaborative.test "$env:TEMP\cwc-homepage-402-final.png"
npx playwright screenshot --viewport-size "320,800" --full-page --wait-for-timeout 1500 http://community-works-collaborative.test "$env:TEMP\cwc-homepage-320-final.png"
npx playwright screenshot --viewport-size "767,900" --full-page --wait-for-timeout 1500 http://community-works-collaborative.test "$env:TEMP\cwc-homepage-767-final.png"
Inspect the 402px capture section by section against all six reference crops. Inspect 320px and 767px for clipping, awkward line wrapping, and unintended overlap.
- Step 6: Commit the boundary tests and final tuning
git add tests/mobile-homepage.spec.js styles/components/site-header.css styles/components/site-footer.css views/blocks/homepage-hero/homepage-hero.css views/blocks/pull-quote/pull-quote.css views/blocks/media-text/media-text.css views/blocks/contact-block/contact-block.css views/blocks/our-work/our-work.css
git commit -m "test: verify mobile homepage responsive fidelity"