Component showcase

Services

Offer, menu and catalogue layouts. 9 variants, 9 rendered live.

cards

servicesin useOpen live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/Services.astro
  • src/components/sections/services/ServiceCards.astro
How to pin
{
  "variants": {
    "<section id>": "cards"
  }
}

yes: theme.json variants[<section.id>]="cards" (SectionList passes extras.variants as pinned). Not validated: a wrong id for the kind falls to the component default

Reads
services | services{title,intro,items[{title,text,image?,details?[]}]}
Used by
not tracked (layout is seeded per site)
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
planLayout (components/variants.ts): optionsFor(section) eligibility; a family cycles through unused layouts first; pick(seed, kind:index). Precedence: theme.json variants[sectionId] (facts.pinned, NOT validated against eligibility) > effect registry forced variants (only if eligible) > seeded
Notes
photo cards with icon badge (default); also forced when bento lacks photos

menu

servicesin useOpen live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/Services.astro
  • src/components/sections/services/ServiceMenu.astro
How to pin
{
  "variants": {
    "<section id>": "menu"
  }
}

yes: theme.json variants[<section.id>]="menu" (SectionList passes extras.variants as pinned). Not validated: a wrong id for the kind falls to the component default

Reads
services | services{title,intro,items[{title,text,image?,details?[]}]}
Used by
not tracked (layout is seeded per site)
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
planLayout (components/variants.ts): optionsFor(section) eligibility; a family cycles through unused layouts first; pick(seed, kind:index). Precedence: theme.json variants[sectionId] (facts.pinned, NOT validated against eligibility) > effect registry forced variants (only if eligible) > seeded
Notes
restaurant menu with sticky category rail; required for price lists (isMenuLike)

menu-tabs

servicesin useOpen live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/Services.astro
  • src/components/sections/services/ServiceMenu.astro
How to pin
{
  "variants": {
    "<section id>": "menu-tabs"
  }
}

yes: theme.json variants[<section.id>]="menu-tabs" (SectionList passes extras.variants as pinned). Not validated: a wrong id for the kind falls to the component default

Reads
services | services{title,intro,items[{title,text,image?,details?[]}]}
Used by
not tracked (layout is seeded per site)
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
planLayout (components/variants.ts): optionsFor(section) eligibility; a family cycles through unused layouts first; pick(seed, kind:index). Precedence: theme.json variants[sectionId] (facts.pinned, NOT validated against eligibility) > effect registry forced variants (only if eligible) > seeded
Notes
menu one category at a time

bento

servicesin useOpen live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/Services.astro
  • src/components/sections/services/ServiceBento.astro
How to pin
{
  "variants": {
    "<section id>": "bento"
  }
}

yes: theme.json variants[<section.id>]="bento" (SectionList passes extras.variants as pinned). Not validated: a wrong id for the kind falls to the component default

Reads
services | services{title,intro,items[{title,text,image?,details?[]}]}
Used by
not tracked (layout is seeded per site)
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
planLayout (components/variants.ts): optionsFor(section) eligibility; a family cycles through unused layouts first; pick(seed, kind:index). Precedence: theme.json variants[sectionId] (facts.pinned, NOT validated against eligibility) > effect registry forced variants (only if eligible) > seeded
Notes
4-9 items, all with photos (else cards)

list

servicesin useOpen live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/Services.astro
  • src/components/sections/services/ServiceList.astro
How to pin
{
  "variants": {
    "<section id>": "list"
  }
}

yes: theme.json variants[<section.id>]="list" (SectionList passes extras.variants as pinned). Not validated: a wrong id for the kind falls to the component default

Reads
services | services{title,intro,items[{title,text,image?,details?[]}]}
Used by
not tracked (layout is seeded per site)
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
planLayout (components/variants.ts): optionsFor(section) eligibility; a family cycles through unused layouts first; pick(seed, kind:index). Precedence: theme.json variants[sectionId] (facts.pinned, NOT validated against eligibility) > effect registry forced variants (only if eligible) > seeded
Notes
editorial ruled rows, photo peek on hover; item copy >60 words

stack

servicesin useOpen live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/Services.astro
  • src/components/sections/services/ServiceStack.astro
How to pin
{
  "variants": {
    "<section id>": "stack"
  }
}

yes: theme.json variants[<section.id>]="stack" (SectionList passes extras.variants as pinned). Not validated: a wrong id for the kind falls to the component default

Reads
services | services{title,intro,items[{title,text,image?,details?[]}]}
Used by
not tracked (layout is seeded per site)
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
planLayout (components/variants.ts): optionsFor(section) eligibility; a family cycles through unused layouts first; pick(seed, kind:index). Precedence: theme.json variants[sectionId] (facts.pinned, NOT validated against eligibility) > effect registry forced variants (only if eligible) > seeded
Notes
3-4 sticky stacking cards; needs intro or an image

showcase

servicesin useOpen live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/Services.astro
  • src/components/ui/HScroll.astro
How to pin
{
  "variants": {
    "<section id>": "showcase"
  }
}

yes: theme.json variants[<section.id>]="showcase" (SectionList passes extras.variants as pinned). Not validated: a wrong id for the kind falls to the component default

Reads
services | services{title,intro,items[{title,text,image?,details?[]}]}
Used by
not tracked (layout is seeded per site)
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
planLayout (components/variants.ts): optionsFor(section) eligibility; a family cycles through unused layouts first; pick(seed, kind:index). Precedence: theme.json variants[sectionId] (facts.pinned, NOT validated against eligibility) > effect registry forced variants (only if eligible) > seeded
Notes
pinned horizontal scroller (ui/HScroll); photo catalogue 5..12 items; one hscroll per page

icons

servicesin useOpen live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/Services.astro
  • src/components/sections/services/ServiceIcons.astro
How to pin
{
  "variants": {
    "<section id>": "icons"
  }
}

yes: theme.json variants[<section.id>]="icons" (SectionList passes extras.variants as pinned). Not validated: a wrong id for the kind falls to the component default

Reads
services | services{title,intro,items[{title,text,image?,details?[]}]}
Used by
not tracked (layout is seeded per site)
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
planLayout (components/variants.ts): optionsFor(section) eligibility; a family cycles through unused layouts first; pick(seed, kind:index). Precedence: theme.json variants[sectionId] (facts.pinned, NOT validated against eligibility) > effect registry forced variants (only if eligible) > seeded
Notes
ruled text index for sparse or short-tile items

plans

pricinglead-only (lead-webexpress)Open live
desktop preview
desktop 1280px
mobile preview
mobile 390px
Files
  • src/components/sections/PricingPlans.astro
  • src/components/SectionList.astro
  • src/lib/text.ts
How to pin

no: driven by the section kind

Reads
pricing (lead-webexpress content-schema) | pricing{title,eyebrow?,intro?,plans[{name,tagline?,price,features[],badge?,cta?}],addon?{title,text,cta?}}
Used by
lead-webexpress branch only
Quality gates
22 codes

sections (about/services/stats/faq/testimonials): plain-section thin-card empty-cards empty-area layout-imbalance misaligned-columns short-text-columns text-wall text-fragment headline-is-paragraph heading-truncated duplicate-heading eyebrow-equals-title duplicate-section duplicate-title flattened-list orphan-numbering related-sections-apart placeholder-icon-tile card-no-art empty-art-panel repeated-image

Selected by
content kind: a pricing section renders PricingPlans (one layout, not pinnable)
Notes
Plan cards with a featured (badged) plan and an add-on card; stacks on phones.