diff --git a/.gitignore b/.gitignore index ddfb8dc..81f0d78 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,4 @@ $RECYCLE.BIN/ .cache/ .aider* .continue/ +.playwright-mcp/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 14e926b..c96bfc7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `assets/css/newsletter-popup.css` — restyles the Gravity Form (ID 2, Name/Surname/Email) embedded in the "Newsletter Subscribe Pop Up" Popup Maker popup, the same CSS-bridge approach as `newsletter-signup.css`. Also forces `justify-content: center` on the popup's header-bar group: Popup Maker renders that popup's content through its own hook rather than the main page's content flow, so WordPress's per-request block CSS detection never registers the group's flex-layout centering rule on that render pass — only a narrower Buttons-block-scoped rule loads. The popup's own content (header bar, copy, form embed), its dedicated "Spotlight Newsletter Popup" Popup Theme (container/close-button styling), and its Click Open trigger are all authored directly in wp-admin per Zared's direction to use Popup Maker natively, not as a theme pattern file — see `patterns/newsletter-signup.php`'s header comment. +- `newsletter-signup__cta` class on `patterns/newsletter-signup.php`'s "Subscribe for the latest news" button — the CSS selector configured as that popup's Click Open trigger. - `styles.blocks."core/navigation".elements.link` in `theme.json` — `core/navigation` has no Style panel control for a hover state, so its links previously had none at all. Base text colour `accent-700`, hover `brand-500`. Applies to every `core/navigation` block theme-wide — both the header's existing menu and the new footer menu — from one place. - `assets/css/core-navigation.css` — the `theme.json` navigation link colours above weren't actually rendering: WordPress core's own `core/navigation-link` stylesheet sets a doubled-class `.wp-block-navigation-item__content.wp-block-navigation-item__content { color: inherit }` rule that out-specifies `theme.json`'s generated link-colour selector. This stylesheet matches core's selector at the same specificity, `(0,3,0)`, rather than exceeding it, and wins the tie on source order (enqueued after core's block styles) — going a class deeper would also override a real per-instance link colour a user picks for a single Navigation block in the Site Editor's Style panel (see `docs/foundation-exceptions.md`). - A new WordPress Navigation menu ("Footer", `wp_navigation` post 34013) with the footer's four links (Latest News, Data Desk, Republish, Contact us) as real `wp:navigation-link` items, created directly via the database per Zared's request to make the footer links a proper nav menu rather than plain paragraph links. diff --git a/assets/css/newsletter-popup.css b/assets/css/newsletter-popup.css new file mode 100644 index 0000000..88702fd --- /dev/null +++ b/assets/css/newsletter-popup.css @@ -0,0 +1,85 @@ +/* + * newsletter-popup.php content lives in the "Newsletter Subscribe Pop + * Up" Popup Maker popup (wp-admin), not a theme pattern file — the + * popup's own container (width/border/radius/background) is configured + * on its assigned Popup Theme post, not here. This file only restyles + * the embedded Gravity Form (ID 2)'s own generated markup, the same + * CSS-bridge approach used in newsletter-signup.css for form 1. + */ + +/* + * Popup Maker renders this popup's content through its own hook rather + * than the main page's content flow, so WordPress's per-request block + * CSS detection never registers the header group's flex layout — core + * only prints its is-content-justification-center rule for the block + * types it saw rendered in the main request, and the group block never + * makes that list here. Explicit fallback instead of relying on it. + */ +.newsletter-popup__header { + justify-content: center; +} + +/* + * Popup Maker's own JS sets the popup box's width as a fixed inline + * style (from the popup's "Custom" size setting, 659px) with no + * viewport-based cap, so on a narrow screen the box renders far wider + * than the viewport instead of shrinking — on mobile it overflows off + * the left edge and everything on it reads oversized. max-width still + * constrains an inline width (it's a separate property, not something + * that has to out-specificity it), so this only ever kicks in below + * 659px + the gutter; desktop is unaffected. + */ +.pum-container.theme-34046 { + max-width: calc(100vw - (var(--wp--preset--spacing--20) * 2)); + box-sizing: border-box; +} + +.newsletter-popup .gfield_label { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; +} + +.newsletter-popup .ginput_container input[type="email"], +.newsletter-popup .ginput_container input[type="text"] { + width: 100%; + background-color: var(--wp--preset--color--base); + border: 1px solid var(--wp--preset--color--neutral-400); + border-radius: var(--wp--preset--border-radius--200); + padding: var(--wp--preset--spacing--20) var(--wp--preset--spacing--30); + font-family: var(--wp--preset--font-family--body); + font-size: var(--wp--preset--font-size--200); +} + +/* + * Gravity Forms' own theme.min.css sets padding-bottom:16px on + * .gform_wrapper.gravity-theme .gform_footer at equal (0,3,0) + * specificity — matching its ancestor selector here to outrank it + * instead of reaching for !important. + */ +.newsletter-popup .gform_wrapper.gravity-theme .gform_footer.top_label { + padding-bottom: 0; +} + +.newsletter-popup .gform_footer input.gform_button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 100%; + background-color: var(--wp--preset--color--accent-200); + color: var(--wp--preset--color--neutral-100); + border: none; + border-radius: var(--wp--preset--border-radius--500); + padding: var(--wp--preset--spacing--10) var(--wp--preset--spacing--30); + font-family: var(--wp--preset--font-family--body); + font-size: var(--wp--preset--font-size--300); + font-weight: var(--wp--custom--font-weight--semi-bold); + cursor: pointer; +} + +.newsletter-popup .gform_footer input.gform_button:hover { + background-color: var(--wp--preset--color--accent-300); +} diff --git a/assets/css/newsletter-signup.css b/assets/css/newsletter-signup.css new file mode 100644 index 0000000..155e673 --- /dev/null +++ b/assets/css/newsletter-signup.css @@ -0,0 +1,60 @@ +/* + * A hand-typed border width+style+color+radius block-style attribute + * doesn't byte-match WordPress's own style-engine serialization (see + * patterns/newsletter-signup.php's header comment) — real CSS instead, + * same fix already used for republish-notice. + */ +.newsletter-signup { + border: 1px solid var(--wp--preset--color--neutral-300); + border-radius: var(--wp--preset--border-radius--200); +} + +/* + * newsletter-signup-compact.php embeds the site's real Gravity Form + * rather than static markup, so its input/button come from Gravity + * Forms' own generated HTML, not core/button — restyled directly here + * instead of rebuilt from scratch, the same CSS-bridge approach already + * used for the CC Post Republisher plugin's button in + * republish-notice.css. The field's visible label is screen-reader-only + * since the field's placeholder text (set directly in the form, in + * wp-admin) already communicates its purpose visually, matching Figma. + */ +.newsletter-signup-compact .gfield_label { + position: absolute; + width: 1px; + height: 1px; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; +} + +.newsletter-signup-compact .ginput_container input[type="email"], +.newsletter-signup-compact .ginput_container input[type="text"] { + width: 100%; + background-color: var(--wp--preset--color--base); + border: 1px solid var(--wp--preset--color--neutral-400); + border-radius: var(--wp--preset--border-radius--200); + padding: var(--wp--preset--spacing--20) var(--wp--preset--spacing--30); + font-family: var(--wp--preset--font-family--body); + font-size: var(--wp--preset--font-size--200); +} + +.newsletter-signup-compact .gform_footer input.gform_button { + display: inline-flex; + align-items: center; + justify-content: center; + width: 100%; + background-color: var(--wp--preset--color--accent-200); + color: var(--wp--preset--color--neutral-100); + border: none; + border-radius: var(--wp--preset--border-radius--500); + padding: var(--wp--preset--spacing--10) var(--wp--preset--spacing--30); + font-family: var(--wp--preset--font-family--body); + font-size: var(--wp--preset--font-size--300); + font-weight: var(--wp--custom--font-weight--semi-bold); + cursor: pointer; +} + +.newsletter-signup-compact .gform_footer input.gform_button:hover { + background-color: var(--wp--preset--color--accent-300); +} diff --git a/assets/css/republish-notice.css b/assets/css/republish-notice.css new file mode 100644 index 0000000..abdbf87 --- /dev/null +++ b/assets/css/republish-notice.css @@ -0,0 +1,56 @@ +/* + * The "CC" icon circle needs a fixed size core/group can't set via any + * block attribute — real CSS for size/centering. Border is CSS too, not + * a hand-typed inline-style block attribute — that combination (all + * four border sub-properties at once) triggered a "Block contains + * unexpected or invalid content" editor error, since it didn't match + * WordPress's own generated serialization. + */ +.republish-notice__icon { + display: flex; + align-items: center; + justify-content: center; + width: 2.5em; + height: 2.5em; + flex-shrink: 0; + border: var(--wp--custom--border-width--base) solid var(--wp--preset--color--neutral-700); + border-radius: var(--wp--preset--border-radius--500); +} + +/* + * The plugin's button isn't a core/button, so + * styles/blocks/button/republish-article.json's is-style-republish-article + * class can't reach it — same values, targeted directly instead. The + * icon is the same arrow custom-button.css already uses for that style. + */ +.republish-notice #cc-post-republisher-modal-button-open { + display: inline-flex; + align-items: center; + background-color: var(--wp--preset--color--accent-500); + color: var(--wp--preset--color--base); + border: none; + border-radius: var(--wp--preset--border-radius--200); + padding: var(--wp--preset--spacing--10) var(--wp--preset--spacing--20); + font-size: var(--wp--preset--font-size--200); + font-weight: var(--wp--custom--font-weight--semi-bold); + cursor: pointer; +} + +.republish-notice #cc-post-republisher-modal-button-open:hover { + background-color: var(--wp--preset--color--accent-600); +} + +.republish-notice #cc-post-republisher-modal-button-open::before { + content: ""; + display: inline-block; + width: 1.25em; + height: 1.25em; + margin-inline-end: var(--wp--preset--spacing--5); + background-color: currentcolor; + mask-image: url("../icons/arrow-line-up-right.svg"); + mask-repeat: no-repeat; + mask-size: contain; + -webkit-mask-image: url("../icons/arrow-line-up-right.svg"); + -webkit-mask-repeat: no-repeat; + -webkit-mask-size: contain; +} diff --git a/assets/css/story-card.css b/assets/css/story-card.css index 69f6cd7..5be259d 100644 --- a/assets/css/story-card.css +++ b/assets/css/story-card.css @@ -43,9 +43,17 @@ * image's top-left corner, inset by story-card__media's own padding. * core/group has no "position" style attribute, so this needs real CSS — * .story-card__media is the positioned ancestor every variant shares. + * + * overflow:hidden also contains a broken/missing image to this box: a + * failed image falls back to its raw width/height HTML attributes + * (often large, oversized crops), and with overflow:visible up the + * ancestor chain that box bleeds straight through the rest of the + * page — not just a local-dev demo-content risk, any deleted/failed + * media file on staging or live would do the same without this. */ .story-card__media { position: relative; + overflow: hidden; } .story-card__badge-overlay { @@ -54,3 +62,18 @@ left: var(--wp--preset--spacing--50); z-index: 1; } + +/* + * CSS Grid's default align-items:stretch makes every card in a + * post-template row match the row's tallest card (e.g. one with a much + * longer excerpt) — with no block attribute to opt a grid item out of + * that (same gap as columnStart/rowSpan, confirmed in tasks.md task + * 2.9). The stretch cascades into story-card__media's aspect-ratio + * figure, blowing its image out to the row's full height instead of + * its own 3/2 ratio. Each card sizes to its own content instead. + */ +.story-card, +.story-card-featured, +.story-card-editorial { + align-self: start; +} diff --git a/functions.php b/functions.php index efff314..765f8b4 100644 --- a/functions.php +++ b/functions.php @@ -290,6 +290,24 @@ function spotlight_theme_2026_hide_duplicate_card_image_link( $block_content, $b } add_filter( 'render_block', 'spotlight_theme_2026_hide_duplicate_card_image_link', 10, 2 ); +/** + * Suppresses the CC Post Republisher plugin's own `cc/post-republisher` + * block wherever editors have inserted it directly into post content. + * + * `patterns/republish-notice.php` already renders the plugin's button/modal + * markup with the site's own design, appended once per single.html via + * `wp:pattern`. Editors are used to placing the plugin's default block + * inline in the article body too, which duplicates the button (and its + * element IDs) on the page. The pattern doesn't use this block, so + * suppressing it here only ever removes the redundant inline copy. + * + * @return string + */ +function spotlight_theme_2026_suppress_inline_republish_block() { + return ''; +} +add_filter( 'render_block_cc/post-republisher', 'spotlight_theme_2026_suppress_inline_republish_block' ); + /** * Returns a cache-busting version string for a theme file. * @@ -420,6 +438,37 @@ function spotlight_theme_2026_enqueue_assets() { spotlight_theme_2026_asset_version( 'assets/css/pagination.css' ) ); + // cc-post-republisher-style is only ever auto-enqueued by WordPress when + // a real cc/post-republisher block is detected in the content; our + // pattern embeds the plugin's markup as plain HTML instead (see + // patterns/republish-notice.php), so it never gets picked up on its + // own. Declaring it as a dependency here enqueues the plugin's own + // modal styling — already registered by the plugin — ahead of ours. + wp_enqueue_style( + 'spotlight-theme-2026-republish-notice', + get_theme_file_uri( 'assets/css/republish-notice.css' ), + array( 'cc-post-republisher-style' ), + spotlight_theme_2026_asset_version( 'assets/css/republish-notice.css' ) + ); + + wp_enqueue_style( + 'spotlight-theme-2026-newsletter-signup', + get_theme_file_uri( 'assets/css/newsletter-signup.css' ), + array(), + spotlight_theme_2026_asset_version( 'assets/css/newsletter-signup.css' ) + ); + + // The "Newsletter Subscribe Pop Up" Popup Maker popup preloads its + // content into the front page's own markup, so this loads on the + // same page load as everything else — no separate popup-page hook + // needed. + wp_enqueue_style( + 'spotlight-theme-2026-newsletter-popup', + get_theme_file_uri( 'assets/css/newsletter-popup.css' ), + array(), + spotlight_theme_2026_asset_version( 'assets/css/newsletter-popup.css' ) + ); + // Add wp_enqueue_script() here when assets/js/main.js exists. } add_action( 'enqueue_block_assets', 'spotlight_theme_2026_enqueue_assets' ); diff --git a/openspec/changes/spotlight-pattern-library/design.md b/openspec/changes/spotlight-pattern-library/design.md index 6aed4a7..8d6955e 100644 --- a/openspec/changes/spotlight-pattern-library/design.md +++ b/openspec/changes/spotlight-pattern-library/design.md @@ -107,3 +107,5 @@ Additive per PR — no existing template/part is removed; each PR adds pattern f - Will the dynamic latest-post-swap and province-count-grid map variants ever move from Figma exploration to "Ready for Dev"? Not tracked by this change; a future change would define and build them if/when confirmed. - Posts with more than one category break `spotlight-badge`/`story-card`'s plain category label, since `core/post-terms` always renders every assigned term joined by a separator, with no "primary category" concept. Options discussed with the user (take-first-term via a render filter, a manual primary-category field, or an editorial single-category policy) — to be decided with Zared; not yet implemented. - Special Projects and Perspectives showed identical posts in the same order during task 2.5 testing — possibly the tag-filtering isn't distinguishing them, or the posts genuinely share both tags. Flagged to the user, not yet investigated further. +- `topic-band.php`'s front-page "Explore by topic" tiles use `spacing|50`/`spacing|20` padding, both in the range affected by the `defaultSpacingSizes:false` fix (2026-08-24) — the same class of bug already found and fixed in `topic-band-compact.php` (sidebar) and `trust-bar.html`. Left unverified here: the Figma MCP connection hit a hard View-seat rate limit before this file could be checked against a fresh pull. Deferred to a later PR rather than guessed at. +- `newsletter-signup`'s form integration is resolved for one size variant and open for the other. The sidebar/compact variant embeds the site's real Gravity Form (ID 1, "Sign up to get stories that matter in your inbox," already wired to Mailchimp) — confirmed as the correct, working approach, not a placeholder. The front-page variant's "Subscribe for the latest news" button still has no confirmed destination (submitting its own copy of the form vs. linking to the sidebar form or a dedicated page) — to be resolved with Zared before this button is wired to anything real. diff --git a/openspec/changes/spotlight-pattern-library/tasks.md b/openspec/changes/spotlight-pattern-library/tasks.md index ee3cba9..22c8a81 100644 --- a/openspec/changes/spotlight-pattern-library/tasks.md +++ b/openspec/changes/spotlight-pattern-library/tasks.md @@ -27,15 +27,17 @@ ## 3. Trust, newsletter, and editorial-utility patterns (LS-1721 / PR 3) -- [ ] 3.1 Create `patterns/newsletter-signup.php` (front-page size) and `patterns/newsletter-signup-compact.php` (sidebar size) — satisfying the `newsletter-signup` requirement's two size variants -- [ ] 3.2 Create `patterns/republish-notice.php`, including the CC BY-ND 4.0 attribution copy confirmed in the Figma client annotation — satisfying the `republish-notice` requirement -- [ ] 3.3 Confirm `parts/trust-bar.html` remains an existing template part, not converted to a pattern -- [ ] 3.4 Update `templates/front-page.html`'s `front-page__provincial-newsletter` section to reference `newsletter-signup` via `wp:pattern` -- [ ] 3.5 Update `parts/sidebar-editorial.html`'s newsletter module to reference `newsletter-signup-compact` via `wp:pattern` -- [ ] 3.6 Update `templates/single.html`'s republish section to reference `republish-notice` via `wp:pattern`, replacing `[Republish pattern placeholder]` -- [ ] 3.7 Tighten `parts/sidebar-editorial.html` spacing, borders, and Explore-topics icon treatment to match the Figma "Single Blog Post" sidebar exactly -- [ ] 3.8 Note any dependencies on form-integration decisions (e.g. real newsletter subscription handling vs. static markup) in design.md -- [ ] 3.9 Run `npm run schema:validate` and `npm run theme:validate`; manually verify in the Site Editor and front end +- [x] 3.1 Create `patterns/newsletter-signup.php` (front-page size) and `patterns/newsletter-signup-compact.php` (sidebar size) — satisfying the `newsletter-signup` requirement's two size variants. Front-page variant is static placeholder content (3 hardcoded issues) — this site has real "Edition"/"Spotlight Edition" categories that could back a real query later, deferred pending Zared's feedback on whether it's needed. Sidebar variant embeds the site's real Gravity Form (ID 1, already wired to Mailchimp) rather than static markup, restyled via `assets/css/newsletter-signup.css` to match the design (same CSS-bridge approach as the CC Post Republisher plugin's button in republish-notice.css) — field placeholder text and button label ("Subscribe") set directly in the form via wp-admin. Found and fixed the same "invalid content" bug as republish-notice.php in newsletter-signup.php's outer card: a hand-typed border width+style+color+radius block-style attribute doesn't byte-match WordPress's own serialization; moved to CSS. The front-page button's real destination (form submission vs. linking elsewhere) is unresolved, pending discussion with Zared. +- [x] 3.2 Create `patterns/republish-notice.php`, including the CC BY-ND 4.0 attribution copy confirmed in the Figma client annotation — satisfying the `republish-notice` requirement. The visible "Republish article" button and its modal match the site's own CC Post Republisher plugin's expected markup, so its existing modal JS actually works — the plugin only ever renders that button/modal, so the card, icon, heading, and body copy around it are authored directly. Written as plain HTML wrapped in `wp:html` (not a `wp:cc/post-republisher` block reference): that block's `save()` returns real content, and a self-closing block reference stores none, which broke both the front-end button and editor validation — the plugin's modal JS only looks for the element IDs, not a real block instance, so plain HTML works identically. Card has no background — confirmed unnecessary via live testing. Button styling reuses `styles/blocks/button/republish-article.json`'s values in `assets/css/republish-notice.css`, since the plugin's button isn't a `core/button`. + - Confirmed working end-to-end via Playwright (2026-08-24) after fixing three stacked environment issues, all local-dev/config, not pattern code: Perfmatters was serving the plugin's `modal.js` over an untrusted local `https://` cert (button did nothing — deactivated Perfmatters locally); Classic Editor was forced as the default editor, triggering the plugin's own classic-editor fallback to auto-inject a second button into every post body (deactivated Classic Editor — flagged to Zared since he'd previously asked to keep it on); the plugin's modal CSS only auto-enqueues alongside a real registered block, so it never loaded for our plain-HTML button (fixed by declaring it as a dependency in `functions.php`). Added a defensive `render_block_cc/post-republisher` filter too, in case an editor ever inserts the real block directly. + - Spacing-vs-Figma check (2026-08-24) surfaced a sitewide bug, not scoped to this pattern: `theme.json`'s custom spacing sizes reuse slugs ("20"–"80") that collide with WordPress core's own built-in default spacing scale, and without `defaultSpacingSizes:false`, core's identically-slugged flat values were winning over our fluid `clamp()` ones everywhere — this pattern's padding included. Fixed by adding `"defaultSpacingSizes":false` to `theme.json`; confirmed fluid spacing now resolves correctly here and spot-checked elsewhere (home.html cards, topic-band) with no regressions. +- [x] 3.3 Confirm `parts/trust-bar.html` remains an existing template part, not converted to a pattern — confirmed unchanged. Its spacing needed re-checking against a fresh Figma pull ("Stats Bar" component) after today's theme.json spacing-scale fix: the outer bar's padding was wrong on all four sides (should be vertical-only, spacing|30, with horizontal bounds from wide alignment, not manual padding), each item was missing vertical padding and an explicit icon-to-text gap (both spacing|10), and the heading/description text pairing was relying on the sitewide default gap instead of Figma's explicit spacing|5 — now fixed, plus the divider color corrected from neutral-100 to base to match the design exactly. +- [x] 3.4 Update `templates/front-page.html`'s `front-page__provincial-newsletter` section to reference `newsletter-signup` via `wp:pattern` — replaced the whole existing heading+placeholder group, since the pattern bakes in its own heading (same reasoning as `topic-band-compact.php`) +- [x] 3.5 Update `parts/sidebar-editorial.html`'s newsletter module to reference `newsletter-signup-compact` via `wp:pattern` — same whole-group replacement; confirmed live in the sidebar with the real Gravity Form rendering and styled correctly +- [x] 3.6 Update `templates/single.html`'s republish section to reference `republish-notice` via `wp:pattern`, replacing `[Republish pattern placeholder]` +- [x] 3.7 Tighten `parts/sidebar-editorial.html` spacing, borders, and Explore-topics icon treatment to match the Figma "Single Blog Post" sidebar exactly. Icon/text typography (topic-band.css) already matched Figma correctly, nothing to change there. `topic-band-compact.php`'s tile padding was genuinely wrong (spacing|40 instead of Figma's spacing|20, confirmed against a fresh pull of the sidebar's "Topic Sidebar" component) — not something today's theme.json fix caused, but the fix made it newly visible at its real (larger) size. Also removed the heading's redundant padding-bottom, since the outer wrapper's own blockGap already provides the correct single gap per Figma. +- [x] 3.8 Note any dependencies on form-integration decisions (e.g. real newsletter subscription handling vs. static markup) in design.md — sidebar variant's real Gravity Form embed is confirmed correct; front-page variant's button destination is still open, pending Zared +- [x] 3.9 Run `npm run schema:validate` and `npm run theme:validate`; manually verify in the Site Editor and front end — both pass; all PR 3 patterns and template wiring confirmed live via Playwright throughout today's session ## 4. Related-coverage and onward-journey patterns (LS-1722 / PR 4) diff --git a/parts/sidebar-editorial.html b/parts/sidebar-editorial.html index f7aceb2..571a45c 100644 --- a/parts/sidebar-editorial.html +++ b/parts/sidebar-editorial.html @@ -25,29 +25,7 @@

Track the HIV & TB response

- - - + diff --git a/parts/trust-bar.html b/parts/trust-bar.html index c492779..7a6211c 100644 --- a/parts/trust-bar.html +++ b/parts/trust-bar.html @@ -1,17 +1,17 @@
- -