Skip to content

Commit ca25c1f

Browse files
Merge pull request #102 from gooddata/feat/accessibility-new
feat: accesibility updates
2 parents 95d930d + 1179dcc commit ca25c1f

3 files changed

Lines changed: 78 additions & 45 deletions

File tree

‎assets/scss/homepage.scss‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@
225225
display: block;
226226
}
227227

228-
& p#{&}__title {
228+
& strong#{&}__title {
229229
margin-top: 0;
230230
font-size: 1.125em;
231231
font-family: bca6d3310b5c9dae1dae416e8abc8405,helvetica,arial,sans-serif;

‎layouts/shortcodes/blocks/card.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
{{ $modifiedSvg := replaceRE `(<svg)(.*?>)` "$1 aria-hidden=\"true\"$2" $svgContent 1 }}
3636
{{ $modifiedSvg | safeHTML }}
3737
</div>
38-
<p class="homepage-card__title">{{ .Title }}</p>
38+
<strong class="homepage-card__title">{{ .Title }}</strong>
3939
{{ if $description }}
4040
<p class="homepage-card__text">{{ $description }}</p>
4141
{{ else }}
@@ -56,7 +56,7 @@
5656
{{ end }}
5757
</div>
5858
{{ end }}
59-
<p class="homepage-card__title">{{ $title }}</p>
59+
<strong class="homepage-card__title">{{ $title }}</strong>
6060

6161
{{ if eq .Page.File.Ext "md" }}
6262
{{ .Inner | .Page.RenderString }}

‎static/js/ask-ai-accessibility.js‎

