# Theme Local Services — Improvement & Growth Plan

> Package: capell-app/theme-local-services · Kind: theme · Tier: premium · Product group: Capell Themes · Bundle: themes · Status: Complete

## 1. Snapshot

`LocalServicesThemeServiceProvider` registers theme key `local-services` (`src/LocalServicesThemeServiceProvider.php`), with the layout view `capell-theme-local-services::page` (`resources/views/page.blade.php` — a token-bound shell + skip link that echoes pre-rendered `$content`) and 19 package-owned section views under `resources/views/sections/` (hero, features, services, service-packages, service-areas, locality-proof, proof, reviews-testimonials, trust-badges, opening-hours, structured-data, content-listing, quote-form, quote-estimator, case-studies, before-after-gallery, resources, contact, cta — `navigation` + `footer` are deliberately inherited from foundation, returned as `null` by `isFoundationSection()`). The demo command `capell:theme-local-services-demo` exists and delegates to `InstallLocalServicesThemeDemoAction` → `ThemeDemoPageInstaller`; `DemoCommandTest` proves it installs ≥7 demo pages idempotently. Optional `quote-form`/`quote-estimator` (Form Builder/Bookings) and `resources` (Blog) sections are guarded via `ViewSectionRenderer` extra view data with static fallbacks. The marketplace summary/Composer description now use buyer-facing quote-request, service-area, and click-to-call positioning. Health checks verify provider, package file, manifest, and screenshot boundaries. Screenshots: capell.json `marketplace.screenshots` promotes **4** committed assets (extension-card plus homepage, service-area, and quote-form route captures). `docs/screenshots.json` declares **9** required PNG captures, all committed under `docs/screenshots/`.

## Completed Improvement Slices

- **2026-06-03:** Rewrote marketplace/Composer copy, replaced the critical health-check stub with real diagnostics, and documented the intentional split between runtime `extends: default` and manifest dependency `extends: capell-app/foundation-theme`.
- **2026-06-04:** Moved service-area defaults into translations, made service-area cards data-driven from section render data, added the default contact anchor, removed dead `href="#"` links, added fallback/custom render coverage, and hardened manifest tests.
- **2026-06-05:** Replaced the decorative quote-form fallback with a real translated public form, added configurable `formAction`/`formMethod` support, and covered fallback/custom render paths in tests.
- **2026-06-05:** Added data-driven contact actions with safe `tel:`, `mailto:`, address, and map rendering, translated static fallback states, and focused public render coverage.
- **2026-06-06:** Replaced the 3 SVG marketplace layout diagrams with Capell runner PNG captures and fulfilled the 9-entry `docs/screenshots.json` contract.
- **2026-06-07:** Added hydrated trust/accreditation badge and before/after gallery sections with translated fallbacks, image attributes, public-output safety checks, and anonymous render-budget coverage.
- **2026-06-07:** Added a handle-gated Form Builder quote-form embed, Bookings handoff CTA, and scoped dark-mode shell tokens with contrast-safe overrides for existing section utilities.

## 2. Improvements (existing functionality)

