Swell
Swell is a site for a mobile app (an indie app, a small studio’s product, a subscription app with a free tier or a beta), made of liquid glass over the water: panes that bend the photo behind their rim, photos from edge to edge of the window, and phones whose screens are real markup filled from your content, so there are no screenshots to keep up to date. It has the features (each next to its screen), pricing with subscriptions, what’s new, a journal, support with an AI chat and a beta sign-up.
Swell comes in two variants, chosen when you create the site: files/Sanity (static pages, any
host) and EmDash (--cms emdash, rendered on request on Cloudflare). The
pages are the same in both.
Pages and their content
Section titled “Pages and their content”| Page | Edit it in |
|---|---|
| Home | Pages → Home page (a list of sections) |
| Features, pricing, what’s new, journal, support, beta, 404 | Pages → the page’s own entry |
| What the phones show | Pages → App screens |
| Features | Content → Features |
| Releases (what’s new) | Content → What’s new |
| Journal posts | Content → Journal |
| App name, description, made by, support email, store buttons, navigation, header button, profiles | Pages → Site settings |
| Buttons, labels, chat, search, emails, game texts | Content → Interface texts |
| Privacy, terms | Pages → Legal |
In headlines, words wrapped in *stars* take the accent, as in “Know the water *before* you
get in”.
The phones and the app’s screens
Section titled “The phones and the app’s screens”Every phone and the watch on the site show one of six screens: Today (the verdict, the window, four readings, the tide and the next hours), Forecast (hourly bars), Spots, Check-in, Swim log and Watch. They’re built in markup and read Pages → App screens: the clock, the spot, the verdict, the readings with their units, the tide heights (drawn as a curve), the forecast hours, the spots and their status, the check-in, the log and the app’s tab labels. Put your own app’s words and numbers there and every phone follows.
Pick which screen the hero shows (Home page → Hero → App screen) and which one each feature
shows (Content → Features → App screen). To draw a screen of your own app, add a component in
src/components/screens/ (see AGENTS.md in the project).
Features and releases
Section titled “Features and releases”Features marked Featured come first, then by Order. Each has a name, a tagline, a
summary for cards and search, key points, an optional number (“15 min between updates”) and a
color for its dot; the body is the longer text on the features page, where every feature is
its own section with its phone (/features#<slug>). The features page also lists smaller extras
(offline, watch, widgets, privacy…) over a photo.
Releases are sorted by date, newest first, each with its version, the platforms (iPhone, Android, Apple Watch), a summary and the list of changes as its body. The newest is marked Latest.
Plans and payments
Section titled “Plans and payments”The plans live on the pricing page (Pages → Pricing page → Plans): name, short text, price per month and per month billed yearly (0 for a free plan; empty for one you price per customer), what’s included and whether it’s highlighted. The pricing page has a monthly/yearly switch, a comparison table, questions and a Book a call box (Cal.com) for the plan without a price, such as clubs or teams.
A plan with Stripe checkout turned on gets a button that takes the subscription with Stripe
Checkout, with the free trial set on the pricing page; add your Stripe key and the plan’s monthly
and yearly Price ids to make it work (until then the pricing page explains what’s missing).
After paying, customers land on /pricing/welcome (Pricing page → After paying), and the Stripe
webhook (/api/stripe/webhook) emails them; add your own steps there, such as unlocking the
subscription in your app.
Integrations
Section titled “Integrations”Everything works without keys; each feature switches on with its variables in .env.example.
| Feature | Where | Service |
|---|---|---|
| Subscriptions | Pricing page (and the plans on the home page) | Stripe |
| Book a call | Pricing page, loads on click | Cal.com |
| Beta sign-up | Beta page, with a welcome email | Supabase |
| Newsletter | Journal page, with a welcome email | Supabase |
| Support form | Support page; messages go to the support email in Site settings | Resend |
| AI chat | On every page, answers from your features, plans, questions, releases and journal | Claude |
| Search | ⌘K or the search button: features, releases, journal, pricing, support, beta | built in, or Algolia |
| Analytics | Every page; Google Analytics only after consent | Plausible, Umami, Google Analytics |
Sections
Section titled “Sections”The home page is built from these sections, in any order: Hero (the phone over a photo, with the store buttons), Widgets (the app at a glance, glass widgets over a photo), Features, How it works (numbered steps), Plans (from the pricing page), Numbers, Latest from the journal, Newsletter sign-up and Closing call (store buttons over a photo).
Colors, glass and brand
Section titled “Colors, glass and brand”src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the
accent tints the water and gives the buttons, links and the tide curve; the second is a
warm signal (the swim window, badges, the buoy); the third is a cool light fill. Buttons are
darkened until their white text is readable, the second and third are lightened until dark text
on them is; a color that can’t get there stops the build with a message.
The glass is derived from the accent too, in both themes: white frosted glass over pale water, smoked glass over deep water. Its text is checked against the glass over pure black and pure white, so it stays readable over any photo you put behind it; if it can’t, the glass gets denser. In Chromium browsers small panes (the header, the tab bar, the phones, the widgets) also bend what’s behind their rim like a lens; other browsers show frosted glass. Visitors who ask their system for reduced transparency get solid panels.
Brand also sets the fonts (text, headings, and the mono font for labels and units) and the corners (round like the app, or softer).
Languages
Section titled “Languages”Swell is ready for more languages: see Languages.
The 404 page
Section titled “The 404 page”A small game, Skim: skim a stone across a calm bay. Press anywhere on the sea and pull back like a slingshot (the further, the harder; pull down to throw higher) and let go, or set the angle and power with the arrow keys and press Space; flat and fast skips furthest, and a skip right next to the buoy is worth a bonus. Five stones; the best score is kept in the visitor’s browser.
AI assistants and search engines
Section titled “AI assistants and search engines”AGENTS.mddescribes the project for coding assistants (Claude Code reads it throughCLAUDE.md, Cursor through.cursor/rules/), including what makes the look./llms.txtlists your pages, features, releases and journal for AI assistants, in every language.- Pages carry JSON-LD: the app as a
MobileApplicationwith its plans as offers, the questions on the pricing and support pages, and each post.
Commands
Section titled “Commands”pnpm dev # local site, editor at /keystaticpnpm build # production buildpnpm cms sanity schema|seed # move content to Sanity (see Content → Sanity)In the EmDash variant: npm run dev, npm run deploy (Cloudflare) and npm run emdash:schema
(after changing the content model).