Combinations and light variants

Combinations are the framework's primary colour contract. Every theme-aware component — badge, box, button, alert, card, toast, dropdown, modal, navbar, progress — paints itself by reaching into the same shared map of named (background, foreground) tuples. The modifier classes (.is-primary, .is-warning, .is-danger, .is-success) are the consumer-facing surface of that contract; $default-combination, $enable-light, and the runtime --kc-<combo>-* tokens are the plumbing behind them.

This page exists so the same explanation does not have to repeat across every component reference. Each component's ## Variables table carries a footnote pointing back here for the cross-cutting knobs.

$color-combinations — the global map

The shared roster of combinations lives in src/variables/_color.scss as $combinations. Each entry is a (bg, fg) tuple drawn from the shipped palette:

$combinations: (
  default: (map.get($palette, white),   map.get($palette, black)),
  primary: (map.get($palette, emerald), map.get($palette, white)),
  warning: (map.get($palette, amber),   map.get($palette, white)),
  danger:  (map.get($palette, coral),   map.get($palette, white)),
  success: (map.get($palette, moss),    map.get($palette, white)),
) !default;

The first key (default) is the framework-wide "at-rest" tuple and feeds every component's $default-combination. The remaining keys are consumed by combinations-apply() and emit per-modifier rules (.button.is-primary, .card.is-warning, …) plus per-theme --kc-*-bg / --kc-*-fg tokens via theme/_create.scss. Themes can override individual entries (the dark theme ships darker tuples) — see the per-theme contract below.

$default-combination — the at-rest surface paint

Every theme-aware component declares its own $default-combination with the same shape:

$default-combination: variables.$color-combination-default !default;

It is the (bg, fg) tuple applied to a bare .button, .card, or .toast — anything that lacks a .is-<combo> modifier. Three places to re-tune it:

  • Per-component override at @use ... with (...) time when only one component should diverge.
  • Globally by retuning variables.$color-combinations once and letting every component's default follow.
  • Per-theme by shipping a $combinations override; the runtime tokens flip on theme switch, no recompile needed.

Components never reach back into the map directly to read the default tuple; they always go through $default-combination. That indirection is what lets consumers shadow the default without touching the global map.

$enable-light — the soft-tint companion flag

Every theme-aware component also carries:

$enable-light: variables.$feature-light-variants !default;

When the framework-wide flag is on (the default), each component emits .is-<combo>.is-light selectors that paint the surface using the soft-tint tuples — --kc-<combo>-light-bg / --kc-<combo>-light-fg emitted by theme/_create.scss. The light variant preserves the semantic hue while lowering the contrast intensity, useful for body-copy-dense surfaces where the full-strength brand colour would fight the prose (think soft warning callouts, inline danger summaries, desaturated info toasts).

Strip the variants per-component by overriding $enable-light: false at @use time, or globally by setting variables.$feature-light-variants: false to drop the rules from every component at once.

Per-theme contract

theme/_create.scss validates the colour contract for every theme. Each theme ships a $combinations map; missing keys are filled from the framework defaults and @warn is logged so the consumer is told which tuples they would inherit. The dark theme demonstrates the override pattern — it ships darker (bg, fg) pairs so combinations read correctly against a near-black page surface — but a partial map is the supported, lower-effort path for consumer themes.

The light-variant tokens (--kc-<combo>-light-bg / --kc-<combo>-light-fg) are derived per theme so the soft tint always tracks the active page surface rather than baking a single mix at SCSS-compile time.

Why these are not in per-component variable tables

$enable-light, $default-combination, and $controls (the reveal-mechanism flag shared by modal, navbar, dropdown, and future stateful components) are intentionally absent from each component's ## Variables table. Three reasons:

  1. Identical default across every component. The row would say variables.$color-combination-default next to every component, with identical override semantics. Per-component rows would multiply the same row ~15 times.
  2. They derive from cross-cutting framework primitives. A consumer rebinding variables.$color-combinations once retunes every component automatically. The override surface is the global map, not the per-component knob.
  3. Documenting them per-component is structurally misleading. It suggests the per-component value carries product-specific semantics ("the button's notion of default"), when it is really just the component's pointer into the shared roster.

The † see Combinations and light variants footnote under each component's variable table is the load-bearing UX: it routes consumers to the canonical surface (this page) when they need to re-tune the contract.

Further reading

  • Variable layers — the declarative / semantic / runtime split that combinations live inside.
  • Architecture overview — the SCSS module graph that keeps _variables.scss files leaf-only.
  • --kc-* token reference — the complete roster of runtime tokens, including the per-combination --kc-<combo>-bg / --kc-<combo>-fg and --kc-<combo>-light-bg / --kc-<combo>-light-fg pairs.