Skip to content

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.

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”.

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 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.

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.

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

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).

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).

Swell is ready for more languages: see Languages.

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.

  • AGENTS.md describes the project for coding assistants (Claude Code reads it through CLAUDE.md, Cursor through .cursor/rules/), including what makes the look.
  • /llms.txt lists your pages, features, releases and journal for AI assistants, in every language.
  • Pages carry JSON-LD: the app as a MobileApplication with its plans as offers, the questions on the pricing and support pages, and each post.
Terminal window
pnpm dev # local site, editor at /keystatic
pnpm build # production build
pnpm 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).