024
AI ready Refreshed

Tattoo Studio Template

£29 View live demo or the whole Vault, £79.20

Includes the original v1 design as a separate download.

Seven-page tattoo studio website template that knows what day it is: a live status bar says who is in and whether walk-ins are open, every artist carries a books badge, and a catalogue of painted designs shows which are still available to book. Shown as a six-chair Philadelphia shop; suits piercing studios, barbers, salons and any booking website.

ScrollTriggerFlip advanced
9 more details
flip-layoutmask-revealhover-effectscroll-revealstaggerpage-transitionprogressive-enhancementkeyboard-navigationfocus-management

Updated

About this template

A seven-page site for a studio whose diary changes every week. The look is black, bone and one red: the shop's name is set across the whole home screen in a heavy grotesk, a red gothic line crashes into it, and a black-and-white photograph sits inside the line like a letter. Artists are washed in red; the tattoos keep their own colour, so the work is always the brightest thing on the page. The worked example is Frankford Tattoo, a six-chair shop in an old barber's on Frankford Avenue in Fishtown, Philadelphia, written the way the person behind the counter talks.

Read the full template overview

Under the hero runs a status bar worked out from today's date: walk-ins open or the next walk-in day, the artists in today as links (hover one and the hero photograph turns into their healed work), and a closed-today notice when you set one. Dated pills announce the next Friday-the-13th flash day and who is in the guest chair. Every artist card and artist page carries a live books badge. The Catalogue is a grid of painted traditional designs, each tattooed once: filter it by artist or by what is still available, open any design for its sizes and prices, and book an available one straight into a booking form with a clickable body map and a size slider that shows an honest price band. Aftercare steps through the healing days, and Visit has a real street map and the guest chair.

Making it yours is mostly one file: assets/status.js holds the opening hours, walk-in day, closures, artists and their books, guest dates, the catalogue and which designs are still available, and the size-to-price table. Swap the photographs and copy and the same pages suit a piercing studio, a barber, a nail or lash artist, a hair salon or an illustrator taking commissions.

Tattoo Studio Template - website template preview

What's included

16 items
  • Seven plain HTML pages: Home, Artists, an artist page, Catalogue, Booking, Aftercare and Visit, sharing one stylesheet, one script and one data file
  • Live status bar computed from today's date: walk-ins open or the next walk-in day, who is in today, a closed-today switch and the next Friday the 13th
  • Hover or focus an artist in the status bar and the hero photograph crossfades to their healed work
  • Books badges on every artist card, row and page: walk-ins today, in today, next opening, waitlist or books closed
  • Catalogue of twelve painted designs, each tattooed once, with an Available or Tattooed status per design from one data file
  • Filter the catalogue by Available and by artist with Flip, with a live count and a designed no-results state
  • Open any design in a dialog with its sizes and prices; an available one books straight into the form with the design, artist, idea and size filled in
  • Booking form with a clickable front and back body map, a size slider with a price band from your own table, and in-place validation
  • Artists filter by style with Flip, plus a books-open toggle and a designed no-results state
  • Matched fresh and healed pairs on the artist page with a toggle, and a portfolio viewer
  • Aftercare day scrubber from day 1 to day 30, each with its photograph and instructions
  • Guest-chair cards that drop off when their dates pass, with a stepper on smaller screens
  • Real street map on OpenFreeMap tiles, loaded only when it nears the screen
  • Phone navigation: a fixed today bar with the live status and a menu sheet that lists every page with live counts
  • Self-hosted fonts and bundled WebP images; no build step
  • Accessible: native controls, tabs with arrow keys, polite live regions, full no-JS and reduced-motion pages

Perfect for

5 use cases
  • Tattoo studios and tattoo artists
  • Piercing studios and body art shops
  • Barber shops and hair salons with walk-in days
  • Nail, lash and brow artists who take bookings
  • Illustrators and print studios selling commissions and one-off designs

How it works

5 sections

One data file, read against today's date

Opening hours, the walk-in day, closures, the artists and their working days, books state, guest dates, catalogue designs and the size-to-price table all live in assets/status.js. The script reads it on every page and works out whether walk-ins are open, who is in, when the next walk-in day is and when the next Friday the 13th falls, then fills the status bar, the nav chip, the badges and the pills. The pages carry plain-text fallbacks for visitors without JavaScript, and ?today=YYYY-MM-DD previews any day.

The collision hero

The shop name is two lines of heavy grotesk sized to the width of the screen, the gothic line sits behind its top edge, and the photograph is an inline element sized to the cap height, so it stays in the line at every width and drops beneath the name on phones. On load the gothic line and the photograph are revealed left to right with clip-path and the status bar settles in.

