From 0be50c9498a22b5762c2cf0133ed0f4d32b96c4b Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Tue, 22 Sep 2026 16:53:30 +0200 Subject: [PATCH 01/19] Add five new shared section patterns for phase pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New patterns (patterns/sections/) - phase-common-services.php: "What happens during Discover" pill list + footnote - phase-services-in-phase.php: auto-detects current phase, renders that phase's service card(s) - phase-support-focus.php: two-column support/focus-area section with CTA button - phase-deliverables-and-role.php: "What you receive" / "Your role" two-card section - phase-cta.php: closing CTA with checklist, reuses existing phase button styles Conventions followed - All generic/reusable across the six phase pages, not Discover-only - Discover green via existing var(--wp--custom--color--phase--discover-on-dark) token, no hardcoded hex - No new SCSS — reuses existing pill, card, icon-well and button styles throughout - phase-services-in-phase.php mirrors phase-journey-nav.php's live phase-detection technique Fix - phase-support-focus.php: button was unstyled and rendered in the sitewide default (cyan-blue) instead of the phase accent color; now uses is-style-button-phase-primary Testing - Inserted as live wp:pattern references on /services/discover/ (589) - Verified in Site Editor: all 5 new patterns validate cleanly, no "Attempt recovery" warnings - Confirmed no PHP notices/warnings/fatals on render --- patterns/sections/phase-common-services.php | 86 ++++++++ patterns/sections/phase-cta.php | 84 ++++++++ .../sections/phase-deliverables-and-role.php | 145 +++++++++++++ patterns/sections/phase-services-in-phase.php | 200 ++++++++++++++++++ patterns/sections/phase-support-focus.php | 107 ++++++++++ 5 files changed, 622 insertions(+) create mode 100644 patterns/sections/phase-common-services.php create mode 100644 patterns/sections/phase-cta.php create mode 100644 patterns/sections/phase-deliverables-and-role.php create mode 100644 patterns/sections/phase-services-in-phase.php create mode 100644 patterns/sections/phase-support-focus.php diff --git a/patterns/sections/phase-common-services.php b/patterns/sections/phase-common-services.php new file mode 100644 index 0000000..4ce5306 --- /dev/null +++ b/patterns/sections/phase-common-services.php @@ -0,0 +1,86 @@ + + +
+ + +
+ +

+ +
+ + + +
+ +

+ +
+ + + +
+ +

+ +
+ + + +
+ + +
+ + + +

+ +
+ + +
+ + + +
+ +

+ +
+ +
+ diff --git a/patterns/sections/phase-cta.php b/patterns/sections/phase-cta.php new file mode 100644 index 0000000..8f3734b --- /dev/null +++ b/patterns/sections/phase-cta.php @@ -0,0 +1,84 @@ + + +
+ + +
+ + +
+ +

+ + + +

+ + + +
+ +
+ + + +
+ +
+ +
+ + + +
+ +
+ + +
+ +
+ +
+ + + +

+ +
+ + +
+ +
+ +
+ +
+ diff --git a/patterns/sections/phase-deliverables-and-role.php b/patterns/sections/phase-deliverables-and-role.php new file mode 100644 index 0000000..7135de7 --- /dev/null +++ b/patterns/sections/phase-deliverables-and-role.php @@ -0,0 +1,145 @@ + + +
+ + + +

+ +
+ + + +
+ + +
+ +

+ + + +

+ +
+ + + +
+ + +
+ +
+ +
+ + + +

+ +
+ + + +

+ + + +
+ +
+ +
+ +
+ + + +
+ +
+ +
+ + + +

+ +
+ + + +

