본문으로 건너뛰기
This page is not available in 한국어 yet. You are reading the English version.

Theming and layout

The chat panel has two layouts and one styling mechanism. This page explains both, with the exact rules the panel follows, so you can fit it into your page without trial and error.

Two layouts​

Docked in a containerFloating window
How you get itcontained: true (or the contained attribute)The default
Where the panel isInside your mount target, 10 px from its top-left cornerAppended to document.body, fixed 10 px from the top-left corner of the browser viewport
Moves with the page when it scrollsYesNo
Place cards and minimized chatsStay inside the mount targetPositioned against the viewport
StackingInside your container's stacking orderz-index 10000
Use it whenThe map is one region of a page: a column, a card, a sectionThe map fills the whole viewport

Most pages want the docked layout. The floating window does not know where your map is: on a page where the map is halfway down, the panel still sits at the top-left of the window.

Docking the panel in a container​

contained: true puts the panel inside the element you mount into and positions it against that element. For that to work, the element must be positioned and must have a size. The reliable way to give it both is one wrapper that holds two layers: the map, and the chat mount target on top of it.

<div id="mapHost" style="position:relative; height:520px">
<div id="map" style="position:absolute; inset:0; z-index:0"></div>
<div id="chat" style="position:absolute; inset:0; z-index:1; pointer-events:none"></div>
</div>
Kaleidr.mount('#chat', {
product: 'chat',
publishableKey: 'kld_pk_live_…',
map,
contained: true,
});

Each declaration in that markup has a job:

DeclarationWhy it is there
position:relative and a height on #mapHostGives both layers a box to fill. The height can come from anywhere (a fixed value, a flex or grid track, 100vh), but it must resolve to a real size.
position:absolute; inset:0 on #mapThe map fills the wrapper.
z-index:0 on #mapCreates a stacking context for the map, so the map library's own z-index values (Leaflet uses values up to 1000) stay inside the map layer and cannot rise above the chat layer.
position:absolute; inset:0 on #chatMakes the mount target positioned and gives it the map's size. The panel is placed 10 px from this box's top-left corner and is never taller than this box minus 30 px.
z-index:1 on #chatPuts the chat layer above the map layer.
pointer-events:none on #chatLets clicks, drags and scroll reach the map everywhere the panel is not. The panel, its place cards and its minimized chats switch pointer events back on for themselves.

What goes wrong without them:

SymptomCause
The panel appears at the top-left of the page or of some outer element, not over the mapThe mount target is not positioned, so the panel is placed against the nearest positioned ancestor
The panel appears at the top-left of the browser window and stays there when the page scrollscontained is missing
The panel is cut off or has no room for answersThe mount target has no height
The map cannot be draggedThe chat layer covers the map and pointer-events:none is missing
The panel is hidden behind the map or its controlsThe map layer has no stacking context of its own; add z-index:0 to it

With the element​

The same layout with <kaleidr-map> instead of Kaleidr.mount. The element is the mount target, so the positioning styles go on the element itself. <kaleidr-map> is display:block by default; the snippets write display:block out so that the layout also holds in Safari before 16.4, where the element stays inline.

<div style="position:relative">
<kaleidr-map id="store-map" product="viewer" share-id="YOUR_MAP_ID"
style="display:block; height:480px"></kaleidr-map>
<kaleidr-map product="chat" share-id="YOUR_MAP_ID" contained
map-target="#store-map"
style="display:block; position:absolute; inset:0; z-index:1; pointer-events:none"></kaleidr-map>
</div>

The element cannot receive a live map object, so this form is for driving a Kaleidr viewer or tile element, or for a chat-only panel. To attach to your own Mapbox, MapLibre, Google or Leaflet map, use Kaleidr.mount.

The floating window​

Without contained, the panel is appended to document.body and uses position:fixed. It starts as an input bar 10 px from the top and left edges of the viewport, and expands downward into a chat window when the visitor sends a question. The mount target you pass still has to exist, but it does not determine where the panel is.

Three things about this layout matter in practice:

  • It ignores your map's position. Use it when the map is the page.

  • It sits above your content. The panel and its place cards use z-index 10000. Minimized chats sit above that. A site header, drawer or modal with a lower z-index appears underneath the panel.

  • It can be shifted right, but not down. The property --app-left-inset moves the floating window to the right by the length you give it, and narrows its maximum width by the same amount. Use it to clear a fixed sidebar:

    Kaleidr.mount('#chat', {
    product: 'chat',
    publishableKey: 'kld_pk_live_…',
    map,
    theme: { '--app-left-inset': '280px' },
    });

    There is no property that moves the floating window down or to another corner. The 10 px top and left margins are fixed on the panel itself, so setting margin properties through theme does not move it. If you need the panel somewhere else, use contained and position the container.

Stacking against your own layers​

Set --kct-z-base to move the panel and its place cards to a different level. For example, to keep the panel below a site header that uses z-index: 500:

Kaleidr.mount('#chat', {
product: 'chat',
publishableKey: 'kld_pk_live_…',
map,
theme: { '--kct-z-base': '400' },
});

Set it at mount. Minimized chats are placed 90,000 above the base and read it once, when the panel mounts; they sit at the bottom of the viewport, away from a typical header.

