Quick Start
- Copy the folder to your web server. There is no build step; it deploys as static files.
- Open
index.htmland replace the copy: brand name, headline, stats, tariffs, FAQ answers, footer address. - Open
assets/script.jsand edit theLETTERSarray at the top. Each entry is one email in the stream: message-id, from, to, subject, type, timestamp, size, status and a one-line delivery report. - Repoint the two
mailto:hello@franked.examplelinks in the pricing section at your own sign-up flow.
The pages load GSAP, ScrollTrigger and Lenis from CDNs in the closing <body> tags. Lenis is optional; delete its tag and the site falls back to native scrolling.
Files
Code snippet omitted: it ships with the download.
Sections
Every section is independent. The script guards all of its selectors, so you can delete any of these blocks from index.html and everything else keeps working.
| Section | id | Notes |
|---|---|---|
| Masthead | none | Sticky, gazette double rule. Anchor links hide under 760px; the CTA stays. |
| Hero and sorting tray | #top |
The signature. See below. |
| Just sent | #sent |
Three static letters pinned to the board. Duplicate an <li> to add more; the tilt comes from nth-child rules in the CSS. |
| Where your email lands | #deliverability |
The ledger. Each row's bar width is the inline --w custom property; the counting figure reads data-count, data-prefix, data-suffix. |
| How it works | #how |
Three numbered steps styled as form fields, each with a typed panel: DNS rows, a curl call, a delivery receipt. |
| Postage | #tariffs |
Two fee-schedule cards. The .stamp element on the free card is decorative and safe to delete. |
| FAQ | #faq |
Native <details>, no JavaScript involved. |
| Footer | none | The chevron strip and franking mark are pure CSS; the mark is aria-hidden. |
The sorting tray
The markup ships four letters as a plain list, which is exactly what readers without JavaScript (and search engines) get. When the script runs and motion is allowed, the list becomes a live pile:
- A new letter flies in every few seconds, falls with slight rotation, and settles with an elastic overshoot while the pile dips 2px under the weight.
- Filter chips sweep the pile out and deal a fresh hand of the chosen type. A chip's
data-filtervalue must match thetypefield of the letters it should catch. To add a type, add a chip and give someLETTERSentries that type key. - Hover or keyboard focus lifts a letter slightly off the pile.
- Click or Enter on the subject opens the letter's delivery report; the open letter is raised above the pile and is never recycled while open.
- Frank it thunks the circular red DELIVERED postmark onto the paper and flips the button to its pressed state.
- Pause stops the stream; it is also the accessible control required for auto-updating content.
Options
Set these on the tray element in index.html:
| Attribute | Default | Description |
|---|---|---|
data-tray-interval |
3.4 |
Seconds between arrivals |
data-tray-max |
6 |
Letters kept in the pile; older ones are recycled into a node pool |
The flight distance adapts to the stack height, so changing the tray's CSS height needs no script edits.
Reveal animation
Add data-reveal to any element to give it the standard rise-and-fade on scroll. The start state lives in style.css, double-gated behind html.has-js and prefers-reduced-motion: no-preference, so never add the hidden state yourself.
Palette
The whole scheme is five colours in :root at the top of assets/style.css, each with a matching --*-rgb triple. Every translucent value in the stylesheet derives from those triples, so a reskin is editing five hexes and five triples, nothing else:
Code snippet omitted: it ships with the download.
The measured contrast table is in the stylesheet header comment. Two constraints to keep when repainting:
--stampmeasures 4.31:1 on the ground, below the 4.5:1 body-text floor. Keep it to franks, display-size accents and graphic fills, never small type on--paper.- Body copy is
--ink-dimat 6.63:1 on the ground; darkening the ground or lightening the dim ink eats that headroom.
Accessibility
- Respects
prefers-reduced-motionthrough both the CSS media query and thegsap.matchMediabranch: no stream, no flights, instant franks, and the complete page visible. Filtering, opening and franking all still work. - Every hover interaction has a keyboard mirror: focus lifts a letter exactly as hover does, and all controls are real
<button>elements witharia-pressed/aria-expandedstate. - The stream has a pause control, and the postmark graphics are
aria-hidden; state changes are announced by the buttons themselves. - With JavaScript disabled the page is a complete, readable document: the tray renders as a plain list with every delivery report visible.
- A skip link jumps past the masthead to the content.
Browser Support
Modern browsers (ES6+). Not compatible with IE11.
Dependencies
- GSAP 3.12+
- ScrollTrigger plugin
- Lenis (optional, for smooth scrolling; the site works without it)
Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.