GODRICH
#css애니메이션#버튼#로딩#전환효과#스크롤#대시보드#실전ui#디자인트렌드

CSS 아코디언 애니메이션 9종 — JS 없이, CSS만으로

CSS 아코디언 애니메이션은 클릭한 항목만 펼쳐지고 나머지는 접힌 채로 남는 목록 UI로, 이 글은 JS 없이 CSS만으로 만드는 아홉 가지 방식을 모았습니다. 아홉 개 중 하나는 height 전환에 자바스크립트를 한 줄도 안 씁니다.

자동 재생 · 눌러서 해당 항목으로 · 전부 한 zip

아홉 개는 인기 순이 아니라 아코디언이 실제로 겪는 순서로 놓았습니다. 기본 열고 닫기(01~02) 다음 시각 신호(03~05), 구조 확장(06~07), 마지막으로 강조와 재활용(08~09)입니다. 항목마다 필요한 규칙 조각만 골라 실었고, 완결본은 zip 안에 통째로 들어 있습니다. 09 사이드바처럼 펼침이 내비게이션 쪽으로 넘어가면 아이콘까지 같이 바뀌어야 하는데 그건 CSS 햄버거 메뉴 애니메이션이고, 목록이 아래로 늘어나는 대신 떠서 겹치는 형태는 CSS 드롭다운 메뉴 애니메이션입니다.

01interpolate-size로 height:auto 전환

화면에서 일어나는 일

닫힌 패널이 interpolate-size 하나로 목표 높이까지 자연스럽게 늘어나며 열립니다.

interpolate-size로 height:auto 전환
새 창에서 열기

01 · interpolate-size로 height:auto 전환 — 측정 누적면적 11.691% · 움직인 프레임 17/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

300ms느리게 600msallow-keywords

css 아코디언 효과는 어디에 쓰나

FAQ 목록이나 약관 상세처럼 내용 길이가 정해지지 않은 곳에 씁니다.

.panel {
  height: 0;
  overflow: hidden;
  interpolate-size: allow-keywords;
}
.accordion.is-open .panel {
  height: calc-size(auto, size);
  transition: height $duration $easing;
}
@supports not (height: calc-size(auto, size)) {
  .accordion.is-open .panel { height: auto; }
}

02name 속성으로 하나만 열림

어떤 움직임인가

다른 항목을 열면 열려 있던 항목이 자동으로 닫힙니다.

name 속성으로 하나만 열림
새 창에서 열기

02 · name 속성으로 하나만 열림 — 측정 누적면적 32.087% · 움직인 프레임 11/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

name 속성닫힘 300ms라디오형

언제 쓰나

설정 메뉴나 한 번에 하나만 보여줄 FAQ에 씁니다.

header.addEventListener('click', function () {
  var willOpen = !acc.classList.contains('is-open');
  acc.parentElement.querySelectorAll('.accordion').forEach(function (other) {
    other.classList.remove('is-open');
  });
  if (willOpen) acc.classList.add('is-open');
});

03화살표 마커 회전

무엇이 보이나

열고 닫을 때 오른쪽 화살표가 180도 돌아가며 상태를 알려줍니다.

화살표 마커 회전
새 창에서 열기

03 · 화살표 마커 회전 — 측정 누적면적 18.795% · 움직인 프레임 19/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

180deg머리 오른쪽색 변화 추가

어디에 쓰나

아코디언 헤더와 css 드롭다운 메뉴에 두루 씁니다.

.accordion__chevron {
  transform: rotate(45deg);
  transition: transform $duration $easing;
}
.accordion.is-open .accordion__chevron {
  transform: rotate(-135deg);
}

04본문 페이드+슬라이드

화면에서 일어나는 일

본문이 위에서 살짝 밀려 내려오며 페이드인됩니다.

본문 페이드+슬라이드
새 창에서 열기

04 · 본문 페이드+슬라이드 — 측정 누적면적 18.717% · 움직인 프레임 19/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

8px200ms늦게 시작

쓰는 자리

옵션 설명처럼 펼쳤을 때 시선을 끌어야 하는 내용에 씁니다.

