Skip to content

Theme Provider

KThemeProvider applies a @kong/design-tokens theme to part of your app. By default it themes only its own subtree; with the global prop it themes the entire document (including teleported content). It also provides the active theme to descendants, powering the useTheme composable.

The demo below shows the same components rendered outside and inside a <KThemeProvider> to illustrate how dramatically a theme can change the look of a subtree.

Default theme

Active
Pending
Error

Neo-brutalism theme

Active
Pending
Error
html
<KThemeProvider :theme="neoBrutalismTheme">
  <KButton appearance="primary">Save changes</KButton>
  <KButton appearance="secondary">Cancel</KButton>
  <KBadge appearance="success">Active</KBadge>
  <KBadge appearance="warning">Pending</KBadge>
  <KBadge appearance="danger">Error</KBadge>
  <KInput placeholder="Search..." />
</KThemeProvider>
ts
import { defineKongponentsTheme } from '@kong/kongponents'

const neoBrutalismTheme = defineKongponentsTheme({
  // Sharp corners globally
  '--kui-border-radius-10': '0px',
  '--kui-border-radius-20': '0px',
  '--kui-border-radius-30': '0px',
  '--kui-border-radius-40': '0px',
  '--kui-border-radius-50': '0px',
  // Typography
  '--kui-font-family-text': "'Space Grotesk', sans-serif",
  '--kui-font-weight-bold': '700',
  '--kui-font-weight-medium': '600',
  // Buttons: square, thick black borders, coral primary / yellow secondary
  '--kui-button-border-radius-large': '0px',
  '--kui-button-border-radius-medium': '0px',
  '--kui-button-border-radius-small': '0px',
  '--kui-button-border-width-large': '3px',
  '--kui-button-border-width-medium': '3px',
  '--kui-button-border-width-small': '2px',
  '--kui-button-color-background-primary': '#FF6B6B',
  '--kui-button-color-background-primary-hover': '#FA4A4A',
  '--kui-button-color-background-primary-active': '#E11D1D',
  '--kui-button-color-border-primary': '#000000',
  '--kui-button-color-text-primary': '#000000',
  '--kui-button-color-background-secondary': '#FFD93D',
  '--kui-button-color-background-secondary-hover': '#F5C400',
  '--kui-button-color-background-secondary-active': '#C99E00',
  '--kui-button-color-border-secondary': '#000000',
  '--kui-button-color-text-secondary': '#000000',
  '--kui-button-font-weight': '700',
  '--kui-button-shadow-focus': '0px 0px 0px 2px #FFFFFF, 0px 0px 0px 5px #000000',
  // Input: square with heavy inset border
  '--kui-input-border-radius': '0px',
  '--kui-input-shadow-border': '0px 0px 0px 3px #000000 inset',
  '--kui-input-shadow-border-hover': '0px 0px 0px 3px #000000 inset',
  '--kui-input-shadow-focus': '0px 0px 0px 2px #FFFFFF, 0px 0px 0px 5px #000000',
  '--kui-input-font-weight': '600',
  // Badges
  '--kui-badge-border-radius': '100px',
  '--kui-badge-font-weight': '700',
  '--kui-badge-color-background-success': '#D6FFE6',
  '--kui-badge-color-text-success': '#15803D',
  '--kui-badge-color-background-warning': '#FFF7CE',
  '--kui-badge-color-text-warning': '#9C7A00',
  '--kui-badge-color-background-danger': '#FFE8E6',
  '--kui-badge-color-text-danger': '#E11D1D',
  // Cards
  '--kui-card-color-background': '#FFFFFF',
  // Inputs
  '--kui-input-color-background': '#FFFFFF',
  '--kui-input-color-text': '#000',
})

NOTE

This page focuses on the <KThemeProvider> component. For the full theming model, bundled themes, and runtime switching, see the Theming guide.

Props

theme

A KongponentsTheme object mapping --kui-* tokens to values. Only the tokens you include are overridden; everything else falls back to the @kong/design-tokens defaults. Use defineKongponentsTheme for autocomplete and compile-time validation of token names.

global