1. **Shipped 2026-06-05: Replace decorative quote-form with a real fallback form** — `quote-form.blade.php` now renders a public `<form>` with translated name, phone, postcode, service, and message fields, a configurable `formAction`/`formMethod`, and a default `/contact` POST target. Focused render tests cover default and hydrated submission targets plus public-output safety. — converts the primary CTA from theatre to function — `resources/views/sections/quote-form.blade.php` — M
2. **Shipped 2026-06-04: Fix dead service-area links + hardcoded districts** — `service-areas.blade.php` now renders translated default areas or `$section->items` entries with `label`/`title`/`name`, `url`, and optional `postcode`/`postcodePrefix`. Empty data renders a translated empty state, real URLs render anchors, default areas point to the contact section anchor, and no-URL entries render static labels. — fixes broken links + i18n on a core local-SEO section — `resources/views/sections/service-areas.blade.php` — S
3. **Shipped 2026-06-07: dark-mode system** — the theme now has scoped `.dark .local-services-shell`, `.local-services-shell.dark`, and `prefers-color-scheme: dark` token layers, with card, field, border, background, and text overrides for existing section utility colours. — prevents partial dark UA styling and keeps the premium theme coherent in dark contexts — `resources/css/theme-local-services.css` — M
4. **Shipped 2026-06-07: decide and standardise the Tailwind `tw:` prefix** — theme package Blade stays unprefixed because it is compiled through isolated package `tailwindImport`/`tailwindSource` assets rather than the host store frontend bundle; a source guard pins that exemption. — prevents class collisions / aligns with house rule — all `resources/views/**/*.blade.php` — L
5. **Shipped 2026-06-06: hydrate hero image as the LCP element** — hero media now resolves informative alt text from hydrated media/image alt, heading, or a translated fallback; emits width/height from render data with stable 1600×1000 defaults; and marks the above-fold image as eager, async decoded, and `fetchpriority="high"`. — improves LCP/CLS and a11y on the money page — `resources/views/sections/hero.blade.php`, `resources/lang/en/generic.php` — S
6. **Shipped 2026-06-06: remaining literal section defaults moved to translations** — services, case studies, proof metrics, and resources now source fallback cards plus repeated labels from `resources/lang/en/generic.php` instead of hard-coded English in Blade PHP blocks. — completes translation coverage already started elsewhere — `services.blade.php`, `case-studies.blade.php`, `proof.blade.php`, `resources.blade.php`, `resources/lang/en/generic.php` — S
7. **Radius/border consistency accepted for current theme scope.** — The shell dark-mode/token slice now normalizes card, field, border, background, and text treatment across the theme. A deeper single-token radius sweep would be visual polish, not an active roadmap blocker. — `resources/css/theme-local-services.css` + section views — Done

## 3. Missing Features (gaps)

`capabilities[]` is only `["theme-local-services","theme-local-services-frontend"]` because this package is a renderer-only theme. Against a local-services site, the table-stakes renderer surfaces are now present:

- **Shipped 2026-06-07: LocalBusiness structured data (JSON-LD)** — `structured-data` renders `LocalBusiness`, `Service`, `OfferCatalog`, `FAQPage`, and opening-hours JSON-LD from hydrated render data. **Differentiator.**
- **Shipped 2026-06-05: Click-to-call / contact actions** — the `contact` section now renders hydrated phone, email, address, and map data as real safe links where possible, with translated non-clickable states when data is missing or unsafe. Mobile local-services traffic is call-driven. **Table stakes.**
- **Shipped 2026-06-07: Opening hours section** — `opening-hours` renders hydrated hours and an optional open/closed-now state. **Table stakes** for trades/salon/clinic.
- **Shipped 2026-06-07: Reviews / testimonials** — `reviews-testimonials` renders quote/star/source testimonial cards from hydrated data. **Table stakes** (trust).
- **Shipped 2026-06-07: Trust / accreditation badges** — `trust-badges` renders Gas Safe / NICEIC / DBS / insurance / guarantee-style credentials from hydrated data. **Differentiator** for trades verticals.
- **Shipped 2026-06-07: Before/after gallery** — `before-after-gallery` renders image-pair project proof with service and location context. **Differentiator.**
- **FAQ schema shipped 2026-06-07** — `structured-data` supports `FAQPage` JSON-LD from hydrated `faqs`; a visible FAQ section can still be added as optional future content depth. **Table stakes.**
- **Shipped 2026-06-07: Cross-sell hooks** — `quote-form` now has a handle-gated Form Builder embed branch and a Bookings handoff CTA when the Bookings package is installed and a booking URL/route is available. Blog `resources` integration remains a static/copy-aware fallback. Tie future visible package support to `supports[]` when those dependencies become first-class theme install recommendations.
- **Local SEO depth**: JSON-LD, service-area content, address/map/contact actions, and NAP-style content can now render from hydrated data. Dedicated per-area landing generation and SEO Suite hooks remain future package depth.

## 4. Issues / Risks

