본문 바로가기
웹 프로그래밍/프론트엔드

[HTML/CSS] 사이드바 메뉴 만들기 : 나타나는 2차 메뉴 만들기 feat. transform: translate

by me_in_sk 2023. 5. 16.
반응형

 

 

오늘은 사이드바 메뉴를 만들어 보자. 사이드 메뉴는 이름에 나와 있듯이 사이트의 상단에 위치한 일반적인 메뉴 바와 달리 사이트의 측면에 있는 메뉴 바를 일컫는다. 우리는 이런 사이드바 중에서도 스크롤을 따라오는 메뉴 바를 만들어 사이트의 스크롤이 내려가더라도 항상 화면의 측면에 존재하며, 숨겨진 사이드바가 마우스 포인터를 인식하게 되면 나타나게 되는 사이드바 메뉴를 만들어 보고자 한다. 우선 아래의 완성된 이미지를 보자.

 

 

우선 완성된 이미지를 보자

완성-사이드-메뉴
왼쪽에서 나타나는 사이드바

 

위의 이미지를 보면, 평소에는 왼쪽 벽 속에 숨겨져 보이지 않는 메뉴 바가 마우스 커서를 인식하는 순간 나타나는 구조를 가지고 있는 사이드바의 모습을 보여주며, 2차 메뉴인 드롭다운의 기능 또한 갖고 있다. 시작에 앞서 2차 메뉴의 드롭다운 메뉴를 구현해 보고자 한다면 아래의 링크에 그에 관한 설명이 포스팅되어 있으니, 이를 통해 학습하는 것을 추천한다. 

 

[HTML/CSS] 드롭다운 메뉴 바 만들기 : 2차 메뉴 만들기 feat. position

 

[HTML/CSS] 드롭다운 메뉴 바 만들기 : 2차 메뉴 만들기 feat. position

오늘은 2차 메뉴의 일종인 드롭다운 메뉴를 만들어 보자. 이번에 만들 메뉴는 마우스를 통해 1차 메뉴를 가리키면 2차 메뉴가 내려오며 나타나는 일명 드롭다운 기능을 구현해 보자. 드롭다운 메

me-in-journey.com


 

 

기본 마크업부터 살펴보자

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">

<aside class="side-bar">
  <section class="side-bar__icon-box">
    <section class="side-bar__icon-1">
      <div></div>
      <div></div>
      <div></div>
    </section>
  </section>
  <ul>
    <li>
      <a href="#"><i class="fa-solid fa-cat"></i> menu1</a>
      <ul>
        <li><a href="#">text1</a></li>
        <li><a href="#">text2</a></li>
        <li><a href="#">text3</a></li>
        <li><a href="#">text4</a></li>
      </ul>
    </li>
    <li>
      <a href="#">menu2</a>
      <ul>
        <li><a href="#">text1</a></li>
        <li><a href="#">text2</a></li>
        <li><a href="#">text3</a></li>
        <li><a href="#">text4</a></li>
      </ul>
    </li>
    <li>
      <a href="#">menu3</a>
      <ul>
        <li><a href="#">text1</a></li>
        <li><a href="#">text2</a></li>
        <li><a href="#">text3</a></li>
        <li><a href="#">text4</a></li>
      </ul>
    </li>
    <li>
      <a href="#">menu4</a>
      <ul>
        <li><a href="#">text1</a></li>
        <li><a href="#">text2</a></li>
        <li><a href="#">text3</a></li>
        <li><a href="#">text4</a></li>
      </ul>
    </li>
  </ul>
</aside>

 

위의 HTML 코드를 아무런 CSS 작업 없이 출력한다면 아래와 같은 모습을 보인다.

마크업-메뉴
기본 마크업 메뉴

 

 

우리가 원하는 메뉴 바를 만들어 나가기 위해서는 아래와 같이 노멀라이즈 과정을 통해 내가 원하지 않는 요소들을 제거해주는 작업이 필요하다. 이를 통해 우리가 뜻하지 않은 변수가 생기는 것을 막고 코드를 수월하게 만들어 나갈 수 있게 된다.