Lines changed: 75 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
$(document).ready(function () {
22
let isModalPresent = false;
33
const $askAiButton = $("#ask-ai-button");
4+
const observedShadowRoots = new WeakSet();
45

56
function updateModalState() {
67
const $kapaContainer = $("#kapa-widget-container");
@@ -16,10 +17,12 @@ $(document).ready(function () {
1617
if (exists && !isModalPresent) {
1718
isModalPresent = true;
1819
$askAiButton.attr("aria-expanded", "true");
20+
$askAiButton.attr("aria-controls", "kapa-modal-content");
1921
enhanceModalAccessibility(shadowRoot);
2022
} else if (!exists && isModalPresent) {
2123
isModalPresent = false;
2224
$askAiButton.attr("aria-expanded", "false");
25+
$askAiButton.removeAttr("aria-controls");
2326
}
2427
}
2528

@@ -61,11 +64,12 @@ $(document).ready(function () {
6164
});
6265

6366
// Watch for dynamically added buttons
64-
if (!shadowRoot.getAttribute("data-answer-buttons-observer")) {
65-
shadowRoot.setAttribute("data-answer-buttons-observer", "true");
67+
if (!observedShadowRoots.has(shadowRoot)) {
68+
observedShadowRoots.add(shadowRoot);
6669

6770
const answerButtonsObserver = new MutationObserver(() => {
6871
setupAnswerButtonsAccessibility(shadowRoot);
72+
setupRecaptchaAccessibility(shadowRoot);
6973
});
7074

7175
answerButtonsObserver.observe(shadowRoot, {
@@ -75,6 +79,72 @@ $(document).ready(function () {
7579
}
7680
}
7781

82+
function setupRecaptchaAccessibility(shadowRoot) {
83+
const recaptchaLink = Array.from(shadowRoot.querySelectorAll("a")).find(a => a.textContent.trim() === "reCAPTCHA");
84+
85+
if (!recaptchaLink) {
86+
return;
87+
}
88+
89+
if (recaptchaLink.hasAttribute("data-recaptcha-a11y-setup")) {
90+
return;
91+
}
92+
93+
recaptchaLink.setAttribute("data-recaptcha-a11y-setup", "true");
94+
95+
// Set proper button semantics since it opens a dialog
96+
recaptchaLink.setAttribute("role", "button");
97+
98+
// Set accessible name
99+
if (!recaptchaLink.getAttribute("aria-label")) {
100+
recaptchaLink.setAttribute("aria-label", "Protected by reCAPTCHA");
101+
}
102+
103+
// Make keyboard accessible
104+
if (!recaptchaLink.hasAttribute("tabindex")) {
105+
recaptchaLink.setAttribute("tabindex", "0");
106+
}
107+
108+
// Setup reCAPTCHA dialog accessibility
109+
recaptchaLink.addEventListener("click", function () {
110+
recaptchaLink.setAttribute("aria-expanded", "true");
111+
112+
setTimeout(() => {
113+
const dialog = shadowRoot.querySelector(".mantine-Popover-dropdown[role='dialog']");
114+
if (dialog) {
115+
const dialogText = dialog.querySelector("p");
116+
if (dialogText && !dialogText.id) {
117+
dialogText.id = "recaptcha-dialog-description";
118+
}
119+
120+
if (!dialog.getAttribute("aria-label")) {
121+
dialog.setAttribute("aria-label", "reCAPTCHA information");
122+
}
123+
124+
if (dialogText && dialogText.id) {
125+
dialog.setAttribute("aria-describedby", dialogText.id);
126+
}
127+
128+
dialog.setAttribute("aria-modal", "true");
129+
dialog.focus();
130+
}
131+
}, 100);
132+
});
133+
134+
// Handle dialog close to update aria-expanded
135+
const closeObserver = new MutationObserver(() => {
136+
const dialog = shadowRoot.querySelector(".mantine-Popover-dropdown[role='dialog']");
137+
if (!dialog) {
138+
recaptchaLink.setAttribute("aria-expanded", "false");
139+
}
140+
});
141+
142+
closeObserver.observe(shadowRoot, {
143+
childList: true,
144+
subtree: true
145+
});
146+
}
147+
78148
function setupDeepThinkingButtonAccessibility(shadowRoot) {
79149
const deepThinkingIcon = shadowRoot.querySelector(".tabler-icon-file-search");
80150
if (!deepThinkingIcon) return;
@@ -171,12 +241,12 @@ $(document).ready(function () {
171241
}
172242
}
173243

174-
// Style back to top button
244+
// Style send message button (up arrow icon)
175245
const backToTopSvg = shadowRoot.querySelector(".tabler-icon-arrow-up");
176246
if (backToTopSvg) {
177247
const backToTopButton = backToTopSvg.closest("button");
178248
if (backToTopButton && !backToTopButton.getAttribute("aria-label")) {
179-
backToTopButton.setAttribute("aria-label", "Back to top");
249+
backToTopButton.setAttribute("aria-label", "Send message");
180250
if (!backToTopSvg.getAttribute("aria-hidden")) {
181251
backToTopSvg.setAttribute("aria-hidden", "true");
182252
}
@@ -185,51 +255,14 @@ $(document).ready(function () {
185255

186256
setupDeepThinkingButtonAccessibility(shadowRoot);
187257
setupAnswerButtonsAccessibility(shadowRoot);
258+
setupRecaptchaAccessibility(shadowRoot);
188259

189260
// Fix input accessibility
190261
const kpaInput = shadowRoot.querySelector("#kapa-ask-ai-input");
191262
if (kpaInput) {
192263
kpaInput.setAttribute("aria-label", "Ask me a question about GoodData");
193264
kpaInput.setAttribute("autocomplete", "off");
194265
}
195-
196-
// Fix reCAPTCHA link accessibility
197-
const recaptchaLink = shadowRoot.querySelector("a[data-underline='hover']:not([href])");
198-
if (recaptchaLink && recaptchaLink.textContent.includes("reCAPTCHA")) {
199-
if (!recaptchaLink.getAttribute("aria-label")) {
200-
recaptchaLink.setAttribute("aria-label", "Protected by reCAPTCHA");
201-
}
202-
203-
recaptchaLink.setAttribute("role", "button");
204-
205-
if (!recaptchaLink.hasAttribute("href")) {
206-
recaptchaLink.setAttribute("tabindex", "0");
207-
}
208-
209-
// Setup reCAPTCHA dialog accessibility
210-
recaptchaLink.addEventListener("click", function () {
211-
setTimeout(() => {
212-
const dialog = shadowRoot.querySelector(".mantine-Popover-dropdown[role='dialog']");
213-
if (dialog) {
214-
const dialogText = dialog.querySelector("p");
215-
if (dialogText && !dialogText.id) {
216-
dialogText.id = "recaptcha-dialog-description";
217-
}
218-
219-
if (!dialog.getAttribute("aria-label")) {
220-
dialog.setAttribute("aria-label", "reCAPTCHA information");
221-
}
222-
223-
if (dialogText && dialogText.id) {
224-
dialog.setAttribute("aria-describedby", dialogText.id);
225-
}
226-
227-
dialog.setAttribute("aria-modal", "true");
228-
dialog.focus();
229-
}
230-
}, 100);
231-
});
232-
}
233266
}
234267

235268
setTimeout(function () {

0 commit comments

Comments
 (0)