라디오버튼과 CSS로 만드는 탭(Tab) 인터페이스

라디오버튼은 하나의 그룹으로 묶은 버튼들 중에서 1개만 선택(체크) 상태가 되는 특징이 있습니다.

이런 특징을 활용하면 목록으로 표현되는 컨텐츠를 작은 영역 안에서 탭(Tab)으로 전환해가면서 볼 수 있는 UI를 만들 수 있습니다.

탭 인터페이스는 현재 선택된 탭 1개의 컨텐츠만 보이는 구조이기 때문에 라디오버튼의 특징을 활용하면 자바스크립트 없이도 탭 인터페이스를 구현할 수 있습니다.

<ul class="tabs">
    <li class="tab">
      /* 첫 번째 탭에는 "checked" 속성을 추가해 활성화 시킴 */
      <input type="radio" id="tab-1" name="tab-group-1" checked>
      <label for="tab-1">탭1</label>
      <div class="content">
        <p>컨텐츠1</p>
      </div>
    </li>
    <li class="tab">
      <input type="radio" id="tab-2" name="tab-group-1">
      <label for="tab-2">탭2</label>
      <div class="content">
        <p>컨텐츠2</p>
      </div>
    </li>
    <li class="tab">
      <input type="radio" id="tab-3" name="tab-group-1">
      <label for="tab-3">탭3</label>
      <div class="content">
        <p>컨텐츠3</p>
      </div>
    </li>
</ul>

CSS를 작성할 때 ".tab" 클래스는 ".tabs li" 로 접근을 할 수 있지만, 접근하는 뎁스를 줄이기 위해, 또 목록의 아이템 1개에 대한 정의가 전부인 탭 인터페이스기 때문에 개별 탭에 ".tab" 클래스를 부여해서 CSS 선택자 뎁스를 단순화 했습니다.

라디오버튼으로 반응형 UI를 작성하는 기본 구조인 "input + label + .content" 1개를 탭 1개로 구현하며, "<label>" 태그는 클릭해서 탭을 이동하는 탭 헤더로 사용하게 됩니다.

.tabs {
    position: relative;
    padding: 0;
    list-style: none;
}
.tab {
    float: left;
    padding: 10px 0;
}
.tab label { /* 탭 헤더 */
    position: relative;
    background: #eee;
    padding: 8px 20px;
    border: 1px solid #ccc;
}
.tab [type="radio"] {
    display: none;
}
.tab .content { /* 탭 컨텐츠 */
    display: none;
    position: absolute;
    background: white;
    top: 39px; /*탭 헤더 아래쪽으로 위치 이동 */
    left: 0;
    right: 0;
    padding: 0;
    box-sizing: border-box;
    border: 1px solid #ccc;
}
.tab [type="radio"]:checked ~ label {
    background: white;
    border-bottom: 1px solid white;
    z-index: 2; /* 선택한 탭 헤더를 앞으로 옮겨 컨텐츠 테두리 선에 가려지지 않도록 처리 */
}
.tab [type="radio"]:checked + label + .content {
    z-index: 1;
    display: block;
}

표시하는 컨텐츠는 애니메이션 속성을 추가해서 다음처럼 동적인 움직임이 있는 탭 인터페이스를 만들 수 있습니다.

트랜지션 애니메이션을 적용하려면 높이 값이 있어야 하기 때문에 애니메이션 속성을 추가하는 탭 인터페이스를 구현할 때는 탭 컨텐츠 영역 속성에 높이 값을 추가해야 합니다.

높이 값 속성이 탭 인터페이스에 추가되면 탭 컨텐츠의 모든 내용이  표시되지 않기 때문에 세로 스크롤바가 자동으로 표시되도록 속성(overflow-y: auto;)을 추가해야 합니다.

애니메이션 되는 컨텐츠 내용은 왼쪽에서 오른쪽으로 애니메이션 되며, 현재 선택한 탭 컨텐츠를 제외한 나머지 컨텐츠들은 컨텐츠 영역 너비만큼 왼쪽으로 이동("transform: translateX(-100%);")해 있습니다. 따라서 이 컨텐츠들이 보이지 않도록 감추어야 하기 때문에 ".tabs" 클래스에 "overflow: hidden;" 속성을 추가해 탭 인터페이스 외곽에 있는 컨텐츠 들이 보이지 않도록 해야 합니다.

.tabs {
    position: relative;
    padding: 0;
    list-style: none;
    min-height: 300px; /* 탭 인터페이스 전체 높이 지정 - 애니메이션에 필수 */
    overflow: hidden; /* 탭 외곽에 위치한 나머지 탭 컨텐츠가 보이지 않도록 가림 */
}
.tab {
    float: left;
    padding: 10px 0;
}
.tab label { /* 탭 헤더 */
    position: relative;
    background: #eee;
    padding: 8px 20px;
    border: 1px solid #ccc;
}
.tab [type="radio"] {
    display: none;
}
.tab .content { /* 탭 컨텐츠 영역 */
    position: absolute;
    background: white;
    top: 39px;
    left: 0;
    right: 0;
    bottom: 0;
    box-sizing: border-box;
    border: 1px solid #ccc;
    overflow-y: auto;
}
.tab .content > article { /* 컨텐츠 영역 프레임 유지를 위해 내부에 실제 애니메이션 블록 생성 */
    transform: translateX(-100%);
    transition: all 0.5s ease-in-out;
}
.tab [type="radio"]:checked ~ label {
    background: white;
    border-bottom: 1px solid white;
    z-index: 2; /* 선택한 탭 헤더를 앞으로 옮겨서 컨텐츠 영역 테두리에 가져지지 않도록 함 */
}
.tab [type="radio"]:checked + label + .content {
    z-index: 1;
}
.tab [type="radio"]:checked + label + .content > article { /* 실제 컨텐츠 블록 애니메이션 */
    display: block;
    transform: translateX(0);
}

애니메이션 방향은 원하는대로 설정할 수는 있지만, 애니메이션 효과를 위해 높이를 제한함으로써 예기치 않은 문제가 생길 가능성이 있습니다. 예를 들어 오른쪽에서 왼쪽으로 애니메이션 되도록 할 경우 다음처럼 애니메이션 되는 동안 가로 스크롤바가 생겼다 사라지는 문제가 생깁니다.

완성된 소스는 다음 링크를 클릭해서 다운로드 할 수 있습니다.

checkbox3.zip0.54MB