The widget is built from named parts, called components. You can style each one with data-* attributes on the script tag. For colours, fonts and dark mode across the whole widget, use Theming instead.
The names are kapa.ai’s, so a styled kapa embed keeps its look. kapa’s legacy style names (data-button-bg-color, data-modal-header-bg-color, …) work too. See Legacy and kapa.ai names.
Component style configuration
An attribute is a component name, an optional state, and a CSS property:
data-{component}-{property} data-modal-header-background-color
data-{component}-{state}-{property} data-launcher-button-hover-background-color
data-{component}-{property}-dark data-modal-header-background-color-dark
- A component style wins over the colour palette and the brand colour.
- It applies in light and dark mode. The
-dark version overrides it in dark mode only.
- An attribute that does not name a known component and a supported property is ignored. So is an unsafe value (one containing
;, {, }, <, > or url().
Supported CSS properties
A bare number in a size means pixels.
| Property | Sets |
|---|
background-color, color, opacity | Colours |
border, border-bottom, border-color, border-radius | Borders and corners |
font-family, font-size, font-weight | Text |
height, width, min-height, min-width, max-height, max-width | Size |
padding, padding-top, padding-bottom, padding-left, padding-right | Padding |
padding-x, padding-y | Left and right, or top and bottom padding |
margin-top, margin-bottom, margin-left, margin-right, margin-x, margin-y | Margin |
top, left, right, bottom, z-index | Position |
flex-direction, justify-content | Flex layout |
box-shadow, text-shadow | Shadows |
icon-size | Width and height of the icon inside the component |
Pseudo-state variants
Put a state between the component and the property to style that state only.
| State | Applies | Example |
|---|
hover | While the pointer is over it | data-launcher-button-hover-background-color |
focus | While it, or a field inside it, has focus | data-query-input-focus-border-color |
placeholder | To an input’s placeholder text | data-query-input-placeholder-color |
active | While it is being pressed | data-submit-button-active-background-color |
enabled | To the chosen 👍 or 👎 | data-answer-feedback-button-enabled-background-color |
Available components
A style on a base component applies to the components built on it. A style on the child wins.
| Component | Base | What it is |
|---|
launcher-button | | The button that opens the panel, in the corner or in your header |
launcher-button-label | | The text on the launcher |
modal, modal-content | | The panel (both names reach it) |
modal-inner | | Places the panel. flex-direction and justify-content lay out the full-screen layer; top, right, bottom, left pin the panel to the window; other properties size the panel |
modal-overlay | | The dimmed backdrop. background-color sets its colour; opacity thins that colour without fading the panel |
modal-header | | The header row |
modal-logo | | The logo in the header |
modal-title | | The title in the header |
modal-close-button | | The ✕ in the header |
modal-body | | The conversation area and the search results |
modal-footer | | The footer |
disclaimer, chat-disclaimer | | The notice above the question box (both names reach it) |
consent-screen | | The consent screen |
query-input | | Both input fields. Text properties land on the field inside |
ask-ai-input | query-input | The question box |
search-input | query-input | The Search tab’s field |
submit-button | | The send button |
example-questions | | The group of starter questions |
example-question-button | | One starter question |
conversation-item-question | | The reader’s question |
conversation-item-answer | | The answer text |
conversation-button | | Copy, 👍, 👎 and new conversation |
answer-feedback-button | conversation-button | 👍 and 👎 |
answer-copy-button | conversation-button | Copy answer |
thread-clear-button | conversation-button | New conversation, in the header |
handoff-button | | The Create ticket button |
answer-cta-button | | The call-to-action button under answers |
answer-sources-button | | The Sources label above an answer’s sources |
source-link | | One source |
source-link-primary-heading | | A source’s title |
source-link-secondary-heading | | A source’s number |
search-result | | One search result |
search-result-badge | | A result’s section path |
search-result-primary-text | | A result’s title |
search-result-secondary-text | | A result’s snippet |
search-ask-ai-cta | | The “Ask AI: …” row in search |
switch | | The Ask AI / Search tabs |
switch-label | | One tab |
mcp-button | | The Use MCP header button |
mcp-dropdown | | The MCP menu |
branding, kapa-branding | | The “Powered by” line |
privacy-links | | The footer’s policy links |
captcha-disclaimer | | The “Protected by reCAPTCHA” line |
kapa’s reasoning-mode-selector (the Fast/Thinking switch) has no counterpart, and its attributes are ignored.
Example
<script
async
src="https://widget.kelu.dev/kelu-widget.js"
data-knowledge-base-id="YOUR_KNOWLEDGE_BASE_ID"
data-client-key="kl_pk_..."
data-launcher-button-background-color="#111827"
data-launcher-button-hover-background-color="#374151"
data-modal-header-background-color="#f5f3ff"
data-modal-header-background-color-dark="#1e1b4b"
data-query-input-focus-border-color="#6306B6"
data-example-question-button-border-radius="999px"
data-conversation-button-icon-size="16px"
></script>
Component-specific configuration options
Some components also have options of their own: labels, images and switches.
| Attribute | Description | Default |
|---|
data-launcher-button-hidden | Draw no launcher. Open the panel with an open trigger or the JavaScript API | "false" |
data-launcher-button-text | Label, tooltip and accessible name | "Ask AI" |
data-launcher-button-show-text | Show the label beside the icon, as a pill | "false"; "true" in your header |
data-launcher-button-image | Image on the button | The product logo |
data-launcher-button-image-hidden | Keep the sparkle icon even when a product logo is set | "false" |
data-launcher-button-image-fill | Let the image fill the button, for a logo with its own background | "false" |
data-launcher-button-image-width / -height | Image size | 32px (18px in your header) |
data-launcher-button-shape | "circle", "pill" or "square". A circle never shows the label | Pill with a label, circle without |
data-launcher-button-size | Diameter of a circle, height of a pill or square. An unshaped pill sizes to its label | 60px |
data-launcher-button-text-size | Label size | 15px |
data-launcher-button-animation-enabled | "false" turns off the entrance animation | "true" |
data-launcher-button-hover-animation-enabled | "false" turns off the grow-on-hover | "true" |
data-position | "bottom-right" or "bottom-left" | "bottom-right" |
To put the button in your page, next to your search box, instead of in a corner:
| Attribute | Description | Default |
|---|
data-launcher-button-anchor-selector | CSS selector. Every match gets a button beside it | Not set |
data-launcher-button-anchor-position | "beforebegin", "afterbegin", "beforeend" or "afterend" | "afterend" |
data-launcher-button-anchor-shape | Shape of that button | A rounded rectangle |
data-launcher-button-anchor-size | Its height | 36px |
data-launcher-button-anchor-text-size | Its label size | 13px |
data-launcher-button-anchor-overlay | Lay the button over your header instead of inserting it, so nothing moves | Decided per header |
data-launcher-button-anchor-wait-ms | How long to wait for the element before using the corner instead. -1 waits forever | 10000 |
data-launcher-button-floating | Keep the corner button as well | "false" |
See Put the button in your header.
Modal
| Attribute | Description | Default |
|---|
data-modal-size | Panel width in modal mode | 760px |
data-sidebar-width | Panel width in sidebar mode | 460px |
data-modal-height | Panel height. Never taller than the window | 780px |
data-modal-expanded-size | Width after the ⤢ header button is pressed | 1500px |
data-modal-x-offset / data-modal-y-offset | Move the panel sideways or up and down, such as 4vw | 0 |
data-modal-full-screen | Fill the window | "false" |
data-modal-full-screen-on-mobile | "false" stops it filling the screen under 600px | "true" |
data-modal-lock-scroll | "false" lets the page scroll behind the open panel | "true" |
data-modal-z-index | Stacking order | 2147483647 |
data-modal-overlay-hidden | Draw no backdrop | "false" |
| Attribute | Description | Default |
|---|
data-modal-title | Header title | The product name |
data-modal-title-ask-ai / -search | Title while the chat or the Search tab is showing | data-modal-title |
data-modal-subtitle | The second line. "" removes it | ”Answers with sources from the docs” |
data-modal-logo-src | Header logo | The product logo |
data-modal-logo-src-ask-ai / -search | Logo while the chat or the Search tab is showing | data-modal-logo-src |
data-modal-logo-width / -height | Logo size | 32px |
data-modal-logo-hidden | Hide the logo | "false" |
data-modal-logo-hidden-on-mobile | Hide it under 600px | "false" |
data-modal-close-button-hidden | Hide the ✕ | "false" |
data-modal-expand-button-hidden | Hide the ⤢. It only exists in modal and sidebar mode, and not under 600px | "false" |
Chat disclaimer
| Attribute | Description | Default |
|---|
data-chat-disclaimer | Notice above the question box, such as “Answers are AI-generated”. Markdown | Not set |
Consent screen
Shown when data-consent-required is "true".
| Attribute | Description | Default |
|---|
data-consent-screen-title | Heading | ”Hi there, do you want to use the AI chat?” |
data-consent-screen-disclaimer | Body text. Markdown | A short notice that the chat uses AI |
data-consent-screen-accept-button-text | Accept button | ”I agree, let’s chat!” |
data-consent-screen-reject-button-text | Reject button | ”No, not interested” |
| Attribute | Description | Default |
|---|
data-ask-ai-input-placeholder | Question box placeholder | ”Ask anything about “ |
data-search-input-placeholder | Search tab placeholder | ”Search sources…” |
data-search-input-icon-hidden | Hide the magnifier in the Search tab’s field | "false" |
Example questions
| Attribute | Description | Default |
|---|
data-welcome-message | The greeting above the starter questions | ”Ask me anything about . I answer from the documentation and link every source.” |
data-suggested-questions | Starter questions, split on |. Up to four are shown | The knowledge base’s own |
data-example-questions-columns | How many per row | 1 |
data-show-suggested | "false" shows no starter questions | "true" |
Without a list on the tag, the widget shows the knowledge base’s own starter questions (the knowledge base’s Settings → Starter Questions). Phones always show one per row.
| Attribute | Description | Default |
|---|
data-handoff-button-text | Label of the Create ticket button. Turn it on in Behaviour | ”Create ticket” |
data-answer-cta-button-enabled | Add a call-to-action button under every answer | "false" |
data-answer-cta-button-text | Its label | ”Talk to an expert” |
data-answer-cta-button-link | Where it goes. Without a link there is no button | Not set |
Icon sizes use the icon-size property: data-conversation-button-icon-size for all of them, or data-answer-feedback-button-icon-size, data-answer-copy-button-icon-size, data-thread-clear-button-icon-size for one. Defaults: 14px under an answer, 16px in the header.
Search tab
| Attribute | Description | Default |
|---|
data-search-result-target | The target of a result link | "_blank" |
data-search-ask-ai-cta-hidden | Hide the “Ask AI: …” row | "false" |
data-switch-icon-hidden | Hide the icons on the Ask AI and Search tabs, keeping the labels | "false" |
| Attribute | Description | Default |
|---|
data-mcp-button-text | Button label | ”Use MCP” |
data-mcp-dropdown-description | Line under the menu’s title. [text](url) links work | ”Access this knowledge base via MCP” |
| Attribute | Description | Default |
|---|
data-modal-footer-text | An extra footer line. Markdown | Not set |
data-privacy-links | Footer links as JSON: [{"title":"Privacy","url":"/privacy"}]. Invalid JSON is ignored with a console warning | None |
data-hide-branding | Hide “Powered by Kelu” | "false" |
data-branding-text | Replace “Powered by Kelu” with your own line | Not set |
Hiding or replacing “Powered by Kelu” works on the Enterprise plan only. On other plans the line stays. The “Protected by reCAPTCHA” line stays on every plan when reCAPTCHA is on.