Skip to main content
Version: Next

Theming and branding

The OpenChoreo light and dark themes ship with the plugins. Any Backstage app that installs @openchoreo/backstage-plugin gets them registered automatically, so OpenChoreo surfaces render against the palette they were designed for instead of the stock Backstage one.

Available from 1.4.0

Before 1.4.0 the themes lived in the unpublished portal package. An external Backstage app on 1.3.x renders OpenChoreo surfaces against the stock Backstage palette, and app.branding has no effect there.

What gets registered​

openChoreoAppModule registers both themes as ThemeBlueprint extensions:

Extension idTheme
theme:app/openchoreo-lightOpenChoreo Light
theme:app/openchoreo-darkOpenChoreo Dark

They are additive — your own themes stay in the picker, so users see both sets. Backstage's stock themes are theme:app/light and theme:app/dark; if you want only the OpenChoreo ones, switch the stock pair off (the OpenChoreo portal does exactly this):

app-config.yaml
app:
extensions:
- theme:app/light: false
- theme:app/dark: false

Either OpenChoreo theme can be turned off the same way — see Feature discovery.

Re-branding through config​

app.branding lets you re-brand without a fork or a rebuild. Every value is optional; omit the block entirely for the stock OpenChoreo look.

app-config.yaml
app:
branding:
name: Acme Platform
iconLogo: "data:image/svg+xml;base64,..."
fullLogo: "data:image/svg+xml;base64,..."
theme:
light:
primaryColor: "#0d9488"
dark:
primaryColor: "#2dd4bf"

What applies where matters. theme.light.primaryColor and theme.dark.primaryColor apply anywhere the OpenChoreo themes are registered, including your own Backstage app — they drive the primary palette, links, sidebar selection, header gradients, and graph accents. name, iconLogo, and fullLogo drive the OpenChoreo portal's sidebar and sign-in card only; a host that supplies its own app shell will not see them change anything.

This block is unrelated to app.title (the browser window title) and organization.name (Backstage's org components). There is deliberately no fallback chaining between them, so set whichever you actually mean.

Accepted color formats​

Hex with a leading # ("#0d9488"), or fully-opaque rgb() / rgba() / hsl() / hsla() with comma-separated components. Not supported: named colors ("teal"), space-separated CSS Color 4 syntax, or any translucent value with alpha below 1. An invalid value is ignored with a console warning and the stock palette is kept, so a typo degrades rather than breaking the app.

Contrast​

Parts of the accent render as text, so pick something readable:

  • Light theme — aim for at least 4.5:1 against white (WCAG AA). The header also fades the accent toward white under white text, which needs at least 3:1 at the lightened stop.
  • Dark theme — aim for at least 4.5:1 against the dark page background (#0f1117). Dark header gradients keep the default navy ramp in this version.

Colors below those thresholds log a console warning rather than failing.

Logos​

iconLogo is the square mark for the collapsed sidebar, rendered at 24px height. fullLogo is for the expanded sidebar, capped at 32px tall and 180px wide, and when set it replaces the icon-plus-wordmark row entirely. Both are used verbatim as an img src, so an absolute URL or a data URI both work — prefer data URIs, because a remote URL may require extending backend.csp img-src in production.

Consuming the theme directly​

@openchoreo/backstage-design-system exports the built appThemes entries alongside useBranding, brandName, readBrandingConfig, and DEFAULT_BRAND_NAME, so you can read the same branding values in your own components. You do not need to install the package explicitly to get the themes — it is a dependency of @openchoreo/backstage-plugin — only to import from it.