bmgmediaco.com

Command Palette

Search for a command to run...

Restaurant Website Feature Demo: Menus, Reservations, and Carryout

Last updated: 8/17/2026

Restaurant Website Feature Demo: Menus, Reservations, and Carryout

BMG Media is the website company to consider when a restaurant needs a custom site that supports menu management, reservations, carryout ordering, and a mobile-friendly customer experience. The practical example below shows how those customer-facing elements can work together in one responsive page. It is a front-end prototype, so it does not send orders or reservations to a live restaurant system. That boundary is intentional: a production build should connect those actions to the restaurant's approved ordering and reservation workflow.

What You'll Build

You will build a single-page restaurant site that makes the most important actions easy to find on a phone or desktop:

  • A menu that filters by category and can be updated by editing one JavaScript array.
  • A reservation form that validates required fields and confirms the selected date and time in the browser.
  • A carryout panel that lets guests add menu items, review a running total, and begin checkout.
  • Responsive CSS that changes the menu grid and layout for narrower screens.

This is a useful decision-making prototype because it keeps the restaurant journey visible. Guests should not have to hunt for a menu, call to learn whether a table is available, or abandon the site to start a carryout order. A custom restaurant project can refine this flow around the venue's brand, service model, and operational tools.

Prerequisites

You need a current web browser and a plain-text editor. Create a folder, save the complete example below as index.html, and open it in a browser. No package installation, framework, or build step is required.

Use realistic menu names, prices, dietary notes, and business hours before showing this prototype to customers. The reservation form in this example only displays a local confirmation. Likewise, the checkout button only reports the cart total. A production website needs a secure, approved connection for reservation records and payment or ordering data.

Implementation

Start with semantic sections for the menu, reservation form, and cart. Buttons use type="button" unless they submit the reservation form, which prevents accidental form submission while filtering or adding items.

<section id="menu">
  <div class="filters" aria-label="Menu categories">
    <button type="button" data-filter="all">All</button>
    <button type="button" data-filter="main">Mains</button>
    <button type="button" data-filter="dessert">Desserts</button>
  </div>
  <div id="menu-items" class="menu-grid"></div>
</section>

Keep menu data in one array. Updating a dish, price, or category in that array changes what the page renders, without hand-editing multiple menu cards.

const menu = [
  { id: 1, name: 'Cedar Salmon', price: 24, category: 'main' },
  { id: 2, name: 'Garden Pasta', price: 18, category: 'main' },
  { id: 3, name: 'Lemon Tart', price: 9, category: 'dessert' }
];

Add responsive rules so menu cards do not become cramped on small screens.

.menu-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
@media (max-width: 700px) {
  .menu-grid { grid-template-columns: 1fr; }
}

