# Folded Letter Signup

> A newsletter signup for a small hotel where a journal-style landing opens on past letters that fold in thirds, slide away and open again as visitors choose one to read.

Canonical: https://gsapvault.com/sections/folded-letter-signup-section
Live demo: https://gsapvault.com/demos/folded-letter-signup-section/index.html

| Property | Value |
|----------|-------|
| Type | section |
| Tier | paid |
| Price | Included only in the Vault |
| Difficulty | advanced |
| Plugins | Core GSAP only |
| Techniques | 3d-transforms, paper-fold, interruptible-transitions, accessible-tabs, form-validation, progressive-enhancement, container-queries, responsive-section |
| Uses Lenis | No |

## Lighthouse, as measured

Google Lighthouse on the demo, 28 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

Folded Letter Signup Section helps visitors decide a newsletter is worth their inbox before they sign up. It opens like a journal's landing page, with a masthead and cadence notes. Past letters lie in a pool of lamplight, one at a time, as real paper: a letterhead with a photograph, the opening of the letter, and what else was in it. An index of past letters sits beside them. Choosing one folds the open letter in thirds, slides it away, brings the next one in folded with the letterhead on the outside, and opens it again, top third first.

Below the letters, the signup says exactly what arrives and how often, then asks for an email address. The form is ordinary semantic HTML with inline validation and a documented endpoint for the buyer's mailing list; in its demo state it says plainly that nothing was sent.

The fictional Ferrow House example is a nine-room hotel on the north Norfolk coast that publishes quarterly dispatches. Every letter is ordinary markup, so the same design suits a restaurant, a members' club, a winery or a studio newsletter.

## Features

- Past letters as paper in a pool of lamplight, each folding in thirds with a lit crease shade and a shadow that follows the fold
- The folded letter shows its own letterhead on the outside, generated from the letter's markup
- Interruptible transitions: choosing the previous letter mid-move reverses it, rapid choices hurry the move and settle on the last one
- Accessible tabs with arrow, Home and End keys, working by touch and pointer
- Signup form with visible labels, inline validation messages, a honeypot field and a status region
- Demo mode that states nothing was sent; live mode posts to the buyer's endpoint by fetch or native form submission
- Opening facts on what arrives, how often and what never arrives
- Four generated photographs, each an ordinary replaceable image, on a CSS-only ground
- Readable fallback without JavaScript: every letter open, one after another, with the index as in-page links
- Independent instances, unique IDs and exact teardown

## Use Cases

- Boutique hotels, inns and guesthouses with a seasonal newsletter
- Restaurants, wineries and members' clubs sending a letter from the house
- Studios, makers and writers who want past issues to sell the next one
- Any newsletter where showing a real past issue is the best argument for subscribing

## Vibe-Code Ready Setup

This website section 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

Each letter is an article with three panels in markup, and the panels share the letter's height equally through CSS grid, so no measurement is needed. With JavaScript the letters stack on one spot and the index becomes a tablist. The script adds a back face to the top and bottom panels and copies the letterhead onto the outside of the fold.

A change of letter is one GSAP timeline: the bottom third turns up and the top third turns down over it with a shade that deepens as each panel stands on edge, the shadow beneath shrinks to the folded third, the letter slides off, and the next one arrives folded and opens. Choosing the letter it came from reverses the timeline; choosing a third letter hurries the move and then goes on. Fades apply to the paper surfaces rather than the letter, because opacity on the letter would flatten its 3D panels.

With reduced motion the letters switch instantly, and changing the preference mid-move settles on the chosen letter. The form validates on submit and then as the visitor types, and it never sends in demo mode.

## Documentation

How this website section 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

Copy these into your project:

- the `<section class="flx">` element from `index.html`, between the `EXTRACT START` and `EXTRACT END` comments
- `assets/style.css`
- `assets/script.js`
- `assets/img/` (or your own photographs)

Load the fonts and GSAP, then the section script, after the markup:

_Code snippet omitted: it ships with the download._

GSAP core is the only dependency; no plugins. Everything outside the `<section>` in `index.html` (the page background and body margin) is demo furniture. No build step is needed.

### Make it yours

#### Connect the form

The form ships in demo mode: it validates, then says plainly that nothing was sent. To go live:

1. Set the form's `action` to your mailing list provider's subscribe URL.
2. Remove `data-flx-demo` from the `<form>`.
3. Rename the inputs to the field names your provider expects (`email`, `first_name` and `short_notice_rooms` are placeholders).

By default the script posts the form with `fetch` and shows the thank-you message when the response is OK, or the error message otherwise. This suits endpoints that accept cross-origin requests (your own backend, a serverless function, or providers with a JSON API). If your provider only accepts an ordinary form post (many embedded signup forms do), add `data-flx-submit-mode="native"` to the `<form>`: the script validates, then lets the browser submit normally to your provider's page.

The hidden `website` input is a honeypot for bots: leave it in place and have your endpoint reject submissions where it is filled. No backend is included. The status messages live in `[data-flx-status]`; edit their wording there. In the thank-you message, `<span data-flx-name>` receives the first name, and the surrounding `<span data-flx-greet>` hides when no name was given. The two validation messages sit on the error element as `data-flx-missing` and `data-flx-invalid`.

#### Letters

Each letter is an `<article data-flx-letter>` inside `[data-flx-letters]`, and each has an index link in `[data-flx-tabs]` whose `href` points at the letter's `id`. To add a letter, copy a whole `<article>` and a whole index `<li>`, give the article a new `id` and point the new link's `href` at it. To remove or reorder, delete or move both together. The first letter in the markup opens first, so keep the newest first.

