• 테마

CSS

CSS만으로 자바스크립트 하나 없이 드롭다운 토글을 만들어보자 (feat. focus-within)

CSS 40 2026. 4. 13.

인터넷에 검색해보면 CSS 드롭다운 메뉴의 대부분이 :hover 클래스만 사용해 마우스를 올렸을 때 드롭다운 메뉴가 나타나는 동작을 보여주고 있다.

그러나 때때로, 호버 할 때 뿐만이 아닌 진짜 드롭다운 메뉴처럼 클릭해야 메뉴가 보이게끔 구성하고 싶을 때가 있다. 그리고 대부분은 이런 동작을 구현하기 위해 JavaScript가 개입된다.

하지만 :focus-within을 사용하면 자바스크립트 없이도 클릭 드롭다운 메뉴를 구현할 수 있다.

이번 예제는 Tailwind CSS 의 플러그인인 daisyUIFab 컴포넌트 구성을 분석하여 알게 된 트릭임을 밝힌다.

근데 왜 굳이?


이미 자바스크립트를 통해 요소를 가리고 보이는 것은 크게 어려운 것이 아니다. 그저 상태에 맞춰 요소의 클래스를 더해주거나 빼면 되니까.

Svelte와 같은 프레임워크를 쓴다면 상태 변수에 따라 아예 DOM에 추가되거나 사라지게 만드는 것도 가능하다.

그러나 이런 방식으로 만들면 몇가지 문제가 있다.

Javascript가 없으면 안된다.

당연한 얘기지만 Javascript가 없으면 드롭다운 메뉴를 절대로 열 수가 없다.

예를 들어 아래와 같이 코드를 짜면 Javascript가 있으면 메뉴를 열 수 있다.

<script>
  let active = $state(false);
</script>
<div class="outer">
  <p><span>Div 박스 안의 p 태그 안의 span 텍스트</span></p>
  <div class="line">
    <button onclick={()=>active = !active}>버튼입니다.</button> <span>{String(active)}</span>
    <div class={["dropdown", active && 'active']}>
      <ul>
        <li>드롭다운 메뉴1</li>
        <li>드롭다운 메뉴2</li>
        <li>드롭다운 메뉴3</li>
      </ul>
    </div>
  </div>
</div>
<style>
.outer {
  padding: 1em;
}
.line {
  position: relative;
}
.dropdown {
  display: none;
  position: absolute;
  width: 100px;
  height: 200px;
  background-color: yellow;
  top: 100%;
  left: 0;
}
.dropdown.active {
  display: block;
}
</style>

그리고 버튼을 클릭하면 active 상태 변수가 변함에 따라 .dropdown 요소에 active 클래스가 붙게 되고 기본 display 값이 none 이었던 것에서 block 이 되면서 메뉴가 나타나게 된다.

버튼 클릭 전

<버튼 클릭 전>

버튼 클릭 후

<버튼 클릭 후>

그런데 만약 자바스크립트를 사용할 수 없는 환경이라면? (사실 현대 웹 환경에서 자바스크립트가 없다는 것은 없다고 봐야겠지만)

개발자 도구에서 'Javascript 사용 안 함'을 켜고 다시 들어가보면 아무리 버튼을 클릭해도 메뉴가 나타나지 않는 것을 확인할 수 있을 것이다.

<개발자 도구>

1. MAGIC

우선 당연한 이야기지만 드롭다운 메뉴를 열어줄 버튼을 배치해보자. 이때 드롭다운 메뉴가 버튼의 바로 아래에 위치하게 해주기 위하여 divspan으로 감싸준다. 그리고 드롭다운 메뉴는 position: absolute; top: 100%; left: 0;을 넣어 버튼 아래에 드롭다운 메뉴가 떠있을 수 있도록 해주었다. 평소에는 드롭다운 메뉴가 표시되지 않도록 display: none까지 넣어주었다.

<div class="dropdown-container">
  <button>드롭다운 열기</button>
  <div class="dropdown">
    이것은 드롭다운 메뉴입니다.
  </div>
</div>

스타일은 이렇게 부모 요소에 position: relative를 넣어준다.

.dropdown-container {
  position: relative;
}
.dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  background: #eee;
  display: none;
}

이제 이번 게시글의 주제인 :focus-within이 등장한다.

.dropdown-container:focus-within .dropdown을 넣고 여기에서 .dropdowndisplay 속성을 block으로 해주면 버튼에 포커스가 가 있을 때에만 .dropdown이 보이게 된다.

.dropdown-container:focus-within .dropdown {
  display: block;
}

마우스가 드롭다운 위에 머물러 있을 때 안전하게(?) 떠있을 수 있도록 하고 싶다면 이렇게 확장하면 된다.

.dropdown-container:focus-within .dropdown, .dropdown:hover {
  display: block;
}

2. 단점


단점도 있긴 하다.

1) 드롭다운 내에 포커스 요소가 없으면 창을 고정할 수 없다.

상태를 따로 기록하고 있지는 않아서 드롭다운 메뉴 안에 포커스 요소가 없다고 하면 드롭다운 메뉴를 연 버튼의 포커스가 없어지면 드롭다운 메뉴를 열어 놓을 방법이 없다.

이렇게 버튼에 포커스가 가 있어야 드롭다운 메뉴를 유지할 수 있다.

2) 키보드 탭키로 포커스를 버튼에 두면 드롭다운 메뉴가 열린다.

결국엔 상태로 관리하는 것이 아닌 자식 요소의 포커스 유무로 속성을 적용하는 것이기 때문에 키보드로 탐색할 때에 드롭다운 메뉴가 열리게 된다. (오히려 장점일수도)


마무리

엄청 대단한 팁은 아니긴 하다. 그리고 이런 방식이라면 :focus-within이 아니더라도 버튼과 드롭다운이 같은 계층에 있다면 button:focus + .dropdown과 같은 방식으로도 구현이 가능하다.

.

다들 해피 코딩!