<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<title>Онлайн-заказ</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
Roboto, sans-serif;
background: #05060a;
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.phone-frame {
width: 390px; /* под айфон */
height: 844px;
background: radial-gradient(circle at top, #1f2533, #05060a 60%);
border-radius: 32px;
border: 2px solid #202631;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.7);
overflow: hidden;
position: relative;
}
.app {
display: flex;
flex-direction: column;
height: 100%;
}
header {
padding: 16px 16px 8px;
}
.app-title {
font-size: 20px;
font-weight: 700;
margin-bottom: 4px;
}
.app-subtitle {
font-size: 12px;
opacity: 0.7;
}
.search {
margin-top: 12px;
padding: 8px 10px;
border-radius: 12px;
background: rgba(11, 16, 26, 0.9);
border: 1px solid rgba(255, 255, 255, 0.06);
font-size: 13px;
color: #c5cad8;
}
.search::placeholder {
color: #6f7483;
}
.categories {
padding: 8px 8px 6px;
display: flex;
gap: 8px;
overflow-x: auto;
scrollbar-width: none;
}
.categories::-webkit-scrollbar {
display: none;
}
.category-btn {
white-space: nowrap;
border-radius: 999px;
padding: 6px 12px;
font-size: 12px;
border: 1px solid rgba(255, 255, 255, 0.18);
background: rgba(9, 13, 22, 0.9);
color: #d4d9e6;
cursor: pointer;
transition: all 0.15s ease;
}
.category-btn.active {
background: linear-gradient(135deg, #32d66b, #25b3ff);
color: #05060a;
border-color: transparent;
font-weight: 600;
}
main {
flex: 1;
padding: 4px 12px 90px; /* место под корзину снизу */
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: #353b49 transparent;
}
main::-webkit-scrollbar {
width: 4px;
}
main::-webkit-scrollbar-thumb {
background: #353b49;
border-radius: 4px;
}
.section-title {
font-size: 14px;
font-weight: 600;
margin: 10px 4px 6px;
opacity: 0.95;
}
.product-card {
display: flex;
padding: 10px 8px;
border-radius: 16px;
background: rgba(10, 14, 24, 0.96);
border: 1px solid rgba(255, 255, 255, 0.04);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.5);
margin: 4px 0;
align-items: center;
gap: 10px;
}
.product-icon {
width: 52px;
height: 52px;
border-radius: 14px;
background: radial-gradient(circle at 30% 20%, #ffffff22, #32d66b33);
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
color: #e5fbea;
text-align: center;
padding: 4px;
flex-shrink: 0;
}
.product-info {
flex: 1;
min-width: 0;
}
.product-name {
font-size: 13px;
font-weight: 600;
margin-bottom: 2px;
}
.product-meta {
font-size: 11px;
color: #8c92a3;
margin-bottom: 6px;
}
.product-bottom {
display: flex;
align-items: center;
justify-content: space-between;
}
.product-price {
font-size: 13px;
font-weight: 700;
}
.qty-controls {
display: flex;
align-items: center;
gap: 4px;
}
.qty-btn {
width: 26px;
height: 26px;
border-radius: 999px;
border: none;
background: rgba(22, 30, 45, 0.9);
color: #ffffff;
font-size: 18px;
line-height: 0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: background 0.15s ease, transform 0.05s;
}
.qty-btn:active {
transform: scale(0.95);
}
.qty-btn.plus {
background: linear-gradient(135deg, #32d66b, #25b3ff);
color: #05060a;
}
.qty-value {
min-width: 22px;
text-align: center;
font-size: 13px;
}
.add-btn {
padding: 6px 12px;
border-radius: 999px;
border: none;
background: linear-gradient(135deg, #32d66b, #25b3ff);
font-size: 12px;
font-weight: 600;
color: #05060a;
cursor: pointer;
transition: transform 0.05s ease, box-shadow 0.15s ease;
box-shadow: 0 6px 16px rgba(44, 216, 129, 0.4);
}
.add-btn:active {
transform: translateY(1px);
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
}
.cart-bar {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 10px 14px 16px;
background: linear-gradient(
to top,
rgba(5, 6, 10, 0.98),
rgba(5, 6, 10, 0.92)
);
border-top: 1px solid rgba(255, 255, 255, 0.06);
display: flex;
gap: 10px;
align-items: center;
}
.cart-info {
flex: 1;
font-size: 12px;
line-height: 1.3;
}
.cart-line {
display: flex;
justify-content: space-between;
align-items: baseline;
}
.cart-label {
opacity: 0.7;
}
.cart-total {
font-size: 16px;
font-weight: 700;
}
.cart-items-count {
font-size: 11px;
color: #a2a9bd;
margin-top: 2px;
}
.cart-button {
padding: 10px 14px;
border-radius: 999px;
border: none;
background: linear-gradient(135deg, #32d66b, #25b3ff);
color: #05060a;
font-size: 13px;
font-weight: 700;
cursor: pointer;
white-space: nowrap;
box-shadow: 0 10px 24px rgba(47, 215, 137, 0.6);
}
@media (max-width: 420px) {
.phone-frame {
width: 100vw;
height: 100vh;
border-radius: 0;
border: none;
}
}
</style>
</head>
<body>
<div class="phone-frame">
<div class="app">
<header>
<div class="app-title">Доставка</div>
<div class="app-subtitle">Выберите товары и добавьте в корзину</div>
<input
type="text"
id="searchInput"
class="search"
placeholder="Поиск по меню"
/>
</header>
<div class="categories" id="categories"></div>
<main id="productList"></main>
<div class="cart-bar">
<div class="cart-info">
<div class="cart-line">
<span class="cart-label">Сумма заказа</span>
<span class="cart-total" id="cartTotal">0 ₽</span>
</div>
<div class="cart-items-count" id="cartItemsCount">
0 товаров в корзине
</div>
</div>
<button class="cart-button">Оформить</button>
</div>
</div>
</div>
<script>
const CATEGORIES = [
{
id: "ready",
title: "Готовая еда",
items: [
{ name: "Блинчики домашние", meta: "", price: 550 },
{ name: "Сэндвич с ветчиной и сыром", meta: "", price: 600 },
{ name: "Ролл Цезарь", meta: "", price: 600 },
{
name: "Чизбургер с говяжьей котлетой и сыром",
meta: "",
price: 700,
},
{ name: "Чиабатта с копчёной индейкой", meta: "", price: 500 },
{
name: "Круассан Цезарь с курицей и салатом",
meta: "",
price: 500,
},
{
name: "Курица в соусе карри с рисом басмати",
meta: "",
price: 600,
},
{
name: "Бефстроганов с картофельным пюре",
meta: "",
price: 600,
},
{ name: "Борщ вегетарианский", meta: "", price: 450 },
{ name: "Борщ с мясом", meta: "", price: 450 },
{ name: "Куриный суп", meta: "", price: 450 },
],
},
{
id: "drinks",
title: "Напитки",
items: [
{ name: "Вода без газа 500 мл", meta: "", price: 80 },
{ name: "Вода минеральная 1 л", meta: "", price: 90 },
{ name: "Лимонад 330 мл", meta: "", price: 600 },
{ name: "Морс брусничный 500 мл", meta: "", price: 150 },
{ name: "Сок апельсин 970 мл", meta: "", price: 200 },
{ name: "Сок томатный 750 мл", meta: "", price: 200 },
{ name: "Кола 1,5 л", meta: "", price: 1100 },
{ name: "Энергетический напиток", meta: "", price: 1000 },
{ name: "Сидр безалкогольный 330 мл", meta: "", price: 1000 },
{ name: "Пиво безалкогольное 0,5 л", meta: "", price: 1000 },
{ name: "Вино безалкогольное 750 мл", meta: "", price: 1200 },
],
},
{
id: "snacks",
title: "Снеки",
items: [
{ name: "Чипсы с сыром 140 г", meta: "", price: 900 },
{ name: "Чипсы с беконом 140 г", meta: "", price: 900 },
{ name: "Чипсы с зелёным луком 140 г", meta: "", price: 900 },
{ name: "Крекер с сыром 100 г", meta: "", price: 800 },
{ name: "Крекер сметана-лук 100 г", meta: "", price: 800 },
{ name: "Сухарики томат-зелень 60 г", meta: "", price: 800 },
{ name: "Сухарики ржаные 60 г", meta: "", price: 800 },
],
},
{
id: "cakes",
title: "Торты и печенье",
items: [
{ name: "Медовик 120 г", meta: "", price: 600 },
{ name: "Пирожное картошка 120 г", meta: "", price: 500 },
{ name: "Чизкейк 70 г", meta: "", price: 600 },
{ name: "Эклер ванильный 150 г", meta: "", price: 500 },
{ name: "Вафли 150 г", meta: "", price: 500 },
{ name: "Трубочки вафельные 190 г", meta: "", price: 500 },
],
},
{
id: "choco",
title: "Шоколад и орехи",
items: [
{ name: "Молочный шоколад 80 г", meta: "", price: 1200 },
{
name: "Молочный шоколад с цельным фундуком 80 г",
meta: "",
price: 1200,
},
{
name: "Шоколадный батончик с фундуком 30 г",
meta: "",
price: 1000,
},
{
name: "Шоколадный батончик с кокосом 82 г",
meta: "",
price: 1000,
},
{ name: "Тёмный шоколад 75 г", meta: "", price: 900 },
{ name: "Кешью жареный 113 г", meta: "", price: 100 },
{
name: "Арахис в хрустящей корочке со вкусом сыра 100 г",
meta: "",
price: 100,
},
],
},
{
id: "bakery",
title: "Булочная",
items: [
{ name: "Хлеб пшеничный", meta: "", price: 300 },
{ name: "Хлеб зерновой", meta: "", price: 300 },
{ name: "Лепёшки мексиканские", meta: "", price: 300 },
],
},
{
id: "care",
title: "Гигиена",
items: [
{ name: "Зубная паста 75 г", meta: "", price: 200 },
{ name: "Зубная щётка", meta: "", price: 250 },
{ name: "Шампунь 400 мл", meta: "", price: 400 },
{ name: "Бальзам для волос 387 мл", meta: "", price: 400 },
{ name: "Дезодорант 40 мл", meta: "", price: 300 },
],
},
{
id: "musthave",
title: "Очень надо",
items: [
{ name: "Тетрадь в клетку", meta: "1 шт.", price: 150 },
{ name: "Блокнот", meta: "1 шт.", price: 150 },
{ name: "Ручки шариковые", meta: "4 шт.", price: 80 },
{ name: "Фломастеры", meta: "12 шт.", price: 300 },
{ name: "Носки", meta: "1 пара", price: 159 },
],
},
];
const cart = new Map(); // key: product name, value: {price, qty}
const categoriesEl = document.getElementById("categories");
const productListEl = document.getElementById("productList");
const cartTotalEl = document.getElementById("cartTotal");
const cartItemsCountEl = document.getElementById("cartItemsCount");
const searchInput = document.getElementById("searchInput");
let activeCategoryId = CATEGORIES[0].id;
function formatPrice(value) {
return value.toLocaleString("ru-RU") + " ₽";
}
function getTotalItems() {
let total = 0;
cart.forEach((item) => {
total += item.qty;
});
return total;
}
function getTotalPrice() {
let total = 0;
cart.forEach((item) => {
total += item.qty * item.price;
});
return total;
}
function updateCartUI() {
const items = getTotalItems();
const total = getTotalPrice();
cartTotalEl.textContent = formatPrice(total);
let label = "товаров";
if (items === 1) label = "товар";
else if (items > 1 && items < 5) label = "товара";
cartItemsCountEl.textContent = `${items} ${label} в корзине`;
}
function changeQty(name, price, delta) {
const current = cart.get(name) || { price, qty: 0 };
const newQty = Math.max(0, current.qty + delta);
if (newQty === 0) {
cart.delete(name);
} else {
cart.set(name, { price, qty: newQty });
}
updateCartUI();
// Обновляем счётчик в карточке
const span = document.querySelector(
`.qty-value[data-product="${CSS.escape(name)}"]`
);
if (span) {
span.textContent = newQty;
}
}
function renderCategories() {
categoriesEl.innerHTML = "";
CATEGORIES.forEach((cat) => {
const btn = document.createElement("button");
btn.className =
"category-btn" + (cat.id === activeCategoryId ? " active" : "");
btn.textContent = cat.title;
btn.addEventListener("click", () => {
activeCategoryId = cat.id;
document
.querySelectorAll(".category-btn")
.forEach((b) => b.classList.remove("active"));
btn.classList.add("active");
renderProducts();
});
categoriesEl.appendChild(btn);
});
}
function renderProducts() {
const query = searchInput.value.trim().toLowerCase();
productListEl.innerHTML = "";
CATEGORIES.forEach((category) => {
if (category.id !== activeCategoryId) return;
const sectionTitle = document.createElement("div");
sectionTitle.className = "section-title";
sectionTitle.textContent = category.title;
productListEl.appendChild(sectionTitle);
category.items.forEach((item) => {
if (query) {
const haystack =
(item.name + " " + (item.meta || "")).toLowerCase();
if (!haystack.includes(query)) return;
}
const card = document.createElement("div");
card.className = "product-card";
const icon = document.createElement("div");
icon.className = "product-icon";
icon.textContent = "Без бренда";
const info = document.createElement("div");
info.className = "product-info";
const name = document.createElement("div");
name.className = "product-name";
name.textContent = item.name;
const meta = document.createElement("div");
meta.className = "product-meta";
meta.textContent = item.meta || "Товар";
const bottom = document.createElement("div");
bottom.className = "product-bottom";
const price = document.createElement("div");
price.className = "product-price";
price.textContent = formatPrice(item.price);
const controls = document.createElement("div");
controls.className = "qty-controls";
const minusBtn = document.createElement("button");
minusBtn.className = "qty-btn";
minusBtn.textContent = "−";
minusBtn.addEventListener("click", () =>
changeQty(item.name, item.price, -1)
);
const qtyValue = document.createElement("span");
qtyValue.className = "qty-value";
qtyValue.dataset.product = item.name;
qtyValue.textContent = (cart.get(item.name)?.qty || 0).toString();
const plusBtn = document.createElement("button");
plusBtn.className = "qty-btn plus";
plusBtn.textContent = "+";
plusBtn.addEventListener("click", () =>
changeQty(item.name, item.price, 1)
);
controls.appendChild(minusBtn);
controls.appendChild(qtyValue);
controls.appendChild(plusBtn);
const addBtn = document.createElement("button");
addBtn.className = "add-btn";
addBtn.textContent = "В корзину";
addBtn.addEventListener("click", () =>
changeQty(item.name, item.price, 1)
);
bottom.appendChild(price);
bottom.appendChild(controls);
info.appendChild(name);
info.appendChild(meta);
info.appendChild(bottom);
card.appendChild(icon);
card.appendChild(info);
productListEl.appendChild(card);
});
});
}
searchInput.addEventListener("input", () => {
renderProducts();
});
renderCategories();
renderProducts();
updateCartUI();
</script>
</body>
</html>