/* 노멀라이즈 시작 */
body, ul, li {
  margin: 0;
  padding: 0;
  list-style: none;   	    /* 해당 태그의 list-style을 none으로 하는 것으로 ●을 제거한다 */
}

a {
  color: inherit;   	    /* 부모 엘리먼트의 값을 물려받는다 */
  text-decoration: none;    /* 해당 태그의 text-decoration 속성을 none 값으로 하는 것으로 밑줄을 제거한다 */
}
/* 노멀라이즈 끝 */

 

위의 노멀라이즈를 적용해 보자

노멀라이즈-메뉴
불필요한 속성을 제거한 메뉴

 

 

다음으로, 사이드바 메뉴의 형태를 구성하는 것에 있어 불필요한 2차 메뉴의 모습을 숨겨주겠다. 추가로 사이드바의 크기를 지정하고, 메뉴 항목의 폰트 크기와 영역의 넓이까지 지정해 보자. 이때, 사이드바의 속성 중 position의 값을 fixed로 주게 된다면, 해당 개체는 스크롤의 위치와 관계없이 항상 화면을 따라오게 된다.

 

/* 2차 이상의 메뉴를 숨기기 */
.side-bar > ul ul {
  display: none;
}

/* 사이트의 높이를 5000px로 만들어 스크롤 생성 */
body {
  height: 5000px;
  background-color: #444;
}

/* 사이드바의 너비와 높이를 변수를 통해 통제 */
:root {
  --side-bar-width: 270px;
  --side-bar-height: 90vh;
}

.side-bar {
  position: fixed;    /* 스크롤을 따라오도록 지정 */
  background-color: black;
  width: var(--side-bar-width);
  min-height: var(--side-bar-height);   /* 사이드바의 높이를 전체 화면 높이의 90%로 지정 */
  margin-top: calc((100vh - var(--side-bar-height)) / 2);    /* 사이드바 위와 아래의 마진을 동일하게 지정 */
}

/* 모든 메뉴의 a에 속성값 부여 */
.side-bar ul > li > a {
  display: block;
  color: white;
  font-size: 1.4rem;
  font-weight: bold;
  padding-top: 20px;
  padding-bottom: 20px;
  padding-left: 50px;
}

 

위의 속성들을 적용해 보자

 

스크롤-사이드바
스크롤을 따라오는 사이드바

 

 

위의 이미지와 같이 사이드바가 정상적으로 스크롤을 따라오는 것을 확인할 수 있다. 그렇다면 이번에는 2차 메뉴를 만들 차례이다.

2차 메뉴의 드롭다운 기능 구현은 포스팅 상단의 링크에서 설명했으므로 요약한다면, 이번 사이드바의 드롭다운 2차 메뉴는 하단이 아닌 우측으로 등장한다는 것에서 지난 포스팅과의 차이를 가지며, 2차 메뉴에는 position 속성에 absolute 값을, 2차 메뉴의 부모 엘리먼트에는 position 속성에 relative 값을 주겠다. 우선 아래의 코드를 보자.

 

/* 자식의 position이 absolute일 때 자식을 영역 안에 가두어 준다 */
.side-bar > ul > li {
  position: relative;
}

/* 모든 메뉴가 마우스 인식 시 반응 */
.side-bar ul > li:hover > a {
  background-color: #555;
  border-bottom: 1px solid #999;
}

/* 1차 메뉴의 항목이 마우스 인식 시에 2차 메뉴 등장 */
.side-bar > ul > li:hover > ul {
  display: block;
  position: absolute;
  background-color: #888;
  top: 0;         /* 2차 메뉴의 상단을 1차 메뉴의 상단에 고정 */
  left: 100%;     /* 2차 메뉴를 1차 메뉴의 너비만큼 이동 */
  width: 100%;    /* 1차 메뉴의 너비를 상속 */
}

 

위의 속성들을 적용해 보자

2차-메뉴
1차 메뉴 우측의 2차 메뉴

 

 

위의 이미지에 보이듯이 2차 메뉴가 1차 메뉴의 우측에 등장하는 것을 볼 수 있다. 2차 메뉴 박스에 left 속성에 100%의 값을 주는 것으로 1차 메뉴의 너비만큼을 오른쪽으로 이동시킬 수 있다는 것을 이용한 배치이다.

