You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by jl...@apache.org on 2026/04/09 23:40:43 UTC
(superset) 01/01: fix(embedded): prevent OS dark mode from applying dark borders in light-mode embedded dashboards
This is an automated email from the ASF dual-hosted git repository.
jli pushed a commit to branch sc-100647-dark-border-embedded-dark-mode
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 442b3fb0f74983aafe6252b5772e2093ebd53253
Author: sadpandajoe <jc...@gmail.com>
AuthorDate: Thu Apr 9 23:40:30 2026 +0000
fix(embedded): prevent OS dark mode from applying dark borders in light-mode embedded dashboards
When the OS is in dark mode, browsers apply dark native styling (scrollbars,
form control borders, etc.) to iframes unless the page explicitly declares its
color-scheme. Without this declaration, embedded dashboards using Superset's
default light theme still rendered with dark borders/lines on macOS dark mode.
Fix: set `color-scheme: light` (or `dark` for dark themes) on the `html`
element via GlobalStyles, using the existing `isThemeDark` utility to
determine the correct value from the active Ant Design theme tokens.
Closes sc-100647
Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
---
.../superset-core/src/theme/GlobalStyles.test.tsx | 87 ++++++++++++++++++++++
.../superset-core/src/theme/GlobalStyles.tsx | 13 +++-
2 files changed, 99 insertions(+), 1 deletion(-)
diff --git a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.test.tsx b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.test.tsx
new file mode 100644
index 00000000000..d8a0822bc88
--- /dev/null
+++ b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.test.tsx
@@ -0,0 +1,87 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import React from 'react';
+import { render } from '@testing-library/react';
+import { ThemeProvider } from '@emotion/react';
+import { Theme } from './Theme';
+import { GlobalStyles } from './GlobalStyles';
+import { ThemeAlgorithm } from './types';
+import * as themeUtils from './utils/themeUtils';
+
+// Mock emotion's cache to avoid actual DOM operations
+jest.mock('@emotion/cache', () => ({
+ __esModule: true,
+ default: jest.fn().mockReturnValue({}),
+}));
+
+beforeEach(() => {
+ jest.clearAllMocks();
+});
+
+test('GlobalStyles calls isThemeDark with the current theme to determine color-scheme', () => {
+ const isThemeDarkSpy = jest.spyOn(themeUtils, 'isThemeDark');
+ const lightTheme = Theme.fromConfig({});
+
+ render(
+ <ThemeProvider theme={lightTheme.theme}>
+ <GlobalStyles />
+ </ThemeProvider>,
+ );
+
+ expect(isThemeDarkSpy).toHaveBeenCalledWith(lightTheme.theme);
+ expect(isThemeDarkSpy).toHaveReturnedWith(false);
+});
+
+test('GlobalStyles detects dark theme via isThemeDark', () => {
+ const isThemeDarkSpy = jest.spyOn(themeUtils, 'isThemeDark');
+ const darkTheme = Theme.fromConfig({ algorithm: ThemeAlgorithm.DARK });
+
+ render(
+ <ThemeProvider theme={darkTheme.theme}>
+ <GlobalStyles />
+ </ThemeProvider>,
+ );
+
+ expect(isThemeDarkSpy).toHaveBeenCalledWith(darkTheme.theme);
+ expect(isThemeDarkSpy).toHaveReturnedWith(true);
+});
+
+test('GlobalStyles renders without errors for light theme', () => {
+ const lightTheme = Theme.fromConfig({});
+
+ expect(() => {
+ render(
+ <ThemeProvider theme={lightTheme.theme}>
+ <GlobalStyles />
+ </ThemeProvider>,
+ );
+ }).not.toThrow();
+});
+
+test('GlobalStyles renders without errors for dark theme', () => {
+ const darkTheme = Theme.fromConfig({ algorithm: ThemeAlgorithm.DARK });
+
+ expect(() => {
+ render(
+ <ThemeProvider theme={darkTheme.theme}>
+ <GlobalStyles />
+ </ThemeProvider>,
+ );
+ }).not.toThrow();
+});
diff --git a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
index 33a30a9fa61..bcdcc8a8697 100644
--- a/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
+++ b/superset-frontend/packages/superset-core/src/theme/GlobalStyles.tsx
@@ -29,13 +29,24 @@ import '@fontsource/ibm-plex-mono/600.css';
/* eslint-enable import/extensions */
import { css, useTheme, Global } from '@emotion/react';
+import type { SupersetTheme } from './types';
+import { isThemeDark } from './utils/themeUtils';
export const GlobalStyles = () => {
- const theme = useTheme();
+ const theme = useTheme() as SupersetTheme;
+ const isDark = isThemeDark(theme);
return (
<Global
key={`global-${theme.colorLink}`}
styles={css`
+ // Explicitly declare color-scheme so the browser uses matching native
+ // styling (scrollbars, form controls, etc.) rather than inheriting the
+ // OS dark-mode preference. Without this, an embedded iframe renders
+ // dark borders/scrollbars even when Superset's theme is light.
+ html {
+ color-scheme: ${isDark ? 'dark' : 'light'};
+ }
+
// SPA
html,
body,