- **Dual `extends` meaning is documented, but should stay tested** — capell.json declares the package dependency (`"extends": "capell-app/foundation-theme"`) while `definition()` uses the Theme Studio runtime inheritance key (`extends: 'default'`). The provider now documents the distinction; keep tests around both boundaries so this does not regress. — `src/LocalServicesThemeServiceProvider.php`, `capell.json`, `tests/Unit/LocalServicesThemeDefinitionTest.php`
- **Health check shipped, keep expanding diagnostics as the package grows** — `ThemeLocalServicesHealthCheck` now verifies provider, package file, manifest, and screenshot boundaries. Add any future sections/assets to those diagnostics instead of letting the critical check drift back into a shallow compatibility-only assertion. — `src/Health/ThemeLocalServicesHealthCheck.php`, `capell.json`
- **Surfaces aligned.** — `capell.json` declares `["frontend", "console"]`, matching the README/overview and demo command surface. — `capell.json`, `README.md`
- **Shipped 2026-06-06: screenshot contract fulfilled** — 9 declared PNG capture paths now exist under `docs/screenshots/`, each with runner fixture URL metadata. — `docs/screenshots.json`
- **Shipped 2026-06-06: marketplace SVG placeholders replaced** — the 3 hand-drawn `.svg` layout diagrams have been removed from the marketplace gallery and replaced with real Capell runner PNG captures for homepage, service-area, and quote-form workflows. — `capell.json`, `docs/screenshots/`
- **WCAG**: hero/quote/cta/proof contain many `aria-hidden` decorative bar-charts (acceptable), service-area cards now avoid dead anchors, hero media has an informative translated alt fallback, and the carousel prev/next buttons are `hidden` by default with no documented JS to reveal them (`data-carousel-*` hooks present, no shipped controller in package). — `service-areas.blade.php`, `proof.blade.php`, `services.blade.php`, `case-studies.blade.php`, `resources.blade.php`
- **Carousel JS provenance** — every carousel relies on `data-carousel`/`data-carousel-track`/`data-carousel-prev/next` and `.theme-carousel-button` but the package ships no JS; it assumes foundation-theme provides it. If foundation drops it, every carousel silently degrades to an overflow-scroll with invisible buttons. Document the dependency and add a render/no-JS test. — `resources/views/sections/*` (all carousels)
- **Public-output safety: good.** `PublicOutputSafetyTest` asserts no DB calls / no admin metadata in Blade + lang, and views echo pre-rendered `$content` with no queries — compliant with the public-output rule. Keep this guard and extend it to any new section.
- **Performance budget covered.** — Manifest `frontendRenderBudgetMs: 20`, `adminQueryBudget: 0`, and no-query public rendering are covered by anonymous per-section render tests. — `capell.json`, `tests/Unit/LocalServicesThemeDefinitionTest.php`
- **Anonymous render coverage shipped.** — Local Services-owned sections render anonymously with public-output safety and zero select-query coverage; foundation-owned navigation/footer remain inherited. — `tests/Unit/LocalServicesThemeDefinitionTest.php`, `tests/Unit/PublicOutputSafetyTest.php`

## 5. Marketplace & Selling

The manifest and Composer description now use this buyer-facing 1-sentence summary:

> A conversion-first Capell theme for local trades, clinics, and service businesses — built around quote requests, service-area coverage, and click-to-call trust.

The manifest marketplace description now uses this buyer-facing product story:

> Theme Local Services turns visitors into booked jobs. It ships hero, services, service-area, locality-proof, quote-estimator, case-study, and contact sections tuned for plumbers, electricians, salons, cleaners, and clinics, with a teal/amber palette and a quote desk front-and-centre. Optional Form Builder and Blog integrations upgrade the enquiry form and resources feed when those packages are installed, and the theme inherits foundation navigation, footer, and SEO. Drop in your services and coverage areas and launch a credible local-business site in minutes.

**Media status:** The 3 SVG placeholder diagrams have been replaced with route-backed PNG captures, and the 9-entry `docs/screenshots.json` capture plan is fulfilled. Remaining media depth is optional polish: add a darker theme variation and mobile-specific marketplace crops if marketplace media needs more variation.

**Differentiation / target buyer:** Among the 10 sibling themes (`theme-agency`, `-commerce`, `-corporate`, `-education`, `-healthcare`, `-knowledge`, `-nonprofit`, `-portfolio`, `-saas`), this is the only **lead-capture / quote-led** theme. Target buyer: a solo-operator or small local trade/services business (or the agency building for them) that needs phone calls and quote forms, not a brochure. Lean into the quote-desk + coverage + trust story to separate from `theme-corporate`/`theme-agency`.