+ + + +
+ +
+ +
+ +
+ +
+ +
+ diff --git a/patterns/sections/phase-services-in-phase.php b/patterns/sections/phase-services-in-phase.php new file mode 100644 index 0000000..ccbd6b1 --- /dev/null +++ b/patterns/sections/phase-services-in-phase.php @@ -0,0 +1,200 @@ +` reference block. + * Keywords: phase, discover, create, build, launch, grow, evolve, services, cards, section + * Viewport Width: 1280 + * Inserter: true + * + * @package ls-theme + */ + +$ls_phase_services_by_phase = array( + 'discover' => array( + array( + 'label' => __( 'Discovery', 'ls-theme' ), + 'description' => __( 'Stakeholder workshops, audits, scoping and feasibility — the evidence layer that comes before design or build.', 'ls-theme' ), + 'url' => '/services/discovery/', + 'icon' => 'search', + ), + ), + 'create' => array( + array( + 'label' => __( 'Content', 'ls-theme' ), + 'description' => __( 'Content modelling, editorial workflow and governance that holds up at scale.', 'ls-theme' ), + 'url' => '/services/content/', + 'icon' => 'file-text', + ), + array( + 'label' => __( 'Design', 'ls-theme' ), + 'description' => __( 'Design systems, accessible patterns and Figma→WordPress parity. Already a deeper page.', 'ls-theme' ), + 'url' => '/services/design/', + 'icon' => 'paint-brush', + ), + ), + 'build' => array( + array( + 'label' => __( 'Development', 'ls-theme' ), + 'description' => __( 'Block themes, WooCommerce, integrations and platform refactors built for long-term health.', 'ls-theme' ), + 'url' => '/services/development/', + 'icon' => 'code', + ), + array( + 'label' => __( 'Migrations', 'ls-theme' ), + 'description' => __( 'Audits, mapping and redirects that take legacy platforms apart without losing traction.', 'ls-theme' ), + 'url' => '/services/migrations/', + 'icon' => 'arrows-left-right', + ), + ), + 'launch' => array( + array( + 'label' => __( 'Hosting', 'ls-theme' ), + 'description' => __( 'Managed environments that match the platform, the workflows and the support model around it.', 'ls-theme' ), + 'url' => '/services/hosting/', + 'icon' => 'cloud', + ), + array( + 'label' => __( 'Performance', 'ls-theme' ), + 'description' => __( 'Core Web Vitals, caching, query review and template optimisation against real production data.', 'ls-theme' ), + 'url' => '/services/performance/', + 'icon' => 'gauge', + ), + array( + 'label' => __( 'Security', 'ls-theme' ), + 'description' => __( 'Audits, hardening guidance, monitoring and recovery planning that reduces risk before incidents.', 'ls-theme' ), + 'url' => '/services/security/', + 'icon' => 'shield', + ), + array( + 'label' => __( 'Training', 'ls-theme' ), + 'description' => __( 'Role-specific training and reference materials that move teams from dependency to confidence.', 'ls-theme' ), + 'url' => '/services/training/', + 'icon' => 'graduation-cap', + ), + ), + 'grow' => array( + array( + 'label' => __( 'Support', 'ls-theme' ), + 'description' => __( 'Maintenance, incident response and quiet improvement so platforms keep getting easier to run.', 'ls-theme' ), + 'url' => '/services/support/', + 'icon' => 'lifebuoy', + ), + array( + 'label' => __( 'SEO', 'ls-theme' ), + 'description' => __( 'Technical SEO, internal linking, schema and the publishing discipline that supports visibility.', 'ls-theme' ), + 'url' => '/services/seo/', + 'icon' => 'chart-line-up', + ), + array( + 'label' => __( 'Accessibility', 'ls-theme' ), + 'description' => __( 'Audits, remediation and the semantic structure that helps WCAG 2.2 AA stick over time.', 'ls-theme' ), + 'url' => '/services/accessibility/', + 'icon' => 'wheelchair', + ), + array( + 'label' => __( 'Email marketing', 'ls-theme' ), + 'description' => __( 'Journey planning, consent flows and the connection between email and the WordPress platform behind it.', 'ls-theme' ), + 'url' => '/services/email-marketing/', + 'icon' => 'envelope', + ), + ), + 'evolve' => array( + array( + 'label' => __( 'AI', 'ls-theme' ), + 'description' => __( 'AI-readiness reviews, governance and workflow planning — practical use without messy adoption.', 'ls-theme' ), + 'url' => '/services/ai/', + 'icon' => 'special-interests', + ), + ), +); + +$ls_current_phase_slug = ''; +$ls_services_queried_obj = get_queried_object(); +if ( $ls_services_queried_obj instanceof WP_Post ) { + $ls_current_phase_slug = $ls_services_queried_obj->post_name; +} + +$ls_phase_labels = array( + 'discover' => __( 'Discover', 'ls-theme' ), + 'create' => __( 'Create', 'ls-theme' ), + 'build' => __( 'Build', 'ls-theme' ), + 'launch' => __( 'Launch', 'ls-theme' ), + 'grow' => __( 'Grow', 'ls-theme' ), + 'evolve' => __( 'Evolve', 'ls-theme' ), +); + +$ls_active_phase_label = isset( $ls_phase_labels[ $ls_current_phase_slug ] ) ? $ls_phase_labels[ $ls_current_phase_slug ] : __( 'Discover', 'ls-theme' ); +$ls_active_services = isset( $ls_phase_services_by_phase[ $ls_current_phase_slug ] ) ? $ls_phase_services_by_phase[ $ls_current_phase_slug ] : $ls_phase_services_by_phase['discover']; +$ls_service_rows = array_chunk( $ls_active_services, 4 ); +?> + +
+ + +
+ +

+ + + +

+ + + +

+ +
+ + + $ls_row ) : ?> + +
+ + +
+ +
+ +
+ +
+ + + +
+ +

+ + + +

+ +
+ + + + + +
+ +
+ + +
+ + +
+ diff --git a/patterns/sections/phase-support-focus.php b/patterns/sections/phase-support-focus.php new file mode 100644 index 0000000..0ed025d --- /dev/null +++ b/patterns/sections/phase-support-focus.php @@ -0,0 +1,107 @@ + + +
+ + +
+ +

+ + + +

+ +
+ + + +
+ + +
+ +

+ + + +

+ + + +

+ +
+ + + +
+ +
+ +

+ + + +
+ + +
+ +
+ +
+ + + +

+ +
+ + +
+ +
+ +
+ +
+ + + +
+ +
+ +
+ +
+ From 5766151c81ccf75f4f7dc36d9cc08c3d88dae35e Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Tue, 22 Sep 2026 18:38:41 +0200 Subject: [PATCH 02/19] Fix width tokens, spacing and line-height in phase section patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Width fixes - phase-common-services.php, phase-services-in-phase.php, phase-support-focus.php, phase-deliverables-and-role.php: top-level content wrappers were missing "align":"wide", so they silently inherited the theme's default 800px contentSize instead of the wideSize token, even where an explicit wider contentSize was set - phase-common-services.php: pill list now capped at 1050px, footnote panel at 900px (previously the footnote's border box was rendering wide while only its inner text was constrained — moved the border/padding to a nested inner block so the box itself is capped) Spacing consistency - phase-common-services.php: standardized all four vertical gaps (heading, intro, eyebrow, pills, footnote) to the same var:preset|spacing|30 token, previously a mix of 20/40/30 Typography - phase-common-services.php: tightened line-height on the H2 heading and "Most common services" eyebrow to var:custom|line-height|heading-snug (existing token, same one h1 already uses sitewide) — these short single-line elements were inheriting the sitewide heading-default/ paragraph line-height ratios meant for multi-line text, which baked extra invisible leading into the gaps around them and made otherwise identical margin values look uneven --- patterns/sections/phase-common-services.php | 58 +++++++++++-------- .../sections/phase-deliverables-and-role.php | 8 +-- patterns/sections/phase-services-in-phase.php | 8 +-- patterns/sections/phase-support-focus.php | 8 +-- 4 files changed, 45 insertions(+), 37 deletions(-) diff --git a/patterns/sections/phase-common-services.php b/patterns/sections/phase-common-services.php index 4ce5306..e90c4f3 100644 --- a/patterns/sections/phase-common-services.php +++ b/patterns/sections/phase-common-services.php @@ -35,51 +35,59 @@
- -
- -

+ +
+ +

- -
+ +

- -
- -

+ +
+ +

- -
- - -
- + +
+ +
+ + +
+ - -

- + +

+ +
+ +
-
- -
- -

- + +
+ +
+ +

+ +
+
diff --git a/patterns/sections/phase-deliverables-and-role.php b/patterns/sections/phase-deliverables-and-role.php index 7135de7..9529930 100644 --- a/patterns/sections/phase-deliverables-and-role.php +++ b/patterns/sections/phase-deliverables-and-role.php @@ -61,8 +61,8 @@
- -
+ +

@@ -73,8 +73,8 @@
- -
+ +
diff --git a/patterns/sections/phase-services-in-phase.php b/patterns/sections/phase-services-in-phase.php index ccbd6b1..91fdde8 100644 --- a/patterns/sections/phase-services-in-phase.php +++ b/patterns/sections/phase-services-in-phase.php @@ -143,8 +143,8 @@
- -
+ +

@@ -160,8 +160,8 @@ $ls_row ) : ?> - -
+ +
diff --git a/patterns/sections/phase-support-focus.php b/patterns/sections/phase-support-focus.php index 0ed025d..74532a4 100644 --- a/patterns/sections/phase-support-focus.php +++ b/patterns/sections/phase-support-focus.php @@ -31,8 +31,8 @@
- -
+ +

@@ -43,8 +43,8 @@
- -
+ +
From 6fe372c2b0e8d598dd6e2f20e4976464308f935f Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Tue, 22 Sep 2026 19:10:11 +0200 Subject: [PATCH 03/19] Make Journey Phases nav active-state survive pattern flattening Bug fix - Active-phase highlighting relied entirely on PHP logic baked into the pattern's stored content at render time - WordPress routinely flattens a live pattern reference into a frozen static copy (e.g. simply opening a page in the editor), permanently freezing whatever active state existed at that moment - Re-attaching the pattern only reset the clock until the next flatten, not a real fix Fix - Add inc/phase-page-body-class.php: stamps a page-slug-{phase} class onto on the six phase pages, recomputed by WordPress on every single request regardless of how the page's blocks are stored - phase-journey-nav.php: always render the dot marker instead of only when PHP thinks it's active, so CSS alone can decide whether to show it - phase-journey-nav.scss: drive active/inactive link colour and dot visibility from the body class instead of the old is-active class, with higher selector specificity so it wins even over a stale/incorrect flattened copy - Rebuild assets/css/phase-journey-nav.css Consistency - phase-delivery-numbers.php: switch the stat number colour from the generic brand token to the Discover phase colour token - phase-introduction.php: switch the "Introduction" eyebrow colour from the generic brand token to the Discover phase colour token - Both now match the same base-pattern convention already used in phase-hero.php and phase-journey-nav.php ahead of the future per-phase Pattern Overrides work Verification - Deliberately flattened the nav with no real page context (confirmed zero is-active classes anywhere in the resulting markup) and confirmed the correct phase still highlights correctly on the front end --- assets/css/phase-journey-nav.css | 2 +- functions.php | 3 ++ inc/phase-page-body-class.php | 37 ++++++++++++++ patterns/sections/phase-delivery-numbers.php | 4 +- patterns/sections/phase-introduction.php | 4 +- patterns/sections/phase-journey-nav.php | 25 +++++----- src/scss/structural/phase-journey-nav.scss | 51 +++++++++++++------- 7 files changed, 91 insertions(+), 35 deletions(-) create mode 100644 inc/phase-page-body-class.php diff --git a/assets/css/phase-journey-nav.css b/assets/css/phase-journey-nav.css index af298c3..2bdc7d4 100644 --- a/assets/css/phase-journey-nav.css +++ b/assets/css/phase-journey-nav.css @@ -1 +1 @@ -.ls-phase-journey-nav__step--discover{--ls-phase-item-accent: var(--wp--custom--color--phase--discover-on-dark, var(--wp--custom--color--phase--discover))}.ls-phase-journey-nav__step--create{--ls-phase-item-accent: var(--wp--custom--color--phase--create-on-dark, var(--wp--custom--color--phase--create))}.ls-phase-journey-nav__step--build{--ls-phase-item-accent: var(--wp--custom--color--phase--build-on-dark, var(--wp--custom--color--phase--build))}.ls-phase-journey-nav__step--launch{--ls-phase-item-accent: var(--wp--custom--color--phase--launch-on-dark, var(--wp--custom--color--phase--launch))}.ls-phase-journey-nav__step--grow{--ls-phase-item-accent: var(--wp--custom--color--phase--grow-on-dark, var(--wp--custom--color--phase--grow))}.ls-phase-journey-nav__step--evolve{--ls-phase-item-accent: var(--wp--custom--color--phase--evolve-on-dark, var(--wp--custom--color--phase--evolve))}.ls-phase-journey-nav__link a{transition:color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-phase-journey-nav__step:not(.is-active) .ls-phase-journey-nav__link a{color:color-mix(in srgb, var(--wp--custom--color--text--on-dark-muted) 55%, transparent)}.ls-phase-journey-nav__step:not(.is-active) .ls-phase-journey-nav__link a:hover,.ls-phase-journey-nav__step:not(.is-active) .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}.ls-phase-journey-nav__step.is-active .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}.ls-phase-journey-nav__step.is-active .ls-phase-journey-nav__link a:hover,.ls-phase-journey-nav__step.is-active .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}.ls-phase-journey-nav__link a:focus-visible{outline:2px solid var(--wp--custom--color--text--on-dark);outline-offset:3px;border-radius:var(--wp--preset--border-radius--100)}@media(prefers-reduced-motion: reduce){.ls-phase-journey-nav__link a{transition:none}}@media(max-width: 781px){.ls-phase-journey-nav__items{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}.ls-phase-journey-nav__items::-webkit-scrollbar{display:none}.ls-phase-journey-nav__step{flex-shrink:0}} +.ls-phase-journey-nav__step--discover{--ls-phase-item-accent: var(--wp--custom--color--phase--discover-on-dark, var(--wp--custom--color--phase--discover))}.ls-phase-journey-nav__step--create{--ls-phase-item-accent: var(--wp--custom--color--phase--create-on-dark, var(--wp--custom--color--phase--create))}.ls-phase-journey-nav__step--build{--ls-phase-item-accent: var(--wp--custom--color--phase--build-on-dark, var(--wp--custom--color--phase--build))}.ls-phase-journey-nav__step--launch{--ls-phase-item-accent: var(--wp--custom--color--phase--launch-on-dark, var(--wp--custom--color--phase--launch))}.ls-phase-journey-nav__step--grow{--ls-phase-item-accent: var(--wp--custom--color--phase--grow-on-dark, var(--wp--custom--color--phase--grow))}.ls-phase-journey-nav__step--evolve{--ls-phase-item-accent: var(--wp--custom--color--phase--evolve-on-dark, var(--wp--custom--color--phase--evolve))}.ls-phase-journey-nav__link a{transition:color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-phase-journey-nav__link a{color:color-mix(in srgb, var(--wp--custom--color--text--on-dark-muted) 55%, transparent)}.ls-phase-journey-nav__link a:hover,.ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}.ls-phase-journey-nav__dot{display:none}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a:hover,body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a:hover,body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a:hover,body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a:hover,body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a:hover,body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a:hover,body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__dot{display:inline-flex}.ls-phase-journey-nav__link a:focus-visible{outline:2px solid var(--wp--custom--color--text--on-dark);outline-offset:3px;border-radius:var(--wp--preset--border-radius--100)}@media(prefers-reduced-motion: reduce){.ls-phase-journey-nav__link a{transition:none}}@media(max-width: 781px){.ls-phase-journey-nav__items{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}.ls-phase-journey-nav__items::-webkit-scrollbar{display:none}.ls-phase-journey-nav__step{flex-shrink:0}} diff --git a/functions.php b/functions.php index 376b063..60c56b4 100644 --- a/functions.php +++ b/functions.php @@ -42,6 +42,9 @@ /** Loads bare phase-slug (e.g. /discover/) redirects to their real nested URL */ require get_template_directory() . '/inc/phase-page-redirects.php'; +/** Loads the phase-page body class used to drive the Journey Phases nav's active state */ +require get_template_directory() . '/inc/phase-page-body-class.php'; + /** * Sets up theme supports. */ diff --git a/inc/phase-page-body-class.php b/inc/phase-page-body-class.php new file mode 100644 index 0000000..89baae2 --- /dev/null +++ b/inc/phase-page-body-class.php @@ -0,0 +1,37 @@ +
- -

+ +

diff --git a/patterns/sections/phase-introduction.php b/patterns/sections/phase-introduction.php index 338c3c0..3957084 100644 --- a/patterns/sections/phase-introduction.php +++ b/patterns/sections/phase-introduction.php @@ -27,8 +27,8 @@
- -

+ +

diff --git a/patterns/sections/phase-journey-nav.php b/patterns/sections/phase-journey-nav.php index 9d5bf85..2249940 100644 --- a/patterns/sections/phase-journey-nav.php +++ b/patterns/sections/phase-journey-nav.php @@ -23,16 +23,19 @@ * passed down as a CSS custom property instead of a literal `color`, leaving the actual `color` * property free for that external stylesheet to own per state. * - * IMPORTANT — insert this as a live reference, not a flattened copy: the "auto-detect active - * phase" logic below only re-evaluates on every real front-end request if this pattern stays a - * live `` reference block. WordPress's - * normal "click to insert from the pattern library" flow instead flattens a file-registered - * pattern into a static copy of whatever get_queried_object() resolved to at insertion time (which - * is frequently no page at all, e.g. inside the REST endpoint that serves the inserter panel) — - * baking in a permanently-wrong or permanently-blank active state that can never update again, - * confirmed empirically while building this page. Use the pattern/reference block, or the Insert - * Pattern flow if a future WP version changes this default, but verify the rendered HTML actually - * contains "is-active" before treating a phase page as done. + * Active-state detection does NOT depend on this pattern staying a live reference. An earlier + * version computed the active step purely in PHP via get_queried_object(), which only re-evaluated + * on real front-end requests while the pattern stayed a live `wp:pattern` reference — WordPress + * routinely flattens a pattern into a frozen static copy the moment a page is opened in the + * editor, permanently baking in whatever active state existed at that moment (often wrong or + * blank), and re-attaching the live reference doesn't prevent it from being reflattened again on + * the next edit. The PHP below still computes `$ls_is_active` and adds an `is-active` class as a + * best-effort default, but the actual visual active/inactive treatment (link colour, dot + * visibility) is driven entirely by CSS keyed off a `page-slug-{slug}` body class — see + * ls_theme_add_phase_page_body_class() in inc/phase-page-body-class.php and the body-class rules + * in phase-journey-nav.scss. That body class is recomputed by WordPress core on every single + * request regardless of how this pattern's blocks are stored, so the correct step stays + * highlighted even if this instance is (or becomes) a flattened copy. * Keywords: phase, journey, lifecycle, navigation, section * Viewport Width: 1280 * Inserter: true @@ -113,9 +116,7 @@ - -
diff --git a/src/scss/structural/phase-journey-nav.scss b/src/scss/structural/phase-journey-nav.scss index 118e695..c45bd90 100644 --- a/src/scss/structural/phase-journey-nav.scss +++ b/src/scss/structural/phase-journey-nav.scss @@ -52,35 +52,50 @@ } /* - * Inactive (rest): noticeably darker than the hover state below, so hover actually reads as a - * state change instead of every item already looking hovered — but still built from the same - * on-dark-muted token (dimmed via color-mix against the bar's own dark background) rather than a - * separate hardcoded grey, so it stays part of the same token family as everything else here. + * Inactive (rest, default for every item): noticeably darker than the hover state below, so hover + * actually reads as a state change instead of every item already looking hovered — but still built + * from the same on-dark-muted token (dimmed via color-mix against the bar's own dark background) + * rather than a separate hardcoded grey, so it stays part of the same token family as everything + * else here. The dot is hidden by default; it's always rendered in the markup (never + * PHP-conditional) so the body-class rules below can show/hide it with pure CSS. */ -.ls-phase-journey-nav__step:not(.is-active) .ls-phase-journey-nav__link a { +.ls-phase-journey-nav__link a { color: color-mix(in srgb, var(--wp--custom--color--text--on-dark-muted) 55%, transparent); } -/* Inactive hover/focus: brighter and clearly interactive — never the active phase colour, never the dot. */ -.ls-phase-journey-nav__step:not(.is-active) .ls-phase-journey-nav__link a:hover, -.ls-phase-journey-nav__step:not(.is-active) .ls-phase-journey-nav__link a:focus-visible { +.ls-phase-journey-nav__link a:hover, +.ls-phase-journey-nav__link a:focus-visible { color: var(--wp--custom--color--text--on-dark); } -/* Active (rest): the current phase's own accent colour, passed down per item as a custom property. */ -.ls-phase-journey-nav__step.is-active .ls-phase-journey-nav__link a { - color: var(--ls-phase-item-accent); +.ls-phase-journey-nav__dot { + display: none; } /* - * Active hover/focus: text lightens to the same "on-dark" white used for inactive hover, but the - * dot (a separate icon element, styled independently) is untouched by this rule and keeps reading - * in the phase colour — preserving "this is still the current page" rather than looking like a - * plain hovered-inactive item. + * Active override, keyed off the `page-slug-{phase}` body class from + * inc/phase-page-body-class.php rather than the `is-active` class PHP adds to this step: that body + * class is recomputed by WordPress on every request regardless of whether this pattern is a live + * reference or a flattened copy, so the correct step stays highlighted even if this instance's own + * `is-active` class is stale or was never set. Written per phase (not a generic + * `.is-active`-style rule) because the selector needs both the current page's body class AND the + * matching step's own modifier class to agree on which one item lights up. */ -.ls-phase-journey-nav__step.is-active .ls-phase-journey-nav__link a:hover, -.ls-phase-journey-nav__step.is-active .ls-phase-journey-nav__link a:focus-visible { - color: var(--wp--custom--color--text--on-dark); +@each $ls-phase in discover, create, build, launch, grow, evolve { + body.page-slug-#{$ls-phase} .ls-phase-journey-nav__step--#{$ls-phase} { + .ls-phase-journey-nav__link a { + color: var(--ls-phase-item-accent); + } + + .ls-phase-journey-nav__link a:hover, + .ls-phase-journey-nav__link a:focus-visible { + color: var(--wp--custom--color--text--on-dark); + } + + .ls-phase-journey-nav__dot { + display: inline-flex; + } + } } /* From 804318c97838a3215a59fdd7ac5e24c481174311 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Tue, 22 Sep 2026 19:34:55 +0200 Subject: [PATCH 04/19] Fix Phase 1 colour using wrong token in adaptive patterns (light mode contrast bug) Bug - Six adaptive section patterns (not permanently-dark like the hero/nav) used phase.discover-on-dark as their accent colour, with a same-value fallback to phase.discover that never actually triggered - discover-on-dark is a fixed token meant only for permanently-dark surfaces: it resolves to the same bright phase-one green (#5DE81C) in both the site's light and dark style variations - In light mode this rendered bright neon green text/icons/borders directly on a light background instead of the correct, accessible phase-one-strong (#32800D) - the adaptive phase.discover token was already the right one to use, no fallback needed Fix - phase-delivery-numbers.php, phase-introduction.php, phase-common-services.php, phase-services-in-phase.php, phase-deliverables-and-role.php, phase-support-focus.php: replace the discover-on-dark fallback with the plain, adaptive phase.discover token - Update phase-common-services.php's docblock to explain the token choice Unaffected - phase-hero.php and phase-journey-nav.php are permanently-dark components and correctly keep using phase.discover-on-dark - no change needed there --- patterns/sections/phase-common-services.php | 9 ++++++--- patterns/sections/phase-deliverables-and-role.php | 2 +- patterns/sections/phase-delivery-numbers.php | 4 ++-- patterns/sections/phase-introduction.php | 4 ++-- patterns/sections/phase-services-in-phase.php | 4 ++-- patterns/sections/phase-support-focus.php | 2 +- 6 files changed, 14 insertions(+), 11 deletions(-) diff --git a/patterns/sections/phase-common-services.php b/patterns/sections/phase-common-services.php index e90c4f3..9c6692b 100644 --- a/patterns/sections/phase-common-services.php +++ b/patterns/sections/phase-common-services.php @@ -10,8 +10,11 @@ * before this is reused on the other five pages. Centered eyebrow/heading/intro, a wrapped pill * list (each pill a phase-coloured dot + label, reusing the same bordered-chip treatment as the * site's other tag pills) and a bordered footnote panel. Adapts between the site's light and dark - * style variations via text tokens; the phase accent falls back from a phase's "-on-dark" token to - * its normal token, matching phase-journey-nav.php's convention. + * style variations via text tokens; the phase accent uses the plain phase.{slug} token (not the + * "-on-dark" variant), since that token pair already resolves to the correct light/dark value for + * an adaptive surface — the "-on-dark" variant is reserved for permanently-dark components like + * phase-hero.php and phase-journey-nav.php, whose background never changes with the site's style + * variation. * Keywords: phase, discover, create, build, launch, grow, evolve, services, pills, section * Viewport Width: 1280 * Inserter: true @@ -30,7 +33,7 @@ __( 'Early AI-readiness assessment', 'ls-theme' ), ); -$ls_phase_accent = 'var(--wp--custom--color--phase--discover-on-dark, var(--wp--custom--color--phase--discover))'; +$ls_phase_accent = 'var(--wp--custom--color--phase--discover)'; ?>
diff --git a/patterns/sections/phase-deliverables-and-role.php b/patterns/sections/phase-deliverables-and-role.php index 9529930..4550939 100644 --- a/patterns/sections/phase-deliverables-and-role.php +++ b/patterns/sections/phase-deliverables-and-role.php @@ -18,7 +18,7 @@ * @package ls-theme */ -$ls_phase_accent = 'var(--wp--custom--color--phase--discover-on-dark, var(--wp--custom--color--phase--discover))'; +$ls_phase_accent = 'var(--wp--custom--color--phase--discover)'; $ls_deliverables = array( __( 'A project summary with priorities, risks, and opportunities', 'ls-theme' ), diff --git a/patterns/sections/phase-delivery-numbers.php b/patterns/sections/phase-delivery-numbers.php index e2aeb6f..7e2d547 100644 --- a/patterns/sections/phase-delivery-numbers.php +++ b/patterns/sections/phase-delivery-numbers.php @@ -67,8 +67,8 @@
- -

+ +

diff --git a/patterns/sections/phase-introduction.php b/patterns/sections/phase-introduction.php index 3957084..55b3722 100644 --- a/patterns/sections/phase-introduction.php +++ b/patterns/sections/phase-introduction.php @@ -27,8 +27,8 @@
- -

+ +

diff --git a/patterns/sections/phase-services-in-phase.php b/patterns/sections/phase-services-in-phase.php index 91fdde8..f650551 100644 --- a/patterns/sections/phase-services-in-phase.php +++ b/patterns/sections/phase-services-in-phase.php @@ -145,8 +145,8 @@
- -

+ +

diff --git a/patterns/sections/phase-support-focus.php b/patterns/sections/phase-support-focus.php index 74532a4..6bd1ef1 100644 --- a/patterns/sections/phase-support-focus.php +++ b/patterns/sections/phase-support-focus.php @@ -26,7 +26,7 @@ __( 'Documenting risks, constraints and recommended next steps', 'ls-theme' ), ); -$ls_phase_accent = 'var(--wp--custom--color--phase--discover-on-dark, var(--wp--custom--color--phase--discover))'; +$ls_phase_accent = 'var(--wp--custom--color--phase--discover)'; ?>
From 90404d7432900e50b0f7c862f6138e4ca15a1925 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 10:36:54 +0200 Subject: [PATCH 05/19] Refine Phase Services In Phase section to match prototype Layout - Replace core/columns per-service grid with a left-aligned flex row (a lone service, e.g. Discover's single card, was stretching to the full row width since a single core/column always fills 100%) - Give each card a fixed width via layout.selfStretch/flexSize (the native WP mechanism already used on the Journey Phases nav) instead of an unsupported custom width property - Constrain the intro heading/paragraph group and the card description text to sensible max-widths so both wrap naturally Heading - Switch the section heading from H2/fontSize 700 to H3/fontSize 500, local to this pattern only - incidentally fixes a pre-existing H2 to H4 heading-level skip on this section, not just a visual size fix - Centre the intro paragraph correctly under the heading Card and icon - Section background switched to surface.card, matching the same lifted-panel convention already used on Phase Delivery Numbers - Replace the shared, non-phase-coloured ls-icon-well-brand icon well with a smaller, phase-accented circular well local to this pattern (border/radius/ colour-mix, same technique as phase-hero.php's own badge) - Tighten internal card spacing (icon, title, description, CTA) - Make the icon well and CTA link colour phase-aware using this pattern's existing per-page phase detection, rather than hardcoded to Discover Hover states - Add src/scss/structural/phase-services-in-phase.scss: overrides two shared, non-phase-aware hover custom properties (the card's hover border-colour and the CTA link's hover colour), both of which were falling back to a generic sitewide cyan-blue link-accent token instead of the phase colour - Card hover border uses a muted colour-mix rather than a full-strength swap, toning down an overly strong hover effect - Scoped via the existing page-slug-{phase} body class + a new ls-phase-services-in-phase section class, so no other consumer of the shared card/link styles is affected, and this is reusable as-is once the pattern is attached to the other five phase pages - Wire the new file into functions.php, inc/animations.php and package.json's build scripts; rebuild assets/css/phase-services-in-phase.css Verification - Confirmed zero invalid blocks in a live editor session (found and fixed one missing has-border-color/has-background class along the way) - Confirmed hover colours with a real mouse hover, not just computed values --- assets/css/phase-services-in-phase.css | 1 + functions.php | 1 + inc/animations.php | 11 +++ package.json | 6 +- patterns/sections/phase-services-in-phase.php | 78 +++++++++++-------- .../structural/phase-services-in-phase.scss | 41 ++++++++++ 6 files changed, 104 insertions(+), 34 deletions(-) create mode 100644 assets/css/phase-services-in-phase.css create mode 100644 src/scss/structural/phase-services-in-phase.scss diff --git a/assets/css/phase-services-in-phase.css b/assets/css/phase-services-in-phase.css new file mode 100644 index 0000000..94da63a --- /dev/null +++ b/assets/css/phase-services-in-phase.css @@ -0,0 +1 @@ +body.page-slug-discover .ls-phase-services-in-phase .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--discover);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--discover)}body.page-slug-discover .ls-phase-services-in-phase .is-style-card-service-tile{--ls-card-service-tile-border-active: color-mix( in srgb, var(--wp--custom--color--phase--discover) 60%, var(--wp--custom--color--border--card) )}body.page-slug-create .ls-phase-services-in-phase .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--create);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--create)}body.page-slug-create .ls-phase-services-in-phase .is-style-card-service-tile{--ls-card-service-tile-border-active: color-mix( in srgb, var(--wp--custom--color--phase--create) 60%, var(--wp--custom--color--border--card) )}body.page-slug-build .ls-phase-services-in-phase .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--build);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--build)}body.page-slug-build .ls-phase-services-in-phase .is-style-card-service-tile{--ls-card-service-tile-border-active: color-mix( in srgb, var(--wp--custom--color--phase--build) 60%, var(--wp--custom--color--border--card) )}body.page-slug-launch .ls-phase-services-in-phase .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--launch);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--launch)}body.page-slug-launch .ls-phase-services-in-phase .is-style-card-service-tile{--ls-card-service-tile-border-active: color-mix( in srgb, var(--wp--custom--color--phase--launch) 60%, var(--wp--custom--color--border--card) )}body.page-slug-grow .ls-phase-services-in-phase .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--grow);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--grow)}body.page-slug-grow .ls-phase-services-in-phase .is-style-card-service-tile{--ls-card-service-tile-border-active: color-mix( in srgb, var(--wp--custom--color--phase--grow) 60%, var(--wp--custom--color--border--card) )}body.page-slug-evolve .ls-phase-services-in-phase .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--evolve);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--evolve)}body.page-slug-evolve .ls-phase-services-in-phase .is-style-card-service-tile{--ls-card-service-tile-border-active: color-mix( in srgb, var(--wp--custom--color--phase--evolve) 60%, var(--wp--custom--color--border--card) )} diff --git a/functions.php b/functions.php index 60c56b4..de9a8c1 100644 --- a/functions.php +++ b/functions.php @@ -93,6 +93,7 @@ function ls_theme_setup() { add_editor_style( 'assets/css/button-secondary.css' ); add_editor_style( 'assets/css/button-phase.css' ); add_editor_style( 'assets/css/phase-journey-nav.css' ); + add_editor_style( 'assets/css/phase-services-in-phase.css' ); add_editor_style( 'assets/css/featured-work.css' ); add_editor_style( 'assets/css/where-to-fit.css' ); add_editor_style( 'assets/css/homepage-cta.css' ); diff --git a/inc/animations.php b/inc/animations.php index a6dbb54..e83f783 100644 --- a/inc/animations.php +++ b/inc/animations.php @@ -437,6 +437,17 @@ function ls_theme_get_effect_styles( $context = 'front' ) { return is_page( 'discover' ); }, ), + 'phase-services-in-phase' => array( + 'handle' => 'ls-theme-phase-services-in-phase', + 'path' => 'assets/css/phase-services-in-phase.css', + 'contexts' => array( 'front', 'editor' ), + // Shared across all six phase pages from the start (unlike phase-journey-nav/ + // button-phase above, which started Discover-only), so scoped directly to all six + // known slugs rather than needing a render_block fallback. + 'condition' => static function () { + return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); + }, + ), 'featured-work' => array( 'handle' => 'ls-theme-featured-work', 'path' => 'assets/css/featured-work.css', diff --git a/package.json b/package.json index a48b533..fad00cc 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "description": "LightSpeed Theme is a custom WordPress block theme built by LightSpeed for fast, accessible, maintainable websites using the WordPress Site Editor and block editor.", "type": "module", "scripts": { - "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css", - "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css", + "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css", + "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css", "schema:validate": "node theme-utils.mjs validate-schema", "theme:validate": "node theme-utils.mjs validate-theme", "patterns:escape": "node theme-utils.mjs escape-patterns", "security:scan": "node theme-utils.mjs security-scan", "lint": "npm run lint:json", "lint:json": "node --input-type=module --eval \"import { readFileSync } from 'fs'; import { glob } from 'glob'; const files = await glob(['theme.json', 'styles/**/*.json']); let ok = true; for (const f of files) { try { JSON.parse(readFileSync(f, 'utf8')); } catch (e) { console.error('Invalid JSON:', f, e.message); ok = false; } } if (ok) console.log('All JSON files are valid.'); else process.exit(1);\"", - "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css" + "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css" }, "devDependencies": { "@axe-core/playwright": "^4.13.0", diff --git a/patterns/sections/phase-services-in-phase.php b/patterns/sections/phase-services-in-phase.php index f650551..b5231cd 100644 --- a/patterns/sections/phase-services-in-phase.php +++ b/patterns/sections/phase-services-in-phase.php @@ -9,8 +9,22 @@ * current page by slug (same technique as phase-journey-nav.php) and renders only that phase's * service cards from the single $ls_phase_services_by_phase map below, whose labels/descriptions/ * URLs/icons are kept in sync with the master list in services-service-tiles.php. Each card reuses - * that pattern's existing icon-well (ls-icon-well-brand) and arrow-link (is-style-link-arrow-accent) - * treatments — no new card style. Cards are chunked into rows of up to four via core/columns. + * the shared is-style-card-service-tile shell (background/border/radius/padding — not overridden + * here, since that style is also used by ~16 other patterns sitewide) and the shared + * is-style-link-arrow-accent CTA treatment, but does NOT reuse the shared ls-icon-well-brand icon + * well: that class is a fixed, non-phase colour used elsewhere, so this pattern renders its own + * smaller, phase-accented circular icon well locally instead (same border/radius/colour-mix + * technique already used by phase-hero.php's own badge). Cards render in a left-aligned flex row + * (layout.selfStretch:"fixed" per card, the native WP mechanism also used by + * phase-journey-nav.php's label/items split) rather than core/columns, since a single service + * (e.g. Discover, which has exactly one) would otherwise stretch a lone core/column to the row's + * full width. Main heading uses H3 (not H2) to close a pre-existing H2→H4 heading-level skip on + * this section (H2 eyebrow-adjacent heading straight to H4 card titles) rather than only to + * shrink it visually — this section's own explicit fontSize/level attributes are local to this + * file only; no shared heading style exists sitewide to avoid touching. The CTA link's phase + * accent is set via src/scss/structural/phase-services-in-phase.scss, scoped by the + * page-slug-{phase} body class (see inc/phase-page-body-class.php) rather than an inline custom + * property, for the same block-validation reason documented in phase-journey-nav.scss. * * IMPORTANT — insert this as a live reference, not a flattened copy: like phase-journey-nav.php, * the phase-detection below only re-evaluates per request if this stays a live @@ -139,18 +153,20 @@ $ls_active_phase_label = isset( $ls_phase_labels[ $ls_current_phase_slug ] ) ? $ls_phase_labels[ $ls_current_phase_slug ] : __( 'Discover', 'ls-theme' ); $ls_active_services = isset( $ls_phase_services_by_phase[ $ls_current_phase_slug ] ) ? $ls_phase_services_by_phase[ $ls_current_phase_slug ] : $ls_phase_services_by_phase['discover']; $ls_service_rows = array_chunk( $ls_active_services, 4 ); +$ls_active_phase_slug = isset( $ls_phase_services_by_phase[ $ls_current_phase_slug ] ) ? $ls_current_phase_slug : 'discover'; +$ls_phase_accent = 'var(--wp--custom--color--phase--' . $ls_active_phase_slug . ')'; ?> - -
+ +
- +

- -

+ +

@@ -160,41 +176,41 @@ $ls_row ) : ?> - -
+ +
- -
- -
- -
- -
- + +
+ +
+ +
+ - -
- -

- + +
+ +

+ + +

- - - - -
+
-
- + + + + +
+
- +
diff --git a/src/scss/structural/phase-services-in-phase.scss b/src/scss/structural/phase-services-in-phase.scss new file mode 100644 index 0000000..f96efe3 --- /dev/null +++ b/src/scss/structural/phase-services-in-phase.scss @@ -0,0 +1,41 @@ +/********** Section - Phase Services In Phase (per-phase CTA + hover accents) **********/ + +/* + * The shared is-style-link-arrow-accent style (styles/blocks/paragraphs/link-arrow-accent.json + + * src/scss/structural/links.scss) reads its rest and hover colours from + * --ls-link-arrow-colour/--ls-link-arrow-hover-colour custom properties, both falling back to + * sitewide generic link-accent tokens (a cyan-blue unrelated to any phase). Those properties have + * no equivalent in core/paragraph's declared style attributes, so setting them inline on the block + * itself would always fail block validation the moment the block is re-parsed (the same class of + * bug already fixed on phase-journey-nav.php's step accent) — JSON limitation: a raw custom + * property can't be expressed in a style JSON attribute, see AGENTS.md Theme-First Approach. + * + * The shared is-style-card-service-tile style (styles/sections/cards/card-service-tile.json + + * src/scss/structural/services-service-tiles.scss) swaps its border to + * --ls-card-service-tile-border-active on hover/focus-within, same generic cyan-blue default — + * also not editable here, since that style is reused by ~16 other patterns sitewide. Overridden + * to a muted phase-accent tone (colour-mix'd down, not full-strength) rather than a plain swap, + * per the request to make the hover feel less dominant. + * + * All three scoped per phase via the page-slug-{phase} body class from + * inc/phase-page-body-class.php (same mechanism as phase-journey-nav.scss) rather than + * get_queried_object() in the pattern's own PHP, so the correct phase accent survives this + * section being flattened, and to the .ls-phase-services-in-phase section class so no other + * consumer of these shared styles is affected. + */ +@each $ls-phase in discover, create, build, launch, grow, evolve { + body.page-slug-#{$ls-phase} .ls-phase-services-in-phase { + .is-style-link-arrow-accent { + --ls-link-arrow-colour: var(--wp--custom--color--phase--#{$ls-phase}); + --ls-link-arrow-hover-colour: var(--wp--custom--color--phase--#{$ls-phase}); + } + + .is-style-card-service-tile { + --ls-card-service-tile-border-active: color-mix( + in srgb, + var(--wp--custom--color--phase--#{$ls-phase}) 60%, + var(--wp--custom--color--border--card) + ); + } + } +} From 786a3f4caadeb7f82d290675ccc325b878981e0e Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 10:56:09 +0200 Subject: [PATCH 06/19] Refine Phase Support Focus section layout, typography and bullets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layout - Widen eyebrow/heading wrapper to theme's wide alignment (no custom px) so "AI Readiness Assessment" stays on one line - Add top margin above eyebrow/heading group for breathing room from previous section - Use theme wide alignment for the two-column row instead of full alignwide - Widen column gutter between text and focus-area card (spacing|60 → spacing|70) Typography - Reduce left-column body copy and focus-card heading/list text (fontSize 300 → 200) - Tighten line-height (1.5) and paragraph spacing (spacing|20 → spacing|10) - Reduce CTA top margin (spacing|40 → spacing|30) Bullets - Replace filled/tinted lightspeed/dot icon bullet with a hollow 1px-outlined circle in the Discover accent color - Center each bullet row vertically so the circle aligns with its list text Context - Scoped entirely to patterns/sections/phase-support-focus.php per approved refinement pass - Discover accent color, structure, CTA arrow, and card styling left unchanged - CHANGELOG.md updated under [Unreleased] (LS-4179) --- CHANGELOG.md | 14 ++++ patterns/sections/phase-support-focus.php | 86 ++++++++++++----------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0052660..a78369e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [Unreleased] — Refine Phase Support Focus section layout and typography (LS-4179) + +### Changed + +- `patterns/sections/phase-support-focus.php`: widened the eyebrow/heading wrapper from a fixed `620px` content width to the theme's existing wide alignment (`align:"wide"`, no custom `contentSize`) so "AI Readiness Assessment" stays on one line at desktop without changing the global H2 style, heading level, or inventing a new width token; added top margin above the eyebrow/heading group for more breathing room from the previous section. +- `patterns/sections/phase-support-focus.php`: widened the column gutter between the body copy and focus-area card (`spacing|60` → `spacing|70`) so the section reads more balanced, matching the reference design. +- `patterns/sections/phase-support-focus.php`: changed each focus-area row's vertical alignment from `top` to `center` so the hollow-circle bullet centres against its single-line list text instead of sitting high above it. +- `patterns/sections/phase-support-focus.php`: reduced left-column body copy and focus-card heading/list typography from `fontSize:300` to `fontSize:200`, tightened line-height and paragraph spacing, and reduced the CTA's top margin — all using existing typography/spacing tokens. +- `patterns/sections/phase-support-focus.php`: replaced the filled/tinted `lightspeed/dot` icon bullet with a simple hollow outlined circle (1px border, no fill, no icon) in the approved Discover accent colour, matching the reference's bullet style. + +([LS-4179](https://github.com/lightspeedwp/ls-theme/tree/feature/ls-4179-build-discover-page)) + +--- + ## [Unreleased] — Fix editor validation errors and Icon block attributes on Services patterns (LS-4207) ### Fixed diff --git a/patterns/sections/phase-support-focus.php b/patterns/sections/phase-support-focus.php index 6bd1ef1..79a1584 100644 --- a/patterns/sections/phase-support-focus.php +++ b/patterns/sections/phase-support-focus.php @@ -31,8 +31,8 @@
- -
+ +

@@ -43,61 +43,63 @@
- -
- - -
- -

- + +
+ +
- -

- + +
+ +

+ - -

- -
- + +

+ - -
- -
- -

+ +

+
+ + + +
+ +
+ +

+ + + +
+ + +
+ +
+ - -
- - -
- -
- + +

+
- - -

- +
-
- +
- +
- + - -
+ +
From dca61a047424d4969a5a65da4f417ef66afec51e Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 11:16:07 +0200 Subject: [PATCH 07/19] Refine Phase Deliverables And Role to match prototype Layout - Widen heading wrapper so it stays on one line (size/level unchanged) - Restore native equal-height column stretch, removing the verticalAlignment override that was disabling it - Contain and centre the two-card row, tighten the gutter between cards Surface & typography - Section background: surface.card; card background: surface.card-raised (one step lighter) - Resize card heading/paragraph/list text to match reference; tighten list spacing; restore card padding Bullet fix - Align bullet dots to the first line of wrapped text instead of the whole block, using measured geometry Unaffected - Discover accent colour and all shared/global styles untouched --- .../sections/phase-deliverables-and-role.php | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/patterns/sections/phase-deliverables-and-role.php b/patterns/sections/phase-deliverables-and-role.php index 4550939..ab7d8f2 100644 --- a/patterns/sections/phase-deliverables-and-role.php +++ b/patterns/sections/phase-deliverables-and-role.php @@ -9,8 +9,11 @@ * be pulled out per-page before this is reused on the other five pages. Two bordered cards side by * side (core/columns): "What you receive" (phase-coloured dot-bullet list, same convention as * phase-common-services.php) and "Your role" (identical shell, generic bullet colour since it is not - * a phase-specific deliverable). Adapts between the site's light and dark style variations via text - * tokens. + * a phase-specific deliverable). Adapts between the site's light and dark style variations via + * surface/text tokens — section uses surface.card, cards use surface.card-raised one step lighter, + * for a section-background → card-surface hierarchy. Columns deliberately omit verticalAlignment so + * core/columns' native equal-height stretch applies (each card's own minHeight:100% then fills its + * now-equal-height column) — do not re-add verticalAlignment:"top" here, it disables that stretch. * Keywords: phase, discover, create, build, launch, grow, evolve, deliverables, role, section * Viewport Width: 1280 * Inserter: true @@ -46,22 +49,22 @@ */ $ls_render_bullet_row = function ( $ls_item_text, $ls_dot_color ) { ?> - -
- + +
+ - -

+ +

- -
+ +
- +

@@ -73,13 +76,15 @@
- -
+ +
+ +
- -
- -
+ +
+ +
@@ -94,7 +99,7 @@

- +
- -
- -
+ +
+ +
@@ -126,7 +131,7 @@

- +
+
+
From 31eac3fe36037beeabee7a4a4122ccbb465f803f Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 11:22:00 +0200 Subject: [PATCH 08/19] Fix invalid layout type causing block editor crash Bug fix - patterns/sections/phase-support-focus.php: changed the eyebrow/heading wrapper and two-column wrapper groups from "layout":{"type":"flow"} to "layout":{"type":"default"} - "flow" is not a registered WordPress layout type (valid types are default, constrained, flex, grid); the invalid type crashed the block editor's layout rendering for these two groups, showing "This block has encountered an error and cannot be previewed" Context - Introduced while widening these wrappers to the theme's wide alignment in an earlier refinement pass; "default" preserves the same full-width stacking behaviour with a valid type - Scoped entirely to patterns/sections/phase-support-focus.php (LS-4179) --- patterns/sections/phase-support-focus.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/sections/phase-support-focus.php b/patterns/sections/phase-support-focus.php index 79a1584..17ee102 100644 --- a/patterns/sections/phase-support-focus.php +++ b/patterns/sections/phase-support-focus.php @@ -31,7 +31,7 @@
- +

@@ -43,7 +43,7 @@
- +
From 2aa02f045c2e4343f744066aaa231769ebdf3570 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 11:27:33 +0200 Subject: [PATCH 09/19] Refine Phase Deliverables And Role to match prototype Layout - Widen heading wrapper so it stays on one line (size/level unchanged) - Restore native equal-height column stretch, removing the verticalAlignment override that was disabling it - Contain and centre the two-card row, tighten the gutter between cards Typography - Resize card heading/paragraph/list text to match reference; tighten list spacing; restore card padding Bullet fix - Align bullet dots to the first line of wrapped text instead of the whole block, using measured geometry Card styling - Revert card background to transparent (border only) per design feedback, after a solid surface.card-raised fill didn't land well Unaffected - Discover accent colour and all shared/global styles untouched --- .../sections/phase-deliverables-and-role.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/patterns/sections/phase-deliverables-and-role.php b/patterns/sections/phase-deliverables-and-role.php index ab7d8f2..b4db493 100644 --- a/patterns/sections/phase-deliverables-and-role.php +++ b/patterns/sections/phase-deliverables-and-role.php @@ -9,9 +9,11 @@ * be pulled out per-page before this is reused on the other five pages. Two bordered cards side by * side (core/columns): "What you receive" (phase-coloured dot-bullet list, same convention as * phase-common-services.php) and "Your role" (identical shell, generic bullet colour since it is not - * a phase-specific deliverable). Adapts between the site's light and dark style variations via - * surface/text tokens — section uses surface.card, cards use surface.card-raised one step lighter, - * for a section-background → card-surface hierarchy. Columns deliberately omit verticalAlignment so + * a phase-specific deliverable). Cards are transparent (border only, no background fill) — tried a + * lighter surface.card-raised fill for a section→card hierarchy, reverted per design feedback in + * favour of the plainer bordered-only look. Adapts between the site's light and dark style + * variations via surface/text tokens — section uses surface.card. Columns deliberately omit + * verticalAlignment so * core/columns' native equal-height stretch applies (each card's own minHeight:100% then fills its * now-equal-height column) — do not re-add verticalAlignment:"top" here, it disables that stretch. * Keywords: phase, discover, create, build, launch, grow, evolve, deliverables, role, section @@ -83,8 +85,8 @@
- -
+ +
@@ -115,8 +117,8 @@
- -
+ +
From 434dcdb58e197b378e2eb1f6cc9e3b3b1661a0f8 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 11:54:39 +0200 Subject: [PATCH 10/19] Refine Phase CTA to match prototype Layout - Keep heading on one line via independent width wrapper + rebalanced 62/38 columns - Widen gutter between columns to spacing|100 (largest token) - Switch to a plain align:wide row, matching other phase-page patterns Typography & buttons - Narrower paragraph wrapper with tighter line-height; all text stays 16px+ - Reduce button padding via local overrides (shared button styles untouched) Checklist accessibility fix - Check icon on green circle measured 1.61:1 contrast (fails WCAG 3:1 minimum) - Switch to text.on-light (13:1), same pairing the Primary button already uses - Mute checklist label colour for hierarchy; bump icon size/gap slightly Background - New phase-cta.scss: same grid-over-gradient technique as the hero, own scoped class --- assets/css/phase-cta.css | 1 + functions.php | 1 + inc/animations.php | 10 ++++ package.json | 6 +- patterns/sections/phase-cta.php | 96 +++++++++++++++++------------- src/scss/structural/phase-cta.scss | 28 +++++++++ 6 files changed, 98 insertions(+), 44 deletions(-) create mode 100644 assets/css/phase-cta.css create mode 100644 src/scss/structural/phase-cta.scss diff --git a/assets/css/phase-cta.css b/assets/css/phase-cta.css new file mode 100644 index 0000000..c68f929 --- /dev/null +++ b/assets/css/phase-cta.css @@ -0,0 +1 @@ +.ls-phase-cta{position:relative;overflow:hidden;background-image:linear-gradient(color-mix(in srgb, var(--wp--custom--color--text--on-dark) 2%, transparent) 1px, transparent 1px),linear-gradient(90deg, color-mix(in srgb, var(--wp--custom--color--text--on-dark) 2%, transparent) 1px, transparent 1px),linear-gradient(158deg, var(--wp--custom--color--surface--band-start) 0%, var(--wp--custom--color--surface--band-end) 100%);background-position:0 0,0 0,0 0;background-repeat:repeat,repeat,no-repeat;background-size:32px 32px,32px 32px,auto} diff --git a/functions.php b/functions.php index de9a8c1..7af2553 100644 --- a/functions.php +++ b/functions.php @@ -94,6 +94,7 @@ function ls_theme_setup() { add_editor_style( 'assets/css/button-phase.css' ); add_editor_style( 'assets/css/phase-journey-nav.css' ); add_editor_style( 'assets/css/phase-services-in-phase.css' ); + add_editor_style( 'assets/css/phase-cta.css' ); add_editor_style( 'assets/css/featured-work.css' ); add_editor_style( 'assets/css/where-to-fit.css' ); add_editor_style( 'assets/css/homepage-cta.css' ); diff --git a/inc/animations.php b/inc/animations.php index e83f783..d367375 100644 --- a/inc/animations.php +++ b/inc/animations.php @@ -448,6 +448,16 @@ function ls_theme_get_effect_styles( $context = 'front' ) { return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); }, ), + 'phase-cta' => array( + 'handle' => 'ls-theme-phase-cta', + 'path' => 'assets/css/phase-cta.css', + 'contexts' => array( 'front', 'editor' ), + // Same reasoning as phase-services-in-phase above: shared from the start across all + // six phase pages. + 'condition' => static function () { + return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); + }, + ), 'featured-work' => array( 'handle' => 'ls-theme-featured-work', 'path' => 'assets/css/featured-work.css', diff --git a/package.json b/package.json index fad00cc..5a79ac3 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "description": "LightSpeed Theme is a custom WordPress block theme built by LightSpeed for fast, accessible, maintainable websites using the WordPress Site Editor and block editor.", "type": "module", "scripts": { - "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css", - "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css", + "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css", + "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css", "schema:validate": "node theme-utils.mjs validate-schema", "theme:validate": "node theme-utils.mjs validate-theme", "patterns:escape": "node theme-utils.mjs escape-patterns", "security:scan": "node theme-utils.mjs security-scan", "lint": "npm run lint:json", "lint:json": "node --input-type=module --eval \"import { readFileSync } from 'fs'; import { glob } from 'glob'; const files = await glob(['theme.json', 'styles/**/*.json']); let ok = true; for (const f of files) { try { JSON.parse(readFileSync(f, 'utf8')); } catch (e) { console.error('Invalid JSON:', f, e.message); ok = false; } } if (ok) console.log('All JSON files are valid.'); else process.exit(1);\"", - "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css" + "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css" }, "devDependencies": { "@axe-core/playwright": "^4.13.0", diff --git a/patterns/sections/phase-cta.php b/patterns/sections/phase-cta.php index 8f3734b..7da6954 100644 --- a/patterns/sections/phase-cta.php +++ b/patterns/sections/phase-cta.php @@ -11,7 +11,17 @@ * is-style-button-phase-outline) on the left, a 3-item checklist on the right reusing the * green-circle-check row convention from homepage-why-lightspeed.php. Permanently dark, independent * of the light/dark style variation toggle — same "on-dark" tokens as phase-hero.php and - * phase-journey-nav.php, which this section normally closes out a page alongside. + * phase-journey-nav.php, which this section normally closes out a page alongside. Background grid + * lives in src/scss/structural/phase-cta.scss (className ls-phase-cta), the same technique as + * phase-hero.scss but its own scoped class — deliberately not sharing .ls-phase-hero. Content uses + * a plain align:wide columns row, matching the other phase-page section patterns' convention, + * rather than a narrower constrained wrapper. Heading and paragraph use independent width + * wrappers (not one shared column width) so the heading can stay on one line while the paragraph + * keeps a narrower, more editorial wrap; columns are 62/38 rather than an even 50/50 split for the + * same reason. Buttons keep the shared button-phase-primary/ + * outline styles' colour/border/arrow/height untouched, only their horizontal padding is + * overridden locally (their own inline style, not the shared JSON) — those styles are also used by + * phase-hero.php, so they're not edited directly. * Keywords: phase, discover, create, build, launch, grow, evolve, cta, section * Viewport Width: 1280 * Inserter: true @@ -25,60 +35,64 @@ __( 'Competitor analysis & gap identification', 'ls-theme' ), ); ?> - -
+ +
- +
- -
- -

- + +
+ +

+ - -

- + +
+ +

+ +
+ - -
- -
- + +
+ +
+ - -
- + +
+ +
+
- -
- + - -
- -
- - + +
+
- -
- + + +
+ +
+ +
+ + + +

+
- - -

- +
-
- +
- -
- +
diff --git a/src/scss/structural/phase-cta.scss b/src/scss/structural/phase-cta.scss new file mode 100644 index 0000000..f15edaf --- /dev/null +++ b/src/scss/structural/phase-cta.scss @@ -0,0 +1,28 @@ +/********** Section - Phase CTA (closing CTA for the six phase pages) **********/ + +/* + * Background: the same faint 32px crossed-gradient grid over the permanently-dark + * band-start/band-end diagonal base already used by phase-hero.php's own background (see + * src/scss/structural/phase-hero.scss) — this section normally closes out the same page the hero + * opens, so the two ends of the page share one atmosphere. Deliberately scoped to its own + * .ls-phase-cta class (not reusing .ls-phase-hero) so this stays fully independent of the hero + * file. No radial phase-colour glow here — that's specific to the hero's own upper-content + * composition, not needed on this section. + * + * Left as external background-image (not the block's own inline color.gradient attribute) for the + * same reason documented in phase-hero.scss/blog-hero.scss: an inline `style="background:..."` + * always wins the cascade over an external stylesheet's background-image, so the two can't + * coexist on this block — the pattern's own color.background attribute is left unset so this can + * take over. + */ +.ls-phase-cta { + position: relative; + overflow: hidden; + background-image: + linear-gradient(color-mix(in srgb, var(--wp--custom--color--text--on-dark) 2%, transparent) 1px, transparent 1px), + linear-gradient(90deg, color-mix(in srgb, var(--wp--custom--color--text--on-dark) 2%, transparent) 1px, transparent 1px), + linear-gradient(158deg, var(--wp--custom--color--surface--band-start) 0%, var(--wp--custom--color--surface--band-end) 100%); + background-position: 0 0, 0 0, 0 0; + background-repeat: repeat, repeat, no-repeat; + background-size: 32px 32px, 32px 32px, auto; +} From e0bfcd24d7125122fc61396b1afdffa8d84da477 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 13:42:43 +0200 Subject: [PATCH 11/19] Rebalance section background colours and Support Focus spacing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Background colours - phase-support-focus.php, phase-common-services.php, phase-delivery-numbers.php: added the surface|card background — these are the only three sections that should use it - phase-services-in-phase.php, phase-deliverables-and-role.php: removed the surface|card background, reverting to the default canvas background Spacing - phase-support-focus.php: increased the gap above the two-column content row and above the CTA button (spacing|40 → spacing|60 for both), keeping them equal so the section reads consistently spaced top and bottom of the content block Context - Follow-up refinement to the Discover page section backgrounds and Phase Support Focus spacing (LS-4179) --- patterns/sections/phase-common-services.php | 4 ++-- patterns/sections/phase-deliverables-and-role.php | 4 ++-- patterns/sections/phase-delivery-numbers.php | 2 +- patterns/sections/phase-services-in-phase.php | 4 ++-- patterns/sections/phase-support-focus.php | 12 ++++++------ 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/patterns/sections/phase-common-services.php b/patterns/sections/phase-common-services.php index 9c6692b..a2acad2 100644 --- a/patterns/sections/phase-common-services.php +++ b/patterns/sections/phase-common-services.php @@ -35,8 +35,8 @@ $ls_phase_accent = 'var(--wp--custom--color--phase--discover)'; ?> - -
+ +
diff --git a/patterns/sections/phase-deliverables-and-role.php b/patterns/sections/phase-deliverables-and-role.php index b4db493..1d03d82 100644 --- a/patterns/sections/phase-deliverables-and-role.php +++ b/patterns/sections/phase-deliverables-and-role.php @@ -63,8 +63,8 @@ - -
+ +
diff --git a/patterns/sections/phase-delivery-numbers.php b/patterns/sections/phase-delivery-numbers.php index 7e2d547..6efaa8f 100644 --- a/patterns/sections/phase-delivery-numbers.php +++ b/patterns/sections/phase-delivery-numbers.php @@ -49,7 +49,7 @@ $ls_phase_stats_count = count( $ls_phase_stats ); ?> -
+
diff --git a/patterns/sections/phase-services-in-phase.php b/patterns/sections/phase-services-in-phase.php index b5231cd..9d6ff5f 100644 --- a/patterns/sections/phase-services-in-phase.php +++ b/patterns/sections/phase-services-in-phase.php @@ -156,8 +156,8 @@ $ls_active_phase_slug = isset( $ls_phase_services_by_phase[ $ls_current_phase_slug ] ) ? $ls_current_phase_slug : 'discover'; $ls_phase_accent = 'var(--wp--custom--color--phase--' . $ls_active_phase_slug . ')'; ?> - -
+ +
diff --git a/patterns/sections/phase-support-focus.php b/patterns/sections/phase-support-focus.php index 17ee102..2f0c002 100644 --- a/patterns/sections/phase-support-focus.php +++ b/patterns/sections/phase-support-focus.php @@ -28,8 +28,8 @@ $ls_phase_accent = 'var(--wp--custom--color--phase--discover)'; ?> - -
+ +
@@ -45,8 +45,8 @@
- -
+ +
@@ -98,8 +98,8 @@
- -
+ +
From 3a4dff79362ba820e2d2a0213119dfd678c5ca91 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 13:55:54 +0200 Subject: [PATCH 12/19] Fix vertical divider lines on Phase Delivery Numbers mobile stacking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug fix - src/scss/structural/phase-delivery-numbers.scss (new): below the 782px columns-stacking breakpoint, flips each stat segment's divider from border-inline-end to border-block-end and strips it from the last stat — mirrors services-delivery-numbers.scss exactly - assets/css/phase-delivery-numbers.css (new): compiled output Wiring - package.json: added the new SCSS → CSS mapping to build:css, build:css:dev, and watch:css - inc/animations.php: registered the phase-delivery-numbers bundle, conditional on the six phase-page slugs (same pattern as phase-services-in-phase/phase-cta) - functions.php: added the editor-style enqueue Context - The 3-stat row (patterns/sections/phase-delivery-numbers.php) reuses the shared is-style-stat-segment divider, which is a vertical border by default — correct in the desktop row but left as a stray vertical line once WP stacks the columns to one-per-row on mobile - No PHP pattern changes needed; this was a missing CSS partial (LS-4179) --- assets/css/phase-delivery-numbers.css | 1 + functions.php | 1 + inc/animations.php | 10 ++++++ package.json | 6 ++-- .../structural/phase-delivery-numbers.scss | 36 +++++++++++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 assets/css/phase-delivery-numbers.css create mode 100644 src/scss/structural/phase-delivery-numbers.scss diff --git a/assets/css/phase-delivery-numbers.css b/assets/css/phase-delivery-numbers.css new file mode 100644 index 0000000..80237a4 --- /dev/null +++ b/assets/css/phase-delivery-numbers.css @@ -0,0 +1 @@ +@media(max-width: 781.98px){.ls-phase-stats-row .is-style-stat-segment{border-inline-end:0;border-block-end:1px solid var(--wp--custom--color--border--card);padding-block-end:var(--wp--preset--spacing--30);margin-block-end:var(--wp--preset--spacing--30)}.ls-phase-stats-row>.wp-block-column:last-child .is-style-stat-segment{border-block-end:0;padding-block-end:0;margin-block-end:0}} diff --git a/functions.php b/functions.php index 7af2553..20be813 100644 --- a/functions.php +++ b/functions.php @@ -95,6 +95,7 @@ function ls_theme_setup() { add_editor_style( 'assets/css/phase-journey-nav.css' ); add_editor_style( 'assets/css/phase-services-in-phase.css' ); add_editor_style( 'assets/css/phase-cta.css' ); + add_editor_style( 'assets/css/phase-delivery-numbers.css' ); add_editor_style( 'assets/css/featured-work.css' ); add_editor_style( 'assets/css/where-to-fit.css' ); add_editor_style( 'assets/css/homepage-cta.css' ); diff --git a/inc/animations.php b/inc/animations.php index d367375..46e5694 100644 --- a/inc/animations.php +++ b/inc/animations.php @@ -458,6 +458,16 @@ function ls_theme_get_effect_styles( $context = 'front' ) { return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); }, ), + 'phase-delivery-numbers' => array( + 'handle' => 'ls-theme-phase-delivery-numbers', + 'path' => 'assets/css/phase-delivery-numbers.css', + 'contexts' => array( 'front', 'editor' ), + // Same reasoning as phase-services-in-phase/phase-cta above: shared from the start + // across all six phase pages. + 'condition' => static function () { + return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); + }, + ), 'featured-work' => array( 'handle' => 'ls-theme-featured-work', 'path' => 'assets/css/featured-work.css', diff --git a/package.json b/package.json index 5a79ac3..66ca0f5 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "description": "LightSpeed Theme is a custom WordPress block theme built by LightSpeed for fast, accessible, maintainable websites using the WordPress Site Editor and block editor.", "type": "module", "scripts": { - "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css", - "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css", + "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css", + "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css", "schema:validate": "node theme-utils.mjs validate-schema", "theme:validate": "node theme-utils.mjs validate-theme", "patterns:escape": "node theme-utils.mjs escape-patterns", "security:scan": "node theme-utils.mjs security-scan", "lint": "npm run lint:json", "lint:json": "node --input-type=module --eval \"import { readFileSync } from 'fs'; import { glob } from 'glob'; const files = await glob(['theme.json', 'styles/**/*.json']); let ok = true; for (const f of files) { try { JSON.parse(readFileSync(f, 'utf8')); } catch (e) { console.error('Invalid JSON:', f, e.message); ok = false; } } if (ok) console.log('All JSON files are valid.'); else process.exit(1);\"", - "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css" + "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css" }, "devDependencies": { "@axe-core/playwright": "^4.13.0", diff --git a/src/scss/structural/phase-delivery-numbers.scss b/src/scss/structural/phase-delivery-numbers.scss new file mode 100644 index 0000000..6ae347c --- /dev/null +++ b/src/scss/structural/phase-delivery-numbers.scss @@ -0,0 +1,36 @@ +/********** Phase - Delivery Numbers (all six lifecycle phase pages) **********/ + +/* + * Phase Delivery Numbers (LS-4179, single consumer patterns/sections/phase-delivery-numbers.php). + * Rest-state look for the 3 stat segments comes entirely from the shared + * styles/sections/cards/stat-segment.json (its border-inline-end divider, reused as-is from the + * Work archive's engagement row) — this file only overrides that divider's orientation once the + * row stacks to 1 column on mobile/tablet. Identical fix to + * src/scss/structural/services-delivery-numbers.scss, applied to this pattern's own row class + * (`ls-phase-stats-row` instead of `ls-delivery-stats-row`) since the two patterns render on + * different page sets and are kept independently overridable. + */ + +// JSON limitation: a divider that must be a vertical border-inline-end in a horizontal row but +// a horizontal border-block-end once that same row stacks to a single column has no +// theme.json/block-attribute key for "current layout orientation" — see AGENTS.md Theme-First +// Approach. 781.98px = 782px, matching WP core's own .wp-block-columns stacking breakpoint +// used sitewide (e.g. services-delivery-numbers.scss, work-archive-sections.scss) — the point at +// which this row's 3 columns actually become a vertical stack. +@media (max-width: 781.98px) { + .ls-phase-stats-row .is-style-stat-segment { + border-inline-end: 0; + border-block-end: 1px solid var(--wp--custom--color--border--card); + padding-block-end: var(--wp--preset--spacing--30); + margin-block-end: var(--wp--preset--spacing--30); + } + + // Each stat is the only child of its own .wp-block-column, so a bare + // .is-style-stat-segment:last-child would always match (trivially the last child of its own + // parent) — target the last *column* instead, to only strip the trailing divider once. + .ls-phase-stats-row > .wp-block-column:last-child .is-style-stat-segment { + border-block-end: 0; + padding-block-end: 0; + margin-block-end: 0; + } +} From 4046334bbf65ecbdd5a0684d9e11d86a15db05c0 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 14:20:31 +0200 Subject: [PATCH 13/19] Fix mobile bullet/dot alignment on Common Services and Support Focus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug fix - src/scss/structural/phase-common-services.scss (new): below 782px, stacks the pill list into a single column, reduces the stadium border-radius to a smaller rounded-rectangle radius, and nudges the accent dot down so it centres on the label's first line instead of the whole (possibly wrapped) row - src/scss/structural/phase-support-focus.scss (new): same first-line centring fix for the hollow-circle bullets in the focus-area list, computed from this bullet's own size (border + spacing|5 padding) - assets/css/phase-common-services.css, assets/css/phase-support-focus.css (new): compiled output - patterns/sections/phase-support-focus.php: added a className to the focus-area list wrapper so the new CSS can scope to it Wiring - package.json: added both new SCSS → CSS mappings to build:css, build:css:dev, and watch:css - inc/animations.php: registered both bundles, conditional on the six phase-page slugs (same pattern as the other phase bundles) - functions.php: added both editor-style enqueues Context - Both bullets/dots were vertically centred against a row's full height, correct only while the label stayed on one line; once a label wraps (mobile width, or a longer label), the marker floated above the visual centre of the wrapped text instead of levelling with its first line (LS-4179) --- assets/css/phase-common-services.css | 1 + assets/css/phase-support-focus.css | 1 + functions.php | 2 + inc/animations.php | 20 +++++++ package.json | 6 +- patterns/sections/phase-support-focus.php | 4 +- .../structural/phase-common-services.scss | 56 +++++++++++++++++++ src/scss/structural/phase-support-focus.scss | 46 +++++++++++++++ 8 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 assets/css/phase-common-services.css create mode 100644 assets/css/phase-support-focus.css create mode 100644 src/scss/structural/phase-common-services.scss create mode 100644 src/scss/structural/phase-support-focus.scss diff --git a/assets/css/phase-common-services.css b/assets/css/phase-common-services.css new file mode 100644 index 0000000..20d0f6a --- /dev/null +++ b/assets/css/phase-common-services.css @@ -0,0 +1 @@ +@media(max-width: 781.98px){.ls-phase-common-services__pills{flex-direction:column !important;align-items:stretch !important}.ls-phase-common-services__pills>.wp-block-group.has-border-color{width:100%;max-width:none;box-sizing:border-box;align-items:flex-start !important;border-radius:var(--wp--preset--border-radius--300) !important}.ls-phase-common-services__pills>.wp-block-group.has-border-color>.wp-block-icon{margin-top:calc((var(--wp--preset--font-size--200)*var(--wp--custom--line-height--paragraph) - 6px)/2)}} diff --git a/assets/css/phase-support-focus.css b/assets/css/phase-support-focus.css new file mode 100644 index 0000000..8520062 --- /dev/null +++ b/assets/css/phase-support-focus.css @@ -0,0 +1 @@ +@media(max-width: 781.98px){.ls-phase-focus-areas>.wp-block-group{align-items:flex-start !important}.ls-phase-focus-areas>.wp-block-group>.wp-block-group.has-border-color{margin-top:calc((var(--wp--preset--font-size--200)*var(--wp--custom--line-height--paragraph) - (2px + var(--wp--preset--spacing--5)*2))/2)}} diff --git a/functions.php b/functions.php index 20be813..f9090db 100644 --- a/functions.php +++ b/functions.php @@ -96,6 +96,8 @@ function ls_theme_setup() { add_editor_style( 'assets/css/phase-services-in-phase.css' ); add_editor_style( 'assets/css/phase-cta.css' ); add_editor_style( 'assets/css/phase-delivery-numbers.css' ); + add_editor_style( 'assets/css/phase-common-services.css' ); + add_editor_style( 'assets/css/phase-support-focus.css' ); add_editor_style( 'assets/css/featured-work.css' ); add_editor_style( 'assets/css/where-to-fit.css' ); add_editor_style( 'assets/css/homepage-cta.css' ); diff --git a/inc/animations.php b/inc/animations.php index 46e5694..b42b591 100644 --- a/inc/animations.php +++ b/inc/animations.php @@ -468,6 +468,26 @@ function ls_theme_get_effect_styles( $context = 'front' ) { return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); }, ), + 'phase-common-services' => array( + 'handle' => 'ls-theme-phase-common-services', + 'path' => 'assets/css/phase-common-services.css', + 'contexts' => array( 'front', 'editor' ), + // Same reasoning as phase-delivery-numbers/phase-services-in-phase/phase-cta above: + // shared from the start across all six phase pages. + 'condition' => static function () { + return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); + }, + ), + 'phase-support-focus' => array( + 'handle' => 'ls-theme-phase-support-focus', + 'path' => 'assets/css/phase-support-focus.css', + 'contexts' => array( 'front', 'editor' ), + // Same reasoning as phase-common-services/phase-delivery-numbers above: shared from + // the start across all six phase pages. + 'condition' => static function () { + return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); + }, + ), 'featured-work' => array( 'handle' => 'ls-theme-featured-work', 'path' => 'assets/css/featured-work.css', diff --git a/package.json b/package.json index 66ca0f5..7297961 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "description": "LightSpeed Theme is a custom WordPress block theme built by LightSpeed for fast, accessible, maintainable websites using the WordPress Site Editor and block editor.", "type": "module", "scripts": { - "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css", - "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css", + "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css", + "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css", "schema:validate": "node theme-utils.mjs validate-schema", "theme:validate": "node theme-utils.mjs validate-theme", "patterns:escape": "node theme-utils.mjs escape-patterns", "security:scan": "node theme-utils.mjs security-scan", "lint": "npm run lint:json", "lint:json": "node --input-type=module --eval \"import { readFileSync } from 'fs'; import { glob } from 'glob'; const files = await glob(['theme.json', 'styles/**/*.json']); let ok = true; for (const f of files) { try { JSON.parse(readFileSync(f, 'utf8')); } catch (e) { console.error('Invalid JSON:', f, e.message); ok = false; } } if (ok) console.log('All JSON files are valid.'); else process.exit(1);\"", - "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css" + "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css" }, "devDependencies": { "@axe-core/playwright": "^4.13.0", diff --git a/patterns/sections/phase-support-focus.php b/patterns/sections/phase-support-focus.php index 2f0c002..cfd0bab 100644 --- a/patterns/sections/phase-support-focus.php +++ b/patterns/sections/phase-support-focus.php @@ -72,8 +72,8 @@

- -
+ +
diff --git a/src/scss/structural/phase-common-services.scss b/src/scss/structural/phase-common-services.scss new file mode 100644 index 0000000..61ae923 --- /dev/null +++ b/src/scss/structural/phase-common-services.scss @@ -0,0 +1,56 @@ +/********** Phase - Common Services (all six lifecycle phase pages) **********/ + +/* + * Phase Common Services (LS-4179, single consumer + * patterns/sections/phase-common-services.php). Desktop rest-state is a centred, wrapped "pill + * cloud" of accent-dot + short-label chips, each fully rounded (border-radius|500, a true + * stadium shape) — fine while every label stays on one line, since the chips are only ever as + * tall as one line of text and the centred dot sits level with it. + * + * Below the 782px columns-stacking breakpoint used sitewide (see e.g. + * services-delivery-numbers.scss), several of these labels are long enough to wrap onto two + * lines. Left unchanged, that breaks the pill in three ways: the stadium shape stretches into a + * lopsided capsule around two lines instead of one, the dot (vertically centred against the + * chip) floats at the vertical midpoint of two lines instead of sitting with the first line, and + * the chips' own variable widths under `justifyContent:center` produce a jagged, off-alignment + * stack rather than a clean list. This switches the wrap container to a single-column, full-width + * stack, drops each chip to a smaller rounded-rectangle radius (matching the card radius used + * elsewhere in this same pattern, e.g. its footnote panel), and re-anchors the dot to the top of + * the (now potentially two-line) text — the same top-alignment fix already used for the Focus + * Areas bullet list in phase-support-focus.php, so the two sections share one mobile list + * language. + */ + +// JSON limitation: switching a flex row's direction/alignment and a chip's border-radius based on +// the current viewport has no theme.json/block-attribute key for "below this breakpoint" — see +// AGENTS.md Theme-First Approach. 781.98px = 782px, WP core's own .wp-block-columns stacking +// breakpoint, reused here for consistency even though this row is a `core/group` flex layout, +// not `core/columns`, so it inherits no automatic stacking of its own. +@media (max-width: 781.98px) { + // `!important` needed: WP's own generated unique-class rule for this flex layout + // (`is-layout-flex`) otherwise wins the cascade over a plain class override — same reasoning + // documented in stats-bar.scss. + .ls-phase-common-services__pills { + flex-direction: column !important; + align-items: stretch !important; + } + + .ls-phase-common-services__pills > .wp-block-group.has-border-color { + width: 100%; + max-width: none; + box-sizing: border-box; + align-items: flex-start !important; + // `!important` needed: the stadium radius comes from the chip's own inline `style` attribute + // (its block JSON style attribute), which always wins over a plain class selector. + border-radius: var(--wp--preset--border-radius--300) !important; + } + + // The dot (a fixed 6px icon) sits flush with the row's top edge once align-items switches to + // flex-start above — correct for the *row*, but visually high compared with the text, which + // renders with its own line-height (font-size|200 × the paragraph line-height ratio) above and + // below the glyph. Nudge the dot down by half the gap between one text line's full height and + // the dot's own height, so it centres on the first line specifically rather than the row. + .ls-phase-common-services__pills > .wp-block-group.has-border-color > .wp-block-icon { + margin-top: calc((var(--wp--preset--font-size--200) * var(--wp--custom--line-height--paragraph) - 6px) / 2); + } +} diff --git a/src/scss/structural/phase-support-focus.scss b/src/scss/structural/phase-support-focus.scss new file mode 100644 index 0000000..36e3031 --- /dev/null +++ b/src/scss/structural/phase-support-focus.scss @@ -0,0 +1,46 @@ +/********** Phase - Support Focus (all six lifecycle phase pages) **********/ + +/* + * Phase Support Focus (LS-4179, single consumer + * patterns/sections/phase-support-focus.php). Same underlying issue as + * phase-common-services.scss's mobile fix: each focus-area row centres its accent bullet + * (verticalAlignment: center) against the row's full height, which is correct while the row's + * label stays on one line — the layout this pattern was originally built and reviewed at. Once + * the right-hand column narrows enough (either at this pattern's own two-column mobile stack, or + * simply a narrower viewport) for a label to wrap onto two or three lines, centring against the + * whole row leaves the bullet floating well above the visual middle of the text block instead of + * levelled with its first line. + * + * Unlike the dot icon in phase-common-services (a fixed 6px SVG), this bullet is a bordered, + * padding-sized circle (border-radius|500, 1px border, spacing|5 padding on all sides), so its + * total rendered height is itself token-driven rather than a literal pixel — the margin-top below + * computes half the gap between one text line's full height and the circle's own height entirely + * from custom properties, so it stays correct whatever the spacing/typography tokens resolve to + * at a given viewport width. + */ + +// JSON limitation: overriding a flex row's cross-axis alignment based on the current viewport has +// no theme.json/block-attribute key for "below this breakpoint" — see AGENTS.md Theme-First +// Approach. 781.98px = 782px, WP core's own .wp-block-columns stacking breakpoint, reused here +// for consistency (see phase-common-services.scss, services-delivery-numbers.scss). +@media (max-width: 781.98px) { + // `!important` needed: WP's own generated unique-class rule for this flex layout + // (`is-layout-flex`) otherwise wins the cascade over a plain class override — same reasoning + // documented in stats-bar.scss and phase-common-services.scss. + .ls-phase-focus-areas > .wp-block-group { + align-items: flex-start !important; + } + + // Nudge the bullet down by half the gap between one text line's full height (font-size|200 × + // the paragraph line-height ratio) and the bullet's own height (its 1px border plus + // spacing|5 padding on both the top and bottom), so it centres on the first line specifically + // instead of the row. + .ls-phase-focus-areas > .wp-block-group > .wp-block-group.has-border-color { + margin-top: calc( + ( + var(--wp--preset--font-size--200) * var(--wp--custom--line-height--paragraph) - + (2px + var(--wp--preset--spacing--5) * 2) + ) / 2 + ); + } +} From f53f5b388faa757ce8034edbd21be09db0ad983a Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 23 Sep 2026 15:40:40 +0200 Subject: [PATCH 14/19] Add Phase FAQ and Phase Where To Go Next sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New patterns - patterns/sections/phase-faq.php: shared "Frequently asked questions" section for all six lifecycle phase pages, currently authored with Discover's own five questions - patterns/sections/phase-where-to-go-next.php: shared "Where to go next" section, currently authored with Discover's own two next-step cards, in a real 2-column row (the source Figma frame's 4-column grid was broken and only ever renders two cards) Reuse (no new components, no new tokens) - FAQ reuses the existing yoast/faq-block + schema-faq accordion (patterns/section-faq.php, faq.scss, faq-accordion.js) as-is - Where To Go Next reuses the existing Card - Link Row section style and Link Arrow Accent paragraph style, the same "Explore service"/"Read more" convention already used in phase-services-in-phase.php Phase colour accents - src/scss/structural/phase-faq.scss, phase-where-to-go-next.scss (new): phase-scoped colour overrides (page-slug-{phase} body class, one rule per phase) so the FAQ's open-state border/icon and the cards' hover border/background/icon/link colours match the current phase's accent instead of the sitewide generic link-accent token — without touching the shared components' defaults used elsewhere (homepage FAQ, Work archive, Services page) - assets/css/phase-faq.css, phase-where-to-go-next.css (new): compiled output Wiring - package.json: added both SCSS → CSS mappings to build:css, build:css:dev, and watch:css - inc/animations.php: registered both bundles, conditional on the six phase-page slugs (same pattern as the other phase bundles) - functions.php: added both editor-style enqueues - CHANGELOG.md: added [Unreleased] entry (LS-4179) --- CHANGELOG.md | 14 ++++ assets/css/phase-faq.css | 1 + assets/css/phase-where-to-go-next.css | 1 + functions.php | 2 + inc/animations.php | 20 +++++ package.json | 6 +- patterns/sections/phase-faq.php | 84 +++++++++++++++++++ patterns/sections/phase-where-to-go-next.php | 77 +++++++++++++++++ src/scss/structural/phase-faq.scss | 32 +++++++ .../structural/phase-where-to-go-next.scss | 37 ++++++++ 10 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 assets/css/phase-faq.css create mode 100644 assets/css/phase-where-to-go-next.css create mode 100644 patterns/sections/phase-faq.php create mode 100644 patterns/sections/phase-where-to-go-next.php create mode 100644 src/scss/structural/phase-faq.scss create mode 100644 src/scss/structural/phase-where-to-go-next.scss diff --git a/CHANGELOG.md b/CHANGELOG.md index a78369e..0ba3a4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [Unreleased] — Add Phase FAQ and Phase Where To Go Next sections (LS-4179) + +### Added + +- `patterns/sections/phase-faq.php`: shared "Frequently asked questions" section for all six lifecycle phase pages, currently authored with Discover's own five questions. Reuses the existing Yoast FAQ block and its `schema-faq` accordion component (`patterns/section-faq.php`, `assets/css/faq.css`, `assets/js/faq-accordion.js`) as-is — no new accordion, JS, or markup pattern introduced. +- `patterns/sections/phase-where-to-go-next.php`: shared "Where to go next" section for all six lifecycle phase pages, currently authored with Discover's own two next-step cards. Reuses the existing Card - Link Row section style and Link Arrow Accent paragraph style (the same "Explore service"/"Read more" convention already used in `phase-services-in-phase.php`) in a real 2-column row, rather than the source Figma frame's broken 4-column grid. +- `src/scss/structural/phase-faq.scss` → `assets/css/phase-faq.css`: phase-scoped override (via the `page-slug-{phase}` body class, one rule per phase) so the FAQ accordion's open-state border and toggle-icon colour match the current phase's accent instead of the sitewide generic link-accent token, without affecting any other page that uses the shared `schema-faq` component. +- `src/scss/structural/phase-where-to-go-next.scss` → `assets/css/phase-where-to-go-next.css`: same phase-scoped override technique for the Card - Link Row hover border/background/icon colours and the "Read more" link's colour, without affecting the Work archive or Services page, which also use these shared styles. +- Wired both new bundles into `package.json` (`build:css`/`build:css:dev`/`watch:css`), `inc/animations.php` (conditional on the six phase-page slugs, same pattern as the other phase bundles), and `functions.php` (editor styles). + +([LS-4179](https://github.com/lightspeedwp/ls-theme/tree/feature/ls-4179-build-discover-page)) + +--- + ## [Unreleased] — Refine Phase Support Focus section layout and typography (LS-4179) ### Changed diff --git a/assets/css/phase-faq.css b/assets/css/phase-faq.css new file mode 100644 index 0000000..7c66e28 --- /dev/null +++ b/assets/css/phase-faq.css @@ -0,0 +1 @@ +body.page-slug-discover .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--discover)}body.page-slug-discover .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--discover);border-bottom-color:var(--wp--custom--color--phase--discover)}body.page-slug-create .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--create)}body.page-slug-create .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--create);border-bottom-color:var(--wp--custom--color--phase--create)}body.page-slug-build .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--build)}body.page-slug-build .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--build);border-bottom-color:var(--wp--custom--color--phase--build)}body.page-slug-launch .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--launch)}body.page-slug-launch .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--launch);border-bottom-color:var(--wp--custom--color--phase--launch)}body.page-slug-grow .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--grow)}body.page-slug-grow .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--grow);border-bottom-color:var(--wp--custom--color--phase--grow)}body.page-slug-evolve .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--evolve)}body.page-slug-evolve .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--evolve);border-bottom-color:var(--wp--custom--color--phase--evolve)} diff --git a/assets/css/phase-where-to-go-next.css b/assets/css/phase-where-to-go-next.css new file mode 100644 index 0000000..7b732fd --- /dev/null +++ b/assets/css/phase-where-to-go-next.css @@ -0,0 +1 @@ +body.page-slug-discover .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--discover);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--discover) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--discover)}body.page-slug-discover .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--discover);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--discover)}body.page-slug-create .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--create);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--create) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--create)}body.page-slug-create .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--create);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--create)}body.page-slug-build .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--build);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--build) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--build)}body.page-slug-build .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--build);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--build)}body.page-slug-launch .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--launch);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--launch) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--launch)}body.page-slug-launch .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--launch);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--launch)}body.page-slug-grow .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--grow);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--grow) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--grow)}body.page-slug-grow .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--grow);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--grow)}body.page-slug-evolve .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--evolve);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--evolve) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--evolve)}body.page-slug-evolve .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--evolve);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--evolve)} diff --git a/functions.php b/functions.php index f9090db..08ef622 100644 --- a/functions.php +++ b/functions.php @@ -98,6 +98,8 @@ function ls_theme_setup() { add_editor_style( 'assets/css/phase-delivery-numbers.css' ); add_editor_style( 'assets/css/phase-common-services.css' ); add_editor_style( 'assets/css/phase-support-focus.css' ); + add_editor_style( 'assets/css/phase-faq.css' ); + add_editor_style( 'assets/css/phase-where-to-go-next.css' ); add_editor_style( 'assets/css/featured-work.css' ); add_editor_style( 'assets/css/where-to-fit.css' ); add_editor_style( 'assets/css/homepage-cta.css' ); diff --git a/inc/animations.php b/inc/animations.php index b42b591..abf45e9 100644 --- a/inc/animations.php +++ b/inc/animations.php @@ -488,6 +488,26 @@ function ls_theme_get_effect_styles( $context = 'front' ) { return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); }, ), + 'phase-faq' => array( + 'handle' => 'ls-theme-phase-faq', + 'path' => 'assets/css/phase-faq.css', + 'contexts' => array( 'front', 'editor' ), + // Same reasoning as phase-support-focus/phase-common-services above: shared from the + // start across all six phase pages. + 'condition' => static function () { + return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); + }, + ), + 'phase-where-to-go-next' => array( + 'handle' => 'ls-theme-phase-where-to-go-next', + 'path' => 'assets/css/phase-where-to-go-next.css', + 'contexts' => array( 'front', 'editor' ), + // Same reasoning as phase-faq/phase-support-focus above: shared from the start across + // all six phase pages. + 'condition' => static function () { + return is_page( array( 'discover', 'create', 'build', 'launch', 'grow', 'evolve' ) ); + }, + ), 'featured-work' => array( 'handle' => 'ls-theme-featured-work', 'path' => 'assets/css/featured-work.css', diff --git a/package.json b/package.json index 7297961..6b5e9de 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,15 @@ "description": "LightSpeed Theme is a custom WordPress block theme built by LightSpeed for fast, accessible, maintainable websites using the WordPress Site Editor and block editor.", "type": "module", "scripts": { - "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css", - "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css", + "build:css": "sass --no-source-map --no-charset --style=compressed src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css src/scss/structural/phase-faq.scss:assets/css/phase-faq.css src/scss/structural/phase-where-to-go-next.scss:assets/css/phase-where-to-go-next.css", + "build:css:dev": "sass --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css src/scss/structural/phase-faq.scss:assets/css/phase-faq.css src/scss/structural/phase-where-to-go-next.scss:assets/css/phase-where-to-go-next.css", "schema:validate": "node theme-utils.mjs validate-schema", "theme:validate": "node theme-utils.mjs validate-theme", "patterns:escape": "node theme-utils.mjs escape-patterns", "security:scan": "node theme-utils.mjs security-scan", "lint": "npm run lint:json", "lint:json": "node --input-type=module --eval \"import { readFileSync } from 'fs'; import { glob } from 'glob'; const files = await glob(['theme.json', 'styles/**/*.json']); let ok = true; for (const f of files) { try { JSON.parse(readFileSync(f, 'utf8')); } catch (e) { console.error('Invalid JSON:', f, e.message); ok = false; } } if (ok) console.log('All JSON files are valid.'); else process.exit(1);\"", - "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css" + "watch:css": "sass --watch --no-source-map --no-charset --style=expanded src/scss/animations.scss:assets/css/animations.css src/scss/gsap-animations.scss:assets/css/gsap-animations.css src/scss/structural/taxonomy-filter.scss:assets/css/taxonomy-filter.css src/scss/structural/work-project-card.scss:assets/css/work-project-card.css src/scss/structural/work-archive-sections.scss:assets/css/work-archive-sections.css src/scss/structural/card-shells.scss:assets/css/card-shells.css src/scss/structural/cta-buttons.scss:assets/css/cta-buttons.css src/scss/structural/home-hero.scss:assets/css/home-hero.css src/scss/structural/work-hero.scss:assets/css/work-hero.css src/scss/structural/work-single-hero.scss:assets/css/work-single-hero.css src/scss/structural/blog-hero.scss:assets/css/blog-hero.css src/scss/structural/blog-all-articles.scss:assets/css/blog-all-articles.css src/scss/structural/blog-writing-cta.scss:assets/css/blog-writing-cta.css src/scss/structural/faq.scss:assets/css/faq.css src/scss/structural/links.scss:assets/css/links.css src/scss/structural/button-secondary.scss:assets/css/button-secondary.css src/scss/structural/featured-work.scss:assets/css/featured-work.css src/scss/structural/where-to-fit.scss:assets/css/where-to-fit.css src/scss/structural/homepage-cta.scss:assets/css/homepage-cta.css src/scss/structural/stats-bar.scss:assets/css/stats-bar.css src/scss/structural/homepage-card-rows.scss:assets/css/homepage-card-rows.css src/scss/structural/homepage-why-lightspeed.scss:assets/css/homepage-why-lightspeed.css src/scss/structural/search-results.scss:assets/css/search-results.css src/scss/structural/search-hero.scss:assets/css/search-hero.css src/scss/structural/services-hero.scss:assets/css/services-hero.css src/scss/structural/services-linked-decisions.scss:assets/css/services-linked-decisions.css src/scss/structural/services-service-clusters.scss:assets/css/services-service-clusters.css src/scss/structural/services-service-tiles.scss:assets/css/services-service-tiles.css src/scss/structural/services-delivery-numbers.scss:assets/css/services-delivery-numbers.css src/scss/structural/corner-glow.scss:assets/css/corner-glow.css src/scss/structural/phase-hero.scss:assets/css/phase-hero.css src/scss/structural/button-phase.scss:assets/css/button-phase.css src/scss/structural/phase-journey-nav.scss:assets/css/phase-journey-nav.css src/scss/structural/phase-services-in-phase.scss:assets/css/phase-services-in-phase.css src/scss/structural/phase-cta.scss:assets/css/phase-cta.css src/scss/structural/phase-delivery-numbers.scss:assets/css/phase-delivery-numbers.css src/scss/structural/phase-common-services.scss:assets/css/phase-common-services.css src/scss/structural/phase-support-focus.scss:assets/css/phase-support-focus.css src/scss/structural/phase-faq.scss:assets/css/phase-faq.css src/scss/structural/phase-where-to-go-next.scss:assets/css/phase-where-to-go-next.css" }, "devDependencies": { "@axe-core/playwright": "^4.13.0", diff --git a/patterns/sections/phase-faq.php b/patterns/sections/phase-faq.php new file mode 100644 index 0000000..984f738 --- /dev/null +++ b/patterns/sections/phase-faq.php @@ -0,0 +1,84 @@ + __( 'What exactly happens during the Discover stage?', 'ls-theme' ), + 'answer' => __( 'Discover brings together research, audits, technical review and strategic clarification before design or development begins. We work with your team to understand the platform, the business pressure and the real shape of the opportunity.', 'ls-theme' ), + ), + array( + 'question' => __( 'How long does the Discover phase typically take?', 'ls-theme' ), + 'answer' => __( 'Most engagements run between four and eight weeks. The scope depends on the size of the existing platform, the number of stakeholders and how much migration or integration analysis is needed.', 'ls-theme' ), + ), + array( + 'question' => __( 'Is this only for large projects?', 'ls-theme' ), + 'answer' => __( 'No. Discover is useful any time the route forward is unclear or the current platform carries enough complexity that guessing would create risk later.', 'ls-theme' ), + ), + array( + 'question' => __( 'How is AI used in this stage?', 'ls-theme' ), + 'answer' => __( "AI helps us audit content, find structural patterns and surface issues faster. Human review, judgement and accountability remain central \xe2\x80\x94 AI accelerates the work, it doesn't replace the thinking.", 'ls-theme' ), + ), + array( + 'question' => __( 'What are the main deliverables?', 'ls-theme' ), + 'answer' => __( 'A clearer brief, a stronger sense of delivery scope, earlier visibility on risk and complexity, and a realistic basis for moving into Create or Build.', 'ls-theme' ), + ), +); + +$ls_faq_block_questions = array(); +foreach ( $ls_phase_faqs as $ls_faq_index => $ls_faq ) { + $ls_faq_block_questions[] = array( + 'id' => 'faq-question-' . ( $ls_faq_index + 1 ), + 'question' => $ls_faq['question'], + 'answer' => $ls_faq['answer'], + 'images' => array(), + ); +} +?> + +
+ + +
+ +

+ + + +

+ +
+ + + +
+ +
+ $ls_faq ) : ?> +
+ +

+
+ +
+ +
+ +
+ diff --git a/patterns/sections/phase-where-to-go-next.php b/patterns/sections/phase-where-to-go-next.php new file mode 100644 index 0000000..8543b42 --- /dev/null +++ b/patterns/sections/phase-where-to-go-next.php @@ -0,0 +1,77 @@ + __( 'Create', 'ls-theme' ), + 'description' => __( 'Once the evidence is in place, Create turns that clarity into usable design and content structure.', 'ls-theme' ), + 'url' => '/services/create/', + ), + array( + 'title' => __( 'Our Process', 'ls-theme' ), + 'description' => __( 'See where Discover sits inside the full lifecycle.', 'ls-theme' ), + 'url' => '/about/process/', + ), +); + +$ls_phase_accent = 'var(--wp--custom--color--phase--discover)'; +?> + +
+ + +
+ +

+ +
+ + + +
+ + + +
+ + + +
+ + + +
+ +
+ diff --git a/src/scss/structural/phase-faq.scss b/src/scss/structural/phase-faq.scss new file mode 100644 index 0000000..f2b3603 --- /dev/null +++ b/src/scss/structural/phase-faq.scss @@ -0,0 +1,32 @@ +/********** Section - Phase FAQ (per-phase open-state accent) **********/ + +/* + * The shared schema-faq accordion (patterns/section-faq.php + src/scss/structural/faq.scss) + * reads its open-question border colour from a --ls-faq-border-active custom property, falling + * back to the sitewide generic link-accent token (a cyan-blue unrelated to any phase) since the + * accordion is also reused by the homepage and any other page that inserts it. Overridden here to + * the current phase's accent so the open FAQ row matches the rest of the phase page's accent + * language (eyebrow, bullets, buttons). + * + * The question row's own toggle icon (a rotated border-drawn chevron) is hard-coded to the same + * generic link-accent token directly in faq.scss's ::after rule rather than reading the + * --ls-faq-border-active custom property, so it needs its own override here to keep the icon and + * the section border colour matching once phase-tinted — same JSON limitation as above (a raw + * border-colour override on a pseudo-element has no block-attribute equivalent). + * + * Scoped per phase via the page-slug-{phase} body class from inc/phase-page-body-class.php, same + * mechanism as phase-services-in-phase.scss, and to the .ls-phase-faq section class so no other + * consumer of the shared schema-faq component is affected. + */ +@each $ls-phase in discover, create, build, launch, grow, evolve { + body.page-slug-#{$ls-phase} .ls-phase-faq { + .schema-faq.is-accordion .schema-faq-section { + --ls-faq-border-active: var(--wp--custom--color--phase--#{$ls-phase}); + } + + .schema-faq.is-accordion .schema-faq-question::after { + border-right-color: var(--wp--custom--color--phase--#{$ls-phase}); + border-bottom-color: var(--wp--custom--color--phase--#{$ls-phase}); + } + } +} diff --git a/src/scss/structural/phase-where-to-go-next.scss b/src/scss/structural/phase-where-to-go-next.scss new file mode 100644 index 0000000..7853504 --- /dev/null +++ b/src/scss/structural/phase-where-to-go-next.scss @@ -0,0 +1,37 @@ +/********** Section - Phase Where To Go Next (per-phase hover accents) **********/ + +/* + * The shared is-style-card-link-row style (styles/sections/cards/card-link-row.json + + * src/scss/structural/work-archive-sections.scss) reads its hover border/background/icon colours + * from --ls-card-link-row-border-active/--ls-card-link-row-background-active/ + * --ls-card-link-row-icon-active custom properties, all falling back to sitewide generic + * link-accent tokens (a cyan-blue unrelated to any phase) since that style is also reused by the + * Work archive and Services page. The shared is-style-link-arrow-accent "Read more" link reads + * --ls-link-arrow-colour/--ls-link-arrow-hover-colour the same way (see + * phase-services-in-phase.scss for the identical reasoning — a raw custom property can't be + * expressed in a style JSON attribute, so it can't be set inline on the block itself; JSON + * limitation, see AGENTS.md Theme-First Approach). + * + * Both scoped per phase via the page-slug-{phase} body class from + * inc/phase-page-body-class.php, same mechanism as phase-services-in-phase.scss, and to the + * .ls-phase-where-to-go-next section class so no other consumer of these shared styles is + * affected. + */ +@each $ls-phase in discover, create, build, launch, grow, evolve { + body.page-slug-#{$ls-phase} .ls-phase-where-to-go-next { + .is-style-card-link-row { + --ls-card-link-row-border-active: var(--wp--custom--color--phase--#{$ls-phase}); + --ls-card-link-row-background-active: color-mix( + in srgb, + var(--wp--custom--color--phase--#{$ls-phase}) 10%, + transparent + ); + --ls-card-link-row-icon-active: var(--wp--custom--color--phase--#{$ls-phase}); + } + + .is-style-link-arrow-accent { + --ls-link-arrow-colour: var(--wp--custom--color--phase--#{$ls-phase}); + --ls-link-arrow-hover-colour: var(--wp--custom--color--phase--#{$ls-phase}); + } + } +} From 6599a430776d0bbb15ccf322f1eeea1aab8c6933 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Sun, 27 Sep 2026 19:11:32 +0200 Subject: [PATCH 15/19] Refine Phase FAQ visuals and fix accordion collapse bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refinement pass - phase-faq.php: wider unified containers so the heading stays on one line, intro paragraph resized to standard body token, re- centred after review - phase-faq.scss: scoped visual redesign of the shared schema-faq accordion for this pattern only — larger radius, subtler border, tighter row gap, taller centred rows, CSS-only circular plus/minus control in the phase accent colour Bug fix - faq.scss: fixed a large empty gap under closed FAQ rows — the shared accordion's grid-template-rows collapse never actually resolved to zero on this element; replaced with a reliable max-height collapse. Fixed at the root since it affects every consumer of this shared component, not just this pattern - Also fixed a specificity bug where the old chevron's rotation was still overriding the new plus/minus icon on open Confirmed this pattern uses the Yoast FAQ block, matching existing precedent in the repo (LS-4179) --- CHANGELOG.md | 22 +++++ assets/css/faq.css | 2 +- assets/css/phase-faq.css | 2 +- patterns/sections/phase-faq.php | 16 ++-- src/scss/structural/faq.scss | 14 +++- src/scss/structural/phase-faq.scss | 124 ++++++++++++++++++++++++----- 6 files changed, 145 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ba3a4d..212ff74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [Unreleased] — Fix FAQ accordion closed-state gap (LS-4179) + +### Fixed + +- `src/scss/structural/faq.scss`: the shared `schema-faq` accordion's closed-row collapse used `grid-template-rows: 0fr` → `1fr`, which does not actually resolve to zero on this element — it is both the grid container and, via its own anonymous content box, the only grid item, so the browser's intrinsic-sizing pass for an auto-height container falls back to the answer text's own content height regardless of the `0fr` value (confirmed via computed styles; `min-height: 0` did not change it either). This left a large empty gap below every closed question. Replaced with a `max-height: 0` → `1000px` collapse, a more reliable technique for this exact shape. Root-caused and fixed in the shared component (not scoped to `.ls-phase-faq`) since it affects every consumer of `section-faq.php`'s accordion, not just this pattern. + +([LS-4179](https://github.com/lightspeedwp/ls-theme/tree/feature/ls-4179-build-discover-page)) + +--- + +## [Unreleased] — Refine Phase FAQ section to match prototype (LS-4179) + +### Changed + +- `patterns/sections/phase-faq.php`: increased section top padding (`spacing|90` → `100`) for more breathing room before the heading; widened the heading/intro/FAQ containers from `620px`/`880px` to a unified `780px` so the heading stays on one line at desktop without reducing its font size; changed the intro paragraph from centred to left-aligned and reduced its size from `fontSize:300` to the theme's standard `fontSize:200` body-copy token. +- `src/scss/structural/phase-faq.scss`: added a scoped visual redesign of the shared `schema-faq` accordion for this pattern only (`.ls-phase-faq`) — larger corner radius (`border-radius|400`), a subtler rest-state border (colour-mixed down), tighter gap between rows (`spacing|10`), taller/vertically-centred rows (`spacing|50` vertical padding), and a circular plus/minus control (sized via `spacing|50`, tinted with the current phase accent) replacing the shared component's small rotated-chevron icon. Implemented as a scoped override so the homepage FAQ and any other consumer of `schema-faq` are unaffected. +- Fixed a CSS specificity bug in the same file where the shared component's open-state chevron rotation was still winning over the new plus/minus icon's transform, distorting it when a question was expanded. + +([LS-4179](https://github.com/lightspeedwp/ls-theme/tree/feature/ls-4179-build-discover-page)) + +--- + ## [Unreleased] — Add Phase FAQ and Phase Where To Go Next sections (LS-4179) ### Added diff --git a/assets/css/faq.css b/assets/css/faq.css index 07ee6fa..3e15450 100644 --- a/assets/css/faq.css +++ b/assets/css/faq.css @@ -1 +1 @@ -.schema-faq{display:flex;flex-direction:column;gap:var(--wp--preset--spacing--20)}.schema-faq.is-accordion .schema-faq-section{--ls-faq-border-rest: var(--wp--custom--color--border--card);--ls-faq-border-active: var(--wp--custom--color--link--accent);position:relative;overflow:hidden;border:1px solid var(--ls-faq-border-rest);border-radius:var(--wp--preset--border-radius--200);background:var(--wp--custom--color--surface--card);box-shadow:none;transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.schema-faq.is-accordion .schema-faq-section:hover{border-color:var(--ls-faq-border-active)}.schema-faq.is-accordion .schema-faq-section.is-open{background:var(--wp--custom--color--surface--card-raised);border-color:var(--ls-faq-border-active)}.schema-faq.is-accordion .schema-faq-section:focus-within{border-color:var(--ls-faq-border-active);box-shadow:0 0 0 2px var(--wp--custom--color--focus--ring)}.schema-faq.is-accordion .schema-faq-question{display:flex;align-items:center;gap:var(--wp--preset--spacing--20);margin:0;padding:var(--wp--preset--spacing--20) var(--wp--preset--spacing--30);font-weight:var(--wp--custom--typography--font-weight--bold);cursor:pointer;transition:inherit}.schema-faq.is-accordion .schema-faq-question::after{content:"";display:block;flex:none;width:.625rem;height:.625rem;margin-inline-start:auto;border-right:2px solid var(--wp--custom--color--link--accent);border-bottom:2px solid var(--wp--custom--color--link--accent);transform:translateY(-1px) rotate(45deg);transform-origin:center;transition:transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--emphasised),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.schema-faq.is-accordion .schema-faq-question:focus-visible{outline:2px solid var(--wp--custom--color--focus--ring);outline-offset:-2px}.schema-faq.is-accordion .schema-faq-section.is-open>.schema-faq-question::after{transform:translateY(1px) rotate(-135deg)}.schema-faq.is-accordion .schema-faq-answer{display:grid;grid-template-rows:0fr;margin:0;padding:0 var(--wp--preset--spacing--30);overflow:hidden;opacity:0;transition:grid-template-rows var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),opacity var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),padding var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.schema-faq.is-accordion .schema-faq-section.is-open>.schema-faq-answer{grid-template-rows:1fr;padding:0 var(--wp--preset--spacing--30) var(--wp--preset--spacing--20);opacity:1}@media(prefers-reduced-motion: reduce){.schema-faq.is-accordion .schema-faq-section,.schema-faq.is-accordion .schema-faq-question::after,.schema-faq.is-accordion .schema-faq-answer{transition:none}} +.schema-faq{display:flex;flex-direction:column;gap:var(--wp--preset--spacing--20)}.schema-faq.is-accordion .schema-faq-section{--ls-faq-border-rest: var(--wp--custom--color--border--card);--ls-faq-border-active: var(--wp--custom--color--link--accent);position:relative;overflow:hidden;border:1px solid var(--ls-faq-border-rest);border-radius:var(--wp--preset--border-radius--200);background:var(--wp--custom--color--surface--card);box-shadow:none;transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.schema-faq.is-accordion .schema-faq-section:hover{border-color:var(--ls-faq-border-active)}.schema-faq.is-accordion .schema-faq-section.is-open{background:var(--wp--custom--color--surface--card-raised);border-color:var(--ls-faq-border-active)}.schema-faq.is-accordion .schema-faq-section:focus-within{border-color:var(--ls-faq-border-active);box-shadow:0 0 0 2px var(--wp--custom--color--focus--ring)}.schema-faq.is-accordion .schema-faq-question{display:flex;align-items:center;gap:var(--wp--preset--spacing--20);margin:0;padding:var(--wp--preset--spacing--20) var(--wp--preset--spacing--30);font-weight:var(--wp--custom--typography--font-weight--bold);cursor:pointer;transition:inherit}.schema-faq.is-accordion .schema-faq-question::after{content:"";display:block;flex:none;width:.625rem;height:.625rem;margin-inline-start:auto;border-right:2px solid var(--wp--custom--color--link--accent);border-bottom:2px solid var(--wp--custom--color--link--accent);transform:translateY(-1px) rotate(45deg);transform-origin:center;transition:transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--emphasised),border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.schema-faq.is-accordion .schema-faq-question:focus-visible{outline:2px solid var(--wp--custom--color--focus--ring);outline-offset:-2px}.schema-faq.is-accordion .schema-faq-section.is-open>.schema-faq-question::after{transform:translateY(1px) rotate(-135deg)}.schema-faq.is-accordion .schema-faq-answer{max-height:0;margin:0;padding:0 var(--wp--preset--spacing--30);overflow:hidden;opacity:0;transition:max-height var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),opacity var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard),padding var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.schema-faq.is-accordion .schema-faq-section.is-open>.schema-faq-answer{max-height:1000px;padding:0 var(--wp--preset--spacing--30) var(--wp--preset--spacing--20);opacity:1}@media(prefers-reduced-motion: reduce){.schema-faq.is-accordion .schema-faq-section,.schema-faq.is-accordion .schema-faq-question::after,.schema-faq.is-accordion .schema-faq-answer{transition:none}} diff --git a/assets/css/phase-faq.css b/assets/css/phase-faq.css index 7c66e28..31b1214 100644 --- a/assets/css/phase-faq.css +++ b/assets/css/phase-faq.css @@ -1 +1 @@ -body.page-slug-discover .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--discover)}body.page-slug-discover .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--discover);border-bottom-color:var(--wp--custom--color--phase--discover)}body.page-slug-create .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--create)}body.page-slug-create .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--create);border-bottom-color:var(--wp--custom--color--phase--create)}body.page-slug-build .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--build)}body.page-slug-build .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--build);border-bottom-color:var(--wp--custom--color--phase--build)}body.page-slug-launch .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--launch)}body.page-slug-launch .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--launch);border-bottom-color:var(--wp--custom--color--phase--launch)}body.page-slug-grow .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--grow)}body.page-slug-grow .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--grow);border-bottom-color:var(--wp--custom--color--phase--grow)}body.page-slug-evolve .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--evolve)}body.page-slug-evolve .ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{border-right-color:var(--wp--custom--color--phase--evolve);border-bottom-color:var(--wp--custom--color--phase--evolve)} +.ls-phase-faq .schema-faq{gap:var(--wp--preset--spacing--10)}.ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-rest: color-mix(in srgb, var(--wp--custom--color--border--card) 55%, transparent);border-radius:var(--wp--preset--border-radius--400)}.ls-phase-faq .schema-faq.is-accordion .schema-faq-question{position:relative;padding-top:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50);padding-right:calc(var(--wp--preset--spacing--30) + var(--wp--preset--spacing--50) + var(--wp--preset--spacing--10))}.ls-phase-faq .schema-faq.is-accordion .schema-faq-question::before{content:"";position:absolute;top:50%;right:var(--wp--preset--spacing--30);width:var(--wp--preset--spacing--50);height:var(--wp--preset--spacing--50);transform:translateY(-50%);border-radius:var(--wp--preset--border-radius--500);background:color-mix(in srgb, var(--ls-faq-border-active) 15%, transparent);transition:background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{content:"";display:block;position:absolute;top:50%;right:var(--wp--preset--spacing--30);width:var(--wp--preset--spacing--50);height:var(--wp--preset--spacing--50);margin-inline-start:0;border:none;transform:translateY(-50%);transform-origin:center;background-image:linear-gradient(currentColor, currentColor),linear-gradient(currentColor, currentColor);background-position:center;background-size:40% 2px,2px 40%;background-repeat:no-repeat;color:var(--ls-faq-border-active);transition:background-size var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--emphasised)}.ls-phase-faq .schema-faq.is-accordion .schema-faq-section.is-open>.schema-faq-question::after{background-size:40% 2px,0 2px;transform:translateY(-50%)}@media(prefers-reduced-motion: reduce){.ls-phase-faq .schema-faq.is-accordion .schema-faq-question::before,.ls-phase-faq .schema-faq.is-accordion .schema-faq-question::after{transition:none}}body.page-slug-discover .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--discover)}body.page-slug-create .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--create)}body.page-slug-build .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--build)}body.page-slug-launch .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--launch)}body.page-slug-grow .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--grow)}body.page-slug-evolve .ls-phase-faq .schema-faq.is-accordion .schema-faq-section{--ls-faq-border-active: var(--wp--custom--color--phase--evolve)} diff --git a/patterns/sections/phase-faq.php b/patterns/sections/phase-faq.php index 984f738..c048c4c 100644 --- a/patterns/sections/phase-faq.php +++ b/patterns/sections/phase-faq.php @@ -51,22 +51,22 @@ ); } ?> - -
+ +
- +
- -

+ +

- -

+ +

- +
diff --git a/src/scss/structural/faq.scss b/src/scss/structural/faq.scss index d777de1..4ee75a5 100644 --- a/src/scss/structural/faq.scss +++ b/src/scss/structural/faq.scss @@ -76,14 +76,20 @@ } .schema-faq-answer { - display: grid; - grid-template-rows: 0fr; + // grid-template-rows: 0fr → 1fr (the usual CSS-only collapse technique) does not + // actually resolve to zero here: this element is both the grid container and, via its + // own anonymous content box, the only grid item, so the intrinsic-sizing pass for an + // auto-height container falls back to the content's own height regardless of the 0fr + // track value (verified: min-height:0 does not change this). max-height is a more + // reliable cross-browser collapse for this exact shape — the fixed cap below is simply + // generous enough that no realistic FAQ answer gets clipped when open. + max-height: 0; margin: 0; padding: 0 var(--wp--preset--spacing--30); overflow: hidden; opacity: 0; transition: - grid-template-rows var(--wp--custom--animation--duration--base) + max-height var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), opacity var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), @@ -92,7 +98,7 @@ } .schema-faq-section.is-open > .schema-faq-answer { - grid-template-rows: 1fr; + max-height: 1000px; padding: 0 var(--wp--preset--spacing--30) var(--wp--preset--spacing--20); opacity: 1; } diff --git a/src/scss/structural/phase-faq.scss b/src/scss/structural/phase-faq.scss index f2b3603..ad76743 100644 --- a/src/scss/structural/phase-faq.scss +++ b/src/scss/structural/phase-faq.scss @@ -1,32 +1,114 @@ -/********** Section - Phase FAQ (per-phase open-state accent) **********/ +/********** Section - Phase FAQ (visual redesign + per-phase open-state accent) **********/ /* - * The shared schema-faq accordion (patterns/section-faq.php + src/scss/structural/faq.scss) - * reads its open-question border colour from a --ls-faq-border-active custom property, falling - * back to the sitewide generic link-accent token (a cyan-blue unrelated to any phase) since the - * accordion is also reused by the homepage and any other page that inserts it. Overridden here to - * the current phase's accent so the open FAQ row matches the rest of the phase page's accent - * language (eyebrow, bullets, buttons). + * The shared schema-faq accordion (patterns/section-faq.php + src/scss/structural/faq.scss) is + * reused as-is for its markup, JS-driven expand/collapse, and accessibility (role="button", + * aria-expanded, focus-visible), but this pattern's own reference calls for a visibly different + * rest-state treatment: a much larger corner radius, a subtler border, tighter row gaps, and a + * circular plus/minus control in place of the shared component's small rotated-chevron icon. + * Rather than changing those shared defaults (which would also affect the homepage FAQ and any + * other page that inserts section-faq.php), every rule below is scoped to .ls-phase-faq so only + * this pattern's rendering changes. * - * The question row's own toggle icon (a rotated border-drawn chevron) is hard-coded to the same - * generic link-accent token directly in faq.scss's ::after rule rather than reading the - * --ls-faq-border-active custom property, so it needs its own override here to keep the icon and - * the section border colour matching once phase-tinted — same JSON limitation as above (a raw - * border-colour override on a pseudo-element has no block-attribute equivalent). - * - * Scoped per phase via the page-slug-{phase} body class from inc/phase-page-body-class.php, same - * mechanism as phase-services-in-phase.scss, and to the .ls-phase-faq section class so no other - * consumer of the shared schema-faq component is affected. + * JSON limitation: none of this has a theme.json/block-attribute equivalent — the accordion's + * question/answer markup is plain hard-coded HTML from the Yoast FAQ block (not built from + * block-supports-aware sub-blocks), so there is no style JSON path to reach it at all; see + * AGENTS.md Theme-First Approach. + */ +.ls-phase-faq { + .schema-faq { + // Tighter rhythm between rows than the shared component's default spacing|20 gap. + gap: var(--wp--preset--spacing--10); + } + + .schema-faq.is-accordion .schema-faq-section { + // Subtler rest-state border than the shared component's default border.card at full + // strength — color-mix'd down so the outline is present but doesn't compete with the card. + --ls-faq-border-rest: color-mix(in srgb, var(--wp--custom--color--border--card) 55%, transparent); + border-radius: var(--wp--preset--border-radius--400); + } + + .schema-faq.is-accordion .schema-faq-question { + position: relative; + padding-top: var(--wp--preset--spacing--50); + padding-bottom: var(--wp--preset--spacing--50); + // Extra inset on the right so the question text never runs under the circular control. + padding-right: calc( + var(--wp--preset--spacing--30) + var(--wp--preset--spacing--50) + var(--wp--preset--spacing--10) + ); + } + + // The circular control's tinted background. A separate pseudo-element from the plus/minus + // glyph below so the glyph can sit on top of it in natural paint order without extra markup. + .schema-faq.is-accordion .schema-faq-question::before { + content: ""; + position: absolute; + top: 50%; + right: var(--wp--preset--spacing--30); + width: var(--wp--preset--spacing--50); + height: var(--wp--preset--spacing--50); + transform: translateY(-50%); + border-radius: var(--wp--preset--border-radius--500); + background: color-mix(in srgb, var(--ls-faq-border-active) 15%, transparent); + transition: background-color var(--wp--custom--animation--duration--base) + var(--wp--custom--animation--easing--standard); + } + + // The plus/minus glyph itself: two crossed bars drawn with a pair of linear-gradient + // backgrounds (a common CSS-only plus/minus icon technique) rather than the shared + // component's single rotated-corner chevron, since the reference calls for a true plus that + // resolves to a minus when open, not a chevron. No new icon asset needed. + .schema-faq.is-accordion .schema-faq-question::after { + content: ""; + display: block; + position: absolute; + top: 50%; + right: var(--wp--preset--spacing--30); + width: var(--wp--preset--spacing--50); + height: var(--wp--preset--spacing--50); + margin-inline-start: 0; + border: none; + transform: translateY(-50%); + transform-origin: center; + background-image: linear-gradient(currentColor, currentColor), linear-gradient(currentColor, currentColor); + background-position: center; + background-size: 40% 2px, 2px 40%; + background-repeat: no-repeat; + color: var(--ls-faq-border-active); + transition: background-size var(--wp--custom--animation--duration--base) + var(--wp--custom--animation--easing--emphasised); + } + + // Open state: collapse the vertical bar only, leaving a minus — same technique used + // elsewhere in the theme for CSS-only state icons, no JS change needed. Also re-asserts + // transform: the shared faq.scss's own open-state rule (rotate(-135deg), for its chevron) has + // higher selector specificity than the rest-state override above, so it would otherwise still + // win and visibly distort this plus/minus glyph. + .schema-faq.is-accordion .schema-faq-section.is-open > .schema-faq-question::after { + background-size: 40% 2px, 0 2px; + transform: translateY(-50%); + } + + @media (prefers-reduced-motion: reduce) { + .schema-faq.is-accordion .schema-faq-question::before, + .schema-faq.is-accordion .schema-faq-question::after { + transition: none; + } + } +} + +/* + * The open-state border colour and the new circular control both read from the + * --ls-faq-border-active custom property, which the shared faq.scss falls back to a sitewide + * generic link-accent token (unrelated to any phase). Overridden here to the current phase's + * accent so both match the rest of the phase page's accent language (eyebrow, bullets, buttons), + * scoped per phase via the page-slug-{phase} body class from inc/phase-page-body-class.php, same + * mechanism as phase-services-in-phase.scss. */ @each $ls-phase in discover, create, build, launch, grow, evolve { body.page-slug-#{$ls-phase} .ls-phase-faq { .schema-faq.is-accordion .schema-faq-section { --ls-faq-border-active: var(--wp--custom--color--phase--#{$ls-phase}); } - - .schema-faq.is-accordion .schema-faq-question::after { - border-right-color: var(--wp--custom--color--phase--#{$ls-phase}); - border-bottom-color: var(--wp--custom--color--phase--#{$ls-phase}); - } } } From a21cd605b297e0b5571c067054106e42226fd432 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Sun, 27 Sep 2026 19:30:45 +0200 Subject: [PATCH 16/19] Refine Phase Where To Go Next cards to match prototype MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Layout - phase-where-to-go-next.php: narrowed the cards row into a centred 1050px container (was full-width align:wide), tightened the card gap, increased the eyebrow-to-cards gap and section bottom padding, reduced card title/CTA typography to closer tokens (still above the 16px floor) - Removed verticalAlignment:"top" from the columns/column blocks so core/columns' default equal-height column stretch applies Equal height + bottom-aligned CTAs - phase-where-to-go-next.scss: scoped override so each card fills its now-equal-height column (height:100%), which combined with the card's existing flex-column layout and the "Read more" link's existing margin-top:auto bottom-aligns both CTAs regardless of description length — no fixed heights, no grid rebuild needed - Also increased card padding, reduced border radius, and softened the border colour, scoped to this pattern only so the Work archive and Services page's use of the same shared Card - Link Row style is unaffected. Discover phase accent colour unchanged Follow-up - Widened the cards container back up to 1050px after review (an existing width already used elsewhere in this pattern family) once the first narrower pass over-corrected the card widths --- CHANGELOG.md | 11 ++++ assets/css/phase-where-to-go-next.css | 2 +- patterns/sections/phase-where-to-go-next.php | 52 ++++++++++--------- .../structural/phase-where-to-go-next.scss | 31 ++++++++++- 4 files changed, 70 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 212ff74..fa148fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [Unreleased] — Refine Phase Where To Go Next section to match prototype (LS-4179) + +### Changed + +- `patterns/sections/phase-where-to-go-next.php`: narrowed the cards row into a `900px` centred container (previously full `align:wide`), tightened the gap between cards (`spacing|20` → `10`), increased the gap between the eyebrow and the cards (`spacing|30` → `40`), increased section bottom padding (`spacing|90` → `100`), reduced the card title to `fontSize:300` and the "Read more" CTA to the standard `fontSize:200` body token, and removed the `verticalAlignment:"top"` on the columns/column blocks so `core/columns`' default equal-height column stretch applies. +- `src/scss/structural/phase-where-to-go-next.scss`: added a scoped override (`.ls-phase-where-to-go-next .is-style-card-link-row`) so each card fills its now-equal-height column (`height:100%`), which combined with the card's existing flex-column layout and the "Read more" link's existing `margin-top:auto` bottom-aligns both CTAs regardless of description length — no fixed heights, no CSS Grid rebuild needed. Also increased card padding (`spacing|20` → `30`), reduced the border radius (`border-radius|400` → `300`), and softened the border colour, scoped to this pattern only so the Work archive and Services page's use of the same shared Card - Link Row style is unaffected. The existing Discover phase accent colour is unchanged. + +([LS-4179](https://github.com/lightspeedwp/ls-theme/tree/feature/ls-4179-build-discover-page)) + +--- + ## [Unreleased] — Fix FAQ accordion closed-state gap (LS-4179) ### Fixed diff --git a/assets/css/phase-where-to-go-next.css b/assets/css/phase-where-to-go-next.css index 7b732fd..3995bc8 100644 --- a/assets/css/phase-where-to-go-next.css +++ b/assets/css/phase-where-to-go-next.css @@ -1 +1 @@ -body.page-slug-discover .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--discover);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--discover) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--discover)}body.page-slug-discover .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--discover);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--discover)}body.page-slug-create .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--create);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--create) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--create)}body.page-slug-create .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--create);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--create)}body.page-slug-build .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--build);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--build) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--build)}body.page-slug-build .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--build);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--build)}body.page-slug-launch .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--launch);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--launch) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--launch)}body.page-slug-launch .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--launch);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--launch)}body.page-slug-grow .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--grow);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--grow) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--grow)}body.page-slug-grow .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--grow);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--grow)}body.page-slug-evolve .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--evolve);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--evolve) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--evolve)}body.page-slug-evolve .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--evolve);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--evolve)} +.ls-phase-where-to-go-next .is-style-card-link-row{height:100%;padding:var(--wp--preset--spacing--30);border-color:color-mix(in srgb, var(--wp--custom--color--border--card) 55%, transparent);border-radius:var(--wp--preset--border-radius--300)}body.page-slug-discover .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--discover);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--discover) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--discover)}body.page-slug-discover .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--discover);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--discover)}body.page-slug-create .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--create);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--create) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--create)}body.page-slug-create .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--create);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--create)}body.page-slug-build .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--build);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--build) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--build)}body.page-slug-build .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--build);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--build)}body.page-slug-launch .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--launch);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--launch) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--launch)}body.page-slug-launch .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--launch);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--launch)}body.page-slug-grow .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--grow);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--grow) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--grow)}body.page-slug-grow .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--grow);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--grow)}body.page-slug-evolve .ls-phase-where-to-go-next .is-style-card-link-row{--ls-card-link-row-border-active: var(--wp--custom--color--phase--evolve);--ls-card-link-row-background-active: color-mix( in srgb, var(--wp--custom--color--phase--evolve) 10%, transparent );--ls-card-link-row-icon-active: var(--wp--custom--color--phase--evolve)}body.page-slug-evolve .ls-phase-where-to-go-next .is-style-link-arrow-accent{--ls-link-arrow-colour: var(--wp--custom--color--phase--evolve);--ls-link-arrow-hover-colour: var(--wp--custom--color--phase--evolve)} diff --git a/patterns/sections/phase-where-to-go-next.php b/patterns/sections/phase-where-to-go-next.php index 8543b42..a7129c5 100644 --- a/patterns/sections/phase-where-to-go-next.php +++ b/patterns/sections/phase-where-to-go-next.php @@ -35,8 +35,8 @@ $ls_phase_accent = 'var(--wp--custom--color--phase--discover)'; ?> - -
+ +
@@ -46,32 +46,36 @@
- -
- + +
+ +
+ - -
- - + +
+ - + +
+
- +
diff --git a/src/scss/structural/phase-where-to-go-next.scss b/src/scss/structural/phase-where-to-go-next.scss index 7853504..8c17c3d 100644 --- a/src/scss/structural/phase-where-to-go-next.scss +++ b/src/scss/structural/phase-where-to-go-next.scss @@ -1,4 +1,33 @@ -/********** Section - Phase Where To Go Next (per-phase hover accents) **********/ +/********** Section - Phase Where To Go Next (visual redesign + per-phase hover accents) **********/ + +/* + * Equal-height cards + bottom-aligned CTA: core/columns already stretches its columns to equal + * height by default (the block's own CSS only overrides that to align-items:flex-start when a + * verticalAlignment is set, which the pattern no longer sets), so each .wp-block-column is + * already the same height as its tallest sibling. What's missing is telling the card itself to + * fill that column height — a plain block box only ever sizes to its own content — so the + * article's own height is set to 100% here. Once the card is genuinely as tall as the column, + * its own flex-column layout (already declared in the pattern's block attributes) plus the + * "Read more" paragraph's own margin-top:auto (already in the pattern) push the CTA to the + * bottom, matching the reference regardless of how much either card's description wraps. No + * fixed/hardcoded height anywhere, and nothing here needs a CSS Grid rebuild of the row: JSON + * limitation is only "fill 100% of the parent's flex-stretched height", which has no + * block-attribute equivalent — see AGENTS.md Theme-First Approach. + * + * Padding/border/radius overrides use the same colour-mix'd-down, scoped-override technique + * already established in phase-faq.scss for the same reason: these are the shared + * Card - Link Row style's rest-state values, also used by the Work archive and Services page, so + * they're overridden here rather than in styles/sections/cards/card-link-row.json to avoid + * changing that shared look anywhere else it's used. + */ +.ls-phase-where-to-go-next { + .is-style-card-link-row { + height: 100%; + padding: var(--wp--preset--spacing--30); + border-color: color-mix(in srgb, var(--wp--custom--color--border--card) 55%, transparent); + border-radius: var(--wp--preset--border-radius--300); + } +} /* * The shared is-style-card-link-row style (styles/sections/cards/card-link-row.json + From 2aca588109402585b0596a3bde23a97b9e8e339e Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Sun, 27 Sep 2026 19:50:54 +0200 Subject: [PATCH 17/19] Fix Discover page contrast and heading hierarchy failures Bug fix - Raise inactive phase-nav link color mix from 55% to 70% opacity to meet WCAG AA 4.5:1 contrast (was 3.58:1) - Bump phase-introduction headings from h3 to h2 (were skipping h2 directly under the h1 hero) - Bump phase-where-to-go-next card headings from h4 to h3 (were skipping h3 after an h2 section) Context - Found via SINGLE_PAGE_URL playwright run against /services/discover/ (accessibility + page-structure specs) - Rebuilt assets/css/phase-journey-nav.css from the updated SCSS --- assets/css/phase-journey-nav.css | 2 +- patterns/sections/phase-introduction.php | 8 ++++---- patterns/sections/phase-where-to-go-next.php | 4 ++-- src/scss/structural/phase-journey-nav.scss | 6 ++++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/assets/css/phase-journey-nav.css b/assets/css/phase-journey-nav.css index 2bdc7d4..b9db564 100644 --- a/assets/css/phase-journey-nav.css +++ b/assets/css/phase-journey-nav.css @@ -1 +1 @@ -.ls-phase-journey-nav__step--discover{--ls-phase-item-accent: var(--wp--custom--color--phase--discover-on-dark, var(--wp--custom--color--phase--discover))}.ls-phase-journey-nav__step--create{--ls-phase-item-accent: var(--wp--custom--color--phase--create-on-dark, var(--wp--custom--color--phase--create))}.ls-phase-journey-nav__step--build{--ls-phase-item-accent: var(--wp--custom--color--phase--build-on-dark, var(--wp--custom--color--phase--build))}.ls-phase-journey-nav__step--launch{--ls-phase-item-accent: var(--wp--custom--color--phase--launch-on-dark, var(--wp--custom--color--phase--launch))}.ls-phase-journey-nav__step--grow{--ls-phase-item-accent: var(--wp--custom--color--phase--grow-on-dark, var(--wp--custom--color--phase--grow))}.ls-phase-journey-nav__step--evolve{--ls-phase-item-accent: var(--wp--custom--color--phase--evolve-on-dark, var(--wp--custom--color--phase--evolve))}.ls-phase-journey-nav__link a{transition:color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-phase-journey-nav__link a{color:color-mix(in srgb, var(--wp--custom--color--text--on-dark-muted) 55%, transparent)}.ls-phase-journey-nav__link a:hover,.ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}.ls-phase-journey-nav__dot{display:none}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a:hover,body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a:hover,body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a:hover,body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a:hover,body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a:hover,body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a:hover,body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__dot{display:inline-flex}.ls-phase-journey-nav__link a:focus-visible{outline:2px solid var(--wp--custom--color--text--on-dark);outline-offset:3px;border-radius:var(--wp--preset--border-radius--100)}@media(prefers-reduced-motion: reduce){.ls-phase-journey-nav__link a{transition:none}}@media(max-width: 781px){.ls-phase-journey-nav__items{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}.ls-phase-journey-nav__items::-webkit-scrollbar{display:none}.ls-phase-journey-nav__step{flex-shrink:0}} +.ls-phase-journey-nav__step--discover{--ls-phase-item-accent: var(--wp--custom--color--phase--discover-on-dark, var(--wp--custom--color--phase--discover))}.ls-phase-journey-nav__step--create{--ls-phase-item-accent: var(--wp--custom--color--phase--create-on-dark, var(--wp--custom--color--phase--create))}.ls-phase-journey-nav__step--build{--ls-phase-item-accent: var(--wp--custom--color--phase--build-on-dark, var(--wp--custom--color--phase--build))}.ls-phase-journey-nav__step--launch{--ls-phase-item-accent: var(--wp--custom--color--phase--launch-on-dark, var(--wp--custom--color--phase--launch))}.ls-phase-journey-nav__step--grow{--ls-phase-item-accent: var(--wp--custom--color--phase--grow-on-dark, var(--wp--custom--color--phase--grow))}.ls-phase-journey-nav__step--evolve{--ls-phase-item-accent: var(--wp--custom--color--phase--evolve-on-dark, var(--wp--custom--color--phase--evolve))}.ls-phase-journey-nav__link a{transition:color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard)}.ls-phase-journey-nav__link a{color:color-mix(in srgb, var(--wp--custom--color--text--on-dark-muted) 70%, transparent)}.ls-phase-journey-nav__link a:hover,.ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}.ls-phase-journey-nav__dot{display:none}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a:hover,body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-discover .ls-phase-journey-nav__step--discover .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a:hover,body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-create .ls-phase-journey-nav__step--create .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a:hover,body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-build .ls-phase-journey-nav__step--build .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a:hover,body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-launch .ls-phase-journey-nav__step--launch .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a:hover,body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-grow .ls-phase-journey-nav__step--grow .ls-phase-journey-nav__dot{display:inline-flex}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a{color:var(--ls-phase-item-accent)}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a:hover,body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__link a:focus-visible{color:var(--wp--custom--color--text--on-dark)}body.page-slug-evolve .ls-phase-journey-nav__step--evolve .ls-phase-journey-nav__dot{display:inline-flex}.ls-phase-journey-nav__link a:focus-visible{outline:2px solid var(--wp--custom--color--text--on-dark);outline-offset:3px;border-radius:var(--wp--preset--border-radius--100)}@media(prefers-reduced-motion: reduce){.ls-phase-journey-nav__link a{transition:none}}@media(max-width: 781px){.ls-phase-journey-nav__items{overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none}.ls-phase-journey-nav__items::-webkit-scrollbar{display:none}.ls-phase-journey-nav__step{flex-shrink:0}} diff --git a/patterns/sections/phase-introduction.php b/patterns/sections/phase-introduction.php index 55b3722..5c0197a 100644 --- a/patterns/sections/phase-introduction.php +++ b/patterns/sections/phase-introduction.php @@ -31,8 +31,8 @@

- -

+ +

@@ -55,8 +55,8 @@

- -

+ +

diff --git a/patterns/sections/phase-where-to-go-next.php b/patterns/sections/phase-where-to-go-next.php index a7129c5..2c4d634 100644 --- a/patterns/sections/phase-where-to-go-next.php +++ b/patterns/sections/phase-where-to-go-next.php @@ -56,8 +56,8 @@