In the docked layout the panel is confined to your container's stacking context, so the z-index of the chat layer decides how it stacks against the rest of the page, and --kct-z-base only orders things inside it.

Small screens​

On a viewport 768 px wide or narrower, the panel switches to a bottom-sheet layout. In the docked layout the same switch happens when the container is 768 px wide or narrower and the device is a phone-class device (a narrow window or a touch-first pointer). A narrow map column on a desktop keeps the regular layout.

Theme tokens​

The panel is styled with CSS custom properties. These are the ones a page usually changes. Each default is the value the panel uses when you set nothing.

PropertyDefaultWhat it controls
--highlight-color#2da84aThe accent color, used across the panel, place cards and pins.
--highlight-color-on#ffffffText and icons drawn on top of the accent.
--glass-bgrgba(255, 255, 255, 1)The background of the panel, the chat window and place cards.
--ui-bg-solid#ffffffThe background of solid surfaces such as dialogs.
--glass-border1px solid rgba(0, 0, 0, 0.08)The outline of the panel and cards.
--color-primary-whitergba(0, 0, 0, 0.88)Primary text. The name is historical; the default is dark.
--color-secondary-whitergba(0, 0, 0, 0.55)Secondary text.
--ui-font-scale1.2A multiplier on every text size and on spacing. The intended range is 0.8 to 1.6.
--kct-z-base10000The z-index of the panel and its place cards. Set it at mount.
--app-left-inset0pxA horizontal offset for the floating window. No effect in the docked layout.

There is no --kd-accent property. The accent is --highlight-color.

The panel has no built-in dark theme and does not follow prefers-color-scheme. For a dark panel, set the surface and text properties yourself:

Kaleidr.mount('#chat', {
product: 'chat',
publishableKey: 'kld_pk_live_…',
map,
contained: true,
theme: {
'--highlight-color': '#8b5cf6',
'--glass-bg': 'rgba(24, 24, 27, 0.96)',
'--ui-bg-solid': '#18181b',
'--glass-border': '1px solid rgba(255, 255, 255, 0.12)',
'--color-primary-white': 'rgba(255, 255, 255, 0.92)',
'--color-secondary-white': 'rgba(255, 255, 255, 0.6)',
},
});

Check the result in your page. The table lists the main properties, not every one the stylesheet defines.

Setting the theme​

There are three ways in, and they do the same thing.

At mount, as an object:

Kaleidr.mount('#chat', {
product: 'chat',
publishableKey: 'kld_pk_live_…',
map,
contained: true,
theme: { '--highlight-color': '#7c3aed', '--ui-font-scale': '1' },
});

On the element, as a JSON string:

<kaleidr-map product="chat" publishable-key="kld_pk_live_…" contained
theme='{"--highlight-color":"#7c3aed"}'
style="display:block; position:absolute; inset:0; z-index:1; pointer-events:none"></kaleidr-map>

Changing the theme attribute after the element has mounted applies the new value.

After mount, with setTheme:

const handle = Kaleidr.mount('#chat', { product: 'chat', publishableKey: 'kld_pk_live_…', map });

handle.setTheme({ '--highlight-color': '#0ea5e9' });
handle.setTheme('{"--ui-font-scale":"1.4"}'); // a JSON string works too

Rules that apply to all three:

  • The value is an object of property names to string values, or a JSON string of such an object.
  • A key without the leading -- gets one added: { 'highlight-color': '#7c3aed' } works.
  • setTheme adds to what is already set. It does not reset properties you leave out.
  • Named themes such as "dark" do not exist. A string that is not JSON, or an array, is ignored with a [kaleidr] warning in the console.
  • setTheme can be called immediately after mount. The call is queued until the panel has loaded.

Themes are page-wide​

The properties are written to the <html> element's inline style (document.documentElement.style). They are not scoped to the panel or to the mount. Three consequences:

  • Two chat mounts on one page share one theme. The last value written wins for both.

  • Your own CSS sees the properties. If your stylesheet reads var(--highlight-color), it now gets the value you gave the panel.

  • destroy() does not remove them. Clear a property yourself when you need to:

    document.documentElement.style.removeProperty('--highlight-color');

Property names you may already use​

The panel's stylesheet gives every property it uses a default on :root, at zero specificity. Your own stylesheet always wins over those defaults. That cuts both ways:

  • If your site already defines --highlight-color, --glass-bg, --ui-font-scale or another property from the table on :root, the panel uses your value without being asked. If that is not what you want, pass the panel's own value through theme, which is an inline style and overrides both.
  • If your CSS reads one of the panel's property names without defining it (for example var(--spacing-base, 8px)), it resolves to the panel's default once the panel has mounted, not to your fallback.

The panel's sizes, spacing, radii and margins are also set directly on the panel's own elements, so a value your site defines for those names does not change the panel.

What you cannot change​

  • The language of the panel's own labels, such as its buttons. The header title, the input placeholder and the language of the answers are mount options, not theme tokens: title, placeholder and lang. See Title and placeholder.
  • The corner the panel starts in, or its 10 px margins.
  • The panel's font. It ships its own typeface, embedded in the stylesheet.
  • The look of the pins beyond the accent color.