# Florist

> A specimen-sheet florist website template: five ready bunches hang from one hairline with their prices beneath them, a bunch can be taken apart and built stem by stem in a fan, everything reserved collects on an order slip that writes itself into an email, and the paper takes its colour from the plate on screen. Re-point it to a flower shop, a wedding florist, a plant shop, a tea merchant or a candle maker.

Canonical: https://gsapvault.com/templates/florist-template
Live demo: https://gsapvault.com/demos/florist-template/index.html

| Property | Value |
|----------|-------|
| Type | template |
| Tier | paid |
| Price | £10 |
| Difficulty | intermediate |
| Plugins | ScrollTrigger |
| Techniques | scrub, scroll-reveal, mask-reveal, clip-path, parallax, scroll-linked-color, drag, reorder, click-state, keyboard-reorder, keyboard-navigation, touch-gestures, stagger |
| Uses Lenis | Yes |

## Lighthouse, as measured

Google Lighthouse on the demo, 9 September 2026. A measurement of the demo as shipped, not a promise for your page.

| Category | Score |
|----------|-------|
| Accessibility | 100 |
| Best practices | 100 |

No performance score is published: it depends on the deployment (server compression, caching, CDN, connection and device) rather than on the code, so measure it where it will live.

## Overview

A complete one-page site for anyone who sells cut flowers, by the stem or made up into bunches. Mignonette is the worked example: a cut-flower studio at 41 Broadway Market in London Fields, buying forty varieties from one grower in Kent and cutting them the morning they are sold.

The page is built as a stack of specimen sheets. A hairline runs across each one, the botanical name sits above it, the plain name below, and the photograph stands on the line. The wordmark hangs from the hero's rule at display scale with a single stem breaking the right edge. The same grammar then carries every section: a name above a rule, a fact below it.

The second sheet is the shop's: five bunches made up that morning hang from one full-bleed hairline in a single row, wider where the bunch is bigger, each with its number, stem count, contents, price and a Reserve. Reserving one slides the order slip in from the right — the page's one basket, with numbered lines, quantities, a collection window, a name and a phone number — and the whole slip goes into one prefilled email. That is what someone who wants flowers this afternoon actually needs, and it is the plainest thing on the page.

The visitor handles the flowers rather than a feature. Pressing a stem on the bench hands it into a fan that pivots on one point like a real bunch, drag or arrow keys move it through the arrangement, and the count and total follow. Further down, four plates run edge to edge and the paper takes its tint from whichever one is on screen, so the page warms and cools as the strip passes.

Everything is one script, one stylesheet and twenty bundled photographs. Change the twelve colour values in the token block, drop your own stems and bunches into assets/img/, and rewrite the specimens: the layout is the same for a wedding florist, a plant shop, a tea merchant or a candle maker.

## Features

- Complete one-page site: specimen-sheet hero, five ready bunches, an order slip, a stem-by-stem bunch builder, this week's cut, the bench, the standing order and the footer
- A hero where the wordmark hangs from a hairline and one stem breaks the right edge
- Five ready-made bunches hanging from one full-bleed hairline, each with its contents, price and a Reserve that puts it on the slip
- A demo order slip that slides in from the right: quantities, line totals, a collection window, a name and a phone number, and a total that is written into one prefilled email
- Both buying paths feed the one slip — the ready bunches and the bunch built at the bench
- A bunch builder: press a stem and it flies into a fan that pivots on a single point
- Drag a stem sideways, or use the arrow buttons, to move it through the bunch
- A running stem count and price total, with a plain-language list under the fan
- Four edge-to-edge plates with a hairline between them and a scroll-linked paper tint
- Twenty bundled, graded WebP photographs with source credits
- Keyboard-operable throughout, with a designed reduced-motion state and a complete no-JS document
- Semantic colour tokens with matching RGB triples, and every contrast pair measured
- Sections are independent: delete any one of them and the rest keeps working

