🪗 HTML UI 패턴

아코디언 UI
(Accordion)

클릭하면 펼쳐지고 다시 클릭하면 접히는 아코디언. FAQ, 메뉴, 콘텐츠 접기에 필수적인 UI 패턴을 CSS transition과 JS로 부드럽게 만들어봐요.

🪗 CSS Transition ⚡ JS 경량 🎨 3가지 스타일 📋 복사 가능한 코드
① Concept

아코디언이란?

클릭 한 번으로 콘텐츠를 펼치고 접는 UI 컴포넌트예요. 좁은 공간에 많은 정보를 담을 수 있어요.

💡

핵심 트릭: max-height: 0max-height: 500px 전환이에요. height: auto에는 transition이 안 되지만, max-height에는 돼요. 이 한 가지 CSS 트릭으로 부드러운 펼침/접힘을 구현할 수 있어요.

FAQ 섹션

자주 묻는 질문을 아코디언으로 만들면 질문 목록을 한눈에 볼 수 있고, 원하는 답변만 펼쳐 볼 수 있어요. 고객 지원 페이지의 필수 패턴이에요.

모바일 메뉴

모바일에서 상단 카테고리를 아코디언으로 만들면 서브메뉴를 터치로 펼칠 수 있어요. 쇼핑몰 카테고리, 블로그 태그 목록에 자주 써요.

⚙️

설정 패널

고급 설정, 필터 옵션 등을 아코디언으로 접어두면 기본 화면이 깔끔해져요. 사용자가 필요할 때만 펼쳐서 상세 설정을 볼 수 있어요.

📖

긴 콘텐츠 접기

상품 설명, 약관, 긴 텍스트를 "더 보기" 버튼으로 접어두는 패턴도 아코디언의 변형이에요. 스크롤을 줄여 UX를 향상시켜요.

② Demo

3가지 스타일 직접 체험

각 항목을 클릭해서 아코디언이 펼쳐지는 것을 확인해보세요. 기본형은 하나 열면 다른 것이 자동으로 닫혀요.

① 기본형 — FAQ 아코디언 (하나만 열림)

바이브 코딩(Vibe Coding)은 AI에게 원하는 기능을 말로 설명하고, AI가 생성한 코드를 직접 붙여넣고 실행하면서 만들어가는 개발 방식이에요. 프로그래밍 언어를 깊이 몰라도 아이디어를 빠르게 구현할 수 있어요.
Claude(Anthropic), ChatGPT(OpenAI), Cursor IDE, GitHub Copilot 등이 있어요. 바이브 코딩 초보자라면 Claude나 ChatGPT로 코드를 생성하고, Cursor로 실행해보는 조합을 추천해요. 바이브툴킷은 이런 도구들로 만든 예제 모음이에요.
네, 가능해요! AI에게 원하는 결과를 한국어로 설명하면 완성된 HTML + CSS + JS 코드를 받을 수 있어요. 하지만 기본 개념을 조금 알면 AI에게 더 정확한 요청을 할 수 있어요. 바이브툴킷의 각 페이지가 그 개념을 쉽게 설명해드려요.
네! GitHub Pages, Netlify, Vercel은 정적 웹사이트를 무료로 배포할 수 있어요. 파일을 업로드하거나 저장소를 연결하면 자동으로 https://내주소.netlify.app 같은 URL이 생성돼요. 커스텀 도메인도 저렴하게 연결할 수 있어요.
탭은 같은 위치에서 내용이 바뀌고, 아코디언은 항목이 위아래로 펼쳐져요. 탭은 선택지가 3~5개일 때, 아코디언은 항목이 많거나 모바일에서 세로 스크롤이 자연스러울 때 유리해요. FAQ처럼 질문이 많을 때는 아코디언이 더 적합해요.

② 보더형 — 테두리 강조 스타일

