diff --git a/.Jules/palette.md b/.Jules/palette.md index 7fb6a23..954af5f 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -17,3 +17,7 @@ ## 2024-07-10 - prefers-reduced-motion 지원 추가 **Learning:** 시스템 레벨에서 애니메이션 줄이기(prefers-reduced-motion)를 설정한 사용자를 위해 과도한 애니메이션과 부드러운 스크롤을 비활성화하는 것이 필요합니다. 이때 `0s` 대신 `0.01ms`를 사용하여 `transitionend`와 같은 브라우저 이벤트가 정상적으로 발생하도록 해야 자바스크립트 콜백이 멈추는(hanging) 문제를 방지할 수 있습니다. **Action:** 항상 `styles.css` 하단에 `prefers-reduced-motion: reduce` 미디어 쿼리를 추가하여 모든 요소의 `animation-duration`과 `transition-duration`을 `0.01ms`로 설정하고 `scroll-behavior: auto`를 적용합니다. + +## 2024-07-17 - Roving tabindex and Keyboard Navigation for Tabs +**Learning:** ARIA tablists require roving `tabindex` and arrow key navigation for proper keyboard accessibility. Without it, users have to tab through every single tab to get to the panels, which is inefficient. +**Action:** When creating custom tabs using ARIA `role="tablist"` and `role="tab"`, ensure only the selected tab is in the natural tab order (`tabindex="0"`), while others are removed (`tabindex="-1"`). Handle `ArrowLeft` and `ArrowRight` to switch focus and selection simultaneously. diff --git a/components/index.html b/components/index.html index 615c223..51295a5 100644 --- a/components/index.html +++ b/components/index.html @@ -101,10 +101,10 @@

Badge & Tag

Tabs

.krds-tabs · Figma Layout/Tabs 59:11

-
- - - +
+ + +
개요 패널 내용입니다.
diff --git a/components/krds-gallery.js b/components/krds-gallery.js index 672e72f..8ed28df 100644 --- a/components/krds-gallery.js +++ b/components/krds-gallery.js @@ -1,13 +1,27 @@ // Tabs: minimal roving behavior. ponytail: native buttons + aria, no framework. document.querySelectorAll(".krds-tabs").forEach((tabs) => { const tabList = [...tabs.querySelectorAll('[role="tab"]')]; - tabList.forEach((tab) => { - tab.addEventListener("click", () => { - tabList.forEach((t) => { - const sel = t === tab; - t.setAttribute("aria-selected", sel); - document.getElementById(t.getAttribute("aria-controls")).hidden = !sel; - }); + + const selectTab = (tab) => { + tabList.forEach((t) => { + const sel = t === tab; + t.setAttribute("aria-selected", sel); + t.setAttribute("tabindex", sel ? "0" : "-1"); + document.getElementById(t.getAttribute("aria-controls")).hidden = !sel; + }); + tab.focus(); + }; + + tabList.forEach((tab, index) => { + tab.addEventListener("click", () => selectTab(tab)); + + tab.addEventListener("keydown", (e) => { + if (e.key === "ArrowRight" || e.key === "ArrowLeft") { + e.preventDefault(); + const direction = e.key === "ArrowRight" ? 1 : -1; + const newIndex = (index + direction + tabList.length) % tabList.length; + selectTab(tabList[newIndex]); + } }); }); });