[Study] 개발 공부/[React] 리액트 공부

[React] 모바일에서는 메뉴가 버튼으로 바뀌는 반응형 헤더 만들어보기

지공A 2024. 11. 4. 17:15

여태 반응형 웹 사이트를 제대로 구현해보지 못한게 아쉬워서, 포트폴리오에서는 반응형 웹 사이트를 만들어 보려고 한다!

 

웹에서는 기본적으로 헤더 메뉴가, 모바일에서는 메뉴 버튼을 누르면 메뉴가 보이도록 하고 디자인에도 차별점을 줘봤다.

모바일
모바일 햄버거 메뉴 클릭 시

 

1. isNavVisible 상태를 생성하고, toggleNav 함수를 햄버거 메뉴의 onClick에 연결한다.

const [isNavVisible, setIsNavVisible] = useState(false);

  const toggleNav = () => {
    setIsNavVisible(!isNavVisible);
  };

 

2. 모바일의 햄버거 메뉴 컴포넌트 <NavMobile>을 모바일에서만 보이도록 미디어 쿼리를 설정해줬다.

 - 햄버거 메뉴 컴포넌트인 NavMobile과, 모바일용 Nav 메뉴 css를 주석으로 달아놨다. (기본 Nav css 생략)

const NavMobile = styled.div<{ isNavVisible: boolean }>`
  display: none;
  width: 40px;
  height: 40px;
  cursor: pointer;

  /* 모바일에서만 보이게 */
  @media (max-width: 768px) {
    display: block;
  }

  span {
    display: block;
    width: 40px;
    height: 2px;
    /* 메뉴가 열리면(isNavVisible) 흰색, 닫히면 검은색 */
    background-color: ${(props) => (props.isNavVisible ? "#fff " : "#000")};
    margin-top: 19px;
    position: relative;

    /* 햄버거 메뉴의 윗 줄 막대 */
    &::before {
      content: ""; // 가상 요소에 content 속성이 필수!
      width: 40px;
      height: 2px;
      background-color: ${(props) => props.theme.colors.black000};
      position: absolute;
      right: 0;
      top: 6px;
      // 메뉴가 열리면, 막대 길이를 반으로 줄어드는데, 자연스럽게 애니메이션 효과 적용
      transition: width 0.3s;
    }
    &::after {
      content: "";
      width: 40px;
      height: 2px;
      background-color: ${(props) => props.theme.colors.black000};
      position: absolute;
      left: 0;
      bottom: 6px;
      transition: width 0.3s;
    }
  }
`;

 

const Nav = styled.nav<{ isNavVisible: boolean }>`
  @media (max-width: 768px) {
    position: absolute;
    top: 72px;
    right: 0;
    background-color: rgba(254, 168, 180);
    backdrop-filter: blur(15px);
    z-index: 10000;
    min-width: 150px;
    padding: 20px 0;
    height: 100vh;
    overflow-y: auto;
    /* 활성화 시켰을 때, 왼쪽으로 나오는 효과 */
    transform: translateX(${(props) => (props.isNavVisible ? "0" : "100%")});
    opacity: ${(props) => (props.isNavVisible ? "1" : "0")};
    transition: transform 0.3s ease, opacity 0.3s ease;
    visibility: ${(props) => (props.isNavVisible ? "visible" : "hidden")};

    ul {
      list-style: none;
      padding: 0;
      margin: 0;

      li {
        display: block;
        text-align: right;

        a {
          display: inline-block;
          padding: 10px;
          color: ${(props) => props.theme.colors.black000};
          text-transform: uppercase;
          font-size: 16px;
          font-weight: 700;
          position: relative;

          &::before {
            content: "";
            width: calc(100% - 16px);
            height: 1px;
            background-color: ${(props) => props.theme.colors.white000};
            position: absolute;
            left: 8px;
            bottom: 10px;
            transform: scaleX(0);
            transition: transform 0.3s ease;
          }

          &:hover {
            color: ${(props) => props.theme.colors.white000};
            &::before {
              transform: scaleX(0.9);
            }
          }
        }
      }
    }
    /* isNavVisible이면 즉, 모바일메뉴가 열리면, show 클래스가 추가된다
        show 클래스가 추가되면 NavMobile 햄버거 메뉴 막대의 width를 반으로 줄이고, 흰색으로 변경한다. */
    &.show + ${NavMobile} span::before {
      width: 20px;
      background-color: ${(props) => props.theme.colors.white000};
    }
    &.show + ${NavMobile} span::after {
      background-color: ${(props) => props.theme.colors.white000};
      width: 20px;
    }
  }