004
AI ready Refreshed

Restaurant Template

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

Includes the original v1 design as a separate download.

A five-page restaurant site laid on a red tablecloth: one dish fills the home page, and on the Menus page printed menu cards lie scattered on the cloth for visitors to pick up, drag, flick aside and turn over for allergens. Worked example: an oyster and natural-wine bar in Bristol.

ScrollTriggerDraggableInertiaPlugin Lenis intermediate
13 more details
Flip draggablethrowcard-stack3d-transformskeyboard-manipulationflip-layoutfilter-chipsform-feedbacksvg-route-drawingscroll-revealstaggerprogressive-enhancement

Updated

About this template

A complete five-page site for a restaurant, bar or cafe, built on three surfaces: a tomato-red tablecloth, grey-white menu paper and black ink. The home page is one dish filling the screen, with a paper slip carrying the headline and a bill strip of today's facts lying across the bottom edge. The worked example is Bar Ostra, an oyster and natural-wine bar on Bristol's harbour.

Read the full template overview

The Menus page is the centrepiece: four printed menus (lunch, dinner, oysters, wine) lie scattered on a photograph of the cloth. Visitors pick a card up, move it, flick it to the edge of the table, or turn it over to read allergens and notes on the back, then tidy the cards into a row or fan them like a hand. Every other page has its own hands-on piece: an oyster board that re-sorts by brine and builds a plate, a by-the-glass list that switches measure and builds a tasting flight, a party sheet that composes itself from room, numbers and set menu, and a week of opening hours with an 'open at' scrubber and a live street map of each way to the door.

To make it yours, rewrite the four cards, swap the cloth and dish photographs, and change one colour token for your own tablecloth or brand colour.

Restaurant Template - website template preview

What's included

10 items
  • Five complete pages: Home, Menus, Bar & Wine, Parties and Visit, with shared header and footer
  • Menu card table: drag, flick aside, turn over for allergens, Tidy, Fan and Deal back, with keyboard and touch equivalents
  • Without JavaScript the menus print as readable sheets with their backs below them
  • Oyster board that re-sorts by a brine dial and builds a half dozen or dozen with a running total
  • Booking strip with guests, real dates and times that follow your opening hours
  • By-the-glass list with a 125 ml / 175 ml / bottle switch, style filter and a three-wine flight builder
  • Party builder that composes a priced party sheet and fills in the enquiry form
  • Week of opening hours with a live 'open now' line, an 'open at' time scrubber and a live street map that draws each way to the door
  • One red colour token, two Google Fonts, baked WebP photography including generated cloth-and-dish frames
  • Plain multi-page navigation with a short cross-document fade where browsers support it

Perfect for

5 use cases
  • Seafood restaurants, oyster bars and wine bars
  • Trattorias, bistros and brasseries with lunch and dinner menus
  • Cafes and bakeries that want their menu boards online
  • Taquerias, tapas bars and small-plates restaurants
  • Restaurants that take private parties and set-menu bookings

How it works

5 sections

Five plain pages

Each page is an ordinary HTML file that loads one shared script and stylesheet. Features look for their own markup and do nothing when it is absent, so a page or section can be deleted without touching the rest. Links between pages are normal links with a CSS view-transition fade.

The menu card table

Each menu is a list item with a front and a back face. With JavaScript the cards become loose sheets on the tablecloth photograph, positioned by three arrangements (scatter, tidy and fan) that are recalculated for the table's width. Draggable with InertiaPlugin handles the pick-up and the flick; on touch screens a card is dragged by its title band so the page still scrolls. Keyboard users lift a card with Enter, move it with the arrow keys and turn it with F.

Opening hours drive the page

One hours table in the script feeds the booking times, the week strip, the live open-now line and the scrubber, so changing your hours in one place updates all of them. Day chips show real dates counted from today.

Builders and lists

The oyster board, wine list and party builder read their prices and options from data attributes in the markup, so adding a wine, an oyster or a set menu is a copy-and-edit of one element. Flip animates the oyster board's re-sort; the wine filter cross-fades so nothing below jumps.

Fallbacks

Content is complete without JavaScript: cards print as sheets, the hours show as a table and the map becomes a link to OpenStreetMap. Reduced motion keeps every interaction but removes travel: cards turn with a crossfade, flicks stop where they are dropped and routes appear without drawing.

Plugins ScrollTrigger, Draggable, InertiaPlugin, Flip
Difficulty Intermediate
Smooth scroll Lenis integration
Includes HTML + JS + CSS source, documentation, AI setup prompt, lifetime updates

Lighthouse, as measured

Google Lighthouse on this template's demo (5 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

Will this work for a restaurant that is not a seafood bar?

Yes. The design is cloth, paper and one dish photograph, which suits a trattoria, bistro, taqueria, cafe or wine bar as well as an oyster bar. Rewrite the four menu cards, replace the hero and tablecloth photographs, and change the red token to your own cloth or brand colour. The oyster board becomes a specials board by editing its rows.

How do I change the menus on the card table?