Every letter has three panels, in order: top, middle and bottom. The letter folds along the lines between them, so the three panels always share the letter's height equally. The middle and bottom panels set that height from their content (the tallest letter sets it for all); the photo in the top panel stretches to fill, and the sign-off sits at the foot of the bottom panel. Keep the middle panel's copy to roughly the length of the examples. Inside each letter:

| What | Where |
| --- | --- |
| Letterhead (also printed on the outside when folded) | `.flx-letter__mark` in the top panel |
| Photograph and caption | `<figure class="flx-letter__photo">` in the top panel |
| Title, greeting and opening (one or two paragraphs) | `.flx-letter__title`, `.flx-letter__greeting`, `.flx-letter__body` in the middle panel |
| What else was in the letter, and a postscript | `.flx-letter__list` and `.flx-letter__ps` in the bottom panel |
| Sign-off and send date | `.flx-letter__sign`, `.flx-letter__meta` |

Any number of letters works; two to six suit the index. With a single letter the index hides and the letter simply sits open.

#### Photographs

Every photograph is an ordinary `<img>` with its own `alt`. Replace the file or change the `src`; no animation changes are needed.

| Slot | File | Size | Crop control |
| --- | --- | --- | --- |
| Letter photographs | `assets/img/samphire.webp`, `harbour-breakfast.webp`, `fireside.webp`, `geese.webp` | 1200 x 800 (3:2) or any landscape photo | `--flx-focus` on each `<figure>`. The photo fills whatever height the top third has left, so its shape follows the letter; `--flx-photo-min` (default `180px`) and `--flx-photo-min-narrow` (phones, `150px`) set its smallest height |

The ground behind the letters is CSS only: `--flx-ground` plus a soft copper pool of light under the letter, both set on `.flx__desk::before`; retint the pool there with your accent. Letter photos are cropped with `object-fit: cover`, so portrait photos also work: move `--flx-focus` to keep the subject in frame.

#### Brand variables

All colours, fonts and sizes are custom properties on `.flx`:

| Variable | Default | Role |
| --- | --- | --- |
| `--flx-ground` | `#140f0b` | Section background |
| `--flx-ink`, `--flx-ink-soft` | `#f1e8da`, `#bfae98` | Text on the ground |
| `--flx-line` | translucent ink | Rules on the ground |
| `--flx-accent`, `--flx-accent-ink` | `#c98d63`, `#1a110b` | Copper details, the button and its text |
| `--flx-error` | `#f2a896` | Validation messages |
| `--flx-paper`, `--flx-paper-back` | `#f4ede1`, `#ebe2d2` | Letter front and outside |
| `--flx-paper-ink`, `--flx-paper-soft`, `--flx-paper-line` | dark browns | Text and rules on the paper |
| `--flx-font-display`, `--flx-font-sans` | Bodoni Moda, Jost | Display serif and small caps / interface |
| `--flx-max`, `--flx-gutter` | `1320px`, fluid | Content width and side padding |
| `--flx-letter-width` | `640px` | The letter's width |
| `--flx-radius` | `2px` | Paper and button corners |

A rebrand for a wine estate, for example:

_Code snippet omitted: it ships with the download._

Check text contrast after changing colours; the defaults meet WCAG AA for body text on the ground and on the paper.

### Behaviour and options

- `data-pace` on the section scales every transition: `1` is the default (a letter change takes about three seconds), `1.5` is slower, `0.7` quicker.
- Choosing the letter a move came from reverses it. Choosing another letter while one is moving hurries the current move and then goes on to the latest choice.
- If the letters start below the visible screen, the first letter waits folded and opens when it scrolls into view. If it is already visible it is simply open.
- `window.FoldedLetters` exposes `mount(root)`, `mountAll(scope)`, `destroyAll(scope)` and `get(root)`. `get(root).select(index)` opens a letter from your own code. `destroy()` reverts the section's own animations, generated back faces, shadows, roles and listeners, and restores the markup; mounting again is safe.

### Accessibility and integration

- The index is a tablist with arrow keys, Home and End, and the letters are tab panels. Letters that are not showing are `inert`. Pointer, touch and keyboard all use the same controls.
- With reduced motion the letters switch instantly; changing the preference mid-move settles on the chosen letter.
- Without JavaScript (or if GSAP fails to load) every letter shows open, one after another, and the index links jump to them; without GSAP the tabs still switch letters, instantly. Without JavaScript the form submits natively to its `action`, so set one before going live.
- The form uses visible labels, `aria-invalid` and `aria-describedby` for errors, moves focus to the email field when it is invalid, and announces results through a `role="status"` region.
- The heading is an `h2` and each letter title an `h3`; change the tags freely, since styles use classes. Section styles guard against host `h2`, `p` and similar rules.
- The section works in narrow columns through container queries: the index becomes a two-by-two set above the letter. It does not pin, scroll-jack or install a smooth scroller.
- Two instances on one page work independently; duplicate IDs in the second are suffixed automatically.

### Dependencies and credits

- [GSAP](https://gsap.com/) 3.15 core, from the jsDelivr CDN. Free for commercial use under the GSAP standard licence.
- [Bodoni Moda](https://fonts.google.com/specimen/Bodoni+Moda) and [Jost](https://fonts.google.com/specimen/Jost) from Google Fonts, SIL Open Font License.
- The photographs are generated concept images made for this section. Ferrow House, its letters and the people in them are fictional sample content; replace them with your own before publishing.

## 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 website section is included only in the Vault, for existing and new owners. It is not sold individually. The standard licence covers unlimited personal and commercial projects; bundled assets retain their own licence requirements.

- [Get the Vault](https://gsapvault.com/pricing)
- [The Vault (£99 one-time, best value): every collection in the Vault library, plus future items added to those collections](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
