Skip to content
kitn AI/UI

Custom theme

This is the same <kai-chat> that powers every other example, reskinned into “Aurora” — a neon-on-noir identity with a magenta accent, a cyan focus ring, and sharp corners. Nothing pierces the Shadow DOM: a map of inherited --kai-* custom properties on the host does all of it. Toggle the site’s theme to watch a second, matched palette take over.

The --kai-color-* and --kai-radius tokens are inherited CSS custom properties. Set them on the <kai-chat> element (or any ancestor) and they propagate into the Shadow DOM through var() fallbacks — no stylesheet import, no internal classes touched.

A bold look comes from overriding more than the accent. Aurora moves every surface, the focus ring, the inline-code tint, and the corner radius together so the result reads as a deliberate design, not a recolored default:

<kai-chat id="chat" chat-title="Aurora"></kai-chat>
<script type="module">
import '@kitn.ai/ui/elements';
const chat = document.getElementById('chat');
// Scope the whole Aurora map to this element only.
const aurora = {
'--kai-color-background': 'hsl(252 40% 6%)', // deep-noir surface
'--kai-color-card': 'hsl(250 36% 10%)', // message bubbles / panels
'--kai-color-foreground': 'hsl(250 30% 96%)',
'--kai-color-primary': 'hsl(322 90% 58%)', // magenta accent
'--kai-color-primary-foreground': 'hsl(250 40% 7%)',
'--kai-color-ring': 'hsl(186 95% 56%)', // cyan focus glow
'--kai-color-border': 'hsl(250 30% 20%)',
'--kai-color-code-foreground': 'hsl(186 90% 66%)', // cyan inline code
'--kai-radius': '0.25rem', // sharp corners
};
for (const [token, value] of Object.entries(aurora)) {
chat.style.setProperty(token, value);
}
// Wire messages + kai-submit as usual.
chat.messages = [];
chat.addEventListener('kai-submit', (e) => { /* stream reply */ });
</script>

The aurora glow and the framed shell around the chat are host-side chrome — plain CSS on a wrapper that sits outside the element. The component never knows it’s there.

A single token value applies in whichever mode the page is in, so a theme that’s intentional in both needs separate maps — one for light, one for dark. Each is self-contained, with the accent and ring darkened for light mode to keep text and focus at WCAG AA:

/* dark — neon on near-black indigo */
.dark kai-chat {
--kai-color-background: hsl(252 40% 6%);
--kai-color-card: hsl(250 36% 10%);
--kai-color-primary: hsl(322 90% 58%);
--kai-color-ring: hsl(186 95% 56%);
--kai-color-code-foreground: hsl(186 90% 66%);
}
/* light — same identity, inverted onto a cool studio white */
kai-chat {
--kai-color-background: hsl(240 40% 98%);
--kai-color-card: hsl(0 0% 100%);
--kai-color-primary: hsl(322 82% 46%); /* darker → AA on light */
--kai-color-ring: hsl(192 90% 38%); /* darker → visible on light */
--kai-color-code-foreground: hsl(192 90% 32%);
}

The demo above sets these from JavaScript, re-applying the matching map whenever the page’s theme flips — handy when your overrides live in code rather than a stylesheet.

  • Theming guide — the full token reference, the --kai-color-* alias system, scoped subtree theming, and dark-mode setup.
  • Drop-in chat — the streaming loop that backs any <kai-chat> instance.
  • kai-chat referenceproseSize, codeTheme, and codeHighlight for non-color appearance control.