Each menu is one list item in menus.html with a front face and a back face. Edit the dish lines and prices on the front and the allergen table on the back. You can have three or five cards instead of four: the scatter, tidy and fan arrangements are calculated from however many cards are present.

Does the booking strip take real reservations?

No. It is an unwired front end that collects guests, day and time and shows a confirmation ticket without sending anything. Without JavaScript it falls back to a mailto form. Point it at your booking system (ResDiary, OpenTable, SevenRooms) or a form handler such as Formspree, or replace it with your provider's widget.

Is the party enquiry form connected to anything?

No. It collects name, email, date, guests, room and a message, and the party builder pre-fills the last three. Submitting shows a notice that nothing was sent. Connect it to Formspree, Netlify Forms or your CRM, or keep the mailto fallback it already has for visitors without JavaScript.

Where do I change opening hours?

In the HOURS object near the top of assets/script.js, in minutes after midnight for the bar and the kitchen. The booking times, the week strip, the open-now line and the time scrubber all read from it. Update the printed hours in the footer and the booking panel to match, since those are plain text for visitors without JavaScript.

Can I use my own food photography instead of the red cloth shots?

Yes, and you should. The hero, the menus table and the bar list sit on photographs of one tablecloth in hard sun, so a matching set of your own works best: one dish shot from above on your cloth, and the same cloth empty for the menus page. Edit assets/img-manifest.json and rebake, or drop in your own WebP files at the same names.

Paid template

Purchase to unlock the code.

Buying Restaurant 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 (npx serve .).
  2. Every page is a plain HTML file: index.html (Home), menus.html, bar.html (Bar & Wine), parties.html and visit.html.
  3. All pages share assets/style.css and assets/script.js. There is no build step.
  4. Deploy the folder as it is to any static host.

Using It With Your Own Design

  • Colour: the whole palette is the token block at the top of assets/style.css. Change --cloth (and --cloth-deep, --cloth-rgb) to your own tablecloth or brand colour; keep text on it at 4.5:1 or better.
  • Photographs: the hero, the menus table and the wine list sit on photographs of one cloth in hard sun. Replace them as a set: one dish shot from above on your cloth (hero.webp, hero-portrait.webp), the same cloth empty with props at the edges (table.webp, table-portrait.webp) and a crop of plain cloth (cloth.webp) for the red bands.
  • Type: Anybody (display, wide and heavy) and Brygada 1918 (menus and body), self-hosted in assets/fonts/ (see Fonts below). Swap them in the @font-face rules at the top of assets/style.css, the preloads in each page's <head> and --font-display / --font-body.
  • Copy and prices: menus, wines, oysters, rooms and set menus are plain markup with prices in data- attributes; edit them in place.

Pages and Configuration

Page Sections Hands-on piece
Home Dish hero with paper slip and bill strip, today's oysters, the room, from the kitchen, booking Oyster board and booking strip
Menus Red head band, the card table, notes Menu card table
Bar & Wine Split hero, by the glass, growers rail, oyster hour Measure switch, style filter, flight builder
Parties Photo hero, rooms band, party builder, enquiry form Party sheet builder
Visit Harbour hero, hours, getting here, access, booking Week strip, 'open at' scrubber, route map

Every feature looks for its own markup and does nothing when it is missing, so you can delete a section, or a page and its nav link, without touching the script.

Opening hours

HOURS near the top of assets/script.js holds bar and kitchen hours for each day in minutes after midnight (720 is noon, 1440 is midnight; null means closed). The booking times, the Visit week strip, the live open-now line and the time scrubber all read from it. The footer and booking panel print the hours as text for visitors without JavaScript: update those too.

The menu card table (menus.html)

Each card is an <li class="card" data-card="..."> inside [data-cards], with a front face and a back face (data-face="front" / "back"). Each face has a .card__grip title band and a [data-flip] button.

  • Add or remove cards freely: the Scatter, Tidy and Fan arrangements are calculated from however many cards there are and from the table's width.
  • Visitors drag a card anywhere (on touch screens, by its title band so the page still scrolls), flick it to the edge of the table, double-click or press the button to turn it over. Deal back appears when a card has been pushed off the cloth.
  • Keyboard: Tab to a card, Enter or Space lifts it, arrow keys move it (Shift for larger steps), F turns it over, Escape or Enter puts it down.
  • On phones the cards start tidied into a readable column.

Oyster board (Home)

Rows are [data-oyster] items with data-brine (1 creamy to 5 briny), data-price and data-short (the code shown on the plate). The dial re-sorts rows by distance from the chosen brine; + Add fills a half dozen, or a dozen with the checkbox. It is a list for the guest, not an order. Use it as a specials board by changing the rows and the dial label.

Booking strip (Home, Visit)

[data-booking] collects guests (1 to 8), day and time. Day chips get real dates from today; times outside the kitchen's hours are hidden and a believable spread is marked full (isFull() in the script: replace it with your system's availability). It sends nothing. Without JavaScript the form posts to a mailto: address. Connect it to your booking provider or a form handler.

Wine list (Bar & Wine)