이로써 사이드바의 드롭다운 2차 메뉴의 기능은 구현이 되었다. 이제는 튀어나와 있는 사이드바 메뉴를 숨겨보도록 하자.

우리는 이러한 기능을 transform 속성을 통해 구현해 볼 것이다. transform 속성은 개체의 이동을 위한 속성으로, 사이드바의 박스 크기를 기준으로 일정 부분만 남기고 모두 화면 밖으로 이동시킨다면 숨은 모습의 사이드바를 만들 수 있게 된다. 아래의 코드를 살펴보자.

 

/* 사이드바 너비의 80%만큼 왼쪽으로 이동 */
.side-bar {
  border-radius: 20px;
  transform: translate(calc(var(--side-bar-width) * -0.8), 0);  /* X축 이동, Y축 고정 */
  transition: .5s;
}

/* 마우스 인식 시 원래의 위치로 이동 */
.side-bar:hover {
  transform: translate(-20px, 0);   /* 둥근 모서리의 너비만큼 X축 이동, Y축 고정 */
}

 

위의 속성들을 적용해 보자

 

숨겨지는-메뉴
사이드에서 등장하는 메뉴 바

 

이제 사이드바 메뉴로써 갖추어야 할 기능들은 얼추 갖춘 느낌이다. 여기에 추가로 사이드바가 나타나는 효과를 잘 나타내기 위해 나타나는 사이드바에 맞춰 반응하는 아이콘을 추가해 보겠다.

아이콘 또한 마찬가지로 메뉴 박스가 오른쪽으로 이동하는 만큼을 왼쪽으로 이동해 준다면, 아이콘은 제자리에 가만히 있는 것처럼 보이게 된다.

아이콘-효과
메뉴 박스에 맞춰 변하는 아이콘

 

이로써 평소에는 사이드바 전체 너비의 80%를 숨기고 있다가 마우스를 인식할 때만 나오는 것을 transform 속성을 통해 완성해 보았다. 위의 방식은 어디까지나 메뉴 바의 모습을 화면 밖으로 밀어내는 것에 불과하며, 실제로 늘어나는 사이드 메뉴와는 엄연히 다른 종류이다. 둘의 차이점이 무엇인지, 늘어나는 방식은 어떻게 만드는 것인지에 대해서는 다음 포스팅에서 다룰 예정이니 사이드바 메뉴가 늘어나며 등장하는 것을 원한다면 다음 포스팅을 참고 바란다.

 

 

[HTML/CSS] 사이드바 메뉴 만들기 : 늘어나는 2차 메뉴 만들기 feat. overflow: hidden

 

[HTML/CSS] 사이드바 메뉴 만들기 : 늘어나는 2차 메뉴 만들기 feat. overflow: hidden

지난 포스팅에서는 transform 속성을 이용하여 미리 만들어둔 사이드바 메뉴를 화면 너머로 숨겨놨다가 나타나게 하는 방식으로 사이드바 메뉴를 만들었다. 잘못된 방식은 아니지만, 개인마다 사

me-in-journey.com


◆ 함께 보면 좋은 글

 

위 단계들의 종합 CSS 코드를 남기며 이번 포스팅을 마친다.
/* 노멀라이즈 시작 */
body, ul, li {
  margin: 0;
  padding: 0;
  list-style: none;   /* 해당 태그의 list-style을 none으로 하는 것으로 ●을 제거한다 */
}

a {
  color: inherit;   /* 부모 엘리먼트의 값을 물려받는다 */
  text-decoration: none;    /* 해당 태그의 text-decoration 속성을 none 값으로 하는 것으로 밑줄을 제거한다 */
}
/* 노멀라이즈 끝 */

/* 커스텀 시작 */
.side-bar > ul ul {
  display: none;
}

/* 사이트의 높이를 5000px로 만들어 스크롤 생성 */
body {
  height: 5000px;
  background-color: #444;
}

/* 사이드바 시작 */

/* 사이드바의 너비와 높이를 변수를 통해 통제 */
:root {
  --side-bar-width: 270px;
  --side-bar-height: 90vh;
}

