Skip to content

Commit a38d39a

Browse files
committed
feat: fix tab content IDs and enhance accessibility with aria attributes
1 parent 4f36af2 commit a38d39a

File tree

3 files changed

+82
-2
lines changed

3 files changed

+82
-2
lines changed

app/tab-sheet/tab-sheet.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ <h2>Tab Sheet</h2>
66
<div slot="tab" for="tab3">Tab 3</div>
77

88
<div id="tab1">Tab 1 content</div>
9-
<div id="tab1">Tab 2 content</div>
10-
<div id="tab1">Tab 3 content</div>
9+
<div id="tab2">Tab 2 content</div>
10+
<div id="tab3">Tab 3 content</div>
1111
</tab-sheet>

components/tab-sheet/tab-sheet.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
:host {
2+
--cl-focused-fg: var(--cl-focused);
3+
--cl-focused-bg: white;
24
display: flex;
35
}
46

@@ -13,10 +15,12 @@
1315
flex-direction: row;
1416
align-items: center;
1517
justify-content: start;
18+
border-bottom: 1px solid var(--cl-border);
1619
}
1720

1821
.content {
1922
flex: 1;
23+
padding: var(--padding);
2024
}
2125

2226
:host(.bottom) .tab-sheet {
@@ -34,3 +38,21 @@
3438
:host(.right) .header, :host(.left) .header {
3539
flex-direction: column;
3640
}
41+
42+
::slotted([aria-hidden]) {
43+
display: none;
44+
}
45+
46+
.header ::slotted(*) {
47+
margin: 0;
48+
padding: var(--padding, 0.5rem);
49+
cursor: pointer;
50+
background: #f0f0f0;
51+
}
52+
53+
/* Add styles for the selected slotted items */
54+
.header ::slotted([aria-selected="true"]) {
55+
color: var(--cl-focused-fg);
56+
background: var(--cl-focused-bg);
57+
border: 1px solid var(--cl-border);
58+
}

components/tab-sheet/tab-sheet.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,68 @@ import { HTML } from "./tab-sheet.html.js";
22
export class TabSheet extends HTMLElement {
33
static name = Object.freeze("tab-sheet");
44

5+
#clickHandler = this.#click.bind(this);
6+
57
constructor() {
68
super();
79
this.attachShadow({ mode: "open" });
810
this.shadowRoot.innerHTML = HTML;
11+
this.style.display = "none";
12+
}
13+
14+
connectedCallback() {
15+
// 1. get all elements in the <slot> element.
16+
const slot = this.shadowRoot.querySelector(".content slot");
17+
const elements = slot.assignedElements();
18+
19+
// 2. for each element, set aria-hidden to true
20+
for (const element of elements) {
21+
element.setAttribute("aria-hidden", "true");
22+
}
23+
24+
requestAnimationFrame(() => {
25+
this.#selectFirstTab();
26+
this.style.display = "flex";
27+
28+
this.shadowRoot.querySelector(".header").addEventListener("click", this.#clickHandler);
29+
})
30+
}
31+
32+
disconnectedCallback() {
33+
this.shadowRoot.querySelector(".header").removeEventListener("click", this.#clickHandler);
34+
}
35+
36+
#click(event) {
37+
const target = event.composedPath()[0];
38+
const id = target.getAttribute("for");
39+
if (id == null) return;
40+
41+
this.selectTab(id);
42+
}
43+
44+
#selectFirstTab() {
45+
const slot = this.shadowRoot.querySelector(".header slot");
46+
const elements = slot.assignedElements();
47+
if (elements.length === 0) return;
48+
49+
const firstTab = elements[0];
50+
const id = firstTab.getAttribute("for");
51+
this.selectTab(id);
52+
}
53+
54+
selectTab(id) {
55+
const currentTab = this.querySelector("[aria-selected]");
56+
if (currentTab != null) {
57+
currentTab.removeAttribute("aria-selected");
58+
const currentId = currentTab.getAttribute("for");
59+
const currentContent = this.querySelector(`#${currentId}`);
60+
currentContent.setAttribute("aria-hidden", "true");
61+
}
62+
63+
const newTab = this .querySelector(`[for="${id}"]`);
64+
newTab.setAttribute("aria-selected", "true");
65+
const newContent = this.querySelector(`#${id}`);
66+
newContent.removeAttribute("aria-hidden");
967
}
1068
}
1169

0 commit comments

Comments
 (0)