← Назад к вопросам
Создать адаптивное навигационное меню
2.0 Middle🔥 171 комментариев
#HTML и CSS
Условие
Создайте адаптивное навигационное меню с мобильной версией (бургер-меню).
Требования
-
Desktop версия (более 768px):
- Горизонтальное меню со ссылками
- Hover-эффекты на пунктах меню
- Выделение активного пункта
-
Mobile версия (менее 768px):
- Кнопка-бургер для открытия меню
- Выезжающее боковое меню или dropdown
- Анимация открытия/закрытия
-
Доступность:
- Корректная работа с клавиатурой
- ARIA-атрибуты для screen readers
Технологии
Можно использовать чистый CSS/JS или React.
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Решение
Задача на адаптивное навигационное меню — показывает понимание responsive design, accessibility и работы с состоянием. Создадим несколько версий.
Решение 1: React с Tailwind CSS
import React, { useState } from "react";
interface NavLink {
label: string;
href: string;
icon?: string;
}
interface NavbarProps {
brand: string;
links: NavLink[];
currentPath?: string;
}
function Navbar({ brand, links, currentPath = "/" }: NavbarProps) {
const [isOpen, setIsOpen] = useState(false);
const toggleMenu = () => {
setIsOpen(!isOpen);
};
const closeMenu = () => {
setIsOpen(false);
};
return (
<nav
className="bg-white shadow-lg"
role="navigation"
aria-label="Main navigation"
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
{/* Логотип/Бренд */}
<div className="flex-shrink-0">
<a
href="/"
className="text-2xl font-bold text-blue-600 hover:text-blue-700"
>
{brand}
</a>
</div>
{/* Desktop Menu */}
<div className="hidden md:flex space-x-1">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className={`px-4 py-2 rounded-lg font-medium transition-colors ${
currentPath === link.href
? "bg-blue-600 text-white"
: "text-gray-700 hover:bg-gray-100"
}`}
aria-current={currentPath === link.href ? "page" : undefined}
>
{link.label}
</a>
))}
</div>
{/* Mobile Menu Button */}
<div className="md:hidden">
<button
onClick={toggleMenu}
className="inline-flex items-center justify-center p-2 rounded-md text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-600"
aria-expanded={isOpen}
aria-controls="mobile-menu"
aria-label="Toggle navigation menu"
>
{/* Hamburger Icon */}
<svg
className={`h-6 w-6 transition-transform ${
isOpen ? "transform rotate-90" : ""
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
</button>
</div>
</div>
</div>
{/* Mobile Menu */}
{isOpen && (
<div
id="mobile-menu"
className="md:hidden bg-gray-50 border-t"
role="menu"
>
<div className="px-2 pt-2 pb-3 space-y-1">
{links.map((link) => (
<a
key={link.href}
href={link.href}
onClick={closeMenu}
className={`block px-3 py-2 rounded-lg font-medium transition-colors ${
currentPath === link.href
? "bg-blue-600 text-white"
: "text-gray-700 hover:bg-gray-200"
}`}
role="menuitem"
aria-current={currentPath === link.href ? "page" : undefined}
>
{link.label}
</a>
))}
</div>
</div>
)}
</nav>
);
}
export default Navbar;
Решение 2: С анимацией и закрытием клавишей Escape
function NavbarAdvanced({ brand, links, currentPath = "/" }: NavbarProps) {
const [isOpen, setIsOpen] = useState(false);
// Закрытие меню при нажатии Escape
React.useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === "Escape") {
setIsOpen(false);
}
};
if (isOpen) {
document.addEventListener("keydown", handleEscape);
// Блокируем скроллинг при открытом меню
document.body.style.overflow = "hidden";
return () => {
document.removeEventListener("keydown", handleEscape);
document.body.style.overflow = "unset";
};
}
}, [isOpen]);
return (
<nav className="bg-white shadow-lg" role="navigation">
<div className="max-w-7xl mx-auto px-4">
<div className="flex justify-between items-center h-16">
{/* Логотип */}
<a href="/" className="text-2xl font-bold text-blue-600">
{brand}
</a>
{/* Desktop Menu */}
<div className="hidden md:flex space-x-1">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className={`px-4 py-2 rounded-lg font-medium transition-all duration-200 ${
currentPath === link.href
? "bg-blue-600 text-white shadow-md"
: "text-gray-700 hover:bg-gray-100 hover:translate-y-[-2px]"
}`}
>
{link.label}
</a>
))}
</div>
{/* Mobile Burger Button */}
<button
onClick={() => setIsOpen(!isOpen)}
className="md:hidden flex flex-col gap-1.5 p-2 hover:bg-gray-100 rounded-lg transition-colors"
aria-expanded={isOpen}
aria-label="Toggle navigation menu"
>
{/* Animated Hamburger */}
<span
className={`w-6 h-0.5 bg-gray-700 transition-all duration-300 ${
isOpen ? "rotate-45 translate-y-2" : ""
}`}
/>
<span
className={`w-6 h-0.5 bg-gray-700 transition-all duration-300 ${
isOpen ? "opacity-0" : ""
}`}
/>
<span
className={`w-6 h-0.5 bg-gray-700 transition-all duration-300 ${
isOpen ? "-rotate-45 -translate-y-2" : ""
}`}
/>
</button>
</div>
</div>
{/* Mobile Menu with Animation */}
<div
className={`md:hidden overflow-hidden transition-all duration-300 ${
isOpen ? "max-h-64" : "max-h-0"
}`}
>
<div className="bg-gray-50 px-4 py-3 space-y-2 border-t">
{links.map((link) => (
<a
key={link.href}
href={link.href}
onClick={() => setIsOpen(false)}
className={`block px-3 py-2 rounded-lg font-medium transition-colors ${
currentPath === link.href
? "bg-blue-600 text-white"
: "text-gray-700 hover:bg-gray-200"
}`}
>
{link.label}
</a>
))}
</div>
</div>
{/* Overlay for Mobile */}
{isOpen && (
<div
className="md:hidden fixed inset-0 bg-black bg-opacity-50 z-40"
onClick={() => setIsOpen(false)}
aria-hidden="true"
/>
)}
</nav>
);
}
Решение 3: Боковое меню с анимацией
function NavbarSidebar({ brand, links, currentPath = "/" }: NavbarProps) {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<nav className="bg-white shadow-lg" role="navigation">
<div className="max-w-7xl mx-auto px-4">
<div className="flex justify-between items-center h-16">
<a href="/" className="text-2xl font-bold text-blue-600">
{brand}
</a>
{/* Desktop Menu */}
<div className="hidden md:flex space-x-1">
{links.map((link) => (
<a
key={link.href}
href={link.href}
className={`px-4 py-2 rounded-lg font-medium ${
currentPath === link.href
? "bg-blue-600 text-white"
: "text-gray-700 hover:bg-gray-100"
}`}
>
{link.label}
</a>
))}
</div>
{/* Mobile Button */}
<button
onClick={() => setIsOpen(!isOpen)}
className="md:hidden p-2 hover:bg-gray-100 rounded-lg"
aria-expanded={isOpen}
aria-label="Toggle navigation menu"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</nav>
{/* Sidebar Menu */}
<div
className={`fixed top-0 left-0 h-screen w-64 bg-white shadow-xl transform transition-transform duration-300 z-50 ${
isOpen ? "translate-x-0" : "-translate-x-full"
}`}
role="menu"
>
<div className="p-4 border-b">
<button
onClick={() => setIsOpen(false)}
className="p-2 hover:bg-gray-100 rounded-lg"
aria-label="Close navigation menu"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div className="p-4 space-y-2">
{links.map((link) => (
<a
key={link.href}
href={link.href}
onClick={() => setIsOpen(false)}
className={`block px-4 py-3 rounded-lg font-medium transition-colors ${
currentPath === link.href
? "bg-blue-600 text-white"
: "text-gray-700 hover:bg-gray-100"
}`}
role="menuitem"
>
{link.label}
</a>
))}
</div>
</div>
{/* Overlay */}
{isOpen && (
<div
className="fixed inset-0 bg-black bg-opacity-50 z-40 md:hidden"
onClick={() => setIsOpen(false)}
aria-hidden="true"
/>
)}
</>
);
}
Пример использования
const navLinks: NavLink[] = [
{ label: "Home", href: "/" },
{ label: "About", href: "/about" },
{ label: "Services", href: "/services" },
{ label: "Blog", href: "/blog" },
{ label: "Contact", href: "/contact" },
];
<Navbar
brand="MyApp"
links={navLinks}
currentPath={location.pathname}
/>
Важные моменты доступности
-
ARIA-атрибуты:
role="navigation"— семантическая рольaria-label— описание кнопокaria-expanded— состояние менюaria-current="page"— активный пункт
-
Клавиатура:
- Поддержка Escape для закрытия
- Tab для навигации
- Enter/Space для активации
-
Screen Readers:
- Правильная семантика
- Описание интерактивных элементов
aria-hiddenдля декоративных элементов
Best Practices
- Responsive: Использовать Tailwind
hidden md:flex - Анимация: CSS transitions для плавного перехода
- State Management: useState для открытия/закрытия
- Effects: useEffect для Escape и блокировки скроллинга
- Доступность: ARIA-атрибуты и клавиатурная навигация
Рекомендации для собеседования
- Покажите базовую версию с Tailwind
- Добавьте анимацию бургер-кнопки
- Реализуйте закрытие по Escape
- Обсудите accessibility (ARIA, семантика)
- Покажите боковое меню вариант (красиво выглядит)
Лучший выбор для production: Вариант 2 с анимацией и управлением эффектами — полный функционал + доступность + UX.