.accordion__body {
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity $duration $easing, transform $duration $easing;
}
.accordion.is-open .accordion__body {
  opacity: 1;
  transform: translateY(0);
  transition-delay: 80ms;
}

05열린 항목 강조 테두리

어떤 움직임인가

열린 항목만 왼쪽 테두리와 배경이 강조됩니다.

열린 항목 강조 테두리
새 창에서 열기

05 · 열린 항목 강조 테두리 — 측정 누적면적 12.473% · 움직인 프레임 17/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

3px배경 포함색만

언제 쓰나

지금 보고 있는 섹션을 표시해야 하는 설정 목록에 씁니다.

.accordion {
  border-left: 3px solid transparent;
  background: $card;
  transition: border-color $duration $easing, background $duration $easing;
}
.accordion.is-open {
  border-left-color: $color;
}

06중첩 아코디언

무엇이 보이나

아코디언 안에 작은 아코디언이 들어 있어 두 단계로 펼쳐집니다.

중첩 아코디언
새 창에서 열기

06 · 중첩 아코디언 — 측정 누적면적 37.072% · 움직인 프레임 20/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

들여쓰기 16px2단계동시 허용

어디에 쓰나

다단계 FAQ나 카테고리 트리 메뉴에 씁니다.

.accordion__panel--inner {
  height: 0;
  overflow: hidden;
  interpolate-size: allow-keywords;
}
.accordion--inner.is-open .accordion__panel--inner {
  height: calc-size(auto, size);
  transition: height $duration $easing;
}

07calc-size() 폴백 패턴

화면에서 일어나는 일

calc-size()를 모르는 브라우저는 max-height 폴백으로 똑같이 움직입니다.

calc-size() 폴백 패턴
새 창에서 열기

07 · calc-size() 폴백 패턴 — 측정 누적면적 18.438% · 움직인 프레임 17/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

@supportsheight: auto 폴백auto 보간

쓰는 자리

크로스 브라우저 지원이 필요한 폼에 씁니다.

@supports (height: calc-size(auto, size)) {
  .accordion.is-open .panel { height: calc-size(auto, size); }
}
@supports not (height: calc-size(auto, size)) {
  .panel { max-height: 0; transition: max-height $duration $easing; }
  .accordion.is-open .panel { max-height: 480px; }
}

08탄성 바운스로 열림

어떤 움직임인가

열릴 때 목표 높이를 살짝 넘었다가 튕기듯 자리를 잡습니다.

탄성 바운스로 열림
새 창에서 열기

08 · 탄성 바운스로 열림 — 측정 누적면적 19.022% · 움직인 프레임 15/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

overshoot 6%cubic-bezier닫힘은 즉시

언제 쓰나

강조하고 싶은 프로모션 아코디언이나 온보딩 카드에 씁니다.

.accordion.is-open .panel {
  height: calc-size(auto, size);
  transition: height $duration $ease-pop;
}
.accordion.is-open .accordion__body {
  animation: bounce-in $duration $ease-pop;
}
@keyframes bounce-in {
  from { transform: scale(1.04); opacity: 0; }
  to   { transform: scale(1); opacity: 1; }
}

09사이드바 메뉴로 재활용

무엇이 보이나

같은 아코디언 구조를 사이드바 메뉴의 하위 항목 접기·펼치기에 그대로 씁니다.

사이드바 메뉴로 재활용
새 창에서 열기

09 · 사이드바 메뉴로 재활용 — 측정 누적면적 20.065% · 움직인 프레임 18/23GODRICH 원작 · zip 에 SCSS·React 포함 · 비밀번호 보기

들여쓰기 12px아이콘 없이항상 하나 열림

어디에 쓰나

대시보드 사이드바나 문서 목차에 씁니다.

.sidebar__list {
  height: 0;
  overflow: hidden;
  interpolate-size: allow-keywords;
  padding-left: 12px;
}
.sidebar.is-open .sidebar__list {
  height: calc-size(auto, size);
  transition: height $duration $easing;
}

어디서 깨지는가 — 함정 하나

height 를 직접 애니메이션하는 이유

