The sibling of Handlebars chart templates and Power BI templates for the CSS themes an Apache Superset or HTML dashboard is painted with. Until 2026-09-11 these were seeded files and nothing else; they are now a library users write to, on the same contract as the other three.
| Apache Superset theme | |
|---|---|
| What it is | A CSS body plus a chart colour scheme, applied to the whole dashboard — and, when it has colour tokens, the palette a design draws with on every target |
| Where it goes | Apache Superset v6: the dashboard's css and metadata.color_scheme. The HTML exporter styles its own page from the same row; with a design, HTML, Superset and Power BI all read its tokens (HTML dashboard §5a) |
| Built-ins | The 40 themes seeded from *, plus 14 palettes without a stylesheet (raster-*) |
| Who can add one | Everyone, in Libraries → Colours |
| Who can edit it | Its author. A built-in answers 409 "copy it instead" |
| Who can use it | Everyone, once it is Public; its author alone while it is Private |
| Limit | 10 per user |
Authoring a theme is free: it runs no AI and consumes no coins.
1. Private or Public
A theme you create starts Private — creating is not publishing. Nobody else lists it, gets it by name, or can start a run with it. Publish it and everyone can; it stays yours to change or delete, and a colleague's PUT still answers 404.
Two consequences worth knowing:
- A blank
style=deals a published theme at random. A private theme is never dealt to anyone, its author included: "surprise me" means from the shared catalogue. - Taking a theme private does not break a finished run. A job records the theme it used by name and re-exports with it whatever the flag says. Deleting the theme is the one case that changes anything, and then the re-export is simply unstyled — which is what an absent theme has always meant.
2. Writing one
Libraries → Colours → New theme. The editor gives you a starter skeleton over the handful of Apache Superset selectors every shipped theme paints, and a live preview beside it.
- Name, description, when to use it, keywords — what the library lists and searches.
- Chart series colours — one of the eleven schemes the platform knows. This is what Apache Superset itself uses for series colours, so it is a closed list: an unknown id would preview one way here and render another way there, and is refused.
- Colours — fifteen named colours (
bg,surface,surface2,text,textMuted,accent,accentDeep,onAccent,border,borderStrong,selected,positive,negative,noticeBg,noticeText), each a CSS colour. They are what an HTML design (HTML dashboard §5a) reads instead of your CSS: with a design, the theme contributes its colours and its series palette and nothing else. Leave them out and the theme is classic only — it works as it always has for Apache Superset and for a classic HTML file, but the design picker refuses to combine it with a design. All fifteen or none: a partial set is refused naming the missing key. Two more are optional:neutral, the warning colour, and eight chart series coloursseries.0…series.7(all eight or none). Without them the series come from the colour scheme above andneutralis a fixed amber — what every target drew before they existed. With them, an HTML file and a Power BI report draw the palette's own series; Apache Superset keeps the scheme, because a bundle can only name a scheme Superset has registered. - Stylesheet — self-contained only, and optional once the colours are given. A theme with colours and no stylesheet is colours only: it serves a design on every target, and a run without a design refuses it (
422 theme-not-classic), because there is nothing to draw it with.
What the CSS may not contain
Both rules are enforced by StyleContentRules, on the seeder and on the write API alike, so a classpath file refused at boot and a body refused at save time are refused in the same words.
- No
@import, and nourl()pointing at a server. Everything this platform generates has to work with no network. Use font families that are installed locally, as the 40 shipped themes do. - No
<. The body is inlined into a<style>element by the preview, where that character would end the element early.
Plus the sizes: 32 768 characters of CSS, 96 for the name, 255 for the description and keywords, 1000 for the when-to-use text.
3. Copying
Copy is on every row, built-ins included, and is the answer to "I want to tweak one of the 40". It pre-fills the editor with the body and fields, clears the identity, and saves as a new theme of yours — always Private to begin with.
4. Shipping one with the platform
Drop a .css file in <name> and give it an entry in DashboardStyleSeeder's CATALOG (description and colour scheme). The seeder upserts on the next boot, keyed by file name, never touches the enabled flag, and stamps PUBLIC every time — a shipped theme is everyone's, and an operator's stray UPDATE must not be able to hide one.
It also needs <name> beside it: the fifteen colours above, read off the CSS body (the PALETTE (reference) comment is a hint, the selectors are the truth). A shipped theme without a tokens file fails the engine's startup naming the theme — a catalogue in which some themes combine with a design and some silently do not is worse than none. The 40 files were authored under one rule: bg/text from .dashboard, surface and border from the chart holder, surface2 from the chart header, textMuted from th, accent from the input's hover border and accentDeep from its focus border, onAccent from the primary button, selected from the row hover, borderStrong as border mixed 35 % toward text, positive/negative as #2A9D5C/#D64545 (lightened on a dark theme), the notice as surface tinted 18 % toward accent.
The palettes without a stylesheet ship the same way, minus the CSS: a tokens file raster-<name> with all fifteen colours, neutral and the eight series, and an entry in DashboardStyleSeeder's PALETTES (display name, description, colour scheme). Their names are raster-* without the number, so they never collide with the Power BI Dashboard-Templates raster-NN-*.
A seeded theme carries no display name: the browser labels it from its file name, as it always has. Only user themes need one.
5. REST
GET /api/v1/dashboards/styles the library (no css bodies)
GET /api/v1/dashboards/styles/{name} one theme incl. css (ETag/304)
POST /api/v1/dashboards/styles create — you become its author
PUT /api/v1/dashboards/styles/{name} update your own
DELETE /api/v1/dashboards/styles/{name} delete your own
Studio proxies all five unmetered and stamps the author name from the token. On the engine, X-User-Id is optional on reads — a call without it sees the built-ins and every published theme, and nothing is editable — and required on writes, which answer 400 without it.
Without an account, two reads are open on the guest prefix: GET /api/v1/guest/dashboards/styles (the list) and GET /api/v1/guest/dashboards/styles/{name} (one theme with its CSS, for the preview). Both send no identity, so they see only the built-ins and published themes — the same CSS a guest's dashboard carries once that theme is chosen. A private theme answers 404 there.
Somebody else's theme answers 404, never 403: a name must not confirm to a non-author that it exists. A built-in answers 409 template-not-editable.
DASH:Gen