nav로 만드는 내비게이션 바
메뉴를 <nav>로 감싸고 링크를 목록에 넣으세요. flexbox가 목록을 가로 바로 바꿔요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Navbar</title>
<style>
body { margin: 0; font-family: system-ui, sans-serif; }
nav { background: #111827; }
nav ul { display: flex; gap: 4px; list-style: none; margin: 0; padding: 8px; }
nav a {
display: block; padding: 8px 14px; border-radius: 6px;
color: #e5e7eb; text-decoration: none;
}
nav a:hover { background: #374151; color: #fff; }
</style>
</head>
<body>
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">Courses</a></li>
<li><a href="#">Pricing</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
</body>
</html>Master HTML the hands-on way
Interactive lessons, real projects, and instant feedback.
START NOW
list-style: none과 padding: 0은 글머리 기호와 들여쓰기를 없애요. 각 링크의 display: block은 padding이 바에서 실제 공간을 차지하게 해서, padding을 포함한 상자 전체가 클릭 영역이 돼요. display: flex 줄을 지우면 같은 메뉴가 세로 목록이 되는데, 사이드바 메뉴가 그런 모양이에요.
<nav> 자체는 아무 모양도 없어요. 역할은 의미를 주는 거예요. 스크린 리더가 내비게이션 랜드마크 로 보여 줘서 사용자가 키 하나로 메뉴로 가거나 메뉴를 건너뛸 수 있어요.
현재 페이지 표시하기
지금 보이는 페이지의 링크에 aria-current="page"를 추가하세요. 스크린 리더에게 "여기가 현재 위치"라고 알려 주고, CSS가 그 링크를 강조할 수 있는 고리도 돼요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Current page</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
nav ul { display: flex; gap: 20px; list-style: none; padding: 0; margin: 0; border-bottom: 2px solid #e5e7eb; }
nav a { display: block; padding: 10px 0; color: #374151; text-decoration: none; }
nav a[aria-current="page"] {
color: #2563eb; font-weight: 600;
box-shadow: inset 0 -3px 0 #2563eb;
}
</style>
</head>
<body>
<nav aria-label="Main">
<ul>
<li><a href="#">Overview</a></li>
<li><a href="#" aria-current="page">Lessons</a></li>
<li><a href="#">Projects</a></li>
</ul>
</nav>
</body>
</html>
aria-current="page"를 다른 링크로 옮기면 강조도 따라가요. .active 클래스 대신 속성에 스타일을 주면 모양과 접근성이 항상 함께 움직여요. 하나만 있고 다른 하나는 없는 상태가 생길 수 없어요.
한 페이지의 여러 nav: 라벨과 브레드크럼
한 페이지에 주 메뉴, 브레드크럼, 목차가 각각 <nav>로 있을 수 있어요. 각각에 aria-label을 주면 스크린 리더가 똑같은 "내비게이션" 세 개 대신 "Breadcrumb 내비게이션"처럼 알려 줘요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Breadcrumb</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
.crumbs ol { display: flex; flex-wrap: wrap; list-style: none; padding: 0; margin: 0; }
.crumbs li + li::before { content: "/"; margin: 0 8px; color: #9ca3af; }
.crumbs a { color: #2563eb; }
</style>
</head>
<body>
<nav class="crumbs" aria-label="Breadcrumb">
<ol>
<li><a href="#">Home</a></li>
<li><a href="#">Docs</a></li>
<li><a href="#">HTML</a></li>
<li><span aria-current="page">nav</span></li>
</ol>
</nav>
<h1>HTML nav</h1>
</body>
</html>
브레드크럼은 순서가 중요해서 순서 있는 목록 <ol>을 써요. 슬래시는 CSS ::before에서 나오기 때문에 스크린 리더가 항목마다 "슬래시"라고 읽지 않아요. 마지막 항목은 현재 페이지라서 링크가 아니에요.
목차
같은 페이지의 섹션으로 가는 링크 목록도 내비게이션이에요. #id 링크는 그 id를 가진 요소로 이동해요. 미리보기에서 눌러 보세요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Table of contents</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
nav { border: 1px solid #e5e7eb; border-radius: 8px; padding: 8px 16px; }
nav h2 { font-size: 14px; margin: 4px 0; }
section { min-height: 200px; border-top: 1px solid #e5e7eb; }
</style>
</head>
<body>
<nav aria-labelledby="toc-title">
<h2 id="toc-title">On this page</h2>
<ol>
<li><a href="#install">Install</a></li>
<li><a href="#configure">Configure</a></li>
<li><a href="#deploy">Deploy</a></li>
</ol>
</nav>
<section id="install"><h2>Install</h2></section>
<section id="configure"><h2>Configure</h2></section>
<section id="deploy"><h2>Deploy</h2></section>
</body>
</html>
여기서는 aria-labelledby로 보이는 제목에서 라벨을 가져와요. 제목이 이미 화면에 있다면 aria-label보다 이게 나아요.
모바일 메뉴 토글
좁은 화면에서는 보통 메뉴를 버튼 뒤에 숨겨요. aria-expanded가 있는 진짜 <button>을 쓰고, 링크는 <nav> 안에 두세요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Menu toggle</title>
<style>
body { font-family: system-ui, sans-serif; margin: 12px; }
button { font: inherit; padding: 6px 12px; border: 1px solid #111827; border-radius: 6px; background: #fff; cursor: pointer; }
#menu { list-style: none; padding: 0; margin: 8px 0 0; border: 1px solid #e5e7eb; border-radius: 8px; }
#menu a { display: block; padding: 10px 14px; color: #111827; text-decoration: none; }
#menu li + li { border-top: 1px solid #e5e7eb; }
</style>
</head>
<body>
<nav aria-label="Main">
<button aria-expanded="false" aria-controls="menu" id="toggle">Menu</button>
<ul id="menu" hidden>
<li><a href="#">Home</a></li>
<li><a href="#">Courses</a></li>
<li><a href="#">Contact</a></li>
</ul>
</nav>
<script>
const toggle = document.getElementById('toggle');
const menu = document.getElementById('menu');
toggle.addEventListener('click', () => {
const open = toggle.getAttribute('aria-expanded') === 'true';
toggle.setAttribute('aria-expanded', String(!open));
menu.hidden = open;
});
</script>
</body>
</html>
aria-expanded는 스크린 리더에게 메뉴가 열려 있는지 알려 주고, hidden 속성은 닫혀 있는 동안 목록을 화면과 접근성 트리 모두에서 없애요. 넓은 화면에서는 미디어 쿼리로 목록을 보여 주고 버튼을 숨기면 돼요.
자주 하는 실수
모든 링크 묶음을 <nav>로 감쌈. 주 메뉴, 브레드크럼, 페이지 번호, 목차에 쓰세요. 텍스트 안의 인라인 링크는 내비게이션 묶음이 아니에요.
<div>로 만든 메뉴 버튼. 키보드로 포커스하거나 누를 수 없어요. <button>을 쓰세요.
라벨 없는 여러 nav. 구별할 수 있도록 각각에 aria-label이나 aria-labelledby를 추가하세요.
현재 링크에 색만 입힘. aria-current="page"를 추가하세요. 색만으로는 알려 주지 않고, 모든 사람에게 보이지 않을 수도 있어요.
실서비스에서 href="#" 링크. 데모에서는 괜찮지만, 실제 메뉴 항목은 새 탭에서 열어도 동작하도록 진짜 URL이 필요해요. href 값이 동작하는 방식은 링크 페이지에서 다뤄요.