[radix-ui] radix-ui 깃허브에 올라온 버그 이슈 수정해보기
Setting display:flex on accordion content creates double animation · Issue #2832 · radix-ui/primitives
Bug report Current Behavior The default display for Accordion.Content is block. Setting it to flex causes a double animation when closing a section. Expected behavior The animation should not be af...
github.com
Setting display:flex on accordion content creates double animation · Issue #2832 · radix-ui/primitives
Bug report Current Behavior The default display for Accordion.Content is block. Setting it to flex causes a double animation when closing a section. Expected behavior The animation should not be af...
github.com
앞선 글에서 shadcn-ui에 대한 버그 이슈에 대해서 의견을 낸 글을 작성하였습니다.
shadcn 자체가 radix-ui라는 헤드리스 라이브러리 위에 구축된 라이브러리라 저는 radix-ui 이슈도 가끔 확인합니다.
그러던 중 실제로 제가 겪은 문제가 이슈로 올라온 글을 보았습니다.
문제
radix-ui의 `Accordion` 또는 `Collapsible`를 사용할 때, radix에서는 아래와 같은 애니메이션 코드를 권장합니다
/* styles.css */
.CollapsibleContent {
overflow: hidden;
}
.CollapsibleContent[data-state='open'] {
animation: slideDown 300ms ease-out;
}
.CollapsibleContent[data-state='closed'] {
animation: slideUp 300ms ease-out;
}
@keyframes slideDown {
from {
height: 0;
}
to {
height: var(--radix-collapsible-content-height);
}
}
@keyframes slideUp {
from {
height: var(--radix-collapsible-content-height);
}
to {
height: 0;
}
}
문제는 이 애니메이션을 `display: flex`인 요소에 적용하면 애니메이션이 두번 동작하는(시각적으로 열려있는 요소를 닫을 때 한번 닫히고, 다시 닫히는,,) 문제였습니다.
이러한 이슈에 대한 재현은 상단 이슈링크에 코드샌드박스 링크가 걸려있습니다.
저는 이 문제를 당장 해결했어야했고, 이를 위해 radix-ui의 깃허브 코드를 살펴보았습니다.
해결
radix-ui 깃허브 코드에서 `--radix-collapsible-content-height` 해당 css 변수를 어떻게 결정하는지 찾아보았습니다.
const rect = node.getBoundingClientRect();
heightRef.current = rect.height;
widthRef.current = rect.width;
결과적으로 동적으로 사이즈를 계산하여 css 변수화 하고 있었습니다.
이것은 일반적으로 문제가 되지 않지만 `display: flex`, `display: grid`같은 경우에는 문제가 될 수 있습니다.
왜냐하면 flex, grid는 해당 영역의 높이를 자동으로 정하기 때문입니다
flexbox의 기본 개념 - CSS: Cascading Style Sheets | MDN
일명 flexbox라 불리는 Flexible Box module은 flexbox 인터페이스 내의 아이템 간 공간 배분과 강력한 정렬 기능을 제공하기 위한 1차원 레이아웃 모델 로 설계되었습니다. 이 글에서는 flexbox의 주요 기능
developer.mozilla.org
위의 글에서 해당 문제와 연관된 부분은 "항목은 교차축의 크기를 채우기 위해 늘어납니다." 부분입니다.
결론은 flex 자체적인 높이 계산과 radix-ui에서 계산하는 높이와 충돌이 일어나게됩니다.
자세히 살펴보면, flex 레이아웃은 자식 요소의 크기에 따라 높이를 자동으로 계산하여 동적으로 배치합니다.
따라서 radix에서 동적으로 계산된 높이와 flex 레이아웃에서 자동으로 계산된 높이가 서로 충돌하여 높이가 두 번 계산되고 결과적으로 애니메이션이 두 번 적용됩니다.
해결
height 대신 max-height를 사용해보았습니다.
max-height는 애니메이션 중에 브라우저에서 크기를 동적으로 계산할 필요가 없기 때문에 flex 레이아웃의 자동 높이 계산과 충돌하지 않습니다.
height 속성은 정확한 높이를 애니메이션하는 데 사용되지만 자동과 같은 동적 값은 지원되지 않기 때문에 충돌이 발생하기 쉽습니다.
반면 max-height는 고정된 값으로 계산되므로 충돌 없이 원활하게 애니메이션이 진행됩니다.
결과적으로 이전 코드와 / 제가 수정을 제시한 코드는 각각 아래와 같습니다
const slideDown = keyframes({
from: { height: 0 },
to: { height: "var(--radix-accordion-content-height)" },
});
const slideUp = keyframes({
from: { height: "var(--radix-accordion-content-height)" },
to: { height: 0 },
});
const slideDown = keyframes({
from: { maxHeight: 0 },
to: { maxHeight: "var(--radix-accordion-content-height)" },
});
const slideUp = keyframes({
from: { maxHeight: "var(--radix-accordion-content-height)" },
to: { maxHeight: 0 },
});