Blocks

Every block in the library catalog (@gronare/stomme/catalog), with its fields as they are written in a page's frontmatter.

A field path with a dot is a field inside a group: media.kind is written as media: { kind: … }. Defaults are what the CMS fills in when an editor adds the block, and what a block renders when a page written by hand leaves the field out; every switch defaults to off. Each block below is shown live, rendered by the same renderer a page uses, with the YAML that made it underneath. Run pnpm dev and open /lookbook to see every block with sample content, /lookbook/<slug> for one sample on its own page (the slug is the sample's label in the lookbook, such as /lookbook/hero), or /admin/blocks.html for the block gallery stomme cms writes. Add a custom block adds your own.

Fields many blocks share

Field Values What it does
eyebrow, heading, intro text the small label, the heading and the line under it
style.surface standard (default), tint, panel, band, dark, gradient the background; the renderer wraps the block in a full-width band, and a block never paints its own
style.accent brand (default), secondary, highlight the colour of the block's rule, icon or number; the eyebrow follows eyebrowColor in the theme instead
layout.beside below (default), equal, narrow, wide places the block beside the one before it, see Two blocks side by side
anchor text an id, so #anchor links scroll to the block; every block reads it, though the CMS offers it only on plans, steps and ctaPanel. On a page with a sectionNav, a block with a heading gets the slugged heading as its id instead

Buttons

A call to action is a cta group: cta.label plus cta.link, where the link is { page: '/about' } for a page on the site or { url: 'https://…' } for anything else, tel: and mailto: included. A second button is cta2, the FAQ aside's button is asideCta, and a featureGrid card carries its own cta.

cta:
  label: Get in touch
  link:
    page: /contact

The older flat pairs still render. block-migrations.json in the package is the full list of renames to the group form:

Blocks Flat pair Group
ctaBox, ctaPanel label, href cta
ctaBox label2, href2 cta2
cover, hero, pageHeader ctaLabel, ctaHref cta
cover, hero cta2Label, cta2Href cta2
faq asideCtaLabel, with asideHref or asideCtaHref asideCta
fragment linkLabel, link cta
featureGrid, on each item linkLabel, link the item's cta
plans, on each plan ctaLabel, ctaHref the plan's cta

Commands, video and headings

  • Command. hero and ctaPanel take command, a copyable code line, see Command.
  • Video. hero and textImage play a looping video when media.kind is video, see Video.
  • Section links. On a page with a sectionNav block, every block with a heading gets an id slugged from the heading, made unique with -2, -3, so the chips can link to it. A page without sectionNav gets no ids.
  • Emphasis. In these fields, words wrapped in *asterisks* are emphasised. In a display heading they are underlined in the heading's colour: the hero and pageHeader H1, the featureGrid, ctaBox, statPanel and plans headings, the callout quote and the fragment statement. In the panel layout of ctaBox the underlined words also turn the brand colour. In smaller text they are set in italics: the plans description, feature text, note.text and footnote, and the definition senses' text. Other plain-text fields show the asterisks as written, and Markdown fields set the words in italics.

Two blocks side by side

Set layout.beside on the second block to equal, narrow or wide and the renderer puts the two in one row: equal splits it down the middle, narrow makes the second block the smaller half, wide the larger. Below 900px wide the pair stacks in source order. A row never takes a third block, so three beside blocks in a row make one pair and one full-width block.

Bring to the first visit

  • Photos of the garden
  • A rough budget

The visit

Length
About an hour
Cost
Free
- type: checklist
  heading: Bring to the first visit
  layout:
    columns: 1
  items:
    - text: Photos of the garden
    - text: A rough budget
- type: factList
  heading: The visit
  layout:
    beside: equal
  items:
    - label: Length
      value: About an hour
    - label: Cost
      value: Free

Icons

featureGrid items, hero highlights and steps items take an icon by name. The names, in the order the CMS picker lists them:

check, star, heart, spark, sparkles, arrow, search, clock, calendar, pin, map, users, chat, bulb, chart, gauge, award, shield, tag, percent, coins, document, gear, refresh, link, globe, wifi, phone, mail, headset, home, roof, key, truck, tree, wrench, hammer, ruler, paint, plug, bolt, sun, panel, battery, leaf, flame, snowflake, thermometer, droplet, wind, spray, drone, camera, compass

An unknown or blank name draws spark. The CMS offers the names as a list in featureGrid and hero, but steps takes items[].icon as free text, so a typo there shows spark.

Hero and headers

hero

Full-width intro with a heading, text and buttons, and an optional image, video, highlights panel or animated motif.

Field Values What it does
eyebrow, heading, intro text heading is the page's H1
ticks[] text short checkmark lines under the intro
cta, cta2 button the button, and a quiet text link beside it; with no cta2, the phone number from the contact settings takes that place as a call link
command text a copyable command
media.kind none (default), image, video, highlights, motif what shows beside the text
media.image, media.imageAlt image, text the picture, also the video's poster
media.video, media.videoWebm, media.videoUrl file, file, URL the video; the URL is used instead of a file when set
media.ratio standard 4:3 (default), wide 16:9, square the media's shape
media.highlights[] icon, title, body the highlights panel
layout.height normal (default), tall tall fills the screen
layout.align top, center (default), bottom where the text sits in a tall hero
layout.width full (default), narrow narrow centres the hero in a reading column
layout.mediaPosition beside (default), below below puts the media under the text at full width
style.surface, style.accent see above
stomme

Start a site in one line.

Scaffold a site, open the CMS and build your first page in an afternoon.

pnpm create stomme my-site
type: hero
eyebrow: stomme
heading: Start a site in one line.
intro: Scaffold a site, open the CMS and build your first page in an afternoon.
command: pnpm create stomme my-site
cta:
  label: Read the tutorial
  link:
    url: "#"
cta2:
  label: See the blocks
  link:
    url: "#"

cover

Full-bleed banner with text over a photo, a looping video, a gradient or an animated pattern.

Field Values What it does
eyebrow, heading, intro text heading is the H1 and keeps line breaks
cta, cta2 button two buttons
media.kind image (default), video, gradient, animated the background; gradient and animated follow the brand colour
media.image, media.imageAlt image, text the photo, also the video's poster
media.video, media.videoUrl file, URL the video
media.overlay light, medium (default), strong how much the background is darkened
layout.align start (default, bottom left), center where the text sits
layout.vAlign empty, end, center, start moves the text up or down, keeping its side
layout.height tall (default), medium
A path between beds of lavender and catmint
Wren & Sorrel

Gardens that get better every year.

We design, build and look after gardens for homes across the valley.

type: cover
eyebrow: Wren & Sorrel
heading: Gardens that get better every year.
intro: We design, build and look after gardens for homes across the valley.
cta:
  label: Book a garden visit
  link:
    url: "#"
media:
  kind: image
  image: ../../starter/public/media/perennial-walk.webp
  imageAlt: A path between beds of lavender and catmint
layout:
  height: medium

Title band for an inner page.

Field Values What it does
eyebrow, heading, intro text heading is the H1
cta button
layout.variant light (default), dark dark puts the title on a dark full-width band, and when there is a cta, a call link to the contact settings' phone number sits beside it (Call followed by the number, worded by strings.contact.call)
layout.width narrow (default), full
style.surface see above
type: pageHeader
eyebrow: Services
heading: Garden care
intro: Pruning, planting and a seasonal plan, from one visit a year to one a month.
cta:
  label: Ask for a quote
  link:
    url: "#"

Text

prose

Rich text from Markdown. Inline images are optimised and placed by a title keyword, see Images in Markdown.

Field Values What it does
eyebrow, heading text
body Markdown
statValue, statLabel text one headline figure under the text
layout.width narrow (default), full
style.surface see above
Our approach

Plant for the tenth summer

A new garden looks its thinnest on the day it is finished. We plan for the garden it becomes, with shrubs that fill out and perennials that seed themselves where they are happy.

Read more about how a project runs.

9 in 10clients still with us after five years

type: prose
eyebrow: Our approach
heading: Plant for the tenth summer
body: |
  A new garden looks its thinnest on the day it is finished. We plan for the
  garden it becomes, with **shrubs that fill out** and perennials that seed
  themselves where they are happy.

  Read more about [how a project runs](#).
statValue: 9 in 10
statLabel: clients still with us after five years

textImage

Text beside an image or a looping video. With no heading and no text, the media fills the row.

Field Values What it does
eyebrow, heading text
body Markdown
media.kind image (default), video
media.image, media.imageAlt image, text also the video's poster
media.fit frame (default), cutout cutout drops the frame and caps the picture at 20rem, for a picture with a transparent background
media.video, media.videoWebm, media.videoUrl file, file, URL
layout.flip false (default) true puts the media on the left
style.surface see above
Care

Pruned at the right time

Most shrubs want cutting just after they flower. We keep a calendar for every garden we look after, so nothing is cut too early.

  • Fruit trees in late winter
  • Lavender after the first flush
Secateurs cutting back a shrub
type: textImage
eyebrow: Care
heading: Pruned at the right time
body: |
  Most shrubs want cutting just after they flower. We keep a calendar for
  every garden we look after, so nothing is cut too early.

  - Fruit trees in late winter
  - Lavender after the first flush
media:
  image: ../../starter/public/media/pruning.webp
  imageAlt: Secateurs cutting back a shrub

textQuote

Body text beside a pulled-out quote.

Field Values What it does
body Markdown
quote, attribution text
layout.flip false (default) true puts the quote on the left
style.surface, style.accent see above

The Hansens wanted somewhere to sit in the evening sun. We moved the patio to the far corner, laid it in reclaimed brick and planted it round with scented things that open at dusk.

We eat outside every night from May.
Eva Hansen, Lower Mill Lane
type: textQuote
body: |
  The Hansens wanted somewhere to sit in the evening sun. We moved the patio
  to the far corner, laid it in reclaimed brick and planted it round with
  scented things that open at dusk.
quote: We eat outside every night from May.
attribution: Eva Hansen, Lower Mill Lane

fragment

An editorial fragment: a large lead line, a short text and an optional link. Fragments in a row drift across the page.

Field Values What it does
eyebrow text usually on the first fragment only
statement text the large line
body text
cta button a link
layout.placement left (default), right, indent alternate them to break the grid
style.surface see above
How we started

One wheelbarrow, one van.

Maja and Tom took on their first garden in 2014, a single border behind a cottage.

Twelve years, four hundred gardens.

Most of them still call us every spring.

- type: fragment
  eyebrow: How we started
  statement: One wheelbarrow, one van.
  body: Maja and Tom took on their first garden in 2014, a single border behind a cottage.
- type: fragment
  statement: Twelve years, four hundred gardens.
  body: Most of them still call us every spring.
  cta:
    label: Meet the team
    link:
      url: "#"
  layout:
    placement: right

sectionNav

A row of chips that jump to the headings below it on the same page. The chips are read from the blocks below, so there is nothing to fill in.

Field Values What it does
label text the row's accessible name; default from strings.sectionNav.label
sections[] anchor, label a fixed list of chips; when set, the renderer uses it instead of reading the headings below. The CMS does not offer it
layout.sticky false (default) true keeps the row at the top of the window while scrolling
style.surface see above

The chips need blocks below them, so this example renders three.

Opening hours

Weekdays
07.00 to 16.00
Saturdays
By appointment

Before your visit

  • A few photos of the garden as it is
  • A rough idea of the budget
- type: sectionNav
- type: factList
  heading: Opening hours
  items:
    - label: Weekdays
      value: 07.00 to 16.00
    - label: Saturdays
      value: By appointment
- type: checklist
  heading: Before your visit
  layout:
    columns: 1
  items:
    - text: A few photos of the garden as it is
    - text: A rough idea of the budget

Cards and lists

featureGrid

Grid of cards with an icon or a number, a title and text. A card can link.

Field Values What it does
eyebrow, heading, intro text
items[] icon, title, body, tag, cta tag is a small label at the card's foot; cta makes the card a link, with Read more when its label is blank
layout.numbered false (default) true shows 01, 02, 03 instead of icons
layout.columns 3 (default), 2, 4 cards per row from 1040px wide; between 700px and 1040px the grid has three columns, below that one
style.surface, style.accent see above
What we do

One team from sketch to tenth summer

Design

A measured plan and a planting list you can keep.

Build

Paths, walls and patios laid by our own crew.

From 4 weeks

Care

Seasonal visits that keep the garden on course.

See care plans →
type: featureGrid
eyebrow: What we do
heading: One team from sketch to tenth summer
items:
  - icon: ruler
    title: Design
    body: A measured plan and a planting list you can keep.
  - icon: hammer
    title: Build
    body: Paths, walls and patios laid by our own crew.
    tag: From 4 weeks
  - icon: leaf
    title: Care
    body: Seasonal visits that keep the garden on course.
    cta:
      label: See care plans
      link:
        url: "#"

pillars, specialistGrid

pillars is a row of airy columns with a large brand numeral and a top rule, for principles or values. specialistGrid is a denser grid with a brand top rule, for a longer list of capabilities.

Field Values What it does
eyebrow, heading, intro text
items[] title, body
style.surface see above
How we work

Three things we hold to

01

Plant for the place

Soil, light and wind decide the list, not the catalogue.

02

Build it to last

Proper foundations under every path and wall.

03

Stay around

The same gardener visits each season.

type: pillars
eyebrow: How we work
heading: Three things we hold to
items:
  - title: Plant for the place
    body: Soil, light and wind decide the list, not the catalogue.
  - title: Build it to last
    body: Proper foundations under every path and wall.
  - title: Stay around
    body: The same gardener visits each season.
Garden care

What a care visit can cover

Hedges

Shaped twice a year, by hand where it shows.

Lawns

Scarifying, feeding and overseeding in spring.

Fruit trees

Winter pruning for shape and a better crop.

Ponds

Cleared in autumn before the leaves settle.

type: specialistGrid
eyebrow: Garden care
heading: What a care visit can cover
items:
  - title: Hedges
    body: Shaped twice a year, by hand where it shows.
  - title: Lawns
    body: Scarifying, feeding and overseeding in spring.
  - title: Fruit trees
    body: Winter pruning for shape and a better crop.
  - title: Ponds
    body: Cleared in autumn before the leaves settle.

plans

A row of pricing plan cards.

Field Values What it does
eyebrow, heading, intro text
plans[] name, pricePrefix, price, period, description, features[] (text), badge, highlight, note (label, text), cta one card per plan; price is shown as written; highlight: true marks the plan; note is set apart under the features
footnote text a small note under the cards
anchor text
style.surface see above
Care plans

Pick how often we come

Seasonal

 £240​per visit

Four visits a year, one each season.

  • Pruning and weeding
  • A written note after each visit

MonthlyMost chosen

from£180​per month

A gardener every month, the same one.

  • Everything in Seasonal
  • Lawn care from March to October
Book a first visit

Prices include green waste removal.

type: plans
eyebrow: Care plans
heading: Pick how often we come
plans:
  - name: Seasonal
    price: £240
    period: per visit
    description: Four visits a year, one each season.
    features:
      - text: Pruning and weeding
      - text: A written note after each visit
  - name: Monthly
    pricePrefix: from
    price: £180
    period: per month
    badge: Most chosen
    highlight: true
    description: A gardener every month, the same one.
    features:
      - text: Everything in Seasonal
      - text: Lawn care from March to October
    cta:
      label: Book a first visit
      link:
        url: "#"
footnote: Prices include green waste removal.

team

Grid of people with a photo and their own contact details.

Field Values What it does
eyebrow, heading, intro text
items[] image, name, role, email, phone, linkUrl, linkLabel, bio with no image, a neutral silhouette is drawn; a blank linkLabel shows the address
style.surface see above
The people

Who you meet

Maja Lind

Head gardener

Trained at Wisley and has planted every border we have built.

Tom Reyes

Builds and hard landscaping

Lays the paths, walls and patios.

type: team
eyebrow: The people
heading: Who you meet
items:
  - name: Maja Lind
    role: Head gardener
    email: maja@example.com
    bio: Trained at Wisley and has planted every border we have built.
  - name: Tom Reyes
    role: Builds and hard landscaping
    phone: 01632 960 412
    bio: Lays the paths, walls and patios.

steps

A numbered process, as bordered rows or as a connected row of icons.

Field Values What it does
eyebrow, heading, intro text
items[] title, kicker, body, icon, image kicker is a short word beside the number, rows only; image is an icon image tinted to the brand colour, used before icon in the flow layouts
anchor text
layout.width narrow (default), full
layout.variant rows (default), flow-soft, flow-filled
style.surface see above
A project

From first visit to first summer

  1. 01
    Visit

    We walk the garden with you

    An hour on site, free of charge.

  2. 02
    Plan

    You get a drawing and a price

    Within two weeks, with a planting list.

  3. 03
    Build

    Our crew builds it

    Most gardens take three to six weeks.

type: steps
eyebrow: A project
heading: From first visit to first summer
items:
  - kicker: Visit
    title: We walk the garden with you
    body: An hour on site, free of charge.
  - kicker: Plan
    title: You get a drawing and a price
    body: Within two weeks, with a planting list.
  - kicker: Build
    title: Our crew builds it
    body: Most gardens take three to six weeks.

checklist

Ticked items in one or two columns.

Field Values What it does
eyebrow, heading, intro text
items[] text, note note is a line under the item
layout.columns 2 (default), 1 any value other than 1 gives two columns
style.surface see above
Included

Every build comes with

  • A measured drawing
  • A planting listCommon and Latin names, with where each plant goes.
  • One year of aftercare
  • Green waste taken away
type: checklist
eyebrow: Included
heading: Every build comes with
items:
  - text: A measured drawing
  - text: A planting list
    note: Common and Latin names, with where each plant goes.
  - text: One year of aftercare
  - text: Green waste taken away

factList

Rows of label and value: specifications, what is included, opening hours.

Field Values What it does
eyebrow, heading, intro text
items[] label, value value is plain text and may hold a Markdown link
layout.columns 1 (default), 2, 3 rows side by side on wide screens
style.surface, style.accent see above
At a glance

A typical small garden

Size
80 to 150 m²
Time to build
Three to four weeks
Best time to start
Late summer, for autumn planting
Price guide
See care plans
type: factList
eyebrow: At a glance
heading: A typical small garden
items:
  - label: Size
    value: 80 to 150 m²
  - label: Time to build
    value: Three to four weeks
  - label: Best time to start
    value: Late summer, for autumn planting
  - label: Price guide
    value: See [care plans](#)

Media

Responsive image grid with captions.

Field Values What it does
eyebrow, heading, intro text
images[] image, alt, caption
layout.columns 3 (default), 2 any value other than 2 gives three columns
style.surface see above
Recent gardens

Finished this year

type: gallery
eyebrow: Recent gardens
heading: Finished this year
images:
  - image: ../../starter/public/media/stone-patio.webp
    alt: A stone patio framed by low planting
    caption: A patio in reclaimed stone
  - image: ../../starter/public/media/lavender-arch.webp
    alt: An arch with lavender on both sides
    caption: Lavender along the arch
  - image: ../../starter/public/media/rock-garden.webp
    alt: A rock garden with alpine plants
    caption: An alpine bed on a slope

collage

A collage of fixed height, one leading picture and a few tiles, with a chip that opens every image in a viewer.

Field Values What it does
images[] image, alt the first five build the collage; all of them open in the viewer
caption text one line under the collage
style.surface see above
A cottage garden on Mill Lane, built over one autumn.
A cottage front garden in full flower
A cottage front garden in full flower
A brick path between beds
A brick path between beds
A gravel garden with grasses
A gravel garden with grasses
Young plants set out before planting
Young plants set out before planting

1 of 4

type: collage
images:
  - image: ../../starter/public/media/cottage-front.webp
    alt: A cottage front garden in full flower
  - image: ../../starter/public/media/brick-path.webp
    alt: A brick path between beds
  - image: ../../starter/public/media/gravel-garden.webp
    alt: A gravel garden with grasses
  - image: ../../starter/public/media/planting.webp
    alt: Young plants set out before planting
caption: A cottage garden on Mill Lane, built over one autumn.

beforeAfter

A draggable before and after slider.

Field Values What it does
eyebrow, heading, intro text
media.before, media.after image the two photos
beforeAlt, afterAlt text
style.surface see above
Before and after

From bare gravel to planting

The bed newly planted
The bed as bare gravel
type: beforeAfter
eyebrow: Before and after
heading: From bare gravel to planting
media:
  before: ../../starter/public/media/gravel-garden.webp
  after: ../../starter/public/media/planting.webp
beforeAlt: The bed as bare gravel
afterAlt: The bed newly planted

Video

hero and textImage play a looping video when media.kind is video. It plays muted and inline, and media.image is its poster. Set media.video to an MP4, and media.videoWebm to a WebM as well to have it offered first; media.videoUrl plays a file from elsewhere instead. A visitor who asks for reduced motion sees the poster, and a button on the video pauses and resumes it.

Edit a page and watch it change

The CMS shows the real page beside the form, so a heading you type is a heading you see.

type: textImage
heading: Edit a page and watch it change
body: The CMS shows the real page beside the form, so a heading you type is a heading you see.
media:
  kind: video
  video: ../assets/admin.mp4
  image: ../assets/admin.webp
  imageAlt: Editing the home page in the CMS

Quote and highlight

callout

A single highlighted statement or quote.

Field Values What it does
eyebrow text
quote text
layout.width narrow (default), xnarrow, full
layout.align left (default), center, right where the column sits; the text stays left-aligned
style.surface, style.accent see above
Our promise
If a plant we put in dies in its first year, we replace it.
type: callout
eyebrow: Our promise
quote: If a plant we put in dies in its *first year*, we replace it.

definition

A dictionary entry: a term, its word class and numbered senses.

Field Values What it does
eyebrow text
term text use · between syllables
wordClass text the italic line under the term
senses[] text, note note is a muted addendum
layout.width xnarrow (default), narrow, full
layout.align left (default), center, right
style.surface see above
The word

per·en·ni·al

adjective · from Latin perennis, lasting the year through

  1. 1of a plant, living for several years, dying back in winter and returning in spring.
  2. 2lasting a long time. what we hope for every garden.
type: definition
eyebrow: The word
term: per·en·ni·al
wordClass: adjective · from Latin perennis, lasting the year through
senses:
  - text: of a plant, living for *several years*, dying back in winter and returning in spring.
  - text: lasting a long time.
    note: what we hope for every garden.

statPanel

A dark statement panel beside a giant number, or two or three figures side by side.

Field Values What it does
eyebrow, heading text
body Markdown rendered as full Markdown, so a blank line starts a new paragraph and links follow the page's language
badges[] text
statValue, statLabel text the big number
stats[] label, value, note replaces the single number when filled
style.accent see above
Aftercare

Most gardens fail in the first summer.

New plants need water until their roots reach down. Our first-year visits cover the dry weeks.

WateringReplacements
Left alone1 in 4

new plants lost in a dry first summer.

With our visits1 in 50

and we replace those free.

type: statPanel
eyebrow: Aftercare
heading: Most gardens fail in the *first summer*.
body: New plants need water until their roots reach down. Our first-year visits cover the dry weeks.
badges:
  - Watering
  - Replacements
stats:
  - label: Left alone
    value: 1 in 4
    note: new plants lost in a dry first summer.
  - label: With our visits
    value: 1 in 50
    note: and we replace those free.

Numbers

statsBar

A row of label and value figures.

Field Values What it does
items[] label, value
style.surface see above
Gardens built
400
Years in the valley
12
Gardeners
6
Clients who return
9 in 10
type: statsBar
items:
  - label: Gardens built
    value: "400"
  - label: Years in the valley
    value: "12"
  - label: Gardeners
    value: "6"
  - label: Clients who return
    value: 9 in 10

timeline

A line of years with a dot per milestone, the last one highlighted.

Field Values What it does
eyebrow, heading, intro text
items[] year, title, detail oldest first; year can be wording such as 00s; detail shows on narrow screens
planned text a dashed note at the end of the line
style.surface see above
Our story

Twelve years in the valley

  1. 2014The first borderOne garden, one van.
  2. 2018Our own crewBuilding paths and walls ourselves.
  3. 2024Six gardenersAnd a nursery bed of our own.

A second team in 2027

type: timeline
eyebrow: Our story
heading: Twelve years in the valley
items:
  - year: "2014"
    title: The first border
    detail: One garden, one van.
  - year: "2018"
    title: Our own crew
    detail: Building paths and walls ourselves.
  - year: "2024"
    title: Six gardeners
    detail: And a nursery bed of our own.
planned: A second team in 2027

logoStrip

A centred row of partner or client names.

Field Values What it does
lead text a lead-in such as Trusted by
logos[] text the names
style.surface see above

Gardens for

Mill Lane AllotmentsThe Old RectoryValley Primary SchoolRiverside Hotel
type: logoStrip
lead: Gardens for
logos:
  - Mill Lane Allotments
  - The Old Rectory
  - Valley Primary School
  - Riverside Hotel

From collections

These blocks read entries from a collection. stomme cms offers a block only while its source is on: the feature flag, or for postList, postArchive and eventList the blog or an article listing, and for catalogList a catalog listing. Content collections lists the entries' fields.

Type Reads Fields besides eyebrow, heading, intro, style.surface
faq faq entries (pick questions, in order), tag (every question with that tag, ignored when entries is set), asideHeading, asideBody, asideCta (a contact aside, shown when any of the heading, the body or the button label is filled; a button with no link points at mailto: and the contact email, or at the home page when no email is set), layout.variant: list (default), accordion (native <details>), cards, split (an index and a reader)
testimonials testimonials none; quote cards
documentList documents group (show one group), layout.grouped (false by default; true gives a heading per group). Each row has an icon with the file extension, the title, the note and date, and the file type and size read off the built file
linkChips towns none; a chip link to each area's page
serviceGrid services services (pick and order a subset; empty shows every service, sorted by its order); cards with an image or a placeholder, linking to each service's page
subpages pages pages (pick pages, in order; empty shows the pages under this one; only published pages are listed), layout.variant: cards (default), tiles, rows, chips (labelled strings.subpages.inSection), siblings (for a subpage: the parent's title, strings.subpages.moreInSection, the pages beside this one and a link up); layout.columns 2 (default), 3, 4 for cards and tiles; media.showImages (false by default; true shows each cover, cards only). A card's text is the page's summary, or its seo.description when the summary is blank. Nothing to show renders nothing
postList the blog or an article listing text: excerpt (default) or full; media.showImages (false; true shows the covers), media.style: top (default) or corner; layout.featured (false; true makes the newest post a lead card tagged strings.latest), layout.columns (3), layout.limit (newest first), layout.archive (a path linked under the list whenever it is set; when the limit cuts the list, a line strings.post.showingLatest sits above the link). A card shows a picture only when the post sets showCover. A post's category shows as a chip beside the publish date
postArchive the blog or an article listing none; every post, grouped by year, newest first
eventList the blog or an article listing, entries with an eventDate layout.limit (6, upcoming events only), layout.showPast (false; true dims past events instead of hiding them), emptyText (shown when nothing is upcoming), feedLabel (the text of a link to <route>/calendar.ics; empty hides it). The page ships every dated entry and the browser hides what has passed, so a static page stays current
catalogList a catalog listing status: empty (every entry), available, reserved or sold; media.showImages (false; true shows the covers); layout.filters (false; true puts category chips above the grid); layout.columns (3). Cards show the cover, a status badge, the price and the listing's specs. source is the listing it reads: the CMS offers the site's catalog listings and picks the only one when there is one. Without source it renders nothing

postList, postArchive, eventList and catalogList carry two more fields: source, the id of the listing to read, and base, the route its entries live under. The CMS offers source as a pick of the site's listings: catalogList always, the other three when the site has two or more article listings (the blog counts as one). base stays hidden. Left out, base is the route of the listing source names, so a page written by hand needs only source. postList, postArchive and eventList read the blog (posts under routes.blog) when source is left out. catalogList needs source; when it names no listing, base is /<source>.

They render the site's own entries, so this reference shows their YAML only.

- type: catalogList
  heading: For sale
  source: boats
  base: /boats
- type: faq
  heading: Common questions
  tag: care
  layout:
    variant: accordion
- type: serviceGrid
  eyebrow: Services
  heading: What we do
- type: postList
  heading: From the journal
  layout:
    limit: 3
    archive: /journal

Calls to action

ctaPanel

A full-width call-to-action band.

Field Values What it does
eyebrow, heading, intro text
command text a copyable command
cta, cta2 button the second is a quieter outlined button
anchor text
Try it

Make a site of your own

The scaffolder asks for a blank site or the demo garden studio.

pnpm create stomme my-site

Start the tutorialBrowse the source

type: ctaPanel
eyebrow: Try it
heading: Make a site of your own
intro: The scaffolder asks for a blank site or the demo garden studio.
command: pnpm create stomme my-site
cta:
  label: Start the tutorial
  link:
    url: "#"
cta2:
  label: Browse the source
  link:
    url: https://github.com/gronare/stomme

Command (a copyable code line)

hero and ctaPanel take command, one line of shell in the code font with a copy button. It scrolls sideways in its own box, so a long command never widens the page, and where the clipboard is unavailable the button selects the text.

Install it and start

pnpm create stomme my-site --demo && cd my-site && pnpm install && pnpm dev
type: ctaPanel
heading: Install it and start
command: pnpm create stomme my-site --demo && cd my-site && pnpm install && pnpm dev

ctaBox

A boxed call to action in three layouts.

Field Values What it does
eyebrow, heading, intro text
cta, cta2 button the second shows as a quiet text link
facts[] label, value chips in split, a readout in panel, ignored in classic
layout.variant classic (default), split, panel
style.surface see above
Next step

Book a garden visit

An hour on site with a gardener, and a written note afterwards.

type: ctaBox
eyebrow: Next step
heading: Book a garden visit
intro: An hour on site with a gardener, and a written note afterwards.
cta:
  label: Book a visit
  link:
    url: "#"
cta2:
  label: or call 01632 960 412
  link:
    url: tel:+441632960412
facts:
  - label: Visit
    value: Free
  - label: Reply
    value: Within two days
layout:
  variant: split

contactForm

A contact form beside a direct-contact card. It posts to the contact handler and opens routes.formSuccess, see Connect the contact form.

Field Values What it does
eyebrow, heading, intro text a blank heading uses the page title; the heading is the page's H1 when the page has no hero, cover or pageHeader
labelName, labelEmail, labelMessage, submitLabel text blank uses strings.contact
showPhone false (default) true adds a phone field
requirePhone false (default)
showCategory, categories[], labelCategory false, text a dropdown; needs at least one category
showUnit, labelUnit, placeholderUnit false, text a short free-text field
showDirectContact false (default) true puts the direct-contact card beside the form; without it the form fills the width
inbox text sent with the message as inbox, for a handler that routes by it
style.surface see above
type: contactForm
eyebrow: Contact
heading: Tell us about your garden
intro: A few lines is plenty. We reply within two working days.
showCategory: true
labelCategory: What is it about?
categories:
  - A new garden
  - Garden care
  - Something else
showDirectContact: false

contactSwitch

Two or more contact forms on one surface, shown one at a time, switched without a page load.

Field Values What it does
eyebrow, heading, intro text the heading is the page's H1 when the page has no hero, cover or pageHeader
items[] label, description, intro, and every contactForm field from labelName to inbox one form per errand; the first is open when the page loads; description shows in the cards layout only. An item with no label is dropped, and a single item shows its form with no switch
layout.variant segmented (default), cards, tabs how the visitor switches
style.surface see above
type: contactSwitch
heading: How can we help?
items:
  - label: A new garden
    intro: Tell us roughly what you have in mind and where.
    inbox: design
    showDirectContact: false
  - label: Garden care
    intro: Say how big the garden is and how often you would like us.
    inbox: care
    showPhone: false
    showDirectContact: false

contactCard

The direct-contact card on its own, filled from the contact settings.

Field Values What it does
show any of phone, email, hours, address, map, socials; default none, so the card shows only its label a part with no data is skipped; map needs lat and lng on the address and a map provider, maps.provider: 'osm' or a Google maps.key, in the site config
label text blank uses strings.contact.direct
tint false (default) true gives a tinted background
style.surface see above

It reads the site's contact settings, so this reference shows its YAML only.

type: contactCard
show:
  - phone
  - email
  - hours

findUs

A map beside the address, contact details and hours, filled from the contact settings. The map shows when the address has lat and lng and the site config sets maps.provider: 'osm', or a Google maps.key. It loads from the provider only when the visitor clicks.

Field Values What it does
heading text blank uses strings.contact.findUs
showHours false (default) true lists the opening hours
style.surface see above

It reads the site's contact settings, so this reference shows its YAML only.

type: findUs
heading: Visit the yard

map

A map picture with the address and two buttons that hand the route to Google Maps or Apple Maps. No third-party script runs on the page until the visitor asks for one: with coords and a map provider (maps.provider: 'osm', or a Google maps.key), the map offers a button that loads an interactive map, as findUs does.

Field Values What it does
address text shown in bold, and used for the route when there are no coordinates
note text a quiet line beside the address
media.image, media.imageAlt image, text a static map you exported; with coords and a Google map key, a site built with an adapter shows Google's still instead, served through the site's own /map/ route
media.credit text printed over the picture; a map from OpenStreetMap must credit © OpenStreetMap contributors
media.pinX, media.pinY 0 to 100 where the pin sits, in percent; leave both blank for no pin
coords.lat, coords.lng numbers land the map app on the door instead of the street
style.surface see above

Mill Lane 4, Lower ValleyThe yard gate is on the left, past the barn.

type: map
address: Mill Lane 4, Lower Valley
note: The yard gate is on the left, past the barn.

Choosing between similar blocks

Block What sets it apart
pillars airy columns with a large brand numeral and a top rule, unlike featureGrid (boxed, small numeral) or specialistGrid (top rule, denser)
specialistGrid denser than pillars, with no large numeral, and flatter than featureGrid, with no box or icon: a longer list of capabilities under one service
team named people with a photo and their own phone, email and profile link; specialistGrid lists roles with no photo and nobody to contact
ctaBox three layouts, with facts feeding the split card's chips and the panel's readout; ctaPanel is the full-width band
prose its pull-stat is for the one headline figure a text section earns; statsBar is the row for several
factList one row per fact, label beside value; statsBar is a row of big numbers, definition a single word being defined
contactSwitch one surface, one form at a time; two contactForm blocks one under the other ask the visitor to read both
map a picture and two route buttons for any address you type, with nothing loaded from a map provider until the visitor clicks; findUs reads the address, contact details and hours from the contact settings

Images in Markdown

Markdown fields, such as the bodies of prose, textImage and textQuote and of blog posts, go through renderMarkdown from @gronare/stomme/markdown. Each inline image becomes a figure, and a title keyword lays it out: left, right, wide or center, plus small or large. With no placement keyword the image is centred in a column up to 30rem wide. The alt text is also printed under the image as a visible caption. Only images under /media/ that are uploads in src/assets/media/ are optimised; any other path is used as written.

![A workbench](/media/pages/about/bench.jpg "right small")

The CMS's Image button writes that Markdown for editors.

A link in a Markdown field to a page on the site gets a trailing slash, [the team](/about) becomes /about/, and in the prose, textImage, textQuote and statPanel bodies on a site with more than one language it points at the page in the language being read. Links to files, bare #anchor links and links to other sites are left as written.