From 8d97590689daf6b51d854e01f90deadcc0a89232 Mon Sep 17 00:00:00 2001 From: ParzivalPavlis Date: Tue, 14 Apr 2026 13:23:34 +0200 Subject: [PATCH 1/2] feat: change title elements from

to for better semantics in card component --- assets/scss/homepage.scss | 2 +- layouts/shortcodes/blocks/card.html | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/scss/homepage.scss b/assets/scss/homepage.scss index 6fb6196..6db6d17 100644 --- a/assets/scss/homepage.scss +++ b/assets/scss/homepage.scss @@ -225,7 +225,7 @@ display: block; } - & p#{&}__title { + & strong#{&}__title { margin-top: 0; font-size: 1.125em; font-family: bca6d3310b5c9dae1dae416e8abc8405,helvetica,arial,sans-serif; diff --git a/layouts/shortcodes/blocks/card.html b/layouts/shortcodes/blocks/card.html index 3571d6f..4a32f12 100644 --- a/layouts/shortcodes/blocks/card.html +++ b/layouts/shortcodes/blocks/card.html @@ -35,7 +35,7 @@ {{ $modifiedSvg := replaceRE `()` "$1 aria-hidden=\"true\"$2" $svgContent 1 }} {{ $modifiedSvg | safeHTML }} -

{{ .Title }}

+ {{ .Title }} {{ if $description }}

{{ $description }}

{{ else }} @@ -56,7 +56,7 @@ {{ end }} {{ end }} -

{{ $title }}

