Theming

A theme is a set of chrome tokens applied with one modifier. It never touches what's inside a card.

Where the boundary is

AdvancedKanban renders chrome — the card's background, border, corner radius, shadow; the column's background and corner radius; the WIP-warning color; the drag ghost's opacity and the drop animation. It does not render anything inside a card or a column header — that's your cardContent and columnHeader view builders, and it stays that way deliberately: a fixed card schema is the most common complaint in Kanban component libraries, and typography/layout choices belong to your app's design system, not the package's.

The one thing the theme can't do for you

Because content is entirely yours, text color inside a card is your responsibility — not the theme's. If your card content uses .primary and your theme sets a fixed light cardBackground under a dark system appearance, you'll get white-on-white. Give your card content an explicit color that's paired with whatever background your theme actually renders, the same way you'd theme any other view.

Applying a theme

KanbanBoard(/* ... */)
    .kanbanTheme(KanbanTheme(
        columnBackground: .gray.opacity(0.08),
        cardBackground: .white,
        cardBorder: .gray.opacity(0.2),
        cardCornerRadius: 12,
        cardSpacing: 8,
        columnWidth: 280,
        columnCornerRadius: 16,
        wipLimitWarningColor: .orange,
        dragGhostOpacity: 0.85,
        dropAnimation: .spring(response: 0.35, dampingFraction: 0.8)
    ))

Read from @Environment(\.kanbanTheme) anywhere below the modifier if your own views want to match — it's a plain SwiftUI environment value, not something internal to the package.

KanbanTheme reference

FieldTypeDefault
columnBackgroundColorSystem secondary background
cardBackgroundColorSystem background
cardBorderColor.gray.opacity(0.25)
cardCornerRadiusCGFloat10
cardSpacingCGFloat8
columnWidthCGFloat280
columnCornerRadiusCGFloat14
wipLimitWarningColorColor.orange
dragGhostOpacityDouble0.85
dropAnimationAnimation.spring(response: 0.35, dampingFraction: 0.8)

KanbanTheme.default is applied automatically if you never call .kanbanTheme(_:) — the values above are exactly what it uses.

Switching themes at runtime

Since .kanbanTheme(_:) is a plain modifier, driving it from @State just works — no special API:

@State private var highContrast = false

KanbanBoard(/* ... */)
    .kanbanTheme(highContrast ? .highContrastTheme : .default)