## Use Cases

- Cut-flower studios that sell by the stem, by the bunch, or both
- Wedding and event florists who want the flowers, not the chrome, to carry the page
- Plant shops, garden centres and nurseries selling a range rather than a product
- Tea, coffee and candle makers whose range is best shown as specimen plates
- Any maker or grower whose photographs deserve a quiet, printed register

## Questions about this template

### What else could I use this florist design for?

The specimen-sheet layout is not tied to flowers. Swap the stems, the names and the twelve colour values and the same page works for a wedding or event florist, a plant shop or garden centre, a tea or coffee merchant, a candle or perfume maker, or a ceramicist selling a small range. The two things that carry the identity are the hairline that runs across every section and the fan of overlapping photographs, so keep those and change the subject.

### Can a customer actually order a bunch through it?

As far as a static page can. Reserve a bunch and the slip slides in with it; add one built at the bench, pick a collection window, leave a name and a number, and Send the order opens the visitor's email client with every line, the total and the collection time in it. Nothing is charged and no order is stored. Point the slip at your own checkout or a Stripe payment link and the page takes real orders; the mail address is one constant in script.js.

### Does the slip remember an order if the panel is closed or the page reloaded?

No. The slip lives in the page for as long as it is open: no cookie, no local storage, no account. Closing the panel keeps the lines; reloading empties them. Two things follow. The lines are a plain array in script.js, so writing them to storage, to a real cart service or to your own database is a small job for a developer. And the demo cannot take payment, so what a buyer sees on screen never becomes a charge.

### How do I add a sixth bunch, or change what is in one?

A bunch is one list item in the row: its photograph and its tag, which carries the number, the stem count, the name, the contents, the price and the Reserve link. Copy any of the five in index.html and change those, and the row re-flows without touching the stylesheet. The plates are all one depth and each column takes a share of the width, so a bigger bunch can be given a wider plate. The tags hold their lines whatever the contents wrap to, so the row stays aligned.

### How do I change a stem's name or price?

Each stem in the bench is one button in index.html carrying data-name and data-price, plus its photograph and its own label underneath. Change those two attributes and the builder, the summary list and the running total all follow, because the script reads them and nothing is counted by hand. Prices are in pence, so 350 shows as £3.50. Adding an eighth stem means copying one button, which keeps the bench a two-row grid; the note about the next cut is a plain block, not a stem.

### Are the flower photographs included, and will my own work?

Yes. All twenty are bundled as optimised WebP files with one grade baked in, and the README credits the Pexels sources. Your own work suits the page best shot against a plain wall or in flat daylight: the rack uses a 46:70 portrait crop, the four plates 76:118, and the five bunch photographs are portrait plates about 620 wide. Busy garden backgrounds will still load, but they break the specimen-sheet look, which depends on every flower sitting on the same kind of ground.

### Does the colour change as I scroll, and can I turn it off?

The paper takes one of four near-white tints from whichever plate is crossing the middle of the screen. It is a single scrubbed tween on the section's background, so removing it is one block in script.js, and the four values then sit unused in the token block. If you would rather the page kept one colour, delete the tween and leave the tint values alone, or set the section's background to your own paper colour in the stylesheet.

### Why does the biggest photograph run off the edge of the page?

The bench photograph is deliberately oversized and breaks the right edge, which is what gives the section its depth against the narrow column of text. It is a negative margin in the stylesheet, so pulling it back inside the margins is one value. On a phone it already runs edge to edge on its own, and if you would rather it sat in the layout, remove the negative margin and the caption padding follows it automatically.

## Vibe-Code Ready Setup

This template includes `START-HERE-AI.md`, a product-specific copy-paste setup prompt for Cursor, Claude Code, ChatGPT, GitHub Copilot, Windsurf, and other coding assistants. It tells the assistant to inspect the existing stack, integrate the supplied files, preserve the design, scope selectors, retain accessibility and responsive behaviour, add framework-appropriate GSAP cleanup, and report what it tested.

