CSS 플렉스박스(flex)와 마진(margin)으로 반응형 메뉴 만들기

CSS 속성 중 블록 요소에 여백을 추가하는 속성은 패딩(padding) 과 마진(margin) 이 있습니다.

그중 마진(margin) 속성은 요소의 외곽에 여백을 설정하는 속성입니다.

그리고,  마진(margin) 속성에는 패딩(padding) 속성에는 없는 "auto" 속성 값 추가로 있습니다.

이 "auto" 속성 값이 레이아웃을 배치하는데 여러가지 마법을 만들어 냅니다.

특히 상하좌우에 남는 공간이 자동으로 배분되기 때문에 "auto" 속성은 레이아웃을 만드는 복잡한 과정을 단순화 해주는 장점이 있습니다.

마진 속성을 사용해 메뉴에 여백을 배치하는 방법을 먼저 알아 보겠습니다.

마진으로 플렉스박스 아이템 여백 배분하기

"auto" 속성 값은 동위 요소들에 부여된 "auto" 갯수만큼 남는 여백을 배분하는 특징이 있습니다.

동위 요소들에 적용된 마진 "auto" 속성 값이 1개면 남는 모든 여백이 "auto" 속성이 부여된 위치에 부여됩니다.

2개면 여백을 반씩 배분해서 적용합니다. 3개면 3등분을 합니다.

다음과 같이 목록으로 만든 메뉴를 마진 "auto" 속성을 이용해 여백으로 배치를 해보겠습니다.

<div class="nav">
  <ul class="menu">
    <li><a href="#">홈</a></li>
    <li><a href="#">상품</a></li>
    <li><a href="#">서비스</a></li>
    <li><a href="#">자료실</a></li>
    <li><a href="#">고객지원</a></li>
    <li><a href="#">마이페이지</a></li>
  </ul>
</div>

플렉스박스로 가로 메뉴를 만들면 다음과 같이 만들어집니다. 기본적으로 오른쪽에 여백이 있고, 플렉스박스 안의 메뉴 아이템들은 왼쪽 정렬로 배치가 됩니다.

html, body{
    margin: 0;
    padding: 0;
    height: 100%;
}

/*메뉴 영역*/
.nav{
    padding: 5px 20px;
    box-sizing: border-box;
    background-color: #fee;
}
/*메뉴 목록*/
.menu{
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
    gap: 1em;
}
/*메뉴 아이템*/
.menu li{
    text-align: center;
}
/*메뉴 마우스 호버*/
.menu li:hover{
    border-bottom: 2px solid #f66;
}

메뉴를 좌우 여백을 지정해서 가운데 정렬을 하려고 할 때

언뜻 다음처럼 메뉴 목록 클래스에 마진 속성을 "auto"로 부여하면 가운데 정렬이 될 것 같지만 실제로는 안됩니다.

.menu{
	margin: 0 auto;
}

플렉스박스로 생성한 메뉴는 가로 영역을 모두 차지하기 때문에 메뉴 외곽에 여백이 없습니다.

마진 "auto" 속성 값의 정확한 개념은 요소 외곽의 여백에 대해 배분이 적용되는 것입니다.

따라서 전체 메뉴 항목들은 가운데 정렬이 되지 않습니다.

플렉스박스로 생성한 메뉴 항목들을 가운데 정렬하려면 다음처럼 첫 메뉴 아이템의 왼쪽과 마지막 메뉴 아이템의 오른쪽에 마진 속성으로 "auto" 를 부여해야 합니다.

.menu li:nth-child(1){
    margin-left: auto;
}
.menu li:nth-last-child(1){
    margin-right: auto;
}

 

마지막 항목 오른쪽에 있는 큰 여백은 목록 태그(<ul>)과 목록 마지막 아이템(<li>) 사이에 있는 여백입니다.

따라서 이 여백은 목록 아이템들 외곽에 있는 여백이 되고 마진 "auto" 속성 값으로 분배가 됩니다.

동위에 있는 "auto" 속성이 2개 있으므로 여백을 반으로 나눠서 "auto" 속성이 지정된 첫 번째 메뉴 아이템 왼쪽과 마지막 메뉴 아이템 오른쪽에 분배합니다.

메뉴는 다음처럼 여백이 분배되어 가운데 정렬되어 표시됩니다.