.side-bar {
  position: fixed;    /* 스크롤을 따라오도록 지정 */
  background-color: black;
  width: var(--side-bar-width);
  min-height: var(--side-bar-height);   /* 사이드바의 높이를 전체 화면 높이의 90%로 지정 */
  margin-top: calc((100vh - var(--side-bar-height)) / 2);    /* 사이드바 위와 아래의 마진을 동일하게 지정 */
}

/* 아이콘 시작 */
.side-bar__icon-box {
  display: flex;
  justify-content: flex-end;
}

.side-bar__icon-1 {
  position: relative;
  width: 23px;
  height: 17px;
  margin: 15px;
  margin-top: 20px;
  transition: .5s;
}

:root {
  --side-bar__icon: .5s;
}

.side-bar__icon-1 > div {
  position: absolute;
  width: 100%;
  height: 20%;
  background-color: white;
  transition: all var(--side-bar__icon);
}

.side-bar__icon-1 > div:nth-of-type(1) {
  top: 0;
  width: auto;
  left: 0;
  right: 0;
  transition: all var(--side-bar__icon), left calc(var(--side-bar__icon) / 2) calc(var(--side-bar__icon) / 2), right calc(var(--side-bar__icon) / 2) calc(var(--side-bar__icon) / 2), height calc(var(--side-bar__icon) / 2) 0s;
}

.side-bar__icon-1 > div:nth-of-type(2) {
  top: 40%;
  transform-origin:bottom left;
}

.side-bar__icon-1 > div:nth-of-type(3) {
  top: 80%;
  left: auto;
  right: 0;
  transform-origin:bottom right;
}


.side-bar:hover .side-bar__icon-1 {
  transform: translate(-198px, 0);
}

.side-bar:hover .side-bar__icon-1 > div:nth-of-type(2) {
  transform:rotate(45deg);
  width: 70.5%;
  height: 25%;
}

.side-bar:hover .side-bar__icon-1 > div:nth-of-type(3) {
  top: 40%;
  transform:rotate(-45deg);
  width: 70.5%;
  height: 25%;
}

.side-bar:hover .side-bar__icon-1 > div:nth-of-type(1) {
  left: 41%;
  right: 41%;
  height: 100%;
  transition: all var(--side-bar__icon), left calc(var(--side-bar__icon) / 2) 0s, right calc(var(--side-bar__icon) / 2) 0s, height calc(var(--side-bar__icon) / 2) calc(var(--side-bar__icon) / 2);
}
/* 아이콘 끝 */

/* 모든 메뉴의 a에 속성값 부여 */
.side-bar ul > li > a {
  display: block;
  color: white;
  font-size: 1.4rem;
  font-weight: bold;
  padding-top: 20px;
  padding-bottom: 20px;
  padding-left: 50px;
  transition: .5s;
}

/* 자식의 position이 absolute일 때 자식을 영역 안에 가두어 준다 */
.side-bar > ul > li {
  position: relative;
}

/* 모든 메뉴가 마우스 인식 시 반응 */
.side-bar ul > li:hover > a {
  background-color: #555;
  border-bottom: 1px solid #999;
}

/* 1차 메뉴의 항목이 마우스 인식 시에 2차 메뉴 등장 */
.side-bar > ul > li:hover > ul {
  display: block;
  position: absolute;
  background-color: #888;
  top: 0;         /* 2차 메뉴의 상단을 1차 메뉴의 상단에 고정 */
  left: 100%;     /* 2차 메뉴를 1차 메뉴의 너비만큼 이동 */
  width: 100%;    /* 1차 메뉴의 너비를 상속 */
}

/* 사이드바 너비의 80%만큼 왼쪽으로 이동 */
.side-bar {
  border-radius: 20px;
  transform: translate(calc(var(--side-bar-width) * -0.8), 0);
  transition: .5s;
}

/* 마우스 인식 시 원래의 위치로 이동 */
.side-bar:hover {
  transform: translate(-20px, 0);   /* 둥근 모서리의 너비만큼 숨겨주기 */
}
/* 사이드바 끝 */

/* 커스텀 끝 */

 

반응형

댓글