fix(gallery): add accessible keyboard navigation (#56)
Co-authored-by: Cathryn Lavery <cathryn@bestself.co>
This commit is contained in:
co-authored by
Cathryn Lavery
parent
4a769fe3eb
commit
e48df3fb1e
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "diagram-design",
|
"name": "diagram-design",
|
||||||
"description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, or DP security matrix diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
|
"description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, or DP security matrix diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
|
||||||
"version": "2.4.2",
|
"version": "2.4.3",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Cathryn Lavery",
|
"name": "Cathryn Lavery",
|
||||||
"url": "https://github.com/cathrynlavery"
|
"url": "https://github.com/cathrynlavery"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "diagram-design",
|
"name": "diagram-design",
|
||||||
"description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, or DP security matrix diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
|
"description": "Create branded architecture, IT current-state, flowchart, sequence, state machine, ER/data model, timeline, swimlane, quadrant, radar/spider, loop/flywheel, nested, tree, org chart, layer stack, Venn, pyramid/funnel, bar, line, Gantt and scatter charts, high-level, process, medallion, data flow, DP integration, or DP security matrix diagrams as standalone HTML/SVG/PNG. Redraw .drawio/.drawio.png/.drawio.svg or Mermaid .mmd sources at a chosen size/detail; onboard brand tokens from a website; add semantic patterns, callouts, accessible motion, or sketchy/hand-drawn styling.",
|
||||||
"version": "2.4.2",
|
"version": "2.4.3",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Cathryn Lavery",
|
"name": "Cathryn Lavery",
|
||||||
"url": "https://github.com/cathrynlavery"
|
"url": "https://github.com/cathrynlavery"
|
||||||
|
|||||||
@@ -194,7 +194,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tabs" id="type-tabs" role="tablist" aria-label="Diagram type">
|
<div class="tabs" id="type-tabs" role="radiogroup" aria-label="Diagram type">
|
||||||
<button class="tab active" data-type="architecture">
|
<button class="tab active" data-type="architecture">
|
||||||
<span class="eyebrow">01</span>Architecture
|
<span class="eyebrow">01</span>Architecture
|
||||||
</button>
|
</button>
|
||||||
@@ -311,7 +311,7 @@
|
|||||||
<div
|
<div
|
||||||
class="tabs variants"
|
class="tabs variants"
|
||||||
id="variant-tabs"
|
id="variant-tabs"
|
||||||
role="tablist"
|
role="radiogroup"
|
||||||
aria-label="Variant"
|
aria-label="Variant"
|
||||||
>
|
>
|
||||||
<button class="tab active" data-variant="">Minimal light</button>
|
<button class="tab active" data-variant="">Minimal light</button>
|
||||||
@@ -334,30 +334,73 @@
|
|||||||
const openRaw = document.getElementById("open-raw");
|
const openRaw = document.getElementById("open-raw");
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
const activeType = document.querySelector("#type-tabs .tab.active");
|
const activeType = [...document.querySelectorAll("#type-tabs .tab")].find(
|
||||||
|
(tab) => tab.dataset.type === state.type,
|
||||||
|
);
|
||||||
const single = activeType && activeType.hasAttribute("data-single");
|
const single = activeType && activeType.hasAttribute("data-single");
|
||||||
const variantTabs = document.querySelectorAll("#variant-tabs .tab");
|
const variantTabs = document.querySelectorAll("#variant-tabs .tab");
|
||||||
if (single) state.variant = "";
|
if (single) state.variant = "";
|
||||||
variantTabs.forEach((tab) => {
|
variantTabs.forEach((tab) => {
|
||||||
tab.disabled = single && tab.dataset.variant !== "";
|
tab.disabled = single && tab.dataset.variant !== "";
|
||||||
tab.classList.toggle("active", tab.dataset.variant === state.variant);
|
|
||||||
});
|
});
|
||||||
|
for (const [containerId, key] of [
|
||||||
|
["type-tabs", "type"],
|
||||||
|
["variant-tabs", "variant"],
|
||||||
|
]) {
|
||||||
|
document.querySelectorAll(`#${containerId} .tab`).forEach((tab) => {
|
||||||
|
const active = tab.dataset[key] === state[key];
|
||||||
|
tab.classList.toggle("active", active);
|
||||||
|
tab.setAttribute("aria-checked", String(active));
|
||||||
|
tab.tabIndex = active ? 0 : -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
const src = `example-${state.type}${state.variant}.html`;
|
const src = `example-${state.type}${state.variant}.html`;
|
||||||
iframe.src = src;
|
iframe.src = src;
|
||||||
openRaw.href = src;
|
openRaw.href = src;
|
||||||
|
const selectedTabs = document.querySelectorAll('[role="radio"][aria-checked="true"]');
|
||||||
|
iframe.setAttribute(
|
||||||
|
"aria-labelledby",
|
||||||
|
[...selectedTabs].map((tab) => tab.id).join(" "),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindTabs(containerId, key) {
|
function bindTabs(containerId, key) {
|
||||||
const container = document.getElementById(containerId);
|
const container = document.getElementById(containerId);
|
||||||
|
const tabs = [...container.querySelectorAll(".tab")];
|
||||||
|
tabs.forEach((tab, index) => {
|
||||||
|
tab.id = `${containerId}-${index}`;
|
||||||
|
tab.setAttribute("role", "radio");
|
||||||
|
tab.setAttribute("aria-controls", "preview");
|
||||||
|
});
|
||||||
|
|
||||||
|
function activate(tab, focus = false) {
|
||||||
|
state[key] = tab.dataset[key];
|
||||||
|
update();
|
||||||
|
if (focus) tab.focus();
|
||||||
|
}
|
||||||
|
|
||||||
container.addEventListener("click", (e) => {
|
container.addEventListener("click", (e) => {
|
||||||
const btn = e.target.closest(".tab");
|
const btn = e.target.closest(".tab");
|
||||||
if (!btn) return;
|
if (!btn) return;
|
||||||
container
|
activate(btn);
|
||||||
.querySelectorAll(".tab")
|
});
|
||||||
.forEach((t) => t.classList.remove("active"));
|
container.addEventListener("keydown", (e) => {
|
||||||
btn.classList.add("active");
|
const current = e.target.closest('[role="radio"]');
|
||||||
state[key] = btn.dataset[key];
|
if (!current || !container.contains(current)) return;
|
||||||
update();
|
const enabledTabs = tabs.filter((tab) => !tab.disabled);
|
||||||
|
const index = enabledTabs.indexOf(current);
|
||||||
|
let next;
|
||||||
|
if (e.key === "ArrowRight" || e.key === "ArrowDown") {
|
||||||
|
next = enabledTabs[(index + 1) % enabledTabs.length];
|
||||||
|
}
|
||||||
|
if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
|
||||||
|
next = enabledTabs[(index - 1 + enabledTabs.length) % enabledTabs.length];
|
||||||
|
}
|
||||||
|
if (e.key === "Home") next = enabledTabs[0];
|
||||||
|
if (e.key === "End") next = enabledTabs.at(-1);
|
||||||
|
if (!next) return;
|
||||||
|
e.preventDefault();
|
||||||
|
activate(next, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user