React

[React] 반응형 네비게이션 바 만들기

swanzzz 2025. 2. 1. 19:51
반응형

네비게이션바를 디자인 하고 디바이스별로 화면을 구상해 보겠습니다.

우선 기본적인 헤더를 디자인 해보겠습니다.

위 그림처럼 좌측은 이미지를 우측은 네브아이템들을 배치하는 형식으로 디자인 해보겠습니다.


1. html 구조 잡기

<header>
  <a class="logo" href="#"><img src="images/swan.png" alt="" /></a>
  <nav>
    <ul class="nav-items">
      <li><a href="">Home</a></li>
      <li><a href="">Link A</a></li>
      <li><a href="">Link B</a></li>
      <li><a href="">Link C</a></li>
    </ul>
  </nav>
</header>

위 코드블럭 처럼 구조를 잡아줍니다.

기본적인 헤더에 이미지와 네브 아이템들이 들어갈 것이고 각각의 로고와 네브 아이템은 클릭이 가능해야 합니다.

따라서 위와 같은 형식으로 구조를 잡아줍니다.


2. CSS 설정

사실 구조만 잡아도 이미 반은 끝난거나 마찬가지 입니다.

위와 같은 모습에서 네브 아이템들만 오른쪽으로 이동 후 네브 아이템에 CSS만 설정해 주면 되기 때문입니다.

2-1. 네브 플로팅

nav {
  float: right;
}

속성을 이용해 네브 아이템만 오른쪽으로 이동시켜 줍니다.

2-2. 로고 크기 조절

.logo {
  display: inline-block;
  height: 36px;
  margin: 12px 25px;
}

.logo > img {
  height: 36px;
}

이제 헤더에 로고를 넣어야 하기 때문에 로고 이미지인 a 태그를 inline-block 요소로 레벨을 변경시켜주고 높이와 마진을 적절히 설정해 줍니다.

2-3. 헤더 설정

header {
  width: 100%;
  height: 60px;
  z-index: 999;
  background-color: #fff;
  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(0, 0, 0, 0.05);
  position: fixed;
  top: 0;
}

로고와 이미지가 위치할 헤더를 설정해 주었습니다.

높이는 임의로 적절한 높이인 60px로 설정해 주었고 화면 전체 상단에 위치가 고정되어야 하기 때문에 너비는 100%로 하고 상단에 fixed 와 top: 0 으로 고정해 주었습니다.

그 다음 네브바가 다른 요소보다 상단에 위치할 수 있도록 z-index 프로퍼티를 설정해 주었고 배경색과 그림자를 이용해 입체적인 효과를 주었습니다.

이렇게 까지 설정해 주었을 때 화면이 이렇게 구성이 됩니다.

이제 ul 요소와 li 요소를 가로로 정렬 후 보기 좋게 디자인을 바꿔 주겠습니다.

2-4. 네브 아이템 설정

.nav-items > li {
  display: inline-block;
}

.nav-items > li > a {
  line-height: 60px;
  padding: 0 30px;
  color: rgba(0, 0, 0, 0.4);
}

기본적으로 li 태그는 block레벨 요소라서 한줄의 하나의 요소가 모든 공간을 차지하여 다음줄로 넘어가게 됩니다.

따라서 block레벨 요소의 특징을 살리며 content 너비를 가지도록 display 레벨을 inline-block으로 변경시켜 주었습니다.

그 후 li 요소 속 a 태그에 보기 좋게 색과 패딩을 설정해 주었고 line-height를 이용해 수직 정렬을 실시해 주었습니다.

이 후 간단하 Reset CSS를 통해 text-decoration을 없애주고 마진과 패딩을 없애 주면 최종적인 모습이 완성됩니다.

여기에 간단히 hover 이벤트만 추가해 주면 데스크탑 네브바 완성입니다.

이제 태블릿 화면과 휴대폰 화면을 기준으로 각각 800px 과 480px의 너비를 가지는 네브바를 디자인 해보겠습니다.


3. 태블릿 화면

태블릿 화면을 구성하기 위해 우선 네브바의 너비를 줄이며 어떤 문제점이 있는지 살펴보겠습니다.

크기를 줄였을 때 반응형으로 제작되지 않아 네브바 밖으로 네브아이템이 벗어나는걸 볼 수 있습니다.

