Dashboard CSS#
Dashboard CSS is a per-dashboard feature that lets editors inject arbitrary CSS into a running dashboard without affecting Superset's global UI chrome (navbar, sidebar, etc.). The CSS string is stored in the dashboards database table, surfaced through the REST API, and injected into the browser <head> only while that dashboard is mounted.
Storage#
The css column on the Dashboard model is a MediumText field — separate from json_metadata, position_json, and all other dashboard configuration. It is part of export_fields , so it round-trips correctly through dashboard export/import (both v0 JSON and v1 YAML formats).
The API schema describes the field simply as "Override CSS for the dashboard." . It appears as an optional String in DashboardPostSchema, DashboardPutSchema, DashboardCopySchema, and ImportV1DashboardSchema , and is returned in every DashboardGetResponseSchema response .
The Flask-AppBuilder legacy admin view also exposes css as an editable column in DashboardMixin, with the description: "The CSS for individual dashboards can be altered here, or in the dashboard view where changes are immediately visible" .
The data property on the model includes css in the dict returned to the frontend .
Editing in the UI#
In dashboard edit mode, the Edit CSS menu item (visible only in editMode) opens the CssEditor modal. The modal wraps an Ace editor (AceCssEditor) and:
- Fires
onChange(css)on every keystroke, which propagates the new CSS up to the dashboard's Redux store viadashboardInfoChanged({ css }). - Provides a Load a CSS template dropdown that fetches available templates from
GET /api/v1/css_template/and populates the editor on selection .
CSS changes previewed in the editor take effect immediately in the live dashboard (before saving), because the injection is driven by Redux state, not by the persisted DB value.
DOM Injection & Isolation#
Custom CSS is applied by injectCustomCss — a small utility in superset-frontend/src/dashboard/util/injectCustomCss.ts. It:
- Creates (or reuses) a
<style>element withclassName = 'CssEditor-css'. - Sets
style.innerHTML = css(with a legacy fallback viastyleSheet.cssText). - Appends the element to
<head>last, ensuring it overrides all other stylesheets . - Returns a cleanup function (
style.remove()) that is called when the dashboard unmounts .
The call site is in DashboardPage inside a useEffect that depends on the css value read from Redux state (state.dashboardInfo.css) with a fallback to dashboard?.css from the API response . When the CSS string changes or is empty, the effect re-runs: the old style element is removed and a new one is injected.
Isolation caveat: Because the <style> tag is appended to <head>, custom CSS rules apply globally to the entire document — they are not automatically scoped to the dashboard DOM subtree. Authors must prefix their selectors manually (e.g. .dashboard-grid .my-widget { … }) if they want to avoid bleeding into the navigation chrome. The dashboard grid root renders inside .dashboard-grid and the tab content inside .dashboard-component-tabs-content, which are the natural scoping ancestors to reference.
Lifecycle Summary#
DB (dashboards.css)
└─► GET /api/v1/dashboard/{id} → dashboardInfo.css in Redux
└─► DashboardPage useEffect → injectCustomCss(css)
└─► <style class="CssEditor-css"> injected into <head>
Edit mode:
CssEditor onChange → dashboardInfoChanged({ css }) → Redux → same useEffect re-fires
SaveDashboard → PUT /api/v1/dashboard/{id} { css } → persists to DB
Key Files#
| File | Purpose |
|---|---|
superset/models/dashboard.py | css column definition (MediumText) |
superset/dashboards/schemas.py | API schema field for css across all CRUD operations |
superset/views/dashboard/mixin.py | Legacy FAB admin field exposure and description |
superset-frontend/src/dashboard/components/CssEditor/index.tsx | Ace-based CSS editor modal with template loading |
superset-frontend/src/dashboard/util/injectCustomCss.ts | DOM injection: creates <style class="CssEditor-css"> in <head> |
superset-frontend/src/dashboard/containers/DashboardPage.tsx | Calls injectCustomCss, wires Redux CSS state to DOM |