Controls where the theme is applied. Defaults to false.

  • false (default): the theme is scoped to this provider's subtree, set as inline --kui-* custom properties on the wrapper element. The rest of the page is untouched.
  • true: the theme is written to the document root (:root) via an injected <style> element, so it cascades everywhere, including teleported content (modals, toasts, popovers). This is equivalent to the plugin theme option, but driven by your template and reactive state.
vue
<template>
  <!-- App-level: themes the whole document, incl. teleported content -->
  <KThemeProvider :theme="activeTheme" global>
    <RouterView />
  </KThemeProvider>
</template>

Teleported content and subtree themes

When global is not set, components that teleport their content to <body> (KModal, KToaster, KPop, KDropdown, KSlideout) render outside the provider's wrapper and will not pick up the subtree theme. To theme teleported content, apply the theme with global (which targets :root).

display

The CSS display value applied to the wrapper element. Defaults to 'contents', which makes the wrapper transparent to layout — its children participate in the parent flex or grid context as if the provider were not there. Accepted values: contents, block, inline, inline-block, flex, inline-flex, grid, inline-grid, flow-root, none.

html
<!-- display: contents (default) — the two buttons sit directly in the flex row -->
<div style="display: flex; gap: 8px;">
  <KThemeProvider :theme="neoBrutalismTheme">
    <KButton appearance="primary">Themed</KButton>
  </KThemeProvider>
  <KButton appearance="secondary">Unthemed sibling</KButton>
</div>

Set display when the wrapper itself needs to participate in layout:

html
<!-- The provider is a flex container; its children lay out inside it -->
<KThemeProvider :theme="neoBrutalismTheme" display="flex" style="gap: 8px;">
  <KButton appearance="primary">A</KButton>
  <KButton appearance="secondary">B</KButton>
</KThemeProvider>

NOTE

display: contents removes the element from the browser accessibility tree in some environments. If the wrapper tag carries semantic meaning (e.g. tag="nav" or tag="section"), set an explicit display value such as display="block" instead.

tag

The tag used to render the provider's wrapper element. Defaults to 'div'.

html
<KThemeProvider :theme="neoBrutalismTheme" tag="section">
  <KButton appearance="primary">Wrapper is a &lt;section&gt;</KButton>
</KThemeProvider>

The wrapper always carries the k-theme-provider class regardless of tag. Tags that could be used for resource injection or script execution (script, style, iframe, object, embed, link, base, meta, and others) are not allowed and fall back to 'div'.

name

An optional value written to the data-kui-theme attribute on the wrapper, useful for targeting the subtree from static CSS. When unset, the attribute is omitted entirely.

html
<KThemeProvider :theme="neoBrutalismTheme" name="portal">
  <KButton appearance="primary">Named subtree</KButton>
</KThemeProvider>

<!-- Renders as: -->
<div class="k-theme-provider" data-kui-theme="portal"> ... </div>
css
/* Target the themed subtree from your own stylesheets */
[data-kui-theme="portal"] .my-custom-component {
  border-color: var(--kui-color-border-primary);
}

NOTE

When the matching @kong/design-tokens [data-kui-theme] CSS file is loaded, name alone (without :theme) activates that named theme for the subtree. Without that CSS file loaded, name only sets the attribute and applies no tokens. Pass both name and :theme to use a named CSS theme as the base and layer runtime overrides on top. See CSS files + data attribute in the guide.

Slots

default

The content that should receive the theme.

html
<KThemeProvider :theme="neoBrutalismTheme">
  <KButton appearance="primary">Save changes</KButton>
  <KButton appearance="secondary">Cancel</KButton>
  <KBadge appearance="success">Active</KBadge>
  <KInput placeholder="Search..." />
</KThemeProvider>

<KThemeProvider> is the declarative entry point to theming. The Theming guide covers the rest of the toolkit:

  • useTheme: composable to read and switch the active theme at runtime.
  • applyTheme: imperative primitive to apply a theme to :root outside a component.
  • themeToCssVars: convert a theme object into a CSS rule string (SSR / static files).
  • Plugin theme option: apply a theme app-wide at install time.

Released under the Apache-2.0 License.