The catalogue

Every design is a painted image on a card that carries its id, artist and status. The script reads the statuses from assets/status.js on load, so changing a design from available to tattooed is one word in one file; tattooed cards are dimmed with a small red mark. Filters reflow the grid with Flip. A card opens a native dialog with the painting, placement, healing time and the size table, and the arrow keys step through whatever the filter is showing.

From the catalogue to the booking form

Book buttons carry the design's id to the booking page, which looks it up in the data file and fills in the artist, the idea and the first size; a tattooed design routes to its artist for a variation instead. The body map and the placement list stay in step either way, and the size slider reads its price band and time from the size table. The form is a front end only and sends nothing until you connect it.

Plain pages, light motion

Every page is a standalone document that loads normally, with a short cross-document fade where the browser supports it. There is no smooth-scrolling library and nothing is pinned: the artist list and the catalogue grid close up around a filter, the artist cards lift on hover like a stencil coming off the skin, and the map loads only when its section nears the screen.

Plugins ScrollTrigger, Flip
Difficulty Advanced
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this template's demo (7 pages, lowest score shown), from the latest scan. A measurement of the demo as shipped, not a promise for your page.

Accessibility
100
Best practices
100

No performance score, on purpose. That figure depends on how you deploy: your server's compression and caching, your CDN, the connection and the device doing the test, none of which the code controls. The same page can score very differently on two consecutive runs, so measure it where it will live.

Questions about this template

Which businesses suit this design besides tattoo studios?

Anything that runs on people, walk-in days and bookings. A piercing studio swaps the catalogue for jewellery; a barber shop or hair salon uses the status bar for walk-ins and the artist pages for each chair; nail, lash and brow artists use the booking form's size and price band for services. Illustrators selling one-off designs can keep the sheet almost as it is.

How do I update who is in, books, guest spots and closures?

Edit assets/status.js. Each artist has the weekdays they work, a books state of open, waitlist or closed, and an optional next-opening date. Guest spots have start and end dates and drop off once they pass. Add a date to the closed list and that day shows a closed notice, nobody is in, and the next walk-in day skips it. Add ?today=2026-11-26 to any page to check.

Does the booking form take real bookings or deposits?

No. It is an unwired front end: it collects the artist, the idea, a reference link, placement, size, colour, preferred days, name, email, phone and an over-18 confirmation, validates them and shows a confirmation in place. Set data-endpoint on the form to send it to Formspree, Netlify Forms or your CRM. It does not hold dates or take the $100 deposit.

How do I put my own designs in the catalogue?

Scan or photograph each painted design, run assets/img-src/flash-prep.py to set every sheet on one paper colour without cropping, and bake it with the manifest. Add a card to catalogue.html and an entry in status.js with its artist, sizes, prices, placement and status. Set status to tattooed once someone has it. The bundled paintings are demo artwork, not designs to tattoo.

Can I use photographs of my own artists and their work?

Yes. Put your originals in assets/img-src or link them in assets/img-manifest.json and run the bake script described in the README; it crops and writes WebP files at the sizes the pages use. Portraits are given a black-to-red gradient map before baking, and the README includes the small script for that. Keep tattoo photographs in true colour; the design relies on the work being the colour.

How do I add a page for each artist?

Copy rosa-delgado.html, change the name, portrait, bio, portfolio and booking figures, and point the artist's page field in status.js at the new file so the status bar and badges link to it. The intro sets the first name in the grotesk and the surname in the gothic face; both scale to the column, so long names wrap rather than overflow.

Paid template

Purchase to unlock the code.

Buying Tattoo Studio Template opens the HTML, CSS and JavaScript source, the full documentation, an AI setup prompt for your editor, and every update we ship to it. Standard license: unlimited personal and commercial projects. Every paid product is tested for desktop and mobile layouts, reduced-motion handling, animation cleanup and keyboard controls wherever there is something to operate.

£29 Standard license, unlimited projects

Browse free effects

Or the whole library: the Vault, £79.20, one payment.

Documentation

Quick Start

  1. Unzip and open index.html in a browser, or serve the folder with any static server (python3 -m http.server). There is no build step.
  2. Open assets/status.js and change the hours, walk-in day, artists, guests and catalogue statuses. Every page reads it.
  3. Change the copy in each HTML page, then swap the photographs (see Images below).

Add ?today=2026-11-26 to any page address to preview another day: a closure, a walk-in Wednesday, a Friday the 13th.

