<script> // ---------- RESTAURANT MENU DATA (realistic, diverse) ---------- const menuData = [ // Appetizers id: 1, name: "Burrata Caprese", price: "$16", category: "Appetizers", desc: "Creamy burrata, heirloom tomatoes, aged balsamic, basil oil & grilled sourdough.", icon: "fa-seedling", img: "https://images.unsplash.com/photo-1601050690597-df0568f70950?w=500&auto=format", dietary: "veg" , id: 2, name: "Truffle Arancini", price: "$14", category: "Appetizers", desc: "Crispy risotto balls, wild mushroom ragu, truffle aioli & parmesan.", icon: "fa-mushroom", img: "https://images.unsplash.com/photo-1546069901-ba9599a7e63c?w=500&auto=format", dietary: "veg" , id: 3, name: "Lamb Keftedes", price: "$18", category: "Appetizers", desc: "Greek-style lamb meatballs, tzatziki, pickled red onion & fresh mint.", icon: "fa-drumstick-bite", img: "https://images.unsplash.com/photo-1559847844-5315695dadae?w=500&auto=format", dietary: "meat" , // Main Courses id: 4, name: "Seared Sea Bass", price: "$34", category: "Mains", desc: "Mediterranean sea bass, saffron orzo, citrus beurre blanc, fennel slaw.", icon: "fa-fish", img: "https://images.unsplash.com/photo-1519708227418-c8fd9a32b7a2?w=500&auto=format", dietary: "seafood" , id: 5, name: "Porcini Risotto", price: "$27", category: "Mains", desc: "Carnaroli rice, wild porcini, parmesan cream, truffle oil & crispy sage.", icon: "fa-bowl-food", img: "https://images.unsplash.com/photo-1476124369491-e7addf5db371?w=500&auto=format", dietary: "veg" , id: 6, name: "Braised Short Rib", price: "$39", category: "Mains", desc: "Red wine braised beef, creamy parsnip purée, roasted carrots & gremolata.", icon: "fa-utensils", img: "https://images.unsplash.com/photo-1600891964092-4316c288032e?w=500&auto=format", dietary: "meat" , id: 7, name: "Herb Chicken Paillard", price: "$28", category: "Mains", desc: "Free-range chicken, lemon-herb marinade, arugula, shaved fennel & aged parmesan.", icon: "fa-egg", img: "https://images.unsplash.com/photo-1604909052743-94ab495dffb9?w=500&auto=format", dietary: "meat" , // Pasta & Risotto section (additional) id: 8, name: "Lobster Ravioli", price: "$36", category: "Pasta", desc: "House-made ravioli, maine lobster, saffron cream, chervil & toasted breadcrumbs.", icon: "fa-lobster", img: "https://images.unsplash.com/photo-1563379926898-05f4575a45e8?w=500&auto=format", dietary: "seafood" , id: 9, name: "Cacio e Pepe", price: "$22", category: "Pasta", desc: "Roman classic, tonnarelli pasta, pecorino romano, toasted black pepper & butter.", icon: "fa-cheese", img: "https://images.unsplash.com/photo-1551183053-bf91a1d81141?w=500&auto=format", dietary: "veg" , // Desserts id: 10, name: "Salted Caramel Tart", price: "$12", category: "Desserts", desc: "Dark chocolate shell, salted caramel ganache, vanilla bean chantilly & fleur de sel.", icon: "fa-cake-candles", img: "https://images.unsplash.com/photo-1587314168485-3236d6710814?w=500&auto=format", dietary: "veg" , id: 11, name: "Lemon Ricotta Cake", price: "$11", category: "Desserts", desc: "Whipped ricotta, lemon curd, wild berries & honeycomb crumble.", icon: "fa-lemon", img: "https://images.unsplash.com/photo-1571115177098-24ec42ed204d?w=500&auto=format", dietary: "veg" , id: 12, name: "Affogato al Caffè", price: "$9", category: "Desserts", desc: "Vanilla gelato, espresso shot, amaretti cookie crumble & dark chocolate shavings.", icon: "fa-mug-hot", img: "https://images.unsplash.com/photo-1579954115545-a95591f28bfc?w=500&auto=format", dietary: "veg" , // Drinks / Cocktails id: 13, name: "Elderflower Spritz", price: "$13", category: "Drinks", desc: "Elderflower liqueur, prosecco, soda, fresh mint & cucumber ribbon.", icon: "fa-wine-glass", img: "https://images.unsplash.com/photo-1560512823-829485b8bf24?w=500&auto=format", dietary: "veg" , id: 14, name: "Smoked Old Fashioned", price: "$16", category: "Drinks", desc: "Bourbon, maple bitters, smoked with cherrywood & orange zest.", icon: "fa-whiskey-glass", img: "https://images.unsplash.com/photo-1470337458703-46ad1756a187?w=500&auto=format", dietary: "veg" , id: 15, name: "Rosemary Paloma", price: "$14", category: "Drinks", desc: "Tequila, grapefruit, rosemary syrup, lime & sparkling water.", icon: "fa-cocktail", img: "https://images.unsplash.com/photo-1551024709-8f23befc6f87?w=500&auto=format", dietary: "veg" ];
// Helper: render menu cards based on active category function renderMenuItems() let filteredItems = menuData; if (activeCategory !== null) filteredItems = menuData.filter(item => item.category === activeCategory);
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <title>Le Bistro | Elegant Restaurant Menu</title> <!-- Google Fonts for refined typography --> <link href="https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,300;14..32,400;14..32,500;14..32,600;14..32,700&family=Playfair+Display:ital,wght@0,400;0,500;0,600;1,400&display=swap" rel="stylesheet"> <!-- Font Awesome 6 (free icons) --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> <style> * margin: 0; padding: 0; box-sizing: border-box; restaurant menu html css codepen
// Helper: render filter buttons function renderFilters() let buttonsHtml = `<button class="filter-btn $activeCategory === null ? 'active' : ''" data-filter="all"><i class="fas fa-utensils"></i> All</button>`; uniqueCategories.forEach(cat => buttonsHtml += `<button class="filter-btn $activeCategory === cat ? 'active' : ''" data-filter="$cat"><i class="fas fa-tag"></i> $cat</button>`; ); filterContainer.innerHTML = buttonsHtml;
// Extract unique categories and order (priority: Appetizers, Mains, Pasta, Desserts, Drinks) const categoryOrder = ["Appetizers", "Mains", "Pasta", "Desserts", "Drinks"]; let uniqueCategories = [...new Map(menuData.map(item => [item.category, item.category])).values()]; // sort according to preferred order, then alphabetically for any extra uniqueCategories.sort((a,b) => let indexA = categoryOrder.indexOf(a); let indexB = categoryOrder.indexOf(b); if (indexA !== -1 && indexB !== -1) return indexA - indexB; if (indexA !== -1) return -1; if (indexB !== -1) return 1; return a.localeCompare(b); ); but unsplash works, reliable
body background: #f9f6ef; font-family: 'Inter', sans-serif; color: #2c2418; line-height: 1.4; scroll-behavior: smooth;
let cardsHtml = ''; filteredItems.forEach(dish => // fallback image if unsplash not reachable? but unsplash works, reliable. provide a backup color style. const imgSrc = dish.img; // Dietary badge tiny let dietBadge = ''; if (dish.dietary === 'veg') dietBadge = '<span class="badge-cat"><i class="fas fa-seedling"></i> Vegetarian</span>'; else if (dish.dietary === 'seafood') dietBadge = '<span class="badge-cat"><i class="fas fa-fish"></i> Seafood</span>'; else if (dish.dietary === 'meat') dietBadge = '<span class="badge-cat"><i class="fas fa-drumstick-bite"></i> Meat</span>'; cardsHtml += ` <div class="menu-card"> <img class="card-img" src="$imgSrc" alt="$dish.name" loading="lazy" onerror="this.src='https://placehold.co/500x300/efe3d4/7a5a3e?text=Le+Bistro'"> <div class="card-content"> <div class="dish-header"> <h3 class="dish-name">$escapeHtml(dish.name)</h3> <span class="dish-price">$dish.price</span> </div> <div class="dish-desc">$escapeHtml(dish.desc)</div> <div class="dish-meta"> <span><i class="fas $dish.icon"></i> Signature</span> $dietBadge ? `<span>$dietBadge</span>` : '' </div> </div> </div> `; ); menuGrid.innerHTML = cardsHtml; ` : '' <
<!-- Menu grid: items rendered dynamically --> <div id="menuGrid" class="menu-grid"></div>