Skip to content

Theming

cartapel ships dark-first with a clean default look, and everything visual is overridable from one theme { } block in config/cartapel.hcl. No CSS files, no rebuild — it hot-reloads like the rest of the config.

Basics

Pick a preset and an accent, and you're done:

hcl
brand      = "Acme Admin"
brand_logo = "logo.svg"        # a bundle asset (served from /static), URL or data URL

theme {
  preset = "cartapel"           # "cartapel" (default) | "django"
  accent = "hsl(33 100% 50%)"  # any CSS color
  mode   = "auto"              # "light" | "dark" | "auto" (default)
}
KeyWhat it does
presetNamed base theme. cartapel is the default dark look; django mimics the classic Django-admin palette.
accentThe highlight color — links, active states, focus rings.
accent_btnPrimary-button color when you want it different from accent.
modeForce light or dark, or follow the visitor's OS with auto.
logo_light / logo_darkPer-mode brand logo, overriding brand_logo for that mode.

Advanced: design tokens

Every color in the UI is a CSS token you can override per mode with the light { } / dark { } maps. Keys are token names without the -- prefix:

hcl
theme {
  dark = {
    page      = "#0b0e14"      # app background
    surface   = "#11151d"      # cards, panels
    accent    = "#7c9cf5"
    good      = "#2fbf71"
    critical  = "#e5484d"
  }
  light = {
    page = "#fafafa"
  }
}

The token vocabulary:

TokenRole
pageApp background.
surface, surface-1surface-3Card / panel layers, lightest to most elevated.
ink, sec, mutedText: primary, secondary, de-emphasized.
accent, accent-btn, accent-btn-inkHighlight color, primary buttons, their text (defaults to white).
border, gridline, hover, press, selectedLines and interaction states.
good, warning, serious, criticalSemantic status colors (alerts, cell color rules).
delta-goodFavorable-delta color on stat tiles.
band, band-ink, band-borderThe table header band. Defaults track surface/ink/border; the django preset colors them.
s1s8Chart series palette.
badge-blue, badge-green, badge-orange, badge-red, badge-violet, badge-grayEnum badge colors.

Any key you put in a light { } / dark { } map is emitted as --<key> — the vocabulary above is what the built-in UI consumes, but you can define extra variables for your own custom pages the same way.

Custom pages and widgets built with the sx SDK consume the same tokens (var(--accent), var(--surface-2), …), so they restyle together with the rest of the panel.

Recipes

Match your product's brand — set accent (and accent_btn if your brand color is too light for white button text).

A light-only internal toolmode = "light" plus a light { } map.

The Django lookpreset = "django" gives the familiar dark-header, light-body admin; layer accent/tokens on top as usual.

Released under the MIT License.