Using It With Your Own Design

  • Colours are tokens at the top of assets/style.css: --ink (ground), --bone (type and paper sections), --red (the accent) and three measured variants for small text (--red-ink on ink, --red-deep on paper, --on-red on red). The comment above them records every contrast pair.
  • Type is three self-hosted faces: Geist (display and body), Grenze Gotisch (the red line in headings, surnames, the footer wordmark) and Red Hat Mono (anything practical). Swap the files in assets/fonts/ and the @font-face rules, then the --font-* tokens.
  • Behaviour hooks are data- attributes. Each feature in assets/script.js looks for its own markup and does nothing if it is missing, so any section or page can be deleted.
  • Other businesses: a piercing studio swaps the catalogue for jewellery, a barber or salon uses the status bar for walk-ins and the artist pages for each chair, an illustrator keeps the catalogue for one-off designs.

Pages and Configuration

Page What it does
index.html Collision hero with dated pills and the live status bar; six chairs; catalogue teaser (four available designs); three steps (the one red band); the room
artists.html Style filter (Flip) with a books-open toggle; artist rows with live badges and healed work; the guest chair
rosa-delgado.html One worked artist page: red-washed portrait, name in two voices, bio, fresh and healed pairs, portfolio viewer, booking band
catalogue.html Twelve painted designs with Available or Tattooed status; filters; the design dialog; FAQ
book.html Deposit policy; the enquiry form with body map and size slider; numbered FAQ
aftercare.html Day scrubber (day 1, 3, 7, 14, 30); the don'ts; touch-ups; when to call the shop or a doctor
visit.html Address and hours (today marked), walk-in rules, guest-chair stepper, street map, access

The shop data (assets/status.js)

One object, window.SHOP:

  • hours: opening hours by weekday number (0 Sunday to 6 Saturday). Days not listed are closed.
  • walkIns: { day, hours, note }. The status bar says "Walk-ins open today" on that weekday and "Next walk-in Wed 28 Oct" otherwise.
  • closed: [{ date: 'YYYY-MM-DD', note }]. On a listed day the bar shows the note, nobody is "in today", and the next walk-in day skips it.
  • flashDay: what the pills say about Friday-the-13th flash days. The dates themselves are found from the calendar, across year boundaries (after Fri 13 Nov 2026 the next is Fri 13 Aug 2027).
  • artists: id, name, short, days (weekdays worked), books (open, waitlist or closed), optional nextOpening and note, page (where the name links) and today (the image and caption the hero shows when their name is hovered).
  • guests: name, from, style, start, end. The pill shows the current or next guest; past ones drop off. The guest cards on Artists and Visit carry data-start/data-end for the same reason: keep them in step.
  • flash: the catalogue. Each design's id, no, name, artist, status (available or tattooed), sizes ([label, price]), placement, healing and note. The catalogue cards, the design dialog and the booking form all read it.
  • sizes: the size-to-price table the booking slider reads (upTo inches, band, time).

Badges read, in order: "Walk-ins today" (in on a walk-in day), "In today", "Next opening Tue 3 Nov", "Books open", "Waitlist only", or the artist's note when books are closed. The text in the HTML is the no-JS fallback.

Status bar and pills (Home)

[data-status-dot], [data-status-short], [data-status-long], [data-today-who] and [data-today-note] are filled on every page they appear on (the nav chip and the phone bar use the short line). Today's artists become links; hovering or focusing one crossfades the hero photograph to their today.img. On touch the link simply opens the artist.

Six chairs (Home)

Each [data-chair] card holds the duotone portrait and a .chair-work image. Hover or focus lifts the portrait away from the bottom like a stencil (a quickTo driving clip-path).

Artist filter (Artists)

Chips carry data-filter matching each row's space-separated data-styles; the "Books open only" toggle checks data-books. Rows fade out, the rest close up with Flip, and the list height eases so the page below slides rather than jumps. A polite live region announces the count; [data-no-results] shows when nothing matches.

Fresh and healed pairs (artist page)

A .pair with data-pair="matched" gets a toggle that crossfades the same piece from fresh to healed. Use data-pair="unmatched" for two different pieces: they sit side by side with their captions and no toggle. Any [data-shot] link opens in the viewer (a native <dialog>; arrow keys step, Escape closes).

Catalogue (Catalogue)

The bundled designs are demo artwork, not designs to tattoo: replace them with your own artists' work.

