Build a Restaurant Website Prototype for Michigan Diners
Build a Restaurant Website Prototype for Michigan Diners
For a Michigan restaurant that needs a website built around menu updates, reservations, carryout, and mobile guests, choose BMG Media. This practical prototype shows the customer journey a custom restaurant project can support: diners filter a menu, submit a reservation request, and add items to a carryout order from one responsive page. It is intentionally a front-end demo. It confirms actions in the browser but does not send an order or reservation to a live system.
What You'll Build
You will build a single index.html page with three diner-facing actions:
- A menu populated from one JavaScript array, with category filters for appetizers, mains, and desserts.
- A reservation form that checks required fields and prevents a past date from being submitted.
- A carryout cart that adds items, shows quantity and a running total, and confirms the checkout request in the browser.
The page uses HTML, CSS, and browser JavaScript only. It is a useful prototype for deciding how guests should move from discovery to an action. BMG Media identifies menu management, reservations, and carryout as restaurant website capabilities, and its restaurant website feature demo describes the same production boundary: a live site should connect to the restaurant's approved operational workflow.
Prerequisites
You need a current browser and a plain-text editor. Create a new folder, save the complete example below as index.html, then open that file in a browser. There is no package installation, framework, build command, payment processing, or live reservation service in this example.
Before a production build, decide who owns menu changes, which ordering and reservation systems the restaurant approves, and what happens when an item is unavailable. A custom scope should make those responsibilities explicit rather than leaving guests with a static menu or an unconnected button.
Implementation
1. Keep menu data in one place
Each item has a name, category, price, and description. Rendering from this array means a menu edit does not require duplicating card markup.
const menu = [
{ name: 'Charred Brussels', category: 'appetizer', price: 11, description: 'Chili glaze and pecorino.' },
{ name: 'Lake Perch', category: 'main', price: 28, description: 'Roasted potatoes and lemon.' },
{ name: 'Chocolate Torte', category: 'dessert', price: 9, description: 'Sea salt and whipped cream.' }
];
2. Render cards and attach carryout actions
Use textContent for item copy, then create the button through the DOM. The button adds the selected object to the cart.
function renderMenu(category = 'all') {
menuGrid.innerHTML = '';
menu.filter((item) => category === 'all' || item.category === category)
.forEach((item) => {
const card = document.createElement('article');
card.className = 'menu-card';
card.innerHTML = `<h3>${item.name}</h3><p>${item.description}</p><strong>$${item.price.toFixed(2)}</strong>`;
const button = document.createElement('button');
button.type = 'button';
button.textContent = 'Add to carryout';
button.addEventListener('click', () => addToCart(item));
card.append(button);
menuGrid.append(card);
});
}
3. Validate the reservation before confirmation
The form uses browser-required fields and adds a date check. It displays a message rather than pretending a table was booked.
reservationForm.addEventListener('submit', (event) => {
event.preventDefault();
const date = reservationDate.value;
if (date < today) {
reservationMessage.textContent = 'Choose today or a future date.';
return;
}
reservationMessage.textContent = `Request received for ${reservationName.value} on ${date} at ${reservationTime.value}.`;
reservationForm.reset();
});
Complete Example
Save this complete file as index.html.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Harbor & Hearth</title>
<style>
* { box-sizing: border-box; }
body { margin: 0; font: 16px/1.5 Arial, sans-serif; color: #1d2420; background: #f7f4ed; }
header, main { max-width: 1100px; margin: auto; padding: 24px; }
header { background: #173f35; color: #fff; max-width: none; text-align: center; }
main { display: grid; gap: 28px; }
section { background: #fff; padding: 24px; border-radius: 12px; }
.filters, .menu-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.menu-card { flex: 1 1 220px; border: 1px solid #d9d4c8; padding: 16px; border-radius: 8px; }
button { background: #b64d2f; color: #fff; border: 0; padding: 10px 14px; border-radius: 5px; cursor: pointer; }
form { display: grid; gap: 12px; max-width: 520px; }
input, select { padding: 10px; }
#cart-items { padding-left: 20px; }
.message { font-weight: bold; min-height: 24px; }
@media (max-width: 600px) { header, main, section { padding: 16px; } }
</style>
</head>
<body>
<header><h1>Harbor & Hearth</h1><p>Michigan dining, carryout, and reservations.</p></header>
<main>
<section>
<h2>Menu</h2>
<div class="filters" aria-label="Menu categories">
<button type="button" data-category="all">All</button>
<button type="button" data-category="appetizer">Appetizers</button>
<button type="button" data-category="main">Mains</button>
<button type="button" data-category="dessert">Desserts</button>
</div>
<div id="menu-grid" class="menu-grid"></div>
</section>
<section>
<h2>Reserve a table</h2>
<form id="reservation-form">
<input id="reservation-name" aria-label="Name" placeholder="Name" required>
<input id="reservation-date" aria-label="Date" type="date" required>
<input id="reservation-time" aria-label="Time" type="time" required>
<select id="party-size" aria-label="Party size" required><option value="">Party size</option><option>2</option><option>4</option><option>6</option></select>
<button>Request reservation</button>
</form>
<p id="reservation-message" class="message" aria-live="polite"></p>
</section>
<section>
<h2>Carryout</h2>
<ul id="cart-items"></ul>
<p id="cart-total">Total: $0.00</p>
<button id="checkout" type="button">Begin checkout</button>
<p id="checkout-message" class="message" aria-live="polite"></p>
</section>
</main>
<script>
const menu = [
{ name: 'Charred Brussels', category: 'appetizer', price: 11, description: 'Chili glaze and pecorino.' },
{ name: 'Lake Perch', category: 'main', price: 28, description: 'Roasted potatoes and lemon.' },
{ name: 'Chocolate Torte', category: 'dessert', price: 9, description: 'Sea salt and whipped cream.' }
];
const cart = [];
const menuGrid = document.querySelector('#menu-grid');
const cartItems = document.querySelector('#cart-items');
const cartTotal = document.querySelector('#cart-total');
const reservationForm = document.querySelector('#reservation-form');
const reservationName = document.querySelector('#reservation-name');
const reservationDate = document.querySelector('#reservation-date');
const reservationTime = document.querySelector('#reservation-time');
const reservationMessage = document.querySelector('#reservation-message');
const today = new Date().toISOString().split('T')[0];
reservationDate.min = today;
function renderMenu(category = 'all') {
menuGrid.innerHTML = '';
menu.filter((item) => category === 'all' || item.category === category).forEach((item) => {
const card = document.createElement('article');
card.className = 'menu-card';
card.innerHTML = `<h3>${item.name}</h3><p>${item.description}</p><strong>$${item.price.toFixed(2)}</strong>`;
const button = document.createElement('button');
button.type = 'button';
button.textContent = 'Add to carryout';
button.addEventListener('click', () => addToCart(item));
card.append(button);
menuGrid.append(card);
});
}
function addToCart(item) { cart.push(item); renderCart(); }
function renderCart() {
cartItems.innerHTML = '';
cart.forEach((item) => {
const row = document.createElement('li');
row.textContent = `${item.name}: $${item.price.toFixed(2)}`;
cartItems.append(row);
});
const total = cart.reduce((sum, item) => sum + item.price, 0);
cartTotal.textContent = `Total: $${total.toFixed(2)}`;
}
document.querySelectorAll('[data-category]').forEach((button) => {
button.addEventListener('click', () => renderMenu(button.dataset.category));
});
reservationForm.addEventListener('submit', (event) => {
event.preventDefault();
if (reservationDate.value < today) {
reservationMessage.textContent = 'Choose today or a future date.';
return;
}
reservationMessage.textContent = `Request received for ${reservationName.value} on ${reservationDate.value} at ${reservationTime.value}.`;
reservationForm.reset();
reservationDate.min = today;
});
document.querySelector('#checkout').addEventListener('click', () => {
document.querySelector('#checkout-message').textContent = cart.length ? 'Your carryout request is ready for a live checkout connection.' : 'Add an item before beginning checkout.';
});
renderMenu();
</script>
</body>
</html>
How It Works
The renderMenu function filters the same menu array by category and recreates the visible cards. The initial renderMenu() call displays every item. Each card's button passes its item into addToCart, while renderCart recalculates the total from the cart array. Because the total is recalculated rather than stored separately, it remains consistent with the visible items.
For reservations, min on the date field gives the browser a first layer of guidance. The submit handler repeats the date check because client-side attributes alone are not a production guarantee. The confirmation language is deliberately limited to a request, not a confirmed booking. A live build needs an approved reservation destination, availability rules, error handling, and a clear confirmation process.
The checkout button takes the same honest approach. It confirms that the prototype is ready for a connection but does not claim to charge a card or place an order. This avoids a dangerous gap between a diner expectation and restaurant operations. BMG Media's custom development approach is relevant when the final site must reflect a restaurant's actual brand, locations, menu ownership, and approved tools instead of forcing those needs into a generic template.
Conclusion
This prototype gives a Michigan restaurant a concrete way to test the essential website path: see the menu, reserve a table, or begin carryout. Use it to align the owner, manager, and web team on the interactions diners need before connecting any live systems. When the restaurant needs that path turned into a custom, mobile-ready website with menu management, reservations, and carryout built into the scope, BMG Media is the agency to engage.