diff --git a/superset-frontend/src/SqlLab/App.jsx b/superset-frontend/src/SqlLab/App.jsx
index 10f1e51bb38c9..82132b79be8bf 100644
--- a/superset-frontend/src/SqlLab/App.jsx
+++ b/superset-frontend/src/SqlLab/App.jsx
@@ -21,7 +21,7 @@ import { createStore, compose, applyMiddleware } from 'redux';
import { Provider } from 'react-redux';
import thunkMiddleware from 'redux-thunk';
import { hot } from 'react-hot-loader/root';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { ThemeProvider } from '@superset-ui/core';
import {
initFeatureFlags,
isFeatureEnabled,
@@ -41,6 +41,7 @@ import setupApp from '../setup/setupApp';
import './main.less';
import '../../stylesheets/reactable-pagination.less';
import '../components/FilterableTable/FilterableTableStyles.less';
+import { theme } from '../preamble';
setupApp();
@@ -109,7 +110,7 @@ if (sqlLabMenu) {
const Application = () => (
-
+
diff --git a/superset-frontend/src/addSlice/App.tsx b/superset-frontend/src/addSlice/App.tsx
index 1e57e171858d8..9602670a08902 100644
--- a/superset-frontend/src/addSlice/App.tsx
+++ b/superset-frontend/src/addSlice/App.tsx
@@ -18,12 +18,13 @@
*/
import React from 'react';
import { hot } from 'react-hot-loader/root';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { ThemeProvider } from '@superset-ui/core';
import setupApp from '../setup/setupApp';
import setupPlugins from '../setup/setupPlugins';
import { DynamicPluginProvider } from '../components/DynamicPlugins';
import AddSliceContainer from './AddSliceContainer';
import { initFeatureFlags } from '../featureFlags';
+import { theme } from '../preamble';
setupApp();
setupPlugins();
@@ -36,7 +37,7 @@ const bootstrapData = JSON.parse(
initFeatureFlags(bootstrapData.common.feature_flags);
const App = () => (
-
+
diff --git a/superset-frontend/src/dashboard/App.jsx b/superset-frontend/src/dashboard/App.jsx
index 336473a64c8d3..da06a0130fa31 100644
--- a/superset-frontend/src/dashboard/App.jsx
+++ b/superset-frontend/src/dashboard/App.jsx
@@ -19,13 +19,14 @@
import { hot } from 'react-hot-loader/root';
import React from 'react';
import { Provider } from 'react-redux';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { ThemeProvider } from '@superset-ui/core';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DynamicPluginProvider } from 'src/components/DynamicPlugins';
import setupApp from '../setup/setupApp';
import setupPlugins from '../setup/setupPlugins';
import DashboardContainer from './containers/Dashboard';
+import { theme } from '../preamble';
setupApp();
setupPlugins();
@@ -33,7 +34,7 @@ setupPlugins();
const App = ({ store }) => (
-
+
diff --git a/superset-frontend/src/explore/App.jsx b/superset-frontend/src/explore/App.jsx
index f0a3db4fe3f3d..e7973fe91f479 100644
--- a/superset-frontend/src/explore/App.jsx
+++ b/superset-frontend/src/explore/App.jsx
@@ -21,7 +21,7 @@ import { hot } from 'react-hot-loader/root';
import { Provider } from 'react-redux';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { ThemeProvider } from '@superset-ui/core';
import { DynamicPluginProvider } from 'src/components/DynamicPlugins';
import ToastPresenter from '../messageToasts/containers/ToastPresenter';
import ExploreViewContainer from './components/ExploreViewContainer';
@@ -29,6 +29,7 @@ import setupApp from '../setup/setupApp';
import setupPlugins from '../setup/setupPlugins';
import './main.less';
import '../../stylesheets/reactable-pagination.less';
+import { theme } from '../preamble';
setupApp();
setupPlugins();
@@ -36,7 +37,7 @@ setupPlugins();
const App = ({ store }) => (
-
+
diff --git a/superset-frontend/src/preamble.ts b/superset-frontend/src/preamble.ts
index 31d547dff0747..8fad06599e732 100644
--- a/superset-frontend/src/preamble.ts
+++ b/superset-frontend/src/preamble.ts
@@ -19,7 +19,8 @@
import { setConfig as setHotLoaderConfig } from 'react-hot-loader';
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
import moment from 'moment';
-import { configure } from '@superset-ui/core';
+import { configure, supersetTheme } from '@superset-ui/core';
+import { merge } from 'lodash';
import setupClient from './setup/setupClient';
import setupColors from './setup/setupColors';
import setupFormatters from './setup/setupFormatters';
@@ -57,3 +58,8 @@ setupColors(
// Setup number formatters
setupFormatters();
+
+export const theme = merge(
+ supersetTheme,
+ bootstrapData?.common?.theme_overrides ?? {},
+);
diff --git a/superset-frontend/src/profile/App.tsx b/superset-frontend/src/profile/App.tsx
index 8fe5754dd0228..8774d63aeb5a5 100644
--- a/superset-frontend/src/profile/App.tsx
+++ b/superset-frontend/src/profile/App.tsx
@@ -21,13 +21,13 @@ import { hot } from 'react-hot-loader/root';
import thunk from 'redux-thunk';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import { Provider } from 'react-redux';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { ThemeProvider } from '@superset-ui/core';
import App from './components/App';
import messageToastReducer from '../messageToasts/reducers';
import { initEnhancer } from '../reduxUtils';
import setupApp from '../setup/setupApp';
-
import './main.less';
+import { theme } from '../preamble';
setupApp();
@@ -46,7 +46,7 @@ const store = createStore(
const Application = () => (
-
+
diff --git a/superset-frontend/src/views/App.tsx b/superset-frontend/src/views/App.tsx
index 3d45d261af7dc..b0c54fe30a93e 100644
--- a/superset-frontend/src/views/App.tsx
+++ b/superset-frontend/src/views/App.tsx
@@ -24,7 +24,7 @@ import { Provider as ReduxProvider } from 'react-redux';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import { QueryParamProvider } from 'use-query-params';
import { initFeatureFlags } from 'src/featureFlags';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { ThemeProvider } from '@superset-ui/core';
import { DynamicPluginProvider } from 'src/components/DynamicPlugins';
import ErrorBoundary from 'src/components/ErrorBoundary';
import Menu from 'src/components/Menu/Menu';
@@ -47,6 +47,7 @@ import setupApp from '../setup/setupApp';
import setupPlugins from '../setup/setupPlugins';
import Welcome from './CRUD/welcome/Welcome';
import ToastPresenter from '../messageToasts/containers/ToastPresenter';
+import { theme } from '../preamble';
setupApp();
setupPlugins();
@@ -68,7 +69,7 @@ const store = createStore(
const App = () => (
-
+
diff --git a/superset-frontend/src/views/menu.tsx b/superset-frontend/src/views/menu.tsx
index 63a920327b314..887b5b76d4dde 100644
--- a/superset-frontend/src/views/menu.tsx
+++ b/superset-frontend/src/views/menu.tsx
@@ -18,15 +18,16 @@
*/
import React from 'react';
import ReactDOM from 'react-dom';
-import { supersetTheme, ThemeProvider } from '@superset-ui/core';
+import { ThemeProvider } from '@superset-ui/core';
import Menu from 'src/components/Menu/Menu';
+import { theme } from '../preamble';
const container = document.getElementById('app');
const bootstrapJson = container?.getAttribute('data-bootstrap') ?? '{}';
const bootstrap = JSON.parse(bootstrapJson);
const menu = { ...bootstrap.common.menu_data };
const app = (
-
+
);
diff --git a/superset/config.py b/superset/config.py
index d7c8d5ef86802..f320f2df3c4bb 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -373,7 +373,7 @@ def _try_json_readsha( # pylint: disable=unused-argument
# EXTRA_CATEGORICAL_COLOR_SCHEMES is used for adding custom categorical color schemes
# example code for "My custom warm to hot" color scheme
-# EXTRA_CATEGORICAL_COLOR_SCHEMES = [
+# EXTRA_CATEGORICAL_COLOR_SCHEMES = [
# {
# "id": 'myVisualizationColors',
# "description": '',
@@ -382,11 +382,29 @@ def _try_json_readsha( # pylint: disable=unused-argument
# ['#006699', '#009DD9', '#5AAA46', '#44AAAA', '#DDAA77', '#7799BB', '#88AA77',
# '#552288', '#5AAA46', '#CC7788', '#EEDD55', '#9977BB', '#BBAA44', '#DDCCDD']
# }]
-#
# This is merely a default
EXTRA_CATEGORICAL_COLOR_SCHEMES: List[Dict[str, Any]] = []
+# THEME_OVERRIDES is used for adding custom theme to superset
+# example code for "My theme" custom scheme
+# THEME_OVERRIDES = {
+# "borderRadius": 4,
+# "colors": {
+# "primary": {
+# "base": 'red',
+# },
+# "secondary": {
+# "base": 'green',
+# },
+# "grayscale": {
+# "base": 'orange',
+# }
+# }
+# }
+
+THEME_OVERRIDES: Dict[str, Any] = {}
+
# EXTRA_SEQUENTIAL_COLOR_SCHEMES is used for adding custom sequential color schemes
# EXTRA_SEQUENTIAL_COLOR_SCHEMES = [
# {
diff --git a/superset/views/base.py b/superset/views/base.py
index 267bdb8ab614f..69c9383ffccc1 100644
--- a/superset/views/base.py
+++ b/superset/views/base.py
@@ -322,6 +322,7 @@ def common_bootstrap_payload() -> Dict[str, Any]:
"feature_flags": get_feature_flags(),
"extra_sequential_color_schemes": conf["EXTRA_SEQUENTIAL_COLOR_SCHEMES"],
"extra_categorical_color_schemes": conf["EXTRA_CATEGORICAL_COLOR_SCHEMES"],
+ "theme_overrides": conf["THEME_OVERRIDES"],
"menu_data": menu_data(),
}