Skip to main content

Themes

Kanban draws its colors, shadows, and typography from CSS custom properties injected by a theme wrapper component. The widget never hardcodes visual values - it reads var(--wx-kanban-*) tokens (and a handful of shared --wx-* tokens) from the nearest ancestor that sets them. To restyle the board, override those variables on a wrapper element; to restyle individual cards or columns at runtime, use the cardCss and columnCss callback props.

Default themes

Two themes ship with the package: Willow (light) and WillowDark (dark). Mount one at your app root so every SVAR component beneath it picks up the same visual baseline:

<script setup>
import { Kanban, Willow } from "@svar-ui/vue-kanban";
</script>

<template>
<Willow>
<Kanban :cards="cards" :columns="columns" />
</Willow>
</template>

Kanban board in the default Willow light theme

For dark mode, swap Willow for WillowDark:

<script setup>
import { Kanban, WillowDark } from "@svar-ui/vue-kanban";
</script>

<template>
<WillowDark>
<Kanban :cards="cards" :columns="columns" />
</WillowDark>
</template>

Kanban board in WillowDark theme

One theme wrapper covers the board, editor, toolbar, and context menu - you don't need to wrap each sub-component separately.

Switching theme at runtime

Both theme wrappers are regular Vue components (SFC), so you can switch between them with a conditional:

<script setup>
import { Kanban, Willow, WillowDark } from "@svar-ui/vue-kanban";
import { ref } from "vue";

const dark = ref(false);
</script>

<template>
<WillowDark v-if="dark">
<Kanban :cards="cards" :columns="columns" />
</WillowDark>
<Willow v-else>
<Kanban :cards="cards" :columns="columns" />
</Willow>

<button :onclick="() => (dark = !dark)">Toggle theme</button>
</template>

CSS variables and overrides

Kanban-specific variables

These variables control the board's own surfaces. They all live under the --wx-kanban-* namespace.

VariableControls
--wx-kanban-bgBoard background
--wx-kanban-column-bgColumn background and sticky header
--wx-kanban-column-over-limit-bgColumn header/body when the card limit is exceeded
--wx-kanban-card-bgCard background
--wx-kanban-card-shadowCard resting shadow
--wx-kanban-card-shadow-hoverCard hover shadow
--wx-kanban-card-shadow-dragDrag ghost shadow
--wx-kanban-border-colorColumn header divider and drop placeholder border
--wx-kanban-drop-placeholder-bgDrop-target highlight tint
--wx-kanban-tag-bgTag chip and default priority chip background
--wx-kanban-avatar-bgAvatar fallback background
--wx-kanban-progress-bgProgress bar track
--wx-kanban-progress-fillProgress bar fill
--wx-kanban-priority-low-bgLow-priority chip background
--wx-kanban-priority-low-colorLow-priority chip text
--wx-kanban-priority-medium-bgMedium-priority chip background
--wx-kanban-priority-medium-colorMedium-priority chip text
--wx-kanban-priority-high-bgHigh-priority chip background
--wx-kanban-priority-high-colorHigh-priority chip text
--wx-kanban-scroll-heightInternal: tracks the scroll container height for collapsed columns

The widget also reads several shared SVAR variables directly - override these on the theme wrapper to tune the board alongside other SVAR widgets:

VariableControls
--wx-border-radiusCard corners, drop placeholder corners, priority chip rounding
--wx-radius-majorColumn corner radius
--wx-icon-border-radiusHover-state rounding on column buttons and the card menu button
--wx-color-primaryFocus ring on cards and column buttons
--wx-color-dangerOver-limit counter color
--wx-color-fontPrimary text (card titles, menu hover)
--wx-color-font-altMuted text (counts, deadlines, descriptions, progress labels)
--wx-color-font-disabledDisabled text
--wx-backgroundBase background (used by card background in both themes)
--wx-background-altAlternate background
--wx-background-hoverHover background on column buttons and card menu button
--wx-font-size-smSmall text size (counts, buttons)
--wx-font-weightBase font weight
--wx-font-weight-mdMedium font weight (column titles, over-limit counters)
--wx-avatar-sizeAvatar dimensions
--wx-avatar-font-sizeAvatar fallback text size

Overriding variables

Wrap the board in a container element and set the variables there. This keeps overrides scoped and avoids leaking into other parts of your app:

<template>
<div class="my-board">
<Willow>
<Kanban :cards="cards" :columns="columns" />
</Willow>
</div>
</template>

<style scoped>
.my-board {
--wx-kanban-card-bg: #fffff0;
--wx-kanban-column-bg: #f0f4f8;
--wx-kanban-card-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
--wx-kanban-progress-fill: #10b981;
}
</style>

Per-card and per-column CSS

For styling that depends on runtime data - a card's priority, whether a column is over its limit, progress state - use the cardCss and columnCss callback props. Each callback returns a CSS class name string that the renderer appends to the element's class list.

Card styling

cardCss receives the card object and its parent column. Return one or more class names:

<script setup>
import { Kanban, Willow } from "@svar-ui/vue-kanban";

const cardCss = (card, column) => {
const cls = card.priority === 3 ? "card-high" : "";
const done = (card.progress ?? 0) >= 1 ? "card-complete" : "";
return `${cls} ${done}`.trim();
};
</script>

<template>
<Willow>
<div class="board-wrapper">
<Kanban :cards="cards" :columns="columns" :cardCss="cardCss" />
</div>
</Willow>
</template>

<style scoped>
.board-wrapper :global(.wx-card.card-high) {
border-top: 3px solid #ef4444;
}
.board-wrapper :global(.wx-card.card-complete) {
opacity: 0.6;
background: #f5f5f5;
}
</style>

cardCss doesn't replace a card's static css property - the renderer combines both, so you can set a permanent class on the card data and add dynamic classes at render time.

Column styling

columnCss receives the column's card array and the column view. It works the same way:

<script setup>
import { Kanban, Willow } from "@svar-ui/vue-kanban";

const columnCss = (cards, column) => {
if (column.overLimit) return "col-wip-exceeded";
if (cards.length === 0) return "col-empty";
return "";
};
</script>

<template>
<Willow>
<div class="board-wrapper">
<Kanban :cards="cards" :columns="columns" :columnCss="columnCss" />
</div>
</Willow>
</template>

<style scoped>
.board-wrapper :global(.wx-column.col-wip-exceeded) {
background: #fef2f2;
}
.board-wrapper :global(.wx-column.col-empty) {
opacity: 0.7;
}
</style>

CSS classes reference

These class names are stable and safe to target in custom stylesheets:

ClassElementApplied when
wx-kanbanBoard rootAlways
wx-boardScroll wrapperAlways
wx-scroll-board.wx-boardrender.columnScroll is false
wx-layout-flex.wx-boardrender.fixedColumnWidth is false
wx-columnColumn sectionAlways
wx-collapsed.wx-columnColumn is collapsed
wx-over-limit.wx-columnColumn exceeds its cardLimit
wx-column-headerColumn headerColumn is expanded
wx-column-cardsCard scroll containerAlways
wx-cardCard article elementAlways
wx-card-rowWrapper around each cardAlways
wx-dragging.wx-card-rowCard is being dragged
wx-drop-placeholderDrop hintDrag is over the column
wx-card-priority-lowPriority chipCard priority is Low
wx-card-priority-mediumPriority chipCard priority is Medium
wx-card-priority-highPriority chipCard priority is High
wx-editor-kanbanEditor wrapperAlways