Each design is a painted image in two sizes, assets/img/flash-<id>.webp (720x1080, the dialog) and flash-<id>-sm.webp (360x540, the cards, the home teaser and the booking card), on --flash-paper so there are no seams. Every design is tattooed once: its status in status.js is available (bookable) or tattooed (the card is dimmed with a small red mark and the dialog offers a variation instead). The script repaints each card from status.js on load; the data-status on each card in catalogue.html is only the no-JS fallback, so keep it roughly in step.

  • Cards are <article data-design data-artist data-status data-alt> with one link inside. Without JavaScript it goes to book.html?design=<id> (or to the artist for a tattooed design); with it, the click opens the dialog.
  • Filters: [data-cat-status] (All, Available) and [data-cat-artist] chips (click again to clear). Cards leave and the rest close up with Flip; a polite live region gives the count and [data-cat-empty] shows when nothing matches.
  • Dialog: one <dialog data-design-dialog> filled from status.js: painting, artist, placement, healing, the size table and the book or variation button. Arrow keys step through the designs the filter is showing; Escape, Close and the backdrop close it and focus returns to the card. catalogue.html?design=panther opens on that design (the home teaser links this way).

To add your own: scan each design on its paper as assets/img-src/flash-<name>.jpg, run python3 assets/img-src/flash-prep.py (Pillow) to set every sheet on one paper colour on a 2:3 page without cropping, add the outputs to the manifest and bake, then add a card and a status.js entry.

Booking form (Book)

book.html?artist=rosa selects the artist; book.html?design=serpent (or the older ?flash=) adds the design card, the artist, the idea and its first size. The body map's zone buttons and the placement <select> mirror each other (the select is the submitted field). The form is a front end only: it validates and shows a confirmation in place, and sends nothing. Set data-endpoint="https://formspree.io/f/..." (or Netlify Forms, or your CRM's endpoint) on the form to POST it; without JavaScript it falls back to its mailto: action.

Aftercare scrubber

Five native radios switch [data-stages] > li[data-day]; each item holds its photograph and instructions. Without JavaScript every day is listed in order.

Guest stepper and map (Visit)

The guest cards sit in a native horizontal scroller with snap points; the arrows step one card and hide when every card fits. The map is MapLibre GL on OpenFreeMap's dark style, loaded when the section nears the screen. Set the pin with data-pin="lng,lat" on [data-map] and update the OpenStreetMap fallback link beside it. Change MAP_STYLE in script.js for another OpenFreeMap style (positron, liberty, bright).

Navigation

Plain page loads with @view-transition { navigation: auto; }; the header keeps its own view-transition name so it stays put. Below 920px the header links give way to a fixed "today" bar (status plus Menu) and a bottom sheet listing every page with live counts. Escape, the Close button and a tap outside close it; focus moves in and returns. Without JavaScript, Menu jumps to the page list in the footer.

Palette

Token Value Use
--ink #0B0A09 Ground
--bone #EEE8DE Type, paper sections (catalogue, form, aftercare card)
--red #E0261C Gothic voice, rules, stamps, buttons, the one red band per page
--red-ink #F2655A Small red text on ink (6.4:1)
--red-deep #B81A12 Small red text on paper (5.4:1)
--smoke #8E877E Meta text on ink (5.6:1)
--go #3DDC84 The live dot only

Red on ink is 4.2:1, so red text is display size only. Small text on the red band is white (4.7:1), not bone (3.9:1).

Accessibility and Fallbacks

  • Native buttons, radios, checkboxes, range and select throughout; catalogue cards are links that open a native dialog, where the arrow keys step designs and Escape closes and returns focus to the card.
  • Focus rings show for keyboard use only (:focus-visible).
  • Reduced motion: no entrance, no reveals, instant filters and swaps, no page fade, and the live dot stops pulsing.
  • No JavaScript: every artist, every catalogue design (each linking to the booking form), every aftercare day and the whole form are in the markup; the status areas show their plain fallback lines.
  • If GSAP fails to load, the reveal gate comes off and the status bar, menu, filters, catalogue and dialog, form, scrubber, stepper and map still work, without motion.

Dependencies

  • GSAP 3.12+ with ScrollTrigger on every page, Flip on Artists (both free on the GSAP CDN). The demo is pinned to 3.15.0.
  • MapLibre GL 4.7.1 (Visit only, loaded on demand) and OpenFreeMap tiles (OpenStreetMap data, no key). Keep the OpenStreetMap attribution.
  • No smooth-scroll library.

Images and Credits