[How AI-assisted setup works](https://gsapvault.com/vibe-coding)

## How It Works

### The sheet

One hairline runs the full width of the hero and the wordmark hangs from it at display scale, with the botanical name above the line and the plain name below. A single photograph stands on the line and reaches past it, breaking the right edge of the page, so the top of the type passes in front of the stem. The same grammar repeats in every section, which is what makes the page read as one document rather than a stack of blocks.

### The ready bunches

Five finished bunches hang from the same full-bleed hairline the section title sits on and stand on one shelf line. Every plate is the same depth, so the numbers, the hairlines, the prices and the Reserve links all line up across the row; the rhythm comes from the widths, and a plate's width is its bunch's size, widest for the twenty-stem bunch. Each plate carries its own tag: number and stem count above a hairline, then name, contents, price and the Reserve link. The contents line holds two lines whatever it wraps to, so one long list of flowers cannot push a tag out of line with the four beside it. Every tag is one list item in the markup: copy it, change the photograph, the name, the contents, the price and the mailto, and the row re-flows itself. The Reserve links are plain mailto: addresses with the bunch and its price prefilled in the subject and body, so a static site takes a real order without a backend; nothing is charged anywhere on the page.

### The order slip

One basket sits behind the page and both buying paths write into it: a Reserve on any bunch, and the bunch built at the bench. It is a panel that slides in from the right rather than a cart page, drawn as the shop's order pad — numbered lines on hairlines, a stepper on each, then the collection window, a name and a phone number. Opening it stops the page: Lenis is halted, the document is pinned and the scrollbar it removes is paid back as padding, and the panel body carries data-lenis-prevent so the wheel scrolls the slip rather than the page behind it. Escape, the close control and the scrim all close it, Tab stays inside it, and focus returns to whatever opened it. The lines are rebuilt when they are added or removed but edited in place when a quantity changes, so the button the visitor just pressed keeps its focus. Nothing is charged and nothing is stored: Send the order is a mailto whose subject and body are rebuilt from the slip whenever it changes, and with scripting off the panel is a plain section at the end of the page whose own mailto still takes an order.

### By the stem

Each stem on the bench is a button carrying its name and its price. Pressing one clones its photograph into a strip on the right, where the strips pivot around a single point at the bottom of the stage the way a bunch is actually held: the spread is the only thing that changes as stems are added, and each strip carries a small fixed wobble so the arrangement never reads as a machine-fanned deck. Dragging a strip sideways moves it through the bunch on a fine pointer; on touch, and from the keyboard, the arrow buttons in the list below do the same job. The list, the count and the total are the bunch stated plainly, and the place a stem is taken back out.

### The ground takes the colour

Four plates run edge to edge with a hairline between them. As the strip passes, the section's ground is scrubbed through four near-white tints, one per plate, so the paper warms towards the red dahlia and cools towards the allium. Each plate also opens from its own base line with a clip-path reveal. Every tint is light enough that body copy stays above 7:1 and the mono labels stay above 5:1 on all four; the values and the measurements are in the token block at the top of the stylesheet.

### The bench and the line

The bench runs one oversized photograph of the work itself off the right edge of the page, with the facts in a narrow column against the left margin, and opens it with a clip-path mask as it comes into view. The standing order is the page's quiet beat: one hairline drawn across the section with four delivery dates sitting on it, and the whole offer in three sentences.

### Without JavaScript

Nothing on this page is reachable only through an animation. The reveal gates are applied by a script in the head before the first paint and removed again if the CDN is blocked, so a blocked script leaves a plain, readable document rather than a blank one. Without JavaScript the bunch builder is absent and the bench reads as a price list of seven stems, which is the information it was carrying anyway; the plates, the bench photograph and the standing-order dates are all in the markup. Under prefers-reduced-motion the page is a complete static document with no reveals, no fanning and no flight, and the bunch builder still works.

## Documentation

How this template works and how it goes into a page. The reference you use once you own the files (worked examples, events, the programmatic API, the class list) ships with the download.

### Quick Start

Open `index.html` in a browser, or serve the folder statically. No build step,
no framework. CDN scripts for GSAP 3.15.0 (core + ScrollTrigger) and Lenis
1.3.17; if they are blocked the head gate is removed again and the page
degrades to a plain, readable document.

### Sections (each independently deletable)

- **The sheet** (`data-hero`) — one hairline across the paper, the wordmark
  hanging from it at display scale, the specimen's Latin name above the line
  and the studio's line below. A single stem stands on the line and breaks the
  right edge of the page, so the end of the wordmark passes in front of it.
  The nav is a two-row masthead inside the paper region; the hairline under it
  stops where the plate begins.
- **The bunches** (`.stall`) — five finished bunches hanging from one
  full-bleed hairline in a single row, one plate per bunch, wider where the
  bunch is bigger. Each plate carries a tag under it: number and stem count
  above a hairline, then name, contents, price and a **Reserve** link.
  Reserve is a plain `mailto:` with the bunch and its price already in the
  subject and body, so the row takes a real order without a backend; nothing is
  charged on the page and the note under the row says so. Every plate is the
  same depth and every contents line holds two lines, so the whole row —
  numbers, hairlines, prices, Reserve links — lines up. Each bunch is one
  `<li>`: copy it and change the photograph, the number, the stem count, the
  contents, the price and the `mailto:`. The mail address is
  `orders@mignonette.co.uk` in five hrefs.
- **The order slip** (`#order`, `.slipwrap`) — the page's one basket, drawn as the
  shop's order pad. **Order** in the masthead, or reserving anything on the page,
  opens it as a panel that slides in from the right; it holds the numbered lines
  with quantity steppers, the collection windows, a name, a phone number and the
  total. **Send the order** is a `mailto:` whose subject and body are rebuilt
  from the slip whenever it changes, so the order arrives as an email. Nothing is
  charged and nothing is stored (no cookie, no local storage). The panel stops
  the page while it is open — Lenis halted, document pinned, and
  `data-lenis-prevent` on the panel body so the wheel scrolls the slip — and
  Escape, the scrim or the close button dismiss it, with Tab kept inside it and
  focus returned to whatever opened it. **With scripting off it is a plain
  section at the end of the page** (the stylesheet only turns it into a panel
  under `.has-js`), reached by the Order link in the masthead, and each bunch's
  Reserve falls back to its own `mailto:`. To point it at a real checkout,
  replace the `sendHref()` builder in `script.js` and the mail address in the
  markup; to change the windows or the collection address, edit the fieldset and
  the header's address line.
- **By the stem** (`data-rack`, `data-stage`) — seven stems as buttons, each
  carrying its name and its price, plus one note for the next cut. Pressing a
  stem flies a clone of its photograph into the fan on the right, where the
  strips pivot around a single point at the bottom of the stage. Drag a strip
  sideways on a fine pointer to move it through the bunch; the arrow buttons in
  the list below do the same from the keyboard, and Remove takes a stem back
  out. The stem count and the price total follow. Add more stems than the grid
  has, change `data-name` and `data-price` on any button, or delete the whole
  section: nothing else on the page depends on it.
- **This week's cut** (`data-cut`) — four plates edge to edge with a hairline
  between them and a caption block under each. Each plate opens from its base
  line as it arrives, and the section's ground is scrubbed through one
  near-white tint per plate, so the paper warms towards the red dahlia and
  cools towards the allium.
- **The bench** (`.bench`) — an oversized photograph of the work itself,
  running off the right edge, with the grower's facts in a narrow column
  against the left margin. The photograph is opened by a clip-path mask.
- **Standing order** (`.standing`) — the quiet beat: the offer in three
  sentences on the left, the wrapped bunch on the right, and one hairline
  drawn across the page with four delivery dates on it.
- **Footer** — the page's one inversion. Ink ground, the address set in the
  display face, hours and contact.

Delete any `<section>` and the rest keeps working. The nav links are the only
cross-section dependency, and they are plain `#` anchors.

### Palette

Eight values and their `--*-rgb` triples at the top of `assets/style.css`:
the chalk paper `#F2F0EA`, the four near-white tints the cut strip moves
through (`--tint-a` to `--tint-d`), the ink `#16171A`, the indigo `#4B3FA8`
and the footer's ground `#101114`. Repaint those and the site reskins; every
rule, dimmed label, focus ring and shadow on the page is one of them at
another alpha.

Measured as used, composited, at the size it is used:

| Pair | Ratio | Notes |
|---|---|---|
| ink on paper | 15.7:1 | headings, the wordmark |
| ink-dim on paper | 7.2:1 | body copy |
| ink-faint on paper | 5.1:1 | mono labels at 11px |
| ink-faint on the four tints | 5.1:1 | worst case; the floor for the page |
| indigo on paper | 7.2:1 | numerals, focus, the selected stem |
| paper on ink-deep | 16.6:1 | the footer |
| paper-dim / paper-faint on ink-deep | 9.8:1 / 6.5:1 | footer body and labels |
| indigo-lift on ink-deep | 7.8:1 | footer links, hover and focus |

Indigo is banned on the ink band: it measures 2.3:1 there, so `--indigo-lift`
exists for the footer and nothing else uses it. Indigo is also never set on a
photograph, and no button uses the accent as a fill, so nothing on the page
competes with a flower.

Type: Italiana (display), Sofia Sans (text), Azeret Mono (labels). Four steps
and nothing between them: the wordmark at 12.6:1 against body copy, section
headings at 3.4:1, and the label as the only small type. There is one palette;
the colour does not switch. The unit is the specimen sheet, so if you change
anything, change the copy first.

### Images

Twenty bundled photographs in `assets/img/`, about 480KB in total, all WebP
graded as one set: saturation 0.80–0.88, brightness 0.96–1.04 per image, gamma
1.00–1.05. The originals are never redistributed.

- Swap photos: replace the files in `assets/img/`, or edit the source URLs in
  `assets/img-manifest.json` and re-run
  `bun scripts/build-template-assets.ts florist-template`.
- Shapes, so replacements crop well: the hero plate is 1100×1500, the seven
  bench stems are 460×700 portrait, the four plates are 760×1300 portrait, the
  five bunch plates are 620px wide and 775–930 tall, the bench photograph is
  1600×1100 landscape and the standing-order photograph is 780×620. The bunch
  plates are cropped by their frame to whatever shape the row gives them, so a
  bunch photograph with the arrangement centred will survive any width.
- The stems work best shot against a plain wall or a flat surface in daylight
  with one bloom per frame, and the bunches as the finished arrangement, wrap
  and all: one subject per frame, plain ground. That is what makes the rack
  read as a specimen sheet rather than a gallery. Set `brightness` per image in
  the manifest, not once for the set: the sources differ in exposure and
  matching perceived brightness is what makes them read as one campaign.
- Credits (Pexels licence, free to use, no attribution required):
  - Paeony 'Sarah Bernhardt', the hero plate: https://www.pexels.com/photo/38163051/
  - Ranunculus 'Tecolote Red': https://www.pexels.com/photo/36412863/
  - Dahlia 'Pooh': https://www.pexels.com/photo/13008755/
  - Scabiosa 'Fama White': https://www.pexels.com/photo/7301732/
  - Sweet pea 'Mollie Rilstone': https://www.pexels.com/photo/14741966/
  - Nigella seed heads: https://www.pexels.com/photo/37979404/
  - Allium seed head, dried: https://www.pexels.com/photo/7301832/
  - Chrysanthemum 'Snowland': https://www.pexels.com/photo/4467127/
  - Tulip 'Belle Époque': https://www.pexels.com/photo/24524262/
  - Dahlia 'Rip City', plate 01: https://www.pexels.com/photo/36420772/
  - *Papaver somniferum*, plate 02: https://www.pexels.com/photo/5652993/
  - Rose 'Lady of Shalott', plate 03: https://www.pexels.com/photo/36507080/
  - Allium 'Purple Sensation', plate 04: https://www.pexels.com/photo/32598700/
  - Sarah at the bench: https://www.pexels.com/photo/3933175/
  - Last Friday's order: https://www.pexels.com/photo/11190649/
  - The House Bunch: https://www.pexels.com/photo/28460750/
  - The Posy: https://www.pexels.com/photo/38042963/
  - The Kitchen Table: https://www.pexels.com/photo/12385171/
  - The Field Bunch: https://www.pexels.com/photo/17080961/
  - The Market Bunch: https://www.pexels.com/photo/34514390/

### Accessibility

Keyboard: every stem, every Remove and Move control, the clear button, the five
Reserve links, the Order link, the panel's close control, the quantity steppers,
the collection radios, the two text fields and every link are real buttons,
inputs and links with visible focus rings; the live region under the bunch
announces each add, removal and move with the count and the new total, so the
state is never only visual. The move controls are the keyboard path for the
drag, which is bound to a mouse only so a touch drag never fights the page
scroll. Each Reserve link carries an `aria-label` naming its bunch, because the
visible word is the same on all five, and it gains a visible `×2` once that
bunch is on the slip. On the slip, a quantity change edits the line in place
rather than redrawing it, so the button that was just pressed keeps its focus,
and removing a line moves focus to the next line's Remove button rather than
dropping it at the top of the page. The slip's own events are announced through
`[data-slip-note]` under the bunch row.

The order slip is a `role="dialog"` panel: opening it moves focus into it,
Tab is kept inside it, Escape closes it, and focus returns to the control that
opened it. While it is open the page is pinned (Lenis stopped and the document
pinned, with the scrollbar's width paid back so nothing shifts sideways) and the
wheel over the panel scrolls the slip, not the page behind it.

Reduced motion is a complete static page, not the animated one with the motion
switched off: no reveals, no clip-path masks, no fanning and no flight, and the
bunch builder still works — a stem appears in place and the total updates.

Without JavaScript the head gate is removed and the page is a plain document.
The five bunches and their tags are all in the markup, so the row — photographs,
contents, prices and Reserve links — works with scripting switched off, and each
Reserve falls back to its own `mailto:` with that one bunch already named. The
order slip is a plain section at the end of the page with its collection details
and its own `mailto:`, so the page can still take an order, and the Order link
in the masthead scrolls to it. The bench reads as a price list of seven stems,
the four plates and their captions, the bench photograph with its facts and the
standing-order dates are all in the markup, and the only thing missing is the
bunch builder itself.

## What You Get

- `index.html`: working demo page
- `assets/script.js`: commented, readable source
- `assets/style.css`: effect styles
- `README.md`: full documentation with examples and framework integration notes
- `START-HERE-AI.md`: product-specific copy-paste prompt for AI-assisted setup
- `LICENSE.txt`: standard license terms
- Lifetime updates: re-download anytime from your library

## Get the Code

This is a premium template. The standard license costs £10 one-time and covers unlimited personal and commercial projects with no attribution required. The only restrictions: no redistribution of the code itself and no competing effect libraries.

- [Buy Florist](https://gsapvault.com/templates/florist-template)
- [The Vault (£99 one-time, best value): everything in the catalogue, including future releases](https://gsapvault.com/effects)

---

From [GSAP Vault](https://gsapvault.com): production-ready GSAP animation effects. Full catalog for agents: https://gsapvault.com/llms-full.txt
