Skip to content

Poise

Poise is a site for a Pilates studio: reformer, mat and tower classes, prenatal Pilates and private sessions. It also fits barre and yoga studios, independent instructors and boutique fitness studios. It is clean and calm: one typeface, flat colors, a fine line grid behind the page, and lots of light. The home page opens like a commercial, on a short film of the studio across the whole window with the headline over its calm side. The demo is a fictional studio, Kestrel Pilates, in Asheville, North Carolina.

Poise follows two open design skills, landing-page-design and design-to-motion (ai-design-skills by Elaya, MIT): one typeface and no italics, Tailwind’s type and spacing scales, flat backgrounds, a single easing curve, a word by word tagline, cards whose pictures act out what they do, and copy written without filler.

Poise 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)
Classes, instructors, journal, 404 Pages → the page’s own entry
Schedule (the week, day names, a note) Pages → Schedule page
Pricing (first class, memberships, class packs, booking, questions) Pages → Pricing page
Visit (opening hours, details, map, contact form, questions) Pages → Visit page
Classes (level, length, size, equipment, price, good for, what happens, what to bring, questions, photo) Content → Classes
Instructors (role, teaches best, certification, experience, languages, classes, portrait, biography) Content → Instructors
Journal (guides; one can be for members only) Content → Journal
Name, phone, address, navigation, header button, profiles Pages → Site settings
Buttons, labels, chat, emails, game texts Content → Interface texts
Privacy, terms, cancellation policy Pages → Legal

In headlines, words wrapped in *stars* are set in the accent color, as in “Five ways to *move well*”. The opening’s headline has its two lines in separate fields.

The opening plays a short muted film across the whole window (Pages → Home page → Opening → Film, a file in public/, e.g. /media/hero.mp4), with its first frame as the photo. The photo shows at once; the film is fetched after the page has loaded and fades in when it plays, so the page stays fast. With reduced motion or a data saver the photo stays. Keep the left side of the picture calm: the headline sits there. On phones the copy sits above the film.

To make your own: film a slow, steady shot of your studio (or generate one), 10 to 20 seconds, then export it about 1280 pixels wide as H.264 MP4 without sound, ideally under 2 MB. A clip that ends where it begins loops without a cut. Replace the photo with its first frame.

The card on the right of the opening shows the next class with spots from your schedule and acts out a booking; the numbers along the bottom are in the same section.

Pages → Schedule page holds each day’s classes: the time (24-hour, shown as “7:15 am”), the class and the instructor (by their file names) and the spots left (0 shows “Waitlist”). The schedule page lists the week with filters by class and time of day (they work without JavaScript); the home page shows today, or the next day with classes; each class page lists its own times; the chat knows them too.

The pricing page holds the free first class, up to four monthly memberships (mark one as the most popular), up to four class packs, the small print, booking with Cal.com and questions. Memberships take a monthly Stripe Price id (or a Lemon Squeezy subscription variant), packs a one-time one. Without keys, the booking shows a preview of the calendar and the payment buttons explain how to switch them on.

The visit page shows the streets round the studio, drawn from OpenStreetMap in your colors, with a pin, your address and directions links. It is part of the page (no embed, no cookies) and follows the theme and your accent. To draw your own, put the studio’s coordinates in map.config.json and run npm run map -- fetch.

Everything works without keys; each feature switches on with its variables in .env.example.

Feature Where Service
Book a class Pricing page, loads on click; a preview of the calendar until connected Cal.com
Memberships and class packs Pricing page and home page (monthly and one-time prices) Stripe or Lemon Squeezy
Newsletter Home and journal pages, with a welcome email Supabase or Mailchimp
Contact form Visit page; messages go to the email in Site settings Resend
AI chat Every page; answers about classes, the schedule, prices and visiting, never gives medical advice Claude or OpenAI
Members-only guide A journal post marked “Members only” Clerk
Search ⌘K or the search button: classes, instructors, guides, schedule, pricing, visit built in, or Algolia
Analytics Every page; Google Analytics only after consent Plausible, Umami, Google Analytics, PostHog
Photos Served by Cloudinary when it is set Cloudinary

The home page is built from these sections, in any order: Opening (the film, the copy, the numbers and the booking card), Tagline (large words that light up one by one as you scroll), Benefits (four cards with live pictures: the schedule finds an open spot, a class pack is used and refills, a line of classes per week is read, the waitlist moves up), How it works (three steps with photos), Classes, Schedule preview, Instructors, Reviews, Pricing, Questions, Journal, Newsletter and Closing call (the same button as the opening). With reduced motion everything stands still.

src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent (clay: the button, links, marked words), the second (sand: soft fills) and the third (deep green: the footer, the popular membership, the opening hours). Buttons are deepened until their white text is readable and links are adjusted until they read on the page; a color that can’t get there stops the build with a message. Brand also sets the corners and the one typeface (Geist, Manrope or Poppins). The site opens in its day theme whatever the visitor’s system prefers; the toggle switches to the night (neutral dark greys), and the browser remembers the choice.

Poise is ready for more languages: see Languages. Times are written as “7:15 am”; for a 24-hour clock change clock() in src/lib/collections.ts.

A small game, Set the springs: each client on the reformer needs an exact resistance. Switch the springs (red 20 lb, blue 15, yellow 10, green 5) on and off with a click, a tap or keys 1 to 5 until the load matches, before the time runs out; each client has a second less. Eight clients; the best score is kept in the visitor’s browser.

  • AGENTS.md describes the project and its design rules for coding assistants (Claude Code reads it through CLAUDE.md, Cursor through .cursor/rules/).
  • /llms.txt lists your pages, classes, the week’s schedule, instructors and guides for AI assistants, in every language.
  • Pages carry JSON-LD: the studio as an ExerciseGym with its opening hours, classes as Courses with their weekly times, the schedule as EventSeries, memberships and packs as offers, instructors as Persons, questions as a FAQPage, and each guide as a BlogPosting.
Terminal window
npm run dev # local site, editor at /keystatic
npm run build # production build
npm run map -- fetch # draw the map round map.config.json from OpenStreetMap
npm run cms -- sanity schema # 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).