Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions assets/icons/ai.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions assets/scss/_styles_project.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
@import "404";
@import "skip-links";
@import "heading-anchor";
@import "ask-ai-button";
@import "theme-switcher";
@import "dark-theme";
@import "cta-banner";
Expand Down
45 changes: 45 additions & 0 deletions assets/scss/ask-ai-button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// (C) 2026 GoodData Corporation
@import "variables/variables";

// "Ask AI" button — opens the Kapa AI widget. The outlined button appearance
// comes from the shared .gd-docs-header-nav__secondary styles; this file only
// owns the container layout and icon sizing. Rendered only when
// Site.Params.askAI.websiteId is configured (see partials/ask-ai-button.html).
.ask-ai-button-container {
display: flex;
margin-left: 20px;
justify-content: flex-start;

button {
display: flex;
align-items: center;
gap: 5px;

&:disabled {
cursor: not-allowed;
opacity: 0.2;

&:hover,
&:focus,
&:active {
color: inherit;
border-color: inherit;
}

&:focus {
box-shadow: inherit;
}
}
}

.ask-ai-icon {
display: flex;
width: fit-content;

svg {
width: 15px;
height: 15px;
margin: 0;
}
}
}
22 changes: 22 additions & 0 deletions layouts/partials/ask-ai-button.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{{/* (C) 2026 GoodData Corporation */}}
{{/* "Ask AI" button — opens the Kapa AI widget (loaded by ask-ai-widget.html).
Rendered only when Site.Params.askAI.websiteId is configured. The widget is
triggered via the `open-kapa-widget` override class. */}}
{{- with .Site.Params.askAI }}
{{- if .websiteId }}
<div class="ask-ai-button-container">
<button
class="gd-docs-header-nav__cta gd-docs-header-nav__secondary open-kapa-widget ask-ai-button"
aria-expanded="false"
aria-haspopup="dialog"
aria-controls="kapa-modal-content"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove static aria-controls — the controlled element doesn't exist on initial load.

aria-controls="kapa-modal-content" is set in the HTML, but the kapa-modal-content element lives inside the Kapa widget's shadow DOM and won't exist until the async script loads and the user opens the modal. The accessibility JS in static/js/ask-ai-accessibility.js already adds aria-controls when the modal appears and removes it when it disappears. The static value is redundant and incorrectly references a non-existent element on initial page load, which can confuse screen readers.

♿ Proposed fix
         aria-expanded="false"
         aria-haspopup="dialog"
-        aria-controls="kapa-modal-content"
         id="ask-ai-button"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
aria-controls="kapa-modal-content"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layouts/partials/ask-ai-button.html` at line 12, Remove the static
aria-controls="kapa-modal-content" attribute from the ask-AI button markup. Keep
the dynamic handling in ask-ai-accessibility.js unchanged so it adds the
attribute when the modal appears and removes it when the modal disappears.

id="ask-ai-button"
>
{{ .buttonText | default "Ask AI" }}
<div class="ask-ai-icon">
{{ with resources.Get "icons/ai.svg" }}{{ ( . | minify).Content | safeHTML }}{{ end }}
</div>
</button>
</div>
{{- end }}
{{- end }}
34 changes: 34 additions & 0 deletions layouts/partials/ask-ai-widget.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{{/* (C) 2026 GoodData Corporation */}}
{{/* Loads the Kapa AI widget bundle. Rendered only when Site.Params.askAI.websiteId
is configured. Pair with partials/ask-ai-button.html, which provides the
trigger button (data-button-hide keeps Kapa's own launcher hidden).

Configure in site params, e.g.:
[params.askAI]
websiteId = "..." # required
projectName = "GoodData"
projectColor = "#ED26B7"
projectLogo = "https://www.gooddata.ai/img/generic/logo-g.svg"
modalDisclaimerTextColor = "#ED26B7"
modalTitleColor = "#1c0d3f"
modalTitleFontFamily = "bca6d3310b5c9dae1dae416e8abc8405,helvetica,arial,sans-serif"
*/}}
{{- with .Site.Params.askAI }}
{{- if .websiteId }}
<script
async
src="https://widget.kapa.ai/kapa-widget.bundle.js"
data-website-id="{{ .websiteId }}"
data-project-name="{{ .projectName | default "GoodData" }}"
data-project-color="{{ .projectColor | default "#ED26B7" }}"
data-project-logo="{{ .projectLogo | default "https://www.gooddata.ai/img/generic/logo-g.svg" }}"
data-button-hide="true"
data-modal-disclaimer-text-color="{{ .modalDisclaimerTextColor | default "#ED26B7" }}"
data-modal-override-open-class="open-kapa-widget"
data-modal-open-on-command-k="true"
data-modal-y-offset="20vh"
data-modal-title-color="{{ .modalTitleColor | default "#1c0d3f" }}"
data-modal-title-font-family="{{ .modalTitleFontFamily | default "bca6d3310b5c9dae1dae416e8abc8405,helvetica,arial,sans-serif" }}"
></script>
{{- end }}
{{- end }}
1 change: 1 addition & 0 deletions layouts/partials/hooks/body-end.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@
<script src="/js/prism-accessibility.js"></script>
{{ $themeSwitcherJS := resources.Get "js/theme-switcher.js" | minify | fingerprint }}
<script src="{{ $themeSwitcherJS.RelPermalink }}" integrity="{{ $themeSwitcherJS.Data.integrity }}" defer></script>
{{ partial "ask-ai-widget.html" . }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for where ask-ai-accessibility.js is loaded in the codebase.
rg -n "ask-ai-accessibility" --type=html --type=md

Repository: gooddata/gooddata-docs-theme

Length of output: 166


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== references to ask-ai =="
rg -n --hidden --glob '!**/.git/**' "ask-ai-accessibility|ask-ai-button|ask-ai-widget" .

echo
echo "== relevant template files =="
git ls-files 'layouts/**' 'static/js/**' | rg 'body-end|ask-ai|hooks|partials|js'

echo
echo "== body-end template =="
sed -n '1,220p' layouts/partials/hooks/body-end.html

Repository: gooddata/gooddata-docs-theme

Length of output: 3405


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== ask-ai-widget partial =="
sed -n '1,220p' layouts/partials/ask-ai-widget.html

echo
echo "== custom-scripts partial =="
sed -n '1,220p' layouts/partials/custom-scripts.html

echo
echo "== any direct include of ask-ai-accessibility.js =="
rg -n "/js/ask-ai-accessibility\.js|ask-ai-accessibility\.js" layouts static assets

Repository: gooddata/gooddata-docs-theme

Length of output: 2308


Load /js/ask-ai-accessibility.js with the other body-end scripts.
layouts/partials/hooks/body-end.html includes the widget partial, but not the accessibility helper. Without it, #ask-ai-button won’t get the dynamic aria-expanded / aria-controls behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@layouts/partials/hooks/body-end.html` at line 14, Add
/js/ask-ai-accessibility.js to the body-end script includes in
layouts/partials/hooks/body-end.html alongside the existing ask-ai widget
partial, ensuring it loads after the widget so `#ask-ai-button` receives its
dynamic aria-expanded and aria-controls behavior.

1 change: 1 addition & 0 deletions layouts/partials/navbar.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<div id="adds-container" class="gd-docs-header-nav__search"></div>
</div>
{{ end }}
{{ partial "ask-ai-button.html" . }}
{{ if .Site.Params.versions }}
<div class="gd-docs-header-nav__right">
{{ partial "navbar-version-selector.html" . }}
Expand Down