Completed 2026-06-07. Every prioritized roadmap row is closed. Theme Local Services now has aligned frontend/console surfaces, buyer-facing marketplace copy, route-backed captures, health diagnostics, public quote form/Form Builder/Bookings branches, click-to-call/contact/map output, structured data, reviews, opening hours, trust badges, before/after gallery, translations, LCP image hints, dark tokens, Tailwind-prefix exemption coverage, anonymous per-section render/no-query guards, and dual-extends tests.

**Keywords/tags:** local business, trades, plumber, electrician, salon, cleaner, quote form, service area, click to call, lead generation, local SEO, LocalBusiness schema.

## 6. Prioritized Roadmap

| Item                                                                             | Bucket | Effort | Impact | Section ref                                                                                                                                                                                       |
| -------------------------------------------------------------------------------- | ------ | ------ | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Replace decorative quote-form with a real fallback `<form>`                      | Done   | M      | High   | §2.1 — closed 2026-06-05: fallback quote form now renders translated name/phone/postcode/service/message fields with configurable submission target.                                              |
| Add click-to-call / address / map to contact section                             | Done   | M      | High   | §3 — closed 2026-06-05: contact cards now render safe `tel:`, `mailto:`, address, and map links from hydrated section data with translated static fallbacks.                                      |
| Fix surfaces mismatch (manifest vs README)                                       | Done   | S      | Low    | §4 — closed 2026-06-05: `capell.json` now declares both `frontend` and `console`, matching the README and demo command surface.                                                                   |
| Remove orphan dark-mode utilities                                                | Done   | M      | Med    | §2.3 — closed 2026-06-07: scoped shell dark tokens now cover card, field, border, background, and text utilities across the theme.                                                                |
| Add LocalBusiness + Service + FAQPage JSON-LD (render-data driven)               | Done   | M      | High   | §3 — closed 2026-06-07: `structured-data` renders LocalBusiness, Service, OfferCatalog, FAQPage, and opening-hours JSON-LD from hydrated section data only.                                       |
| Add reviews/testimonials section + lang keys                                     | Done   | M      | High   | §3 — closed 2026-06-07: `reviews-testimonials` ships translated headings, rating labels, empty state, and hydrated review card rendering.                                                         |
| Add opening-hours section ("open now")                                           | Done   | M      | Med    | §3 — closed 2026-06-07: `opening-hours` renders hydrated hours, by-request fallback copy, and optional open/closed-now status.                                                                    |
| Move remaining literal English to `generic.php` lang file                        | Done   | S      | Med    | §2.6                                                                                                                                                                                              |
| Hero image LCP/CLS + required alt                                                | Done   | S      | Med    | §2.5                                                                                                                                                                                              |
| Resolve `tw:` prefix convention question across all views                        | Done   | S      | Med    | §2.4 — closed 2026-06-07: theme package Blade stays unprefixed because it is compiled through isolated `tailwindImport`/`tailwindSource` package assets; a source guard pins that exemption.      |
| Replace SVG placeholders with real screenshots; fulfil/prune screenshots.json    | Done   | M      | High   | §4, §5 — closed 2026-06-06: 9 route-backed PNG captures are committed and marketplace SVG diagrams were replaced with real workflow screenshots.                                                  |
| Add anonymous render test per section + 0-query/20ms budget guard                | Done   | M      | Med    | §4 — closed 2026-06-07: every Local Services-owned section renders anonymously under the manifest frontend budget with zero select queries.                                                       |
| Add trust/accreditation badge strip + before/after gallery sections              | Done   | M      | Med    | §3 — closed 2026-06-07: `trust-badges` and `before-after-gallery` render hydrated credentials and image-pair project proof with translated empty states and public-output coverage.               |
| Real Form Builder / Bookings embed in quote-form (cross-sell) + dark-mode system | Done   | L      | High   | §3, §2.3 — closed 2026-06-07: quote-form supports handle-gated Form Builder embedding, Bookings handoff CTAs, and scoped shell dark-mode tokens with test coverage.                               |
| Shipped 2026-06-06: Keep dual `extends` semantics explicitly covered by tests    | Done   | S      | Med    | §4 — `ManifestRequirementsTest` pins manifest dependency `capell-app/foundation-theme`, `LocalServicesThemeDefinitionTest` pins runtime `extends: default`, and the provider documents the split. |