이제 이 문제를 해결해 보겠습니다.

@media screen and (max-width: 800px) {
  header{
    height: 120px;
    text-align: center;
  }

  nav {
      float: none;
  }
}

우선 화면 너비 800px을 기준으로 작성해 보겠습니다.

현재 화면은 좌측이나 우측에 따로 요소를 설정하지 않아 너비가 줄어도 네브바가 변경되지 않지만

보통의 웹 페이지의 경우 좌측이나 우측에도 새로운 요소로 화면을 구성하기 때문에 태블릿 화면을 기준으로 작성할 때 네브바의 요소가 CSS에 맞지 않는 경우가 있습니다.

따라서 네브바의 높이를 높이고 float을 해제해 주어 2줄로 네브바를 구성해 주면 됩니다.

그러면 이러한 모습으로 네브바 변경되어 화면을 구성하는 것을 볼 수 있습니다.

태블릿은 여기까지 하면 완료되고 이제 모바일 화면을 기준으로 작성해 보겠습니다.


4. 모바일 화면

모바일 화면의 기본 가로 너비는 480px을 기준으로 합니다.

Google Chrome의 개발자 도구로 모바일 화면을 확인할 수 있는데 보통의 휴대폰의 경우 480px 보다 너비가 더 좁고
폴드나 태블릿의 경우 800보다 더 크기 때문에 그와 같이 구분하여 화면을 구성합니다.

현재 화면을 확인해본 결과 태블릿 기준으로 두줄로 네브바가 구성되었으나 요소가 범위를 벗어난 걸 볼 수 있습니다.

보통 모바일 네브바의 경우 3줄로 표시된 드롭박스에 요소를 넣어 주고 클릭시 x로 변경되는 애니메이션과 함께 네브바가 나타납니다. 이를 한번 구성해 보겠습니다.

4-1. 새로운 html 요소 추가

3줄로 표시된 드롭박스 메뉴를 표시하기 위해 input 태그의 checkbox 속성을 이용하고 label을 이용해 input을 연동하여 커스텀 아이콘을 제작할 수 있습니다.

그러기 위해 기존의 nav 요소에 input 요소와 label 요소를 추가해 주겠습니다.

<header>
  <a class="logo" href="#"><img src="images/swan.png" alt="" /></a>
  <nav>
    <input class="nav-toggle" id="nav-toggle" type="checkbox" />
    <label class="navicon" for="nav-toggle"
           ><span class="navicon-bar"></span
      ></label>
    <ul class="nav-items">
      <li><a href="">Home</a></li>
      <li><a href="">Link A</a></li>
      <li><a href="">Link B</a></li>
      <li><a href="">Link C</a></li>
    </ul>
  </nav>
</header>

기존의 헤더 요소의 nav 위치에 input 과 label 그리고 span 태그를 추가해 주었습니다.

input의 id 와 label의 for 속성을 같은 값을 작성해 연동할 수 있고 label에 커스텀아이콘을 표시하기 위해 span 태그를 이용하여 커스텀아이콘이 들어갈 공간을 마련해 주었습니다.

이제 css를 이용해 커스텀 아이콘을 제작해 보겠습니다.

4-2. 커스텀아이콘 제작

우선 label의 위치부터 잡아주도록 하겠습니다.

input은 화면에 체크박스만 표시되는 상태이기에 기본적으로 display: none 속성을 이용해 화면에 표시하지 않고 label만을 이용해 위치를 잡아주겠습니다.

.navicon {
  position: absolute;
  top: 0;
  right: 0;
  cursor: pointer;
  height: 60px;
  padding: 28px 15px;
  border: 1px solid red;
}

.nav-toggle {
  display: none;
}

간단하게 위치를 잡고 화면에 표시해 보았습니다.

네브 아이콘은 우상단에 위치하고 해당 위치에 3줄의 드롭박스 아이콘이 들어가야 합니다.

이제 해당 공간에 아이콘을 만들어 보겠습니다.

.navicon-bar {
  display: block;
  position: relative;
  width: 20px;
  height: 3px;
  background-color: #333;
}