:root { --accent: #4f46e5; }처럼 전역 변수를 선언하고, color: var(--accent)로 사용해요. 색상을 한 곳에서 바꾸면 전체 사이트에 반영돼요. 다크모드 구현에도 핵심적인 패턴이에요.
const res = await fetch(url);로 API 데이터를 비동기로 가져올 수 있어요. 날씨 앱, 환율 계산기 같은 실시간 데이터를 받아오는 기능에 꼭 필요해요. async/await 패턴과 함께 사용해요.
@media (max-width: 600px) { ... }로 화면 크기별 스타일을 지정해요. Flexbox와 Grid의 auto-fill, minmax()를 활용하면 미디어쿼리 없이도 반응형이 되는 경우가 많아요.

③ 다중 오픈형 — 여러 개 동시에 열기동시 열림

Hero → 기능 소개 → 사용 방법 → 후기 → CTA → 푸터. 이 순서가 랜딩페이지의 기본 공식이에요. 각 섹션은 하나의 메시지만 전달하도록 집중하면 돼요.
<title><meta name="description">은 검색 결과 화면에 표시돼요. title은 60자, description은 160자 이내로 핵심 키워드를 포함해 작성하세요. <link rel="canonical">로 중복 URL 문제도 잡아요.
CSS 변수로 색상을 관리하면 [data-theme="dark"] 속성 하나로 전체 색상을 바꿀 수 있어요. localStorage에 설정을 저장해 새로고침해도 유지되게 해요. 바이브툴킷 다크모드 페이지에서 전체 코드를 볼 수 있어요.
await navigator.clipboard.writeText(text) 세 줄이면 구현 완료예요. 복사 완료 시 버튼 색상을 초록으로 바꿔 피드백을 주고, 2초 후 원래대로 돌아오게 하면 좋은 UX가 완성돼요.
③ Code

HTML + CSS + JS 전체 코드

복사해서 바로 쓸 수 있는 완전한 아코디언 코드예요.

1 — HTML 구조

index.html
<div class="accordion" id="myAccordion">

  <div class="acc-item">
    <button class="acc-btn"
            onclick="toggleSingle(this,'myAccordion')">
      첫 번째 질문이에요
      <span class="acc-arrow"></span>
    </button>
    <div class="acc-body">
      <div class="acc-body-inner">
        첫 번째 답변 내용을 여기에 써요.
      </div>
    </div>
  </div>

  <div class="acc-item">
    <button class="acc-btn"
            onclick="toggleSingle(this,'myAccordion')">
      두 번째 질문이에요
      <span class="acc-arrow"></span>
    </button>
    <div class="acc-body">
      <div class="acc-body-inner">
        두 번째 답변 내용을 여기에 써요.
      </div>
    </div>
  </div>

</div>

2 — CSS (max-height transition 트릭)

style.css
/* 핵심: max-height 전환으로 슬라이드 효과 구현 */
.acc-body {
  max-height: 0;
  overflow: hidden;
  transition: max-height .35s ease;
}
.acc-item.open .acc-body {
  max-height: 500px; /* 콘텐츠보다 크면 됨 */
}

/* 화살표 회전 애니메이션 */
.acc-arrow {
  display: inline-flex;
  transition: transform .3s ease;
}
.acc-item.open .acc-arrow {
  transform: rotate(180deg);
}

/* 아코디언 버튼 기본 스타일 */
.acc-btn {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  background: none;
  border: none;
  font-family: inherit;
  font-size: .93rem;
  font-weight: 600;
  color: #111827;
  cursor: pointer;
  text-align: left;
  gap: 12px;
  transition: background .15s, color .2s;
}
.acc-btn:hover { background: #f9fafb; }
.acc-item.open .acc-btn { color: #4f46e5; }

3 — JavaScript (단일 / 다중 오픈)

script.js
// ① 단일 오픈 (하나 열면 다른 것 자동으로 닫힘)
function toggleSingle(btn, containerId) {
  const container = document.getElementById(containerId);
  const item      = btn.closest('.acc-item');
  const isOpen    = item.classList.contains('open');

  // 모든 항목 닫기
  container.querySelectorAll('.acc-item').forEach(function(el) {
    el.classList.remove('open');
  });

  // 클릭한 항목이 닫혀있었으면 열기
  if (!isOpen) {
    item.classList.add('open');
  }
}

// ② 다중 오픈 (각자 독립적으로 열고 닫힘)
function toggleMulti(btn) {
  btn.closest('.acc-item').classList.toggle('open');
}

// ③ 전체 닫기 버튼 (선택사항)
function closeAll(containerId) {
  document.getElementById(containerId)
    .querySelectorAll('.acc-item')
    .forEach(function(el) { el.classList.remove('open'); });
}
④ Advanced

응용 & 심화 패턴

기본 아코디언을 더 정교하게 만드는 팁이에요.

⚠️

max-height 값 설정 주의: max-height: 500px로 설정했는데 콘텐츠가 500px를 넘으면 잘려요. 콘텐츠 최대 높이보다 여유 있게 설정하거나, 동적으로 scrollHeight를 읽어서 설정하는 방법을 쓰면 돼요.

scrollHeight로 정확한 높이 계산

콘텐츠 높이가 가변적일 때 scrollHeight를 동적으로 읽어 설정하면 어떤 내용도 안전하게 펼쳐져요.

advanced.js
function toggleAccurate(btn) {
  const item = btn.closest('.acc-item');
  const body = item.querySelector('.acc-body');

  if (item.classList.contains('open')) {
    // 닫기: 현재 높이 → 0
    body.style.maxHeight = body.scrollHeight + 'px';
    // 다음 프레임에서 0으로 설정해야 transition이 동작
    requestAnimationFrame(() => {
      body.style.maxHeight = '0';
    });
    item.classList.remove('open');
  } else {
    // 열기: 0 → scrollHeight
    item.classList.add('open');
    body.style.maxHeight = body.scrollHeight + 'px';

    // transition 끝나면 auto로 (내용 늘어나도 대응)
    body.addEventListener('transitionend', () => {
      body.style.maxHeight = 'none';
    }, { once: true });
  }
}
🔄

아이콘 회전

transform: rotate(180deg)transition으로 ▼가 부드럽게 ▲로 회전해요. 단순한 텍스트 ▼ 대신 SVG 아이콘을 쓰면 더 세련돼요.

🪆

중첩 아코디언

아코디언 안에 아코디언을 넣을 수 있어요. 단, max-height가 중첩되면 계산이 복잡해져요. 중첩이 깊어지면 scrollHeight 방식으로 전환하는 것을 추천해요.

접근성 추가

버튼에 aria-expanded="false/true", 패널에 aria-hidden="true/false"를 추가해요. 스크린 리더가 "닫혀있음/열려있음"을 읽어줘서 장애인 사용자도 쓸 수 있어요.

⑤ Prompt Tip

AI 프롬프트 팁

아코디언 UI를 AI에게 요청할 때 이 프롬프트를 참고해보세요.

✦ 바이브코더 프롬프트

아래 프롬프트를 복사해서 AI에 붙여넣어 보세요.

내 웹사이트 하단에 FAQ 아코디언을 만들어줘.

질문/답변 목록:
1. 배송은 얼마나 걸리나요? / 평균 2~3 영업일이 소요돼요.
2. 교환/환불은 어떻게 하나요? / 수령 후 7일 이내 문의주세요.
3. 재고가 없으면 어떻게 되나요? / 입고 알림을 신청할 수 있어요.
4. 해외 배송도 되나요? / 현재 국내 배송만 운영 중이에요.

요구사항:
- 클릭하면 max-height 트랜지션으로 부드럽게 펼쳐짐
- 하나 열면 다른 것은 자동으로 닫힘 (단일 오픈)
- 화살표(▼)가 열릴 때 180도 회전
- 열린 항목 헤더 색상: 인디고 (#4f46e5)
- 모바일에서도 자연스럽게 동작
- 순수 HTML + CSS + Vanilla JS (라이브러리 없이)
⑥ FAQ

자주 묻는 질문

아코디언 구현 시 자주 맞닥뜨리는 질문들이에요.

CSS transition은 수치 사이를 보간(interpolate)해서 애니메이션을 만들어요. auto는 고정 수치가 아닌 브라우저가 계산하는 값이라 보간이 불가능해요. 그래서 max-height: 0 → 500px처럼 구체적인 수치 사이에서는 transition이 잘 동작해요. CSS Grid의 grid-template-rows: 0fr → 1fr 트릭도 비슷한 문제를 해결하는 현대적 방법이에요.
네! HTML의 <details><summary> 태그를 사용하면 JavaScript 없이 기본 아코디언을 만들 수 있어요. <details open>으로 기본 열림 설정도 돼요. 단, 브라우저 기본 스타일이 제한적이고 애니메이션이 없어요. CSS animationdetails[open]에 적용하면 어느 정도 개선할 수 있어요.
useState로 열린 항목의 인덱스를 관리해요. 단일 오픈은 const [openIdx, setOpenIdx] = useState(null), 다중 오픈은 const [openSet, setOpenSet] = useState(new Set())를 사용해요. Radix UI의 Accordion 컴포넌트를 쓰면 접근성까지 포함된 완성된 컴포넌트를 바로 사용할 수 있어요.