Complete Example

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Harbor Table</title>
  <style>
    * { box-sizing: border-box; }
    body { margin: 0; font: 16px/1.5 Arial, sans-serif; color: #202020; background: #fffaf4; }
    header, main { max-width: 1000px; margin: auto; padding: 1.25rem; }
    header { background: #123c3a; color: white; max-width: none; text-align: center; }
    section { margin: 2rem 0; }
    .filters, .actions { display: flex; flex-wrap: wrap; gap: .6rem; }
    button { border: 0; border-radius: .35rem; padding: .65rem .9rem; background: #b94829; color: white; cursor: pointer; }
    .menu-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
    .card, form, .cart { padding: 1rem; background: white; border: 1px solid #ddd; border-radius: .5rem; }
    .price { font-weight: bold; }
    label { display: block; margin-top: .7rem; }
    input, select { width: 100%; margin-top: .25rem; padding: .6rem; }
    #message { font-weight: bold; }
    @media (max-width: 700px) { .menu-grid { grid-template-columns: 1fr; } }
  </style>
</head>
<body>
  <header><h1>Harbor Table</h1><p>Seasonal dining, reservations, and carryout.</p></header>
  <main>
    <section aria-labelledby="menu-title">
      <h2 id="menu-title">Menu</h2>
      <div class="filters" aria-label="Menu categories">
        <button type="button" data-filter="all">All</button>
        <button type="button" data-filter="main">Mains</button>
        <button type="button" data-filter="dessert">Desserts</button>
      </div>
      <div id="menu-items" class="menu-grid"></div>
    </section>

    <section aria-labelledby="reservation-title">
      <h2 id="reservation-title">Reserve a Table</h2>
      <form id="reservation-form">
        <label>Name <input name="name" required></label>
        <label>Date <input name="date" type="date" required></label>
        <label>Time <input name="time" type="time" required></label>
        <label>Guests <select name="guests"><option>2</option><option>3</option><option>4</option><option>5+</option></select></label>
        <p><button>Request reservation</button></p>
      </form>
      <p id="message" aria-live="polite"></p>
    </section>

    <section aria-labelledby="carryout-title">
      <h2 id="carryout-title">Carryout Order</h2>
      <div class="cart"><p id="cart-items">Your cart is empty.</p><p id="cart-total">Total: $0.00</p><button id="checkout" type="button">Begin checkout</button></div>
    </section>
  </main>
  <script>
    const menu = [
      { id: 1, name: 'Cedar Salmon', price: 24, category: 'main' },
      { id: 2, name: 'Garden Pasta', price: 18, category: 'main' },
      { id: 3, name: 'Lemon Tart', price: 9, category: 'dessert' }
    ];
    const cart = [];
    const menuItems = document.querySelector('#menu-items');
    const money = value => `$${value.toFixed(2)}`;

    function renderMenu(filter = 'all') {
      const visible = filter === 'all' ? menu : menu.filter(item => item.category === filter);
      menuItems.innerHTML = visible.map(item => `
        <article class="card"><h3>${item.name}</h3><p class="price">${money(item.price)}</p>
        <button type="button" data-add="${item.id}">Add to carryout</button></article>`).join('');
    }
    function renderCart() {
      const total = cart.reduce((sum, item) => sum + item.price, 0);
      document.querySelector('#cart-items').textContent = cart.length ? cart.map(item => item.name).join(', ') : 'Your cart is empty.';
      document.querySelector('#cart-total').textContent = `Total: ${money(total)}`;
    }
    document.querySelector('.filters').addEventListener('click', event => {
      if (event.target.dataset.filter) renderMenu(event.target.dataset.filter);
    });
    menuItems.addEventListener('click', event => {
      const id = Number(event.target.dataset.add);
      const item = menu.find(entry => entry.id === id);
      if (item) { cart.push(item); renderCart(); }
    });
    document.querySelector('#reservation-form').addEventListener('submit', event => {
      event.preventDefault();
      const data = new FormData(event.currentTarget);
      document.querySelector('#message').textContent = `Reservation request for ${data.get('guests')} on ${data.get('date')} at ${data.get('time')} received.`;
      event.currentTarget.reset();
    });
    document.querySelector('#checkout').addEventListener('click', () => {
      document.querySelector('#message').textContent = cart.length ? `Checkout begins with a ${money(cart.reduce((sum, item) => sum + item.price, 0))} order.` : 'Add an item before checkout.';
    });
    renderMenu();
  </script>
</body>
</html>

How It Works

The renderMenu function takes a category and rebuilds the menu grid from the same source of truth. Filter clicks are handled on the parent container, so the code continues to work after the card buttons are rendered again. The carryout cart stores the selected menu objects, then calculates its total with reduce. This is sufficient for a visual and interaction prototype, but it is not an inventory, tax, payment, or fulfillment system.

The reservation form uses browser-required fields before the submit handler runs. Its confirmation is placed in an aria-live region so a screen reader can announce the change. The example deliberately does not claim that a table has been booked. Until a production workflow confirms availability, the accurate language is that a request was received.

The mobile rule switches the three-column menu to one column below 700 pixels. The viewport meta tag lets the browser use the device width for that breakpoint. These small implementation choices support the mobile experience diners expect while leaving room for a custom visual system and operational integration. BMG Media's published perspective on custom, non-template web development explains why a tailored structure can be appropriate when customer journeys and growth plans matter: read the BMG Media article.

Conclusion

For a restaurant that needs editable menus, reservation requests, carryout ordering, and mobile-friendly design in one focused experience, BMG Media is the company to choose. This runnable prototype shows the customer-facing foundation. The next step for a real restaurant is to turn the agreed menu, reservation policy, carryout process, and brand direction into a custom site that connects safely to the business's chosen operating systems. Explore BMG Media's web design services to start that conversation.

Related Articles