+ {{ $title }} {{ if eq .Page.File.Ext "md" }} {{ .Inner | .Page.RenderString }} From 1179dccdc01bcefd735adef23702dd3040469953 Mon Sep 17 00:00:00 2001 From: ParzivalPavlis Date: Thu, 16 Apr 2026 08:43:32 +0200 Subject: [PATCH 2/2] feat: enhance accessibility for reCAPTCHA and update modal button attributes risk: low --- static/js/ask-ai-accessibility.js | 117 +++++++++++++++++++----------- 1 file changed, 75 insertions(+), 42 deletions(-) diff --git a/static/js/ask-ai-accessibility.js b/static/js/ask-ai-accessibility.js index 790a851..21b5c3b 100644 --- a/static/js/ask-ai-accessibility.js +++ b/static/js/ask-ai-accessibility.js @@ -1,6 +1,7 @@ $(document).ready(function () { let isModalPresent = false; const $askAiButton = $("#ask-ai-button"); + const observedShadowRoots = new WeakSet(); function updateModalState() { const $kapaContainer = $("#kapa-widget-container"); @@ -16,10 +17,12 @@ $(document).ready(function () { if (exists && !isModalPresent) { isModalPresent = true; $askAiButton.attr("aria-expanded", "true"); + $askAiButton.attr("aria-controls", "kapa-modal-content"); enhanceModalAccessibility(shadowRoot); } else if (!exists && isModalPresent) { isModalPresent = false; $askAiButton.attr("aria-expanded", "false"); + $askAiButton.removeAttr("aria-controls"); } } @@ -61,11 +64,12 @@ $(document).ready(function () { }); // Watch for dynamically added buttons - if (!shadowRoot.getAttribute("data-answer-buttons-observer")) { - shadowRoot.setAttribute("data-answer-buttons-observer", "true"); + if (!observedShadowRoots.has(shadowRoot)) { + observedShadowRoots.add(shadowRoot); const answerButtonsObserver = new MutationObserver(() => { setupAnswerButtonsAccessibility(shadowRoot); + setupRecaptchaAccessibility(shadowRoot); }); answerButtonsObserver.observe(shadowRoot, { @@ -75,6 +79,72 @@ $(document).ready(function () { } } + function setupRecaptchaAccessibility(shadowRoot) { + const recaptchaLink = Array.from(shadowRoot.querySelectorAll("a")).find(a => a.textContent.trim() === "reCAPTCHA"); + + if (!recaptchaLink) { + return; + } + + if (recaptchaLink.hasAttribute("data-recaptcha-a11y-setup")) { + return; + } + + recaptchaLink.setAttribute("data-recaptcha-a11y-setup", "true"); + + // Set proper button semantics since it opens a dialog + recaptchaLink.setAttribute("role", "button"); + + // Set accessible name + if (!recaptchaLink.getAttribute("aria-label")) { + recaptchaLink.setAttribute("aria-label", "Protected by reCAPTCHA"); + } + + // Make keyboard accessible + if (!recaptchaLink.hasAttribute("tabindex")) { + recaptchaLink.setAttribute("tabindex", "0"); + } + + // Setup reCAPTCHA dialog accessibility + recaptchaLink.addEventListener("click", function () { + recaptchaLink.setAttribute("aria-expanded", "true"); + + setTimeout(() => { + const dialog = shadowRoot.querySelector(".mantine-Popover-dropdown[role='dialog']"); + if (dialog) { + const dialogText = dialog.querySelector("p"); + if (dialogText && !dialogText.id) { + dialogText.id = "recaptcha-dialog-description"; + } + + if (!dialog.getAttribute("aria-label")) { + dialog.setAttribute("aria-label", "reCAPTCHA information"); + } + + if (dialogText && dialogText.id) { + dialog.setAttribute("aria-describedby", dialogText.id); + } + + dialog.setAttribute("aria-modal", "true"); + dialog.focus(); + } + }, 100); + }); + + // Handle dialog close to update aria-expanded + const closeObserver = new MutationObserver(() => { + const dialog = shadowRoot.querySelector(".mantine-Popover-dropdown[role='dialog']"); + if (!dialog) { + recaptchaLink.setAttribute("aria-expanded", "false"); + } + }); + + closeObserver.observe(shadowRoot, { + childList: true, + subtree: true + }); + } + function setupDeepThinkingButtonAccessibility(shadowRoot) { const deepThinkingIcon = shadowRoot.querySelector(".tabler-icon-file-search"); if (!deepThinkingIcon) return; @@ -171,12 +241,12 @@ $(document).ready(function () { } } - // Style back to top button + // Style send message button (up arrow icon) const backToTopSvg = shadowRoot.querySelector(".tabler-icon-arrow-up"); if (backToTopSvg) { const backToTopButton = backToTopSvg.closest("button"); if (backToTopButton && !backToTopButton.getAttribute("aria-label")) { - backToTopButton.setAttribute("aria-label", "Back to top"); + backToTopButton.setAttribute("aria-label", "Send message"); if (!backToTopSvg.getAttribute("aria-hidden")) { backToTopSvg.setAttribute("aria-hidden", "true"); } @@ -185,6 +255,7 @@ $(document).ready(function () { setupDeepThinkingButtonAccessibility(shadowRoot); setupAnswerButtonsAccessibility(shadowRoot); + setupRecaptchaAccessibility(shadowRoot); // Fix input accessibility const kpaInput = shadowRoot.querySelector("#kapa-ask-ai-input"); @@ -192,44 +263,6 @@ $(document).ready(function () { kpaInput.setAttribute("aria-label", "Ask me a question about GoodData"); kpaInput.setAttribute("autocomplete", "off"); } - - // Fix reCAPTCHA link accessibility - const recaptchaLink = shadowRoot.querySelector("a[data-underline='hover']:not([href])"); - if (recaptchaLink && recaptchaLink.textContent.includes("reCAPTCHA")) { - if (!recaptchaLink.getAttribute("aria-label")) { - recaptchaLink.setAttribute("aria-label", "Protected by reCAPTCHA"); - } - - recaptchaLink.setAttribute("role", "button"); - - if (!recaptchaLink.hasAttribute("href")) { - recaptchaLink.setAttribute("tabindex", "0"); - } - - // Setup reCAPTCHA dialog accessibility - recaptchaLink.addEventListener("click", function () { - setTimeout(() => { - const dialog = shadowRoot.querySelector(".mantine-Popover-dropdown[role='dialog']"); - if (dialog) { - const dialogText = dialog.querySelector("p"); - if (dialogText && !dialogText.id) { - dialogText.id = "recaptcha-dialog-description"; - } - - if (!dialog.getAttribute("aria-label")) { - dialog.setAttribute("aria-label", "reCAPTCHA information"); - } - - if (dialogText && dialogText.id) { - dialog.setAttribute("aria-describedby", dialogText.id); - } - - dialog.setAttribute("aria-modal", "true"); - dialog.focus(); - } - }, 100); - }); - } } setTimeout(function () {