From 3c39119de573892570456adaddf8d384c7c20d17 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Fri, 11 Sep 2026 11:20:53 +0200 Subject: [PATCH 1/6] Add Services "Entry Points" section (LS-1598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add patterns/sections/services-entry-points.php: "You don't have to buy everything at once." — eyebrow/heading/description on the left, a 2x2 grid of 4 entry-point link cards on the right (Start with discovery, Support review, Migration assessment, AI-readiness discussion), each linking to its corresponding service page - Reuses the existing Card - Link Row style and core/icon arrow — no new card style or icon needed Context - Section background uses surface.card-raised (#E8E8E8) rather than surface.card (used by Service Clusters), since Card - Link Row's own card background is already surface.card — matching the section to that token would make the cards blend into their background --- patterns/sections/services-entry-points.php | 105 ++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 patterns/sections/services-entry-points.php diff --git a/patterns/sections/services-entry-points.php b/patterns/sections/services-entry-points.php new file mode 100644 index 0000000..c378f48 --- /dev/null +++ b/patterns/sections/services-entry-points.php @@ -0,0 +1,105 @@ + __( 'Start with discovery', 'ls-theme' ), + 'description' => __( 'When the route forward is unclear and the brief needs evidence.', 'ls-theme' ), + 'url' => '/services/discovery/', + ), + array( + 'label' => __( 'Support review', 'ls-theme' ), + 'description' => __( 'When the platform is live but the day-to-day is fragile.', 'ls-theme' ), + 'url' => '/services/support/', + ), + array( + 'label' => __( 'Migration assessment', 'ls-theme' ), + 'description' => __( 'When the platform needs to move without losing traction.', 'ls-theme' ), + 'url' => '/services/migrations/', + ), + array( + 'label' => __( 'AI-readiness discussion', 'ls-theme' ), + 'description' => __( "When AI work is on the roadmap but the foundations aren't ready.", 'ls-theme' ), + 'url' => '/services/ai/', + ), +); + +$ls_arrow_icon_slug = 'arrow-right'; +?> + +
+ + +
+ + +
+ + +
+ +
+ + + +

+ +
+ + + +

+ + + +

+ +
+ + + +
+ +
+ + + + + + + +
+ +
+ +
+ +
+ +
+ From 7bc4e95934eed96f83375657ae487e7e4fa93007 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Fri, 11 Sep 2026 11:29:19 +0200 Subject: [PATCH 2/6] Closed off completed tasks --- specs/001-services-page/tasks.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/specs/001-services-page/tasks.md b/specs/001-services-page/tasks.md index d7d33d7..683b30f 100644 --- a/specs/001-services-page/tasks.md +++ b/specs/001-services-page/tasks.md @@ -34,7 +34,7 @@ Single WordPress block-theme project. Existing Services page patterns live in **Purpose**: Confirm exact Figma content for the 3 remaining sections before building anything. -- [ ] T001 Pull design context (code, tokens, screenshot) for Entry Points via the Figma MCP +- [x] T001 Pull design context (code, tokens, screenshot) for Entry Points via the Figma MCP tools, node `8044-164205`, from the file `https://www.figma.com/design/OTqchq3sRBzUy6TICruzc3/LightSpeedWP-Design-System` - [ ] T002 [P] Pull design context for Delivery by the Numbers, node `8044-164253`, same Figma @@ -52,7 +52,7 @@ from Figma — no guessing content during implementation. using this repo's own `.agents/skills/` toolchain — not ad hoc manual comparison — before any new pattern file is written. -- [ ] T004 Invoke the `pattern-extractor` skill's analysis phase for Entry Points (Figma +- [x] T004 Invoke the `pattern-extractor` skill's analysis phase for Entry Points (Figma context from T001): propose what to reuse (starting from `styles/sections/cards/card-link-row.json`, already used by `patterns/sections/homepage-where-to-start.php` and @@ -95,30 +95,27 @@ content, per quickstart.md's "Entry Points section present" validation. ### Implementation for User Story 1 -- [ ] T009 [US1] Execute `pattern-extractor`'s build phase for Entry Points, using T004's +- [x] T009 [US1] Execute `pattern-extractor`'s build phase for Entry Points, using T004's approved reuse-or-create proposal — this creates `patterns/sections/services-entry-points.php` with the standard pattern header (Title/Slug/Categories/Block Types/Description/Keywords/Viewport Width/Inserter, matching the format in `patterns/sections/services-service-tiles.php`) and, if new JSON styling is written, `theme-color-token-enforcer` runs automatically per `pattern-extractor`'s own mandatory chain — do not skip it -- [ ] T010 [US1] Write the Entry Point array (per data-model.md: label, description, +- [x] T010 [US1] Write the Entry Point array (per data-model.md: label, description, link/action for each) and render loop, reusing `card-link-row.json` if T004 confirmed it fits, or the new shape-named style if T004 identified a genuine gap (consult `wp-block-style-audit` before writing any new `css` field in that style's JSON) -- [ ] T011 [US1] Render each entry point's icon via `core/icon` with the `lightspeed/{slug}` +- [x] T011 [US1] Render each entry point's icon via `core/icon` with the `lightspeed/{slug}` confirmed in T008 -- [ ] T012 [US1] Assemble the Entry Points section onto the Services page content, positioned +- [x] T012 [US1] Assemble the Entry Points section onto the Services page content, positioned per the Figma page flow (after Service Tiles, before Delivery by the Numbers) -- [ ] T013 [US1] Register a `services-entry-points` bundle in `inc/animations.php` - (`ls_theme_get_effect_styles()` and, if it needs a render marker, - `ls_theme_get_bundle_render_markers()`) with a `condition` of `is_page( 'services' )` set - from the start — do not defer this per research.md's enqueue-condition decision -- [ ] T014 [US1] Add `add_editor_style( 'assets/css/services-entry-points.css' )` to - `functions.php` and wire the new SCSS file (if T004/T007 required one) into - `package.json`'s `build:css`/`build:css:dev`/`watch:css` scripts, matching the existing - `services-*` entries -- [ ] T015 [US1] Run `php -l`, `npm run patterns:escape`, `npm run security:scan`, +- [x] T013 [US1] **N/A** — no `services-entry-points` bundle was needed. The section reuses + `card-link-row.json` and `core/icon` entirely; no new SCSS/JSON styling was written, so + there is nothing to register a stylesheet bundle for. +- [x] T014 [US1] **N/A** — same reason as T013, no new stylesheet exists to add as an editor + style or wire into the build scripts. +- [x] T015 [US1] Run `php -l`, `npm run patterns:escape`, `npm run security:scan`, `npm run schema:validate` (if new JSON was added), and `composer run phpcs` against every file touched in T009-T014 — fix any failure before proceeding; never use `validate_blocks` From 3707f29acdb5a86595fa64a37e6ff98ef78b1929 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Fri, 11 Sep 2026 11:50:34 +0200 Subject: [PATCH 3/6] Add Services "Delivery by the Numbers" section (LS-1598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add patterns/sections/services-delivery-numbers.php: "Twelve years of WordPress engineering, not a slide-deck claim." — a centered eyebrow/heading/description intro followed by 3 centered delivery-scale stats (WordPress depth, Platform launches, Approved client praise) - Reuses the existing Stat Segment style (is-style-stat-segment) — same building block as the Work archive's engagement row — composed centered with the value shown before its label, instead of that pattern's left-aligned label-first order. No new card style needed. Follow-up - Increased the stat label font size one token up (300 → 400) and the description to 16px (100 → 200) for readability --- .../sections/services-delivery-numbers.php | 90 +++++++++++++++++++ specs/001-services-page/tasks.md | 20 ++--- 2 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 patterns/sections/services-delivery-numbers.php diff --git a/patterns/sections/services-delivery-numbers.php b/patterns/sections/services-delivery-numbers.php new file mode 100644 index 0000000..f2a992c --- /dev/null +++ b/patterns/sections/services-delivery-numbers.php @@ -0,0 +1,90 @@ + '12', + 'suffix' => __( '+ yrs', 'ls-theme' ), + 'label' => __( 'WordPress depth', 'ls-theme' ), + 'description' => __( 'Continuous practice since 2014 — block-first since 2022.', 'ls-theme' ), + ), + array( + 'value' => '100', + 'suffix' => __( '+', 'ls-theme' ), + 'label' => __( 'Platform launches', 'ls-theme' ), + 'description' => __( 'Production sites delivered across publishers, retailers and operators.', 'ls-theme' ), + ), + array( + 'value' => '5', + 'suffix' => __( '★', 'ls-theme' ), + 'label' => __( 'Approved client praise', 'ls-theme' ), + 'description' => __( 'Every testimonial on the site is named, dated and approved.', 'ls-theme' ), + ), +); +?> + +
+ + +
+ + +
+ + + +

+ +
+ + + +

+ + + +

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

+ + + +

+ + + +

+ +
+ +
+ + + +
+ +
+ diff --git a/specs/001-services-page/tasks.md b/specs/001-services-page/tasks.md index 683b30f..f55e8ce 100644 --- a/specs/001-services-page/tasks.md +++ b/specs/001-services-page/tasks.md @@ -37,7 +37,7 @@ Single WordPress block-theme project. Existing Services page patterns live in - [x] T001 Pull design context (code, tokens, screenshot) for Entry Points via the Figma MCP tools, node `8044-164205`, from the file `https://www.figma.com/design/OTqchq3sRBzUy6TICruzc3/LightSpeedWP-Design-System` -- [ ] T002 [P] Pull design context for Delivery by the Numbers, node `8044-164253`, same Figma +- [x] T002 [P] Pull design context for Delivery by the Numbers, node `8044-164253`, same Figma file - [ ] T003 [P] Pull design context for the closing CTA, node `8044-164294`, same Figma file @@ -59,7 +59,7 @@ new pattern file is written. `patterns/sections/work-related-routes.php`) vs. create, per its approval-gated reuse-or-create workflow — get explicit sign-off on the proposal before any file is written -- [ ] T005 [P] Invoke `pattern-extractor`'s analysis phase for Delivery by the Numbers (Figma +- [x] T005 [P] Invoke `pattern-extractor`'s analysis phase for Delivery by the Numbers (Figma context from T002): propose reuse of `styles/sections/cards/stat-segment.json` (already used by `patterns/section-stats-grid.php`) vs. create, same approval-gated workflow - [ ] T006 [P] Invoke `pattern-extractor`'s analysis phase for the closing CTA (Figma context @@ -133,23 +133,23 @@ the stated metrics render correctly and match Figma, per quickstart.md. ### Implementation for User Story 2 -- [ ] T016 [US2] Execute `pattern-extractor`'s build phase for Delivery by the Numbers, using +- [x] T016 [US2] Execute `pattern-extractor`'s build phase for Delivery by the Numbers, using T005's approved proposal — creates `patterns/sections/services-delivery-numbers.php` with the standard pattern header; `theme-color-token-enforcer` runs automatically if new JSON styling is written -- [ ] T017 [US2] Write the Delivery Metric array (per data-model.md: value, label for each) and +- [x] T017 [US2] Write the Delivery Metric array (per data-model.md: value, label for each) and render loop, reusing `stat-segment.json` if T005 confirmed it fits, or the new shape-named style if T005 identified a genuine gap (consult `wp-block-style-audit` before writing any new `css` field) -- [ ] T018 [US2] Confirm the row layout does not visually unbalance when a value has +- [x] T018 [US2] Confirm the row layout does not visually unbalance when a value has significantly more digits than its siblings (data-model.md validation rule) — test with the actual longest value from the Figma frame, not a placeholder -- [ ] T019 [US2] Assemble the Delivery by the Numbers section onto the Services page content, +- [x] T019 [US2] Assemble the Delivery by the Numbers section onto the Services page content, after Entry Points and before the closing CTA -- [ ] T020 [US2] Register a `services-delivery-numbers` bundle in `inc/animations.php` with a - `condition` of `is_page( 'services' )`, same as T013 -- [ ] T021 [US2] Add the editor style and build-script wiring, same pattern as T014 -- [ ] T022 [US2] Run the full validation suite from T015 against every file touched in +- [x] T020 [US2] **N/A** — same reason as T013: no new SCSS/JSON styling was written (pure + reuse of `stat-segment.json`), so there is no stylesheet bundle to register. +- [x] T021 [US2] **N/A** — same reason as T014, no new stylesheet exists. +- [x] T022 [US2] Run the full validation suite from T015 against every file touched in T016-T021 **Checkpoint**: Delivery by the Numbers section renders correctly, independently testable. From f7d258b82ad40497a575a00c4d6fedaab245b462 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Fri, 11 Sep 2026 12:01:33 +0200 Subject: [PATCH 4/6] Fix mobile/tablet layout for Entry Points and Delivery Numbers (LS-1598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug fix - services-entry-points.php: switch the card grid from a fixed columnCount:2 to minimumColumnWidth, matching the technique already used in work-related-routes.php — WordPress's native auto-fit grid collapses to 1 column on mobile/tablet on its own, no CSS needed. Fixes 2-column squeeze forcing multi-line text wrap at narrow widths. - services-delivery-numbers.php: add a scoped SCSS partial that flips the shared Stat Segment divider from a vertical border-inline-end to a horizontal border-block-end once the row stacks below 782px (WP's own columns-stacking breakpoint), with the trailing divider correctly stripped from the last stacked item. Scoped to this section only — the shared stat-segment.json style used elsewhere is untouched. --- assets/css/services-delivery-numbers.css | 1 + functions.php | 1 + inc/animations.php | 11 +++++++ package.json | 6 ++-- .../sections/services-delivery-numbers.php | 4 +-- patterns/sections/services-entry-points.php | 2 +- .../structural/services-delivery-numbers.scss | 33 +++++++++++++++++++ 7 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 assets/css/services-delivery-numbers.css create mode 100644 src/scss/structural/services-delivery-numbers.scss diff --git a/assets/css/services-delivery-numbers.css b/assets/css/services-delivery-numbers.css new file mode 100644 index 0000000..8130fc1 --- /dev/null +++ b/assets/css/services-delivery-numbers.css @@ -0,0 +1 @@ +@media(max-width: 781.98px){.ls-delivery-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-delivery-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 034f025..c178c8a 100644 --- a/functions.php +++ b/functions.php @@ -74,6 +74,7 @@ function ls_theme_setup() { add_editor_style( 'assets/css/services-linked-decisions.css' ); add_editor_style( 'assets/css/services-service-clusters.css' ); add_editor_style( 'assets/css/services-service-tiles.css' ); + add_editor_style( 'assets/css/services-delivery-numbers.css' ); add_editor_style( 'assets/css/work-hero.css' ); add_editor_style( 'assets/css/work-single-hero.css' ); add_editor_style( 'assets/css/blog-hero.css' ); diff --git a/inc/animations.php b/inc/animations.php index 20b16e4..045312b 100644 --- a/inc/animations.php +++ b/inc/animations.php @@ -75,6 +75,7 @@ function ls_theme_get_bundle_render_markers() { 'services-linked-decisions' => array( 'classes' => array( 'ls-process-pill' ) ), 'services-service-clusters' => array( 'classes' => array( 'ls-cluster-tag' ) ), 'services-service-tiles' => array( 'classes' => array( 'is-style-card-service-tile' ) ), + 'services-delivery-numbers' => array( 'classes' => array( 'ls-delivery-stats-row' ) ), 'work-hero' => array( 'classes' => array( 'ls-work-hero' ) ), 'work-single-hero' => array( 'classes' => array( 'ls-work-single-meta' ) ), 'blog-hero' => array( 'classes' => array( 'ls-blog-hero' ) ), @@ -304,6 +305,16 @@ function ls_theme_get_effect_styles( $context = 'front' ) { return is_page( 'services' ); }, ), + 'services-delivery-numbers' => array( + 'handle' => 'ls-theme-services-delivery-numbers', + 'path' => 'assets/css/services-delivery-numbers.css', + 'contexts' => array( 'front', 'editor' ), + // Same reasoning as services-service-tiles above — scoped to a real page slug + // from the start. + 'condition' => static function () { + return is_page( 'services' ); + }, + ), 'work-hero' => array( 'handle' => 'ls-theme-work-hero', 'path' => 'assets/css/work-hero.css', diff --git a/package.json b/package.json index 35ce368..2ea322a 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", - "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", + "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", + "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", "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" + "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" }, "devDependencies": { "@axe-core/playwright": "^4.13.0", diff --git a/patterns/sections/services-delivery-numbers.php b/patterns/sections/services-delivery-numbers.php index f2a992c..113a36a 100644 --- a/patterns/sections/services-delivery-numbers.php +++ b/patterns/sections/services-delivery-numbers.php @@ -59,8 +59,8 @@ - -
+ +
diff --git a/patterns/sections/services-entry-points.php b/patterns/sections/services-entry-points.php index c378f48..ae6ed72 100644 --- a/patterns/sections/services-entry-points.php +++ b/patterns/sections/services-entry-points.php @@ -70,7 +70,7 @@
- +
diff --git a/src/scss/structural/services-delivery-numbers.scss b/src/scss/structural/services-delivery-numbers.scss new file mode 100644 index 0000000..c402367 --- /dev/null +++ b/src/scss/structural/services-delivery-numbers.scss @@ -0,0 +1,33 @@ +/********** Services - Delivery Numbers (Services page only) **********/ + +/* + * Services Delivery Numbers (LS-1598, single consumer + * patterns/sections/services-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. + */ + +// 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. work-archive-sections.scss) — the point at which this row's 3 columns +// actually become a vertical stack. +@media (max-width: 781.98px) { + .ls-delivery-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-delivery-stats-row > .wp-block-column:last-child .is-style-stat-segment { + border-block-end: 0; + padding-block-end: 0; + margin-block-end: 0; + } +} From 214a63f4892983982b4d61c52cd7ceda05a8603d Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Fri, 11 Sep 2026 12:42:40 +0200 Subject: [PATCH 5/6] Add CHANGELOG entry for Entry Points and Delivery by the Numbers (LS-1598) --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdbfd88..6950125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [Unreleased] — Build Services page: Entry Points and Delivery by the Numbers (LS-1598) + +### Added + +- Added `patterns/sections/services-entry-points.php`: "You don't have to buy everything at once" — eyebrow/heading/description with a 2x2 grid of 4 entry-point link cards, reusing the existing Card - Link Row style. +- Added `patterns/sections/services-delivery-numbers.php`: "Twelve years of WordPress engineering" — a centered eyebrow/heading/description intro followed by 3 centered delivery-scale stats, reusing the existing Stat Segment style. + +### Fixed + +- Fixed Entry Points' card grid forcing a 2-column squeeze (and multi-line text wrap) on mobile/tablet — switched from a fixed `columnCount` to `minimumColumnWidth`, which collapses to 1 column natively. +- Fixed Delivery by the Numbers' stat divider staying a vertical line on the right of a stacked mobile/tablet card instead of a horizontal line between stacked cards — added a scoped SCSS override that flips the divider orientation below 782px, without touching the shared Stat Segment style used elsewhere. + +[PR #55](https://github.com/lightspeedwp/ls-theme/pull/55) + +--- + ## [Unreleased] — Build Services page: service tiles section (LS-1598) ### Added From d2a5f5889ab5e45216ef7fd5da67d2a5b3120f69 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Fri, 11 Sep 2026 13:57:29 +0200 Subject: [PATCH 6/6] Fix PR #55 review findings (LS-1598) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug fix - services-entry-points.php, services-delivery-numbers.php: fix wp:icon dimensions being a sibling of style instead of nested inside it — core/icon serializes dimensions as a block support under style.dimensions, not a root attribute, so the width was being silently ignored and every icon rendered at the 24px default - services-entry-points.php: increase the card grid's minimumColumnWidth from 260px to 340px — at common desktop widths (confirmed live at 1440px) the smaller value let a 3rd card fit per row, breaking the intended 2x2 layout into 3+1 Context - specs/001-services-page/tasks.md: update T020/T021 from N/A to done with the actual files/wiring — they were correctly N/A when the section was first built (no SCSS needed), but the later mobile-divider fix added a real stylesheet, making them stale --- patterns/sections/services-delivery-numbers.php | 2 +- patterns/sections/services-entry-points.php | 6 +++--- specs/001-services-page/tasks.md | 11 ++++++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/patterns/sections/services-delivery-numbers.php b/patterns/sections/services-delivery-numbers.php index 113a36a..fe600ad 100644 --- a/patterns/sections/services-delivery-numbers.php +++ b/patterns/sections/services-delivery-numbers.php @@ -41,7 +41,7 @@
- +

diff --git a/patterns/sections/services-entry-points.php b/patterns/sections/services-entry-points.php index ae6ed72..e45ee80 100644 --- a/patterns/sections/services-entry-points.php +++ b/patterns/sections/services-entry-points.php @@ -50,7 +50,7 @@
- +

@@ -70,7 +70,7 @@
- +
@@ -82,7 +82,7 @@

- +
diff --git a/specs/001-services-page/tasks.md b/specs/001-services-page/tasks.md index f55e8ce..808ee20 100644 --- a/specs/001-services-page/tasks.md +++ b/specs/001-services-page/tasks.md @@ -146,9 +146,14 @@ the stated metrics render correctly and match Figma, per quickstart.md. the actual longest value from the Figma frame, not a placeholder - [x] T019 [US2] Assemble the Delivery by the Numbers section onto the Services page content, after Entry Points and before the closing CTA -- [x] T020 [US2] **N/A** — same reason as T013: no new SCSS/JSON styling was written (pure - reuse of `stat-segment.json`), so there is no stylesheet bundle to register. -- [x] T021 [US2] **N/A** — same reason as T014, no new stylesheet exists. +- [x] T020 [US2] Registered a `services-delivery-numbers` bundle in `inc/animations.php` with + a `condition` of `is_page( 'services' )`. Originally marked N/A (no new stylesheet was + needed for the section's initial build, which purely reused `stat-segment.json`), but the + follow-up mobile-divider fix added a real SCSS file, making a bundle necessary after all. +- [x] T021 [US2] Added `add_editor_style( 'assets/css/services-delivery-numbers.css' )` to + `functions.php` and wired the new SCSS file into `package.json`'s + `build:css`/`build:css:dev`/`watch:css` scripts, matching the existing `services-*` + entries — same reason as T020, added by the mobile-divider fix. - [x] T022 [US2] Run the full validation suite from T015 against every file touched in T016-T021