아홉 개 전부 height를 직접 애니메이션하는데, 오랫동안 이건 금기였습니다. height를 바꾸면 브라우저가 매 프레임 레이아웃을 다시 계산하기 때문입니다. calc-size()와 interpolate-size는 이 계산을 브라우저가 미리 해 두고 전환 구간에서만 보간하는 방식이라 사정이 다릅니다. 다만 MDN의 interpolate-size 문서를 보면 이 값은 아직 일부 브라우저에만 있고, 나머지는 조용히 무시합니다. 그래서 07 항목처럼 @supports not (height: calc-size(auto, size))로 고정 max-height 폴백을 반드시 같이 넣어야 하는데, 이 폴백 없이 배포했다가 구형 브라우저에서 패널이 아예 안 열리는 걸 뒤늦게 발견했습니다. 긴 FAQ를 아코디언으로 접어 두면 페이지가 길어져 맨 위로 돌아갈 길도 같이 필요해지는데, 그 버튼은 위로가기 버튼 CSS 애니메이션에 스크롤만으로 나타나는 형태로 모여 있습니다.

max-height 폴백 값

폴백 말고 덧붙일 것이 하나 더 있습니다. max-height 폴백은 콘텐츠보다 넉넉하게 고정값을 잡아야 하는데, 너무 크면 닫히는 시간이 실제보다 길게 느껴집니다. 480px 안팎에서 시작해 콘텐츠 최대 높이에 맞춰 줄이는 걸 권하고, 이렇게 다듬은 값이 이미 반영된 아홉 개 파일의 압축 비밀번호는 문장 중간에 있는 34fn5mpm이며, 아래에서 zip으로 받을 수 있습니다.

접근성

동작 줄이기를 켰을 때

운영체제의 동작 줄이기 옵션을 켠 사람에게는 아홉 개 모두 prefers-reduced-motion: reduce로 응답합니다. 열고 닫는 과정만 없애고 열림·닫힘이라는 결과 상태는 그대로 남기는 게 원칙입니다. 08 탄성 바운스는 오버슈트 없이 즉시 상태만 바뀌고, 04 페이드+슬라이드는 위치 이동 없이 투명도만 바뀝니다. prefers-reduced-motion 자체의 문법과 브라우저 지원은 MDN 문서를 참고하십시오.

@media (prefers-reduced-motion: reduce) {
  .accordion__panel, .accordion__chevron, .accordion__body {
    animation: none !important;
    transition: none !important;
  }
}

모달·드로어 같은 다른 전환 효과는 CSS 애니메이션 카테고리에서 찾을 수 있고, 사이트 운영 방향은 소개 페이지에 적어 두었습니다.

FAQ

CSS 아코디언 애니메이션에 꼭 JS가 필요합니까?

여는 동작 자체는 네이티브 <details>나 체크박스 트릭으로 JS 없이도 가능합니다. 다만 02처럼 세 개 이상을 배타적으로 묶거나 06처럼 중첩 상태를 관리하려면 짧은 JS 한두 줄을 곁들이는 편이 안전합니다.

calc-size()는 어느 브라우저에서 됩니까?

이 글을 쓰는 시점 기준 지원이 갈리므로 실제 프로젝트에 쓰기 전에 caniuse.com에서 최신 지원표를 직접 확인하고, 07 항목의 @supports 폴백을 항상 같이 배포하십시오. 05 열린 항목 강조처럼 테두리·그림자로 지금 보는 자리를 알리는 방식을 카드 목록에도 쓰고 싶다면, 같은 신호를 호버로 다룬 CSS 카드 애니메이션 호버 효과를 참고하면 됩니다.

css 아코디언 메뉴 만들기를 아홉 개 다 한 페이지에 넣어도 됩니까?

넣을 수는 있지만 한 화면에는 한두 종류가 좋습니다. 08 탄성 바운스와 05 테두리 강조는 "이 아코디언은 특별하다"를 말하는 용도라 페이지 전체에 하나씩만 쓰는 걸 권합니다.

압축 비밀번호를 입력하세요

비밀번호는 이 글 본문 안에 있습니다. 읽다 보면 나옵니다.