기본적으로 span 태그는 inline 요소로 너비와 마진등을 지정할 수 없습니다. 그렇다고 해당 위치에 한글 ㅡ 나 하이픈(-) 으로 표시하면 글꼴이나 크기에 따라 원하는 대로 표시되지 않을 수 있기에 요소의 레벨을 block으로 변경하고 너비를 지정한 뒤 배경색으로 하이픈(-) 처럼 표시해 주었습니다.

이제 1줄이 생성되었으니 해당 요소에 가상요소 before 와 after를 이용하여 3줄로 표시해 보겠습니다.

.navicon-bar::before,
.navicon-bar::after {
  display: block;
  content: "";
  background-color: #333;
  width: 100%;
  height: 100%;
  position: absolute;
}

.navicon-bar::before {
  top: -7px;
}

.navicon-bar::after {
  top: 7px;
}

가상요소 before 와 after에 들어갈 내용은 동일하니 쉼표로 구분하여 한번에 작성해 주었습니다.

마찬가지로 display 속성을 변경시키고 content는 빈칸으로 표시할 것이니 비워주었습니다.

또한 배경색을 맞춰주고 너비와 높이를 기존의 요소와 동일하게 맞춰주었습니다.

마지막으로 기존 요소에서 약간 높이 차이를 주어 이동시켜주기 위해 포지션을 absolute로 설정해 주었습니다.

이러면 네브 아이템이 들어갈 드롭박스 아이콘은 모두 만들어 주었기 때문에 애니메이션 효과를 구현해 보겠습니다.

4-3. 3줄 -> X로 변경되는 애니메이션 제작

애니메이션을 제작하려면 기존의 요소 3줄에서 2줄이 45도 각도와 -45도 각도로 움직이면 x 표시가 됩니다.

그러면 기존의 navicon-bar를 화면에 표시하지 않고, 가상요소 2개만 움직이며 각도를 틀어주면 되겠습니다.

일단 그렇게 되려면 몇 가지 조건이 필요합니다.

  1. input이 클릭되면 label 기본 요소의 배경색 제거
  2. 가상요소 before가 가운데 자리로 이동하며 45도 회전
  3. 가상요소 after가 가운데 자리로 이동하며 -45도 회전
/* 1. input 클릭시 기존요소 배경색 제거 */
.nav-toggle:checked ~ .navicon > .navicon-bar {
  background-color: transparent;
}

/* 2. 가상요소 before 자리이동 & 회전 */
.nav-toggle:checked ~ .navicon > .navicon-bar::before {
  transform: rotate(45deg);
  top: 0;
}

/* 3. 가상요소 after 자리이동 & 회전 */
.nav-toggle:checked ~ .navicon > .navicon-bar::aftef {
  transform: rotate(-45deg);
  top: 0;
}

여기까지 하면 input 인 nav-toggle이 클릭되면 동일한 레벨의 .navicon 의 자식요소인 navicon-bar를 찾아서 배경색을 제거하고 가상요소를 회전하며 자리를 이동합니다.

그러나 transition을 설정해 주지 않아 부드럽게 움직이지 않고 클릭시 바로바로 바뀌게 될 것입니다. 그럼 기존 요소에 transition을 설정해 부드럽게 움직이도록 바꿔주면 완성입니다.

      .navicon-bar {
        display: block;
        position: relative;
        width: 20px;
        height: 3px;
        background-color: #333;
        transition: background-color 0.2s ease;
      }

      .navicon-bar::before,
      .navicon-bar::after {
        background-color: #333;
        content: "";
        display: block;
        height: 100%;
        width: 100%;
        position: absolute;
        transition: all 0.2s ease;
      }
      .navicon-bar::before {
        top: -7px;
      }
      .navicon-bar::after {
        top: 7px;
      }
      .nav-toggle:checked ~ .navicon > .navicon-bar {
        background-color: transparent;
      }
      .nav-toggle:checked ~ .navicon > .navicon-bar::before {
        transform: rotate(45deg);
        top: 0;
      }
      .nav-toggle:checked ~ .navicon > .navicon-bar::after {
        transform: rotate(-45deg);
        top: 0;
      }
      .nav-toggle {
        display: none;
      }

자 이제 드롭박스 메뉴를 모두 만들었으니 네브 아이템들을 드롭박스에 넣고 media screen 의 너비가 480 보다 아래일 때만 화면에 표시되도록 해주겠습니다.

