From 6d3bb76b17724c2bbf5b00e3e0d138d10bde8fc9 Mon Sep 17 00:00:00 2001 From: Keith Solomon Date: Sat, 27 Jun 2026 17:38:49 -0500 Subject: [PATCH] fix: match mobile header and hero design --- styles/components/site-header.css | 13 +- tests/mobile-homepage.spec.js | 128 +++++++++++++++++++ views/blocks/homepage-hero/homepage-hero.css | 64 +++++++--- views/blocks/homepage-hero/homepage-hero.php | 8 ++ 4 files changed, 190 insertions(+), 23 deletions(-) create mode 100644 tests/mobile-homepage.spec.js diff --git a/styles/components/site-header.css b/styles/components/site-header.css index b496375..d4aaf4f 100644 --- a/styles/components/site-header.css +++ b/styles/components/site-header.css @@ -74,18 +74,19 @@ .site-header .nav-main__toggle { align-items: center; background: var(--color-secondary); - border-radius: 0.375rem; + border-radius: 1.4rem 0.35rem 1.4rem 0.35rem; display: inline-flex; - height: 2.25rem; + height: 2.8125rem; justify-content: center; + margin-block: -0.125rem; margin-right: 1rem; - padding: 0.45rem; - width: 2.25rem; + padding: 0.65rem 1rem; + width: 4.5rem; } .site-header .nav-main__toggle svg { - height: 1.15rem; - width: 1.15rem; + height: 1.5rem; + width: 2rem; } .site-header .nav-main__toggle svg, diff --git a/tests/mobile-homepage.spec.js b/tests/mobile-homepage.spec.js new file mode 100644 index 0000000..91924aa --- /dev/null +++ b/tests/mobile-homepage.spec.js @@ -0,0 +1,128 @@ +import { expect, test } from "@playwright/test"; +import AxeBuilder from "@axe-core/playwright"; + +const homepage = "http://community-works-collaborative.test/"; + +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); +}; + +test.use({ viewport: { width: 402, height: 874 } }); + +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)", + ); +}); + +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); + + const location = page.locator(".homepage-hero__location"); + if ((await location.count()) > 0) { + await expect(location).toBeVisible(); + } +}); + +test("mobile hero CTAs remain fully visible at 320px", async ({ page }) => { + await page.setViewportSize({ width: 320, height: 800 }); + await page.goto(homepage); + + const buttons = page.locator(".homepage-hero .reset .button"); + const firstButton = await getBox(buttons.nth(0)); + const secondButton = await getBox(buttons.nth(1)); + + expectNear(firstButton.y, secondButton.y, 2); + expect(secondButton.x + secondButton.width).toBeLessThanOrEqual(320); +}); + +test("mobile recent posts exposes previous and next controls", async ({ + page, +}) => { + await page.goto(homepage); + + await expect( + page.getByRole("button", { name: "Previous article" }), + ).toBeVisible(); + await expect( + page.getByRole("button", { name: "Next article" }), + ).toBeVisible(); +}); + +test("mobile recent posts advances to the next article", async ({ page }) => { + await page.goto(homepage); + + const cards = page.locator(".recent-posts__card"); + await expect(cards.nth(0)).toBeVisible(); + await expect(cards.nth(1)).toBeHidden(); + + await page.getByRole("button", { name: "Next article" }).click(); + + await expect(cards.nth(0)).toBeHidden(); + await expect(cards.nth(1)).toBeVisible(); +}); + +test("configured footer channels use the prepared SVG icons", async ({ + page, +}) => { + await page.goto(homepage); + + await expect( + page.locator('.social-links img[src*="social-facebook.svg"]'), + ).toBeVisible(); + await expect( + page.locator('.social-links img[src*="social-linkedin.svg"]'), + ).toBeVisible(); +}); + +test("mobile homepage has no automated accessibility violations", async ({ + page, +}) => { + await page.goto(homepage); + + const results = await new AxeBuilder({ page }) + .withTags([ + "wcag2a", + "wcag2aa", + "wcag21a", + "wcag21aa", + "wcag22a", + "wcag22aa", + ]) + .analyze(); + + expect(results.violations).toEqual([]); +}); diff --git a/views/blocks/homepage-hero/homepage-hero.css b/views/blocks/homepage-hero/homepage-hero.css index ab277cd..ba59167 100644 --- a/views/blocks/homepage-hero/homepage-hero.css +++ b/views/blocks/homepage-hero/homepage-hero.css @@ -51,28 +51,33 @@ } } +.homepage-hero__location { + display: none; +} + @media (max-width: 767px) { .homepage-hero { - --hgtHero: 20.75rem; + --hgtHero: clamp(37.5rem, 187vw, 47rem); min-height: var(--hgtHero); } .homepage-hero .content-wrapper { - padding-inline: 1rem; + padding-inline: clamp(1.25rem, 8vw, 2rem) !important; } .homepage-hero .content-wrapper > div { - max-width: 20rem; - padding-top: 2.25rem; + margin-left: clamp(1.25rem, 8vw, 2rem); + max-width: 21.25rem; + padding-top: clamp(3rem, 14vw, 3.75rem); text-align: left; } .homepage-hero .intro h1 { - font-size: clamp(1.8rem, 8vw, 2.2rem); + font-size: clamp(1.75rem, 8vw, 2rem); letter-spacing: 0; - line-height: 0.98; - margin-bottom: 0.75rem; + line-height: 1.24; + margin-bottom: 1.75rem; } .homepage-hero .intro h1 strong { @@ -84,31 +89,56 @@ font-size: var(--text-14px); line-height: 1.3; margin: 0; - max-width: 16rem; + max-width: 18rem; } .homepage-hero .reset { - gap: 0.7rem; + flex-wrap: nowrap; + gap: clamp(0.75rem, 5.5vw, 1.4rem); justify-content: flex-start; - margin-top: 1rem; + margin-top: 2rem; } .homepage-hero .button { - font-size: var(--text-14px); - min-height: 2.25rem; - min-width: 6.35rem; - padding: 0.5rem 0.8rem; + font-size: var(--text-18px); + min-height: 2.75rem; + min-width: clamp(7.5rem, 36.82vw, 9.25rem); + padding: 0.65rem 1rem; + } + + .homepage-hero .reset x-button:nth-child(2) .button { + min-width: clamp(7.75rem, 38.8vw, 9.75rem); } .homepage-hero .heroVector .vector { - bottom: 0.15rem; + bottom: 6.25rem; left: 50%; transform: translateX(-50%); - width: 170%; + width: clamp(43rem, 190vw, 48rem); } .homepage-hero .heroMedia img, .homepage-hero .heroMedia video { - object-position: center top; + 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; } } diff --git a/views/blocks/homepage-hero/homepage-hero.php b/views/blocks/homepage-hero/homepage-hero.php index 4af4f0b..8e900ea 100644 --- a/views/blocks/homepage-hero/homepage-hero.php +++ b/views/blocks/homepage-hero/homepage-hero.php @@ -83,6 +83,14 @@ $wrapper = blockWrapperAttributes( $classes, $is_preview ); + +