Rows are [data-wine] with data-style (petnat, white, skin, red) and data-p125, data-p175, data-pbtl prices. Add a style by adding a radio to the Style fieldset with a matching value. Ticking three wines prices a flight of three 75 ml pours at 60% of the 125 ml prices.

Party builder and enquiry form (Parties)

Rooms (data-cap), set menus and add-ons (data-price) are radios and checkboxes in [data-builder]. The sheet shows a per-head price, a total and a warning when the guests exceed the room. Use this in my enquiry copies the summary, guests and room into the form. The enquiry form is a front end only: it shows a notice instead of sending. Connect it to Formspree, Netlify Forms or your CRM; without JavaScript it falls back to mailto:.

Route map (Visit)

The map is MapLibre GL on OpenFreeMap's Positron style: OpenStreetMap data, free, with no API key or account. Set your location in BAR and your landmarks in STOPS inside buildRoutes(). Each route in ROUTES is a list of [longitude, latitude] points; draw your own with any routing tool (the demo's came from the OSRM foot and car routers) and the tabs map to them in GROUPS. Keep the attribution: OpenStreetMap's licence requires it. Wheel and one-finger drag scroll the page; the map pans with Ctrl/Cmd and wheel, or two fingers on touch. Without JavaScript or WebGL the map is a link to OpenStreetMap.

Palette

Token Value Use
--cloth #C8341F Red fields and buttons; paper text on it 4.59:1
--cloth-deep #9E2716 Hover, photo fallback
--paper #EFEFEB Menu paper and light ground (cool, never cream)
--ink #141414 All text on paper (15.98:1)
--ink-soft #4A4A47 Secondary text (7.71:1)
--red-ink #B92E1C Red text on paper (5.25:1)

Ink on the red is 3.48:1, so it is only used for large type.

Accessibility and Fallbacks

  • Without JavaScript, or if the GSAP CDN is blocked, every page is complete: menus print as sheets with their backs underneath, hours appear as a table, the map is a link to OpenStreetMap, and forms fall back to mailto:.
  • Reduced motion keeps every interaction but removes travel: no flick momentum, cards turn with a crossfade, routes appear without drawing, reveals are off.
  • Cards announce pick-up, turning and being pushed aside through a polite live region; hidden card faces are inert.
  • All controls are native buttons, radios, checkboxes and ranges with visible focus.

Dependencies

  • GSAP 3.15.0 with ScrollTrigger, Draggable, InertiaPlugin and Flip (works with 3.12+), from jsDelivr.
  • MapLibre GL 4.7.1 from unpkg, Visit page only, loaded by assets/script.js when the map nears the viewport (the URLs are MAPLIBRE_JS and MAPLIBRE_CSS); map tiles from OpenFreeMap (no key).
  • Lenis 1.3.17 for optional smooth scrolling (add ?smooth=off to a URL, or data-smooth="off" on <html>, to turn it off).
  • Anybody and Brygada 1918, self-hosted in assets/fonts/.

Each page loads only the plugins it uses.

Fonts

Both families are self-hosted in assets/fonts/, so no page makes a render-blocking third-party request for type. The files are the Latin subsets Google Fonts serves, declared by the @font-face rules at the top of assets/style.css with the same weights, widths and unicode-range Google uses (Anybody is one variable file covering weights 600 to 900 and widths 100% to 150%). Each page preloads Anybody and upright Brygada 1918, which set the navigation, headline and first lines of text. Licence: SIL Open Font License 1.1, full text in assets/fonts/OFL.txt (Anybody: Copyright 2020 The Anybody Project Authors, https://github.com/Etcetera-Type-Co/Anybody; Brygada 1918: Copyright 2020 The Brygada 1918 Project Authors, https://github.com/kosmynkab/Brygada-1918).

To swap a face, put your .woff2 in assets/fonts/, point its @font-face rules and the --font-* token at it, and change the preloads in each page's <head>. To use Google Fonts instead, delete the @font-face rules and the preloads and add the Google Fonts <link> to every page.

Images and Credits

Bundled photographs are art-directed WebP derivatives in assets/img/, listed in assets/img-manifest.json. Rebuild them with bun scripts/build-template-assets.ts restaurant-template --force (in the GSAP Vault repo), or replace a file with your own at the same name and size.

For load speed, the first-screen photographs and the paper texture also ship as AVIF (hero, hero-portrait, table, table-portrait, pour, long-table, harbour, paper), and some smaller photographs have phone-sized copies with a width suffix (harbour-800, bar-people-600 and so on) picked by srcset. These are made from the WebP files, not by the manifest: when you replace a photograph, replace or delete its AVIF and sized copies too, and remove them from the page's <source>, srcset and preload.

AI-generated for this template (masters in assets/img-src/): the oyster platter on red cloth (hero, hero-portrait), the empty tablecloth (table, table-portrait, cloth) and the four wine glasses with anchovies (bar-glasses). They are illustrations of a fictional bar, not photographs of a real one.

Stock photographs from Pexels under the Pexels licence:

Bar Ostra, its address, people, producers and prices are fictional.

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