일단 기존화면에서는 보이지 않게 가려주고 media screen and (max-width: 480px) 일때 display 를 보이도록 설정해 주겠습니다.

@media screen and (max-width: 480px) {
  .navicon {
    display: block;
  }
}

그런다음 네브 아이템들을 가려주고 아이콘을 클릭하면 네브가 커지며 화면에 네브 아이템이 나타나도록 설정해 주겠습니다.

그러기 위해 다음과 같은 설정을 해주겠습니다.

  1. 늘려놓은 헤더 높이 줄이기
  2. 화면에서 nav-items 가리기
  3. input 클릭시 nav-items가 나타나고 배경색과 그림자 효과로 입체효과 주기
  4. li 태그요소에 적용된 inline-block 속성 바꿔주기
@media screen and (max-width: 480px) {
  header {
    height: 60px;
  }

  .nav-items {
    display: none;
  }

  .navicon {
    display: block;
  }

  .nav-toggle:checked ~ .nav-items {
    display: block;
    background-color: #fff;
    box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(0, 0, 0, 0.05);
    width: 100%;
  }

  .nav-items > li {
    display: block;
  }

  .nav-items > li > a {
    line-height: 60px;
  }
}

이렇게 작성해 반응형 네브바를 구성해 보았습니다.

전체 코드는 다음과 같습니다.

html 코드

<header>
  <a class="logo" href="#"><img src="images/swan.png" alt="" /></a>
  <nav>
    <input class="nav-toggle" id="nav-toggle" type="checkbox" />
    <label class="navicon" for="nav-toggle"
           ><span class="navicon-bar"></span
      ></label>
    <ul class="nav-items">
      <li><a href="">Home</a></li>
      <li><a href="">Link A</a></li>
      <li><a href="">Link B</a></li>
      <li><a href="">Link C</a></li>
    </ul>
  </nav>
</header>

css 코드

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

a {
  text-decoration: none;
}

header {
  width: 100%;
  height: 60px;
  z-index: 2000;
  background-color: #fff;
  box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(0, 0, 0, 0.05);
  position: fixed;
  top: 0;
}

nav {
  float: right;
}

.logo {
  display: inline-block;
  height: 36px;
  margin: 12px 25px;
}

.logo > img {
  height: 36px;
}

.nav-items > li {
  display: inline-block;
}

.nav-items > li > a {
  line-height: 60px;
  padding: 0 30px;
  color: rgba(0, 0, 0, 0.4);
}

.nav-items > li > a:hover {
  color: rgba(0, 0, 0, 0.8);
}

.navicon {
  position: absolute;
  top: 0;
  right: 0;
  cursor: pointer;
  height: 60px;
  padding: 28px 15px;
}

.navicon-bar {
  display: block;
  position: relative;
  width: 20px;
  height: 3px;
  background-color: #333;
  transition: background-color 0.2s ease;
}

.navicon-bar::before,
.navicon-bar::after {
  display: block;
  content: "";
  background-color: #333;
  width: 100%;
  height: 100%;
  position: absolute;
  transition: all 0.2s ease;
}

.navicon-bar::before {
  top: -7px;
}

.navicon-bar::after {
  top: 7px;
}

.nav-toggle:checked ~ .navicon > .navicon-bar {
  background-color: transparent;
}

.nav-toggle:checked ~ .navicon > .navicon-bar::before {
  transform: rotate(45deg);
  top: 0;
}

.nav-toggle:checked ~ .navicon > .navicon-bar::after {
  transform: rotate(-45deg);
  top: 0;
}

.nav-toggle {
  display: none;
}

.navicon {
  display: none;
}

@media screen and (max-width: 800px) {
  header {
    height: 120px;
    text-align: center;
  }

  nav {
    float: none;
  }
}

@media screen and (max-width: 480px) {
  header {
    height: 60px;
  }

  .nav-items {
    display: none;
  }

  .navicon {
    display: block;
  }

  .nav-toggle:checked ~ .nav-items {
    display: block;
    background-color: #fff;
    box-shadow: 0 2px 2px rgba(0, 0, 0, 0.05), 0 1px 0 rgba(0, 0, 0, 0.05);
    width: 100%;
  }

  .nav-items > li {
    display: block;
  }

  .nav-items > li > a {
    line-height: 60px;
  }
}

반응형