The images in assets/img/ are WebP derivatives baked for this template (cropped, resized and graded). assets/img-manifest.json lists every source and output; to replace one, change its src (a URL or a file in assets/img-src/) and run the bake script from the GSAP Vault repository (bun scripts/build-template-assets.ts tattoo-studio-template --force), or simply drop a WebP of the same size at the same path.

Portraits are turned black-to-red with assets/img-src/duotone.mjs (a gradient map; node assets/img-src/duotone.mjs in.jpg out.jpg 0.7) before baking. Keep tattoo photographs in true colour.

The catalogue paintings, some tattoo images, the shop interior and the street are AI-generated illustrations of fictional tattoos (and a fictional room and street), made for this template. The flash entries read "AI-generated painting of an original flash design, made for this template"; they are not photographs of real clients' work. Replace them with your own artists' healed work. Photographs are from Pexels under the Pexels licence (free to use and modify; credit appreciated).

File Source
hero-chair.webp https://www.pexels.com/photo/955938/ (Pexels licence)
today-rosa.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
today-marcus.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
today-jun.webp https://www.pexels.com/photo/12509431/ (Pexels licence)
today-bea.webp https://www.pexels.com/photo/7664155/ (Pexels licence)
today-kofi.webp https://www.pexels.com/photo/11619031/ (Pexels licence)
today-lila.webp https://www.pexels.com/photo/14303187/ (Pexels licence)
portrait-rosa.webp https://www.pexels.com/photo/32084265/ (Pexels licence), duotoned
portrait-marcus.webp https://www.pexels.com/photo/15022188/ (Pexels licence), duotoned
portrait-jun.webp https://www.pexels.com/photo/7909083/ (Pexels licence), duotoned
portrait-bea.webp https://www.pexels.com/photo/24018701/ (Pexels licence), duotoned
portrait-kofi.webp https://www.pexels.com/photo/7148603/ (Pexels licence), duotoned
portrait-lila.webp https://www.pexels.com/photo/32250565/ (Pexels licence), duotoned
work-rosa-1.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
work-rosa-2.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
work-rosa-3.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
work-marcus-1.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
work-marcus-2.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
work-marcus-3.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
work-jun-1.webp https://www.pexels.com/photo/12509431/ (Pexels licence)
work-jun-2.webp https://www.pexels.com/photo/34155039/ (Pexels licence)
work-jun-3.webp https://www.pexels.com/photo/5088486/ (Pexels licence)
work-bea-1.webp https://www.pexels.com/photo/7664155/ (Pexels licence)
work-bea-2.webp https://www.pexels.com/photo/29211944/ (Pexels licence)
work-bea-3.webp https://www.pexels.com/photo/25526235/ (Pexels licence)
work-kofi-1.webp https://www.pexels.com/photo/11619031/ (Pexels licence)
work-kofi-2.webp https://www.pexels.com/photo/17573238/ (Pexels licence)
work-kofi-3.webp https://www.pexels.com/photo/39081309/ (Pexels licence)
work-lila-1.webp https://www.pexels.com/photo/14303187/ (Pexels licence)
work-lila-2.webp https://www.pexels.com/photo/29004935/ (Pexels licence)
work-lila-3.webp https://www.pexels.com/photo/29832878/ (Pexels licence)
work-rosa-4.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
room.webp AI-generated photograph of a fictional studio, made for this template (Codex, gpt-6-luna)
street.webp AI-generated photograph of a fictional street and shopfront, made for this template (Codex, gpt-6-luna)
interior.webp https://www.pexels.com/photo/7005659/ (Pexels licence)
aftercare-day-1.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
aftercare-day-3.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
aftercare-day-7.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
aftercare-day-14.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
aftercare-day-30.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
rosa-pair1-fresh.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
rosa-pair1-healed.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
rosa-pair2-fresh.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
rosa-pair2-healed.webp AI-generated illustration of a fictional tattoo, made for this template (Codex, gpt-6-luna)
flash-serpent.webp, flash-serpent-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-dagger.webp, flash-dagger-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-rose.webp, flash-rose-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-swallow.webp, flash-swallow-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-panther.webp, flash-panther-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-heart.webp, flash-heart-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-moth.webp, flash-moth-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-moon.webp, flash-moon-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-anchor.webp, flash-anchor-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-eye.webp, flash-eye-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-tulip.webp, flash-tulip-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)
flash-web.webp, flash-web-sm.webp AI-generated painting of an original flash design, made for this template (Codex, gpt-6-luna)

Fonts: Geist, Grenze Gotisch and Red Hat Mono, SIL Open Font License (assets/fonts/OFL.txt).

Worked examples, the events and programmatic API, and the class reference ship with the download, alongside the full source.