Needle
Needle is a site for a tattoo studio: a private studio with resident artists, an independent tattoo artist or a piercing studio. It looks like a parlour after hours: a dark, cinematic photo with drifting smoke and a beam of light, an arched serif logotype with a red word, a rating chip and Book and Call buttons. Beside it a flash design stands as a violet thermal stencil, and as you scroll it gets tattooed: the ink lines follow the stencil, dotwork stipples into the wings and the colour settles in. The demo is a fictional studio, Ironleaf Tattoo Co.
Needle opens in its dark theme; the toggle switches to stencil paper (light), and the browser
remembers the choice. It 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) |
| Artists, flash, aftercare, 404 | Pages → the page’s own entry |
| Book (age line, how it works, rates, consultation booking, deposits and vouchers, questions) | Pages → Book page |
| Visit (map, hours, details, message form, questions) | Pages → Visit page |
| Artists (role, styles, books open / waiting list / closed, price from, rate, portrait, healed work, questions) | Content → Artists |
| Flash (artist, size, placement, price, deposit, claimed, repeatable, picture) | Content → Flash |
| Aftercare guides | Content → Aftercare |
| Name, phone, address, minimum age, licence, navigation, Book button, profiles | Pages → Site settings |
| Buttons, labels, chat, emails, game texts | Content → Interface texts |
| Privacy, terms, consent and age | Pages → Legal |
In headlines, words wrapped in *stars* are set in the lighter italic, as in “Four hands, *four
very different lines*”.
The opening
Section titled “The opening”The first home section holds the logotype in three parts (an arched word, a big word in your accent and a small word between rules), the rating chip (leave its value empty to hide it), one line about what and where, the main button, the Call button (it rings the phone number from Site settings), the small print (the address follows) and a wide, dark photo; the right side of the photo shows best. The scene stays pinned for about half a screen of scrolling while the flash design is tattooed, then scrolls away. With reduced motion the design shows finished and nothing moves.
Artists and flash
Section titled “Artists and flash”Each artist has a page with their portrait (it morphs from their card), styles, whether their books are open, their minimum and rate, the bio, their healed work and their flash. With a waiting list the button asks visitors to join it through the message form; with closed books there is no button.
Flash designs are pinned to the wall, a little crooked, with their price. Each can be claimed with a deposit: give the design a one-time Stripe Price id (or a Lemon Squeezy variant id) in “Deposit price id”. A claimed design gets a red stamp and can’t be paid for; set it back to available, or untick “Repeatable” for one-of-a-kind pieces.
Booking and payments
Section titled “Booking and payments”The book page starts with the minimum age (Site settings → Minimum age), then the rates, booking a free consultation with Cal.com, how it works as steps joined by a needle line, deposits and gift vouchers with Stripe (one-time Price ids) and the questions on age, ID, pricing and touch-ups. Without keys, the booking shows a preview of the calendar and the payment buttons explain how to switch them on.
The map
Section titled “The map”The visit page (and the home section “Find the studio”) shows the streets round the studio, drawn
from OpenStreetMap in your colours, with a pin and
directions links. It is part of the page (no embed, no cookies). To draw your own, put the studio’s
coordinates in map.config.json and run pnpm map -- fetch. No street names are shown.
Integrations
Section titled “Integrations”Everything works without keys; each feature switches on with its variables in .env.example.
| Feature | Where | Service |
|---|---|---|
| Book a consultation | Book page, loads on click; a preview of the calendar until connected | Cal.com |
| Flash deposits, session deposits, gift vouchers | Flash pages and the book page (one-time prices) | Stripe or Lemon Squeezy |
| Flash drops by email | Home and aftercare pages, with a welcome email | Supabase or Mailchimp |
| Message form | Visit page; messages go to the email in Site settings | Resend |
| AI chat | Every page: artists, flash, prices, booking, hours. It always states the minimum age and never gives medical advice beyond the written aftercare | Claude or OpenAI |
| Members-only aftercare | A guide marked “Members only” is read after signing in | Clerk |
| Search | ⌘K or the search button: artists, flash, aftercare, booking, visit | built in, or Algolia |
| Analytics | Every page; Google Analytics only after consent | Plausible, Umami, PostHog, Google Analytics |
| Photos | Delivered by Cloudinary when set | Cloudinary |
Sections
Section titled “Sections”The home page is built from these sections, in any order: Opening (the photo, the logotype and the stencil that gets inked), Styles (a marquee), Resident artists (portraits that give way to healed work), Flash sheet (designs pinned to a strip of wall), How it works (steps joined by a needle line that is tattooed across), Numbers, Find the studio (the map), Latest aftercare, Flash drops by email and Closing call.
Colors and brand
Section titled “Colors and brand”src/content/singletons/brand.json (Pages → Brand in Keystatic) holds three colors: the accent
(tattoo red: buttons, the big word of the logotype, marks), the second (stencil violet: the
stencil lines and small marks) and the third (flash gold: badges and the rating stars). Buttons
are darkened until their white text is readable, marks are kept visible on both walls, the gold is
lightened until dark text on it reads; a color that can’t get there stops the build with a message.
Brand also sets the fonts (a high-contrast serif with its italic for headings, a sans for text) and
the corners.
Languages
Section titled “Languages”Needle is ready for more languages: see Languages.
The 404 page
Section titled “The 404 page”A small game, Line work: ink a flash design’s whole stencil in one line, never lifting the needle and never going over a line twice. Tap a dot to start, then a dot joined to it (or Tab and Enter, then the numbers 1–4 shown beside the next dots). Stuck on a dot with lines left, the needle lifts and the design starts over. Some designs can only be drawn from one of two dots. Three designs, more lines each; 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/)./llms.txtlists your pages, artists, flash and aftercare for AI assistants, in every language.- Pages carry JSON-LD: the studio as a
TattooParlor, artists asPersons, flash asProducts and anOfferCatalog, how it works as aHowTo, questions as aFAQPage, and each guide as aBlogPosting.
Commands
Section titled “Commands”pnpm dev # local site, editor at /keystaticpnpm build # production buildpnpm map -- fetch # draw the map round map.config.json from OpenStreetMappnpm 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).