"홈" 과 "자료실" 오른쪽에 마진 "auto" 속성 값을 부여하면 여백이 이 위치에 배분되기 때문에 다음처럼 3개로 메뉴가 나누어져 배분됩니다.

.menu li:nth-child(1){
    margin-right: auto;
}
.menu li:nth-child(4){
    margin-right: auto;
}

메뉴 항목(들)을 좌우에 나누어 배치하기

만들게 될 메뉴는 다음과 같이 1개의 목록으로 되어 있습니다.

"로그인", "회원가입" 을 다른 목록으로 분리해서 오른쪽에 배치를 하는 일반적인 구현 방법 대신 마진 속성을 사용해 1개의 목록만으로 구현합니다.

<header>
    <div class="nav">
        <ul class="menu">
            <li><a href="#">홈</a></li>
            <li><a href="#">상품</a></li>
            <li><a href="#">서비스</a></li>
            <li><a href="#">자료실</a></li>
            <li><a href="#">고객지원</a></li>
            <li><a href="#">마이페이지</a></li>
            <li><a href="#">로그인</a></li>
            <li><a href="#">회원가입</a></li>
        </ul>
    </div>
</header>

먼저 ".nav" 클래스는 메뉴를 감싸는 역할을 합니다. 여기서는 메뉴만을 감싸기 때문에 별다른 용도가 없지만, 다른 요소들이나, 메뉴의 상하좌우에 여백을 줄 때 ".nav" 클래스를 활용합니다.

메뉴에 CSS 가 적용되지 않은 상태에서는 다음처럼 보이게 됩니다.

목록 아이템들에 불릿을 없애고 가로 배치가 되도록 CSS를 추가합니다.

플렉스박스를 사용하면 인라인 블록 형태로 메뉴 아이템들이 가로로 배치됩니다.

플렉스박스의 "gap" 속성을 사용해 메뉴 아이템 사이의 여백도 "1em"으로 정해줍니다.

html, body{
    margin: 0;
    padding: 0;
    height: 100%;
}

/*메뉴 영역*/
.nav{
    padding: 5px 20px;
    box-sizing: border-box;
    background-color: #fee;
}
/*메뉴 목록*/
.menu{
    display: flex;
    list-style: none;
    margin: 0;
    padding: 0;
    gap: 1em; /*메뉴 아이템 사이 여백*/
}
/*메뉴 아이템*/
.menu li{
    text-align: center;
}

기본적인 가로 메뉴 배치의 형태가 갖추어집니다.

메뉴 오른쪽에 남는 여백이 "홈" 오른쪽과 "마이페이지" 오른쪽에 분배되어 들어가도록 다음처럼 마진 "auto" 속성을 추가합니다.

/*홈 메뉴 분할*/
.menu li:nth-child(2){
    margin-left: auto;
}
/*오른쪽 메뉴 분할*/
.menu li:nth-child(7){
    margin-left: auto;
}

마진 "auto" 속성은 아이템 사이 여백이 위치할 방향으로 지정하면 되기 때문에 다음처럼 왼쪽 아이템에 오른쪽 방향으로 지정해도 동일합니다.

/*홈 메뉴 분할*/
.menu li:nth-child(1){
    margin-right: auto;
}
/*오른쪽 메뉴 분할*/
.menu li:nth-child(6){
    margin-right: auto;
}
마진 "auto" 속성 값으로 분리한 메뉴 영역

기본 하이퍼링크 CSS 인 메뉴 아이템들을 조금더 예쁘게 보이도록 CSS를 추가해 다듬습니다.

/*오른쪽 메뉴 장식*/
.menu li:nth-last-child(-n+2){
    border-radius: 15px;
    background-color: #faa;
}
/*하이퍼링크 처리*/
.menu li a{
    display: block;
    padding: 5px 15px;
    text-decoration: none;
    letter-spacing: -1px;
    color: #000;
}
완성된 가로 메뉴 디자인

완성된 메뉴 디자인에 마우스 호버시 반응하도록 액션을 부여합니다.

순서를 기준으로 요소(들)을 선택하는 CSS 선택자인 "nth-child", "nth-last-child" 에 수식을 적용할 때 변수 "n"을 마이너스 값으로 사용해 선택하면 처음 몇개, 또는 마지막 몇개 요소만 선택할 수 있습니다.

다음처럼 왼쪽 메뉴 마우스 호버를 위한 선택자의 "-n+6" 수식은 처음 6개 요소만 선택됩니다.

/*오른쪽 메뉴 마우스 호버*/
.menu li:nth-last-child(-n+2):hover{
    background-color: #f66;
}
.menu li:nth-last-child(-n+2):hover a{
    color: #fff;
}
/*왼쪽 메뉴 마우스 호버*/
.menu li:nth-child(-n+6):hover{
    border-bottom: 2px solid #f66;
}
완성된 메뉴

모바일용 반응형 세로 메뉴 배치

플렉스박스는 내부 아이템들의 배치 방향을 속성으로 정할 수 있습니다.

가로로 순서대로 배치되는 플렉스 박스 아이템들을 세로로 배치해서 모바일 메뉴를 만들어 보겠습니다.

모바일 대응이 되도록 세로 배치 메뉴는 768px 미만일 때 표시되도록 미디어쿼리에 CSS 속성들을 추가해서 적용합니다.

먼저 미디어쿼리를 하나 만듭니다.

@media screen and (max-width: 767px){
}

만든 미디어쿼리에 CSS를 추가로 정의합니다.

헤더 영역 높이를 "100%" 로 지정해 사이드에 세로로 붙이는 메뉴 높이가 웹 브라우저 높이 영역만큼 채워지도록 합니다.

header{
	height: 100%;
}

앞서 만든 가로 메뉴의 너비를 "260px"로 줄이고 "flex-direction: column;" 속성을 플렉스박스에 추가해 세로로 길게 배치되도록 다음과 같이 CSS를 변경합니다.

/*너비 제한*/
.nav{
  width: 260px;
  height: 100%;
}
/*세로 보기 설정*/
.menu{
  flex-direction: column;
  height: 100%;/*우측 메뉴를 세로 하단으로 고정에 필요*/
}

우측에 분리해서 배치했던 "로그인", "회원가입" 메뉴 항목이 세로 메뉴에서는 하단으로 분리되어 배치되도록 마진 속성을 변경합니다.

왼쪽 마진 속성 값은 반드시 "0"으로 초기화를 해야 합니다. 그렇지 않으면 메뉴 항목이 오른쪽으로 붙게 됩니다.

.menu li:nth-child(7){
  margin-left: 0;
  margin-top: auto;
}

메뉴 항목에 마우스 호버를 하면 상단의 메뉴 항목들은 "2px" 밑줄이 표시되면서 메뉴 항목이 약간씩 밀려 올라가는게 거슬립니다. 가로 메뉴에서는 메뉴를 눈에 띄게 해줘서 좋지만, 세로 메뉴에서는 어울리지 않습니다.

기본 메뉴 상태에서도 투명 밑줄로 "2px"를 적용해서 마우스 호버시 위치가 변경되지 않고 밑줄이 표시되도록 합니다.

/*왼쪽 메뉴 마우스 호버 밑줄 늘어남 처리*/
.menu li:nth-child(-n+6){
  border-bottom: 2px solid rgba(0,0,0,0);
}
완성된 세로 메뉴

완성된 코드는 다음과 같이 됩니다.

가로 메뉴 CSS 밑에 추가해주면 됩니다.

@media screen and (max-width: 767px){
    header{
        height: 100%;
    }
    /*너비 제한*/
    .nav{
        width: 260px;
        height: 100%;
    }
    /*세로 보기 설정*/
    .menu{
        flex-direction: column;
        height: 100%;/*우측 메뉴를 세로 하단으로 고정에 필요*/
    }
    .menu li{
        text-align: initial;
    }
    /*오른쪽 메뉴를 세로 메뉴 하단에 고정*/
    .menu li:nth-child(7){
        margin-left: 0;
        margin-top: auto;
    }
    /*왼쪽 메뉴 마우스 호버 밑줄 늘어남 처리*/
    .menu li:nth-child(-n+6){
        border-bottom: 2px solid rgba(0,0,0,0);
    }    
}

완성된 반응형 메뉴는 다음처럼 동작합니다.

완성된 반응형 메뉴

완성된 반응형 메뉴 소스는 다음 링크에서 다운로드 받을 수 있습니다.

코멘트가 달려있으므로 참고해서 수정할 수 있습니다.

flexmarginmenu.zip0.00MB