아코디언 메뉴는 자바스크립트 없이 순수 CSS만으로 만들 수 있습니다. 숨겨진 체크박스(또는 라디오) input과 label의 클릭 감지, 그리고 :checked 형제 선택자만 있으면 클릭할 때마다 열리고 닫히는 동작이 구현됩니다.
스크립트 삽입이 막힌 환경에서 접이식 메뉴가 필요한 사람, 혹은 왜 이 코드가 이렇게 동작하는지 원리를 확인하려는 사람에게 필요한 내용을 코드 레벨에서 정리합니다.
요약
① 숨겨진 input[type=checkbox|radio]와 label[for=]의 클릭 감지가 열림 상태를 저장하는 역할을 합니다.
② :checked 형제 선택자가 header 배경색과 content의 max-height를 바꿔 여닫히는 것처럼 보이게 만듭니다.
③ checkbox는 항목마다 독립적으로 열리고, radio는 name을 공유해야 한 번에 하나만 열립니다.
왜 자바스크립트 없는 아코디언이 필요한가
접이식 메뉴나 FAQ, 상세정보 접기 UI가 필요한데 자바스크립트를 쓸 수 없는 상황이 실제로 있습니다. 티스토리나 워드프레스 같은 블로그 플랫폼의 HTML 편집 모드는 <script> 태그가 걸러지거나 실행되지 않는 경우가 많습니다.
사내 게시판이나 CMS도 보안 정책상 스크립트 삽입을 막아두는 경우가 흔합니다.
이럴 때는 클릭 이벤트를 감지하는 JS 코드 대신, HTML과 CSS만으로 "클릭하면 상태가 바뀌는" 동작을 구현해야 합니다.
순수 CSS 아코디언이 바로 이 문제를 푸는 방식이고, 별도 라이브러리나 스크립트 로딩 없이 페이지가 가벼워진다는 부수 효과도 있습니다.
체크박스/라디오 해킹의 작동 원리 (직접 코드로 만드는 법)
이 기법의 핵심은 input의 체크 상태를 자바스크립트가 아니라 CSS 선택자로 읽는 것입니다. 순서대로 뜯어보면 이렇습니다.
1단계 — 숨겨진 input과 label을 짝짓는다
실제로 클릭을 받는 요소는 눈에 보이지 않는 input이 아니라 label입니다. label의 for 속성값과 input의 id가 같으면, label을 클릭했을 때 브라우저가 자동으로 그 input을 체크/해제합니다.
다중/단일 개방, 색상, 속도를 조정하면 미리보기와 HTML·CSS 코드가 실시간으로 갱신됩니다. 가입이나 설치 없이 브라우저에서 바로 복사할 수 있습니다.
직접 해보기 →설정을 바꿀 때마다 체크박스/라디오 해킹으로 동작하는 미리보기와 그 결과 코드가 함께 갱신되어, 원하는 조합을 바로 코드로 확인할 수 있습니다.
checkbox와 radio, 그리고 자주 나는 실수
체크박스 모드와 라디오 모드는 input의 type 한 글자만 다르지만 결과는 다릅니다. 코드 레벨에서 무엇이 이 차이를 만드는지 확인해야 할 지점이 세 가지 있습니다.
구분
checkbox 모드
radio 모드
동시에 열리는 항목 수
여러 개 가능 (다중 개방)
최대 1개 (단일 개방)
name 속성
있어도 동작에 영향 없음
반드시 모든 항목이 같은 값을 공유해야 함
항목을 다시 클릭하면
닫힘(체크 해제)
닫히지 않음(다른 항목을 눌러야 닫힘)
세 가지 모두 브라우저가 checkbox와 radio를 원래 다르게 취급하기 때문에 생기는 차이이지, 아코디언 코드가 별도로 분기 처리를 하는 게 아닙니다.
radio는 태생적으로 같은 name 그룹 안에서 하나만 선택되도록 설계된 input이라, 그 성질을 그대로 빌려온 것입니다.
실제 코드를 읽을 때 확인해야 할 실수는 아래 세 가지입니다.
라디오 모드인데 name이 항목마다 다르다. name이 겹치지 않으면 브라우저가 서로 다른 그룹으로 인식해 여러 항목이 동시에 열립니다. "단일 개방"을 의도했다면 모든 input의 name이 정확히 같은 문자열인지부터 확인해야 합니다.
max-height를 실제 내용보다 작게 잡았다. 위에서 본 대로 이 기법은 height: auto를 쓸 수 없어 고정 max-height로 대체합니다. 이미지가 들어가거나 텍스트가 긴 항목의 실제 높이가 지정한 max-height(예: 500px)를 넘으면, 넘는 부분은 스크롤도 없이 그대로 잘려서 보이지 않습니다.
label의 for와 input의 id가 어긋난다. 항목을 추가·삭제하거나 코드를 직접 편집하다가 id 번호가 밀리면, label을 클릭해도 다른 input을 가리키거나 아무 input도 가리키지 않아 클릭이 먹지 않는 것처럼 보입니다. 헤더를 눌러도 반응이 없다면 가장 먼저 for와 id 쌍부터 맞춰봐야 합니다.
정리
아코디언의 열림·닫힘은 자바스크립트가 아니라 숨겨진 input의 체크 상태와 CSS :checked 형제 선택자가 만듭니다.
label의 for와 input의 id가 정확히 일치해야 클릭이 input에 전달됩니다.
checkbox는 항목별 독립 개방, radio는 name을 공유해야 하는 단일 개방이라는 점이 코드상 차이의 전부입니다.
max-height는 실제 콘텐츠보다 넉넉해야 하며, 부족하면 내용이 잘립니다.
값을 바꿔가며 결과를 바로 확인하려면 미리보기와 코드를 함께 보여주는 도구를 쓰는 편이 손으로 값을 하나씩 바꿔보는 것보다 빠릅니다.
다중/단일 개방, 색상, 속도를 조정하면 미리보기와 HTML·CSS 코드가 실시간으로 갱신됩니다. 가입이나 설치 없이 브라우저에서 바로 복사할 수 있습니다.
📖 실제 개발 현장에서 마주하는 CSS 아코디언 도입 사연 3가지
순수 CSS 아코디언은 단순한 토이 프로젝트용 기술이 아닙니다. 엄격한 실무 환경에서 자바스크립트 기반 컴포넌트를 대체하는 강력한 무기로 쓰입니다. 다음은 프론트엔드 개발자들이 실제로 마주한 해결 사례입니다.
사연 1: 엄격한 CSP(콘텐츠 보안 정책)를 뚫고 구현한 정부 포털 메뉴
보안이 생명인 국가망 인트라넷 포털 구축을 맡은 프론트엔드 개발자 A씨는 프로젝트 막바지에 큰 난관에 부딪혔습니다. 인라인 스크립트 태그와 외부 JS 라이브러리 로딩이 강력한 CSP(Content Security Policy) 헤더에 의해 전면 차단되었기 때문입니다. 수십 개의 하위 카테고리를 가진 다단계 FAQ와 내비게이션 트리를 만들어야 했으나, 보안 감사팀의 엄격한 규정 때문에 자바스크립트를 단 한 줄도 쓸 수 없는 절망적인 상황이었습니다.
이때 A씨를 구한 기술이 바로 '체크박스 해킹(Checkbox Hack)'이었습니다. 브라우저 내장 폼 컨트롤인 와
사연 2: 무거운 45KB JS 아코디언을 걷어내고 코어 웹 바이탈(CWV)을 개선한 이커머스
트래픽이 폭주하는 대형 쇼핑몰의 상품 상세 페이지를 담당하는 성능 최적화 엔지니어 B씨. 페이지 최하단에 위치한 FAQ 섹션을 위해 별도로 로드되는 45KB짜리 무거운 자바스크립트 아코디언 위젯 때문에 Total Blocking Time(TBT)이 무려 180ms나 발생하고 있었습니다. 메인 스레드를 점유하는 스크립트 파싱과 렌더링 블로킹 탓에 구글 코어 웹 바이탈의 핵심 지표인 Interaction to Next Paint(INP) 수치도 매우 불량하게 측정되었습니다.
B씨는 과감하게 레거시 JS 위젯을 걷어내고 순수 CSS :checked 가상 클래스를 활용한 아코디언으로 전면 개편했습니다. 그 결과, JS 파싱 및 실행 비용이 0이 되어 INP가 12ms로 수직 낙하했고, 전체 페이지 로드 속도도 35%나 눈에 띄게 향상되었습니다. 결국 이 최적화는 이탈률 감소와 장바구니 전환율 상승이라는 비즈니스적 성과로 직접 이어졌습니다.
IT 매거진 퍼블리셔 C씨는 순수 CSS 아코디언을 구현할 때 전통적으로 흔히 쓰이는 max-height: 2000px 방식을 사용했습니다. 그러나 실제 들어가는 텍스트 높이는 50px에 불과해, 아코디언을 닫을 때 2000px에서 50px까지 줄어드는 긴 시간 동안 화면에 아무 변화가 없는 0.4초의 '유령 딜레이(Lag)' 현상이 발생했습니다. 사용자들은 "버튼을 눌러도 반응이 없다", "UI가 버벅거린다"며 불만을 토로했습니다.
문제의 원인을 파악한 C씨는 최신 모던 CSS 해법인 CSS Grid의 grid-template-rows: 0fr 에서 1fr 로 전환되는 애니메이션 기법을 전격 도입했습니다. 콘텐츠의 실제 렌더링 높이(auto)를 브라우저가 정확히 계산하도록 유도하여, 유령 딜레이 없이 클릭 즉시 즉각적이고 부드러운 완벽한 트랜지션을 구현하는 데 성공했습니다.
🔬 핵심 기술 메커니즘 — CSS 아코디언은 어떻게 동작하는가
자바스크립트의 이벤트 리스너(Event Listener) 개입 없이 브라우저의 기본 HTML 파서와 CSS 렌더링 엔진만으로 DOM 요소의 상태를 제어하는 정교한 원리를 해부해 봅니다.