Build Search template (LS-2594) - #43
Conversation
Patterns - Add search-hero.php: eyebrow, static "Search LightSpeed" heading, description, pill search field - Add search-useful-destinations.php: 4-card grid (FAQ, Pricing, Website packages, Contact) reusing existing card/icon styles - Rebuild template-search.php: hero → results loop → useful destinations Results loop - Add category eyebrow (core/post-terms) and hairline divider per result - Remove unused postType/search/exclude query args ignored by inherit:true - Pages now correctly appear alongside posts in results Notes - No new colour tokens or font-size presets — full reuse of existing tokens/styles - CHANGELOG.md updated under [Unreleased] — Build Search template (LS-2594)
Breadcrumbs and padding - Embed yoast-seo/breadcrumbs directly in search-hero.php instead of the shared breadcrumbs template part, matching blog-hero/work-hero convention - Removes double top padding (hero's own + the shared part's) and aligns the breadcrumb to the wide row instead of a narrower centered one - Bump section top/bottom padding to spacing|90 (matches sitewide hero pattern) Internal rhythm - Increase blockGap between eyebrow, heading, description, and search field by three token steps (60→70→80, 20→30→40, 30→40→50) to match the Figma reference's proportions Divider and background - Add a real border-bottom (border.card token) between hero and results - Add src/scss/structural/search-hero.scss: two corner radial-gradient glows reused from existing effect.hero.brand/effect.hero.cyan tokens (no new tokens), same layered-gradient technique as blog-hero.scss/work-hero.scss - Wire the new stylesheet into package.json build scripts, functions.php editor styles, and inc/animations.php's is_search-conditional enqueue Notes - No new colour tokens or font-size presets - templates/search.html no longer inserts the standalone breadcrumbs part
Whole-card clickable results
- Fix stretched-link on result rows: screen-reader-text sets
position:absolute on the anchor, clipping it to 1px and breaking the
::before{inset:0} hit area (it sized against that 1px box instead of
the card) — switch to font-size:0 (Card - Post's existing technique)
so the anchor stays non-positioned and the whole row is clickable
- Add rightward-shift + title accent-color hover motion, matching the
whole-card hover convention used by other cards in this repo
Dark mode fixes
- Hover title colour was link.accent (dark: cta-500, the same
saturated cyan as CTA buttons) — wrong token for a hover state;
switch to link.accent-hover (dark: cta-300, same family, softer),
which exists specifically for this role
- search-pill.json never set an explicit input colour, so text fell
back to the browser's black UA default — invisible on the dark pill
surface, only working in light mode by coincidence. Add
text.default (typed text) and text.subtle (placeholder), both
already dark/light-paired. Checked both consumers of this shared
style first (Search hero, Blog All Articles header search) — same
latent bug existed in both, now fixed for both
Notes
- No new tokens, no token definitions changed — only which existing
token each rule references
Spacing - Equalize eyebrow→heading and heading→cards gaps to spacing|70 (previously an incorrect flat blockGap, then a wrong asymmetric 80/40 split that made it worse) Font-leading fix - Set line-height:1 on the eyebrow label and heading to remove most added leading - Add .ls-optical-trim (search-hero.scss, already Search-template scoped): text-box-trim/text-box-edge as the forward-looking CSS fix, plus a transform: translateY(calc((1cap - 1lh) / 2)) fallback — browser-calculated from each font's own real metrics (Lexend vs Manrope have different built-in leading), not a guessed pixel value - Removed an earlier calc(spacing--60 - 18px) hardcoded offset that only coincidentally matched one screenshot and wasn't a real fix Notes - No new colour tokens; spacing uses existing spacing|70 preset - cap/lh CSS units and text-box-trim degrade gracefully in unsupported browsers (silently ignored, same as before this fix)
Font-leading fix - Add ls-optical-trim (already built for Useful destinations) and line-height:1 to the hero's eyebrow label and "Search LightSpeed" heading — same Lexend/Manrope leading mismatch as the destinations section, same fix Spacing - Bump eyebrow→heading, heading→description, and description→search field gaps up two token steps each (80→100, 40→60, 50→70)
- Bump core/post-title from level 3 to level 2 in the results loop — previously skipped H2 entirely (H1 hero -> H3 results -> H2 destinations, out of order), violating AGENTS.md's no-skipped- heading-levels requirement - Verified live: H1 -> H2 (each result) -> H2 (destinations) -> H3 (destination cards), no skips
- Consolidate all Search template work into the single existing entry (hero, destinations section, results list, optical-leading fix, search-pill contrast fix, heading hierarchy fix) - Add dev-site verification notes (taxonomy alignment, link checks)
There was a problem hiding this comment.
🟡 Changes recommended
The new stretched-link focus styling likely produces an invisible focus outline for keyboard users, and the new search CSS bundle registration should be aligned with the theme’s render-marker fallback to avoid missing styles when patterns/classes are reused outside search contexts.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Builds out the Search Results template’s main content by replacing the minimal title/search/query-loop layout with a dedicated hero, an improved results list presentation, and a reusable “Useful destinations” section, while ensuring the supporting structural CSS is compiled and enqueued.
Changes:
- Reworked the Search template to use a new hero pattern plus a rebuilt results loop and destinations section.
- Added structural styling for the search hero background and for clickable/hoverable result rows.
- Updated the shared search-pill block style to explicitly set input and placeholder text colours for correct contrast.
File summaries
| File | Description |
|---|---|
| templates/search.html | Removes the standalone breadcrumbs template part in favour of hero-owned breadcrumbs. |
| styles/blocks/search/search-pill.json | Sets input/placeholder text colours for contrast and documents the rationale. |
| src/scss/structural/search-results.scss | Adds stretched-link + hover/focus styles for search result rows. |
| src/scss/structural/search-hero.scss | Introduces search hero layered gradient background and .ls-optical-trim. |
| patterns/template-search.php | Rebuilds main search template pattern: hero → results loop → useful destinations. |
| patterns/sections/search-useful-destinations.php | Adds the “Useful destinations” section pattern used after results. |
| patterns/hero/search-hero.php | Adds the Search hero pattern with breadcrumb, heading, and search-pill field. |
| package.json | Ensures search-hero.scss compiles in build/watch Sass scripts. |
| inc/animations.php | Registers search-hero CSS bundle for front/editor under is_search. |
| functions.php | Adds assets/css/search-hero.css to editor styles. |
| CHANGELOG.md | Documents the new Search template work under Unreleased. |
| assets/css/search-results.css | Compiled output for updated search results structural styles. |
| assets/css/search-hero.css | Compiled output for the search hero background + .ls-optical-trim. |
Review details
Files not reviewed (2)
- assets/css/search-hero.css: Generated file
- assets/css/search-results.css: Generated file
- Files reviewed: 11/13 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Add render-marker fallback entries for search-hero/search-results bundles (ls-search-hero, ls-optical-trim, ls-search-result) so their CSS still loads if those patterns/classes are ever reused outside a search page — previously they had no fallback at all, unlike every other insertable pattern's bundle - Add is_search() to work-archive-sections' head-time condition — Useful destinations reuses its is-style-card-category/ ls-icon-well-brand classes, which were only being caught by the render_block footer fallback, causing a visible flash of unstyled cards after first paint - Move the search-result stretched-link's focus outline from .ls-search-result__link to its ::before overlay — the link itself is font-size:0 (collapsed by design), so the outline was rendering invisible for keyboard users - Gate the optical-leading transform behind @supports not (text-box-trim: trim-both) — in browsers that support text-box-trim, the native trim and the transform fallback were both applying, over-shifting the text
Summary
Builds the Search Results template's main content: a hero with a static "Search LightSpeed" heading and a pill search field, a category-eyebrow results list bound to the real WordPress search query, and a "Useful destinations" section shown below the results regardless of result count. Previously this template had only a bare title/search field/plain query loop.
Hero
patterns/hero/search-hero.php: breadcrumb trail, eyebrow, static "Search LightSpeed" H1 (intentionally not bound to the query term), supporting copy, and a no-button pill search field (is-style-search-pill, reused from the existing Blog All Articles search box). The breadcrumb is embedded in the hero itself rather than the sharedls-theme/breadcrumbstemplate part, so it shares the hero's own padding instead of stacking a second set on top — same convention asblog-hero/work-hero.src/scss/structural/search-hero.scssfor the hero's decorative corner-glow background (two layered radial gradients —color.gradientblock support only accepts one), reusing the existingeffect.hero.brand/effect.hero.cyantokens.Results list
patterns/template-search.php: hero → results query loop → useful destinations.core/querykeepsinherit:true(defers to the main search query, so pages and posts both appear) with the previously-unusedpostType/search/excludeargs removed.core/post-terms), anH2title (level:2— the previouslevel:3skipped a heading level with noH2before it), an excerpt, and a hairline divider (border.card).core/read-morelink hidden withfont-size:0, not the corescreen-reader-textutility (that setsposition:absoluteon the anchor itself, which breaks the stretched-link's::before{inset:0}sizing against the row).Useful destinations
patterns/sections/search-useful-destinations.php: eyebrow, heading, and a 4-card grid (FAQ, Pricing, Website packages, Contact), reusing the existingis-style-card-category/ls-icon-well-brand/is-style-link-arrow-accentstyles and the same Phosphor icon markup already used by404-best-next-routes.php. Kept as a separate, self-contained file per this repo's one-pattern-per-file convention. Always shown below results, matching the 404 template's "Best next routes" precedent.Fixes found along the way
styles/blocks/search/search-pill.json: neither this file norcore-search.jsonset acoloron the search input, so it fell back to the browser's black UA default — invisible against this pill's dark-modesurface.cardbackground. Added explicittext.default/text.subtlefor typed text and placeholder. Fixes both consumers of this shared style (this template's hero and Blog All Articles' header search, which had the identical latent bug).link.accent(dark mode:cta-500, the same saturated cyan as the CTA buttons — too loud for hover-only state on heading-sized text). Changed tolink.accent-hover(dark mode:cta-300, same family, softer), the token that exists specifically for this role..ls-optical-trim(text-box-trim/text-box-edge, with atransform: translateY(calc((1cap - 1lh) / 2))fallback for broader browser support) to both heading/eyebrow pairs in the hero and destinations section.line-heightalone can't fully cancel a font's own built-in leading — Manrope (body) and Lexend (display) carry different amounts of it — which was making the gap above each heading read larger than the gap below, even at identical margin values.cap/lhare real CSS units the browser resolves per-font, not a guessed offset.Investigated, not changed
theme:validate'sstyles/light.json missingfailure is confirmed pre-existing ondevelop, unrelated to this branch.functions.php's existing PHPCS findings (tabs/comment spacing around lines 102-115) are confirmed pre-existing viagit stashcomparison, unrelated to this branch.Test plan
php -lclean on all changed/new PHP filesvendor/bin/phpcs --standard=WordPress— zero findings on all changed files except the confirmed-pre-existingfunctions.phpissue abovenpm run patterns:escape— clean (one pre-existing false-positivewp_json_encodeidiom flagged, same pattern already present elsewhere in the repo)npm run security:scan— cleannpm run schema:validate— all JSON passesnpm run theme:validate— the one failure (styles/light.jsonmissing) confirmed pre-existing ondevelopls-agency.lightspeedwp.dev, this theme's active environment there):categorytaxonomy exists onpostand matches local;pagehas no taxonomies on either, consistent; all four destination links (/faq/,/pricing/,/website-packages/,/contact/) are published thereCloses LS-2594