diff --git a/apps/design-system/components/command-menu.tsx b/apps/design-system/components/command-menu.tsx index e4731f2a055..1c1c0adebdf 100644 --- a/apps/design-system/components/command-menu.tsx +++ b/apps/design-system/components/command-menu.tsx @@ -108,10 +108,6 @@ export function CommandMenu({ ...props }: DialogProps) { Dark - runCommand(() => setTheme('classic-dark'))}> - - Classic dark - runCommand(() => setTheme('system'))}> System diff --git a/apps/design-system/content/docs/theming.mdx b/apps/design-system/content/docs/theming.mdx index 91b144803c9..f5472815a76 100644 --- a/apps/design-system/content/docs/theming.mdx +++ b/apps/design-system/content/docs/theming.mdx @@ -5,11 +5,10 @@ description: Themes used in Supabase Design System currently takes into account varying themes. -Themes currently in development: +Available themes: - Light -- Dark (Classic dark) -- Deep dark +- Dark We also support a system theme, which will automatically switch between light and dark themes based on the user's system settings. diff --git a/apps/design-system/registry/default/example/radio-group-card-with-children.tsx b/apps/design-system/registry/default/example/radio-group-card-with-children.tsx index 74a9b98ed9a..78718460846 100644 --- a/apps/design-system/registry/default/example/radio-group-card-with-children.tsx +++ b/apps/design-system/registry/default/example/radio-group-card-with-children.tsx @@ -6,7 +6,6 @@ import { BASE_PATH } from '@/lib/constants' export default function RadioGroupDemo() { const singleThemes = [ { name: 'Dark', value: 'dark' }, // Classic Supabase dark - { name: 'Classic dark', value: 'classic-dark' }, // Deep Dark Supabase dark { name: 'Light', value: 'light' }, // Classic Supabase light { name: 'System', value: 'system' }, // Classic Supabase light ] as const diff --git a/apps/design-system/styles/globals.css b/apps/design-system/styles/globals.css index 0a55d08da3e..e1f56ecff2f 100644 --- a/apps/design-system/styles/globals.css +++ b/apps/design-system/styles/globals.css @@ -1,5 +1,4 @@ @import 'config/tailwind.config.css'; -@import './../../../packages/ui/build/css/themes/classic-dark.css'; @import 'config/typography.css'; @source '../app/**/*.{js,ts,jsx,tsx}'; diff --git a/apps/docs/styles/globals.css b/apps/docs/styles/globals.css index 58ba8ace980..2a39bcb51c0 100644 --- a/apps/docs/styles/globals.css +++ b/apps/docs/styles/globals.css @@ -1,5 +1,4 @@ @import 'config/tailwind.config.css'; -@import './../../../packages/ui/build/css/themes/faux-classic-dark.css'; @import './code-block.css'; @import './reference.css'; diff --git a/apps/learn/components/command-menu.tsx b/apps/learn/components/command-menu.tsx index 597f5a00fce..75ae67665a5 100644 --- a/apps/learn/components/command-menu.tsx +++ b/apps/learn/components/command-menu.tsx @@ -96,10 +96,6 @@ export function CommandMenu({ ...props }: DialogProps) { Dark - runCommand(() => setTheme('classic-dark'))}> - - Classic dark - runCommand(() => setTheme('system'))}> System diff --git a/apps/learn/styles/globals.css b/apps/learn/styles/globals.css index a0fc87d4f3f..bdb2f8d98c6 100644 --- a/apps/learn/styles/globals.css +++ b/apps/learn/styles/globals.css @@ -1,5 +1,4 @@ @import 'config/tailwind.config.css'; -@import './../../../packages/ui/build/css/themes/classic-dark.css'; @source '../app/**/*.{ts,tsx}'; @source './../../../packages/ui/src/**/*.{tsx,ts,js}'; @@ -173,8 +172,7 @@ --sidebar-ring: hsl(0 0% 70.8%); } -[data-theme='dark'] .preview, -[data-theme='classic-dark'] .preview { +[data-theme='dark'] .preview { --background: hsl(0 0% 14.5%); --foreground: hsl(0 0% 98.5%); --card: hsl(0 0% 0%); diff --git a/apps/lite-studio/styles/globals.css b/apps/lite-studio/styles/globals.css index d468bbe9fef..9262c2c4c46 100644 --- a/apps/lite-studio/styles/globals.css +++ b/apps/lite-studio/styles/globals.css @@ -1,5 +1,4 @@ @import 'config/tailwind.config.css'; -@import './../../../packages/ui/build/css/themes/classic-dark.css'; @import 'config/typography.css'; @source '../app/**/*.{ts,tsx}'; diff --git a/apps/studio/components/interfaces/Account/Preferences/ThemeColorSettings.tsx b/apps/studio/components/interfaces/Account/Preferences/ThemeColorSettings.tsx index b67e6699fb1..c43cdefb926 100644 --- a/apps/studio/components/interfaces/Account/Preferences/ThemeColorSettings.tsx +++ b/apps/studio/components/interfaces/Account/Preferences/ThemeColorSettings.tsx @@ -3,7 +3,7 @@ import { Button, CardContent, Slider } from 'ui' import { useThemeOverrides } from '@/hooks/misc/useThemeOverrides' import { - applyResolvedThemeOverrides, + applyThemeOverrides, getThemeOverrideValue, hasThemeOverrides, previewThemeOverride, @@ -14,7 +14,7 @@ import { themeOverrideToSliderValue, } from '@/lib/theme-overrides' -export const ThemeColorSettings = ({ isVisible = true }: { isVisible?: boolean }) => { +export const ThemeColorSettings = () => { const { mode, overrides, setOverride, resetOverrides } = useThemeOverrides() const [draft, setDraft] = useState({}) const draftRef = useRef({}) @@ -33,8 +33,7 @@ export const ThemeColorSettings = ({ isVisible = true }: { isVisible?: boolean } useEffect( () => () => { - const root = document.documentElement - applyResolvedThemeOverrides(root, root.dataset.theme, modeRef.current, overridesRef.current) + applyThemeOverrides(document.documentElement, modeRef.current, overridesRef.current) }, [] ) @@ -56,8 +55,6 @@ export const ThemeColorSettings = ({ isVisible = true }: { isVisible?: boolean } [setOverride, writeDraft] ) - if (!isVisible) return null - return (
diff --git a/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx b/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx index 19a1bb0d23b..32f41b7544a 100644 --- a/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx +++ b/apps/studio/components/interfaces/Account/Preferences/ThemeSettings.tsx @@ -112,7 +112,7 @@ export const ThemeSettings = () => {
- + { const { resolvedTheme } = useTheme() @@ -10,7 +10,7 @@ export const AppearanceSettingsProvider = () => { useIsomorphicLayoutEffect(() => { if (resolvedTheme === undefined) return - applyResolvedThemeOverrides(document.documentElement, resolvedTheme, mode, overrides) + applyThemeOverrides(document.documentElement, mode, overrides) }, [mode, overrides, resolvedTheme]) return null diff --git a/apps/studio/lib/__tests__/theme-overrides.test.ts b/apps/studio/lib/__tests__/theme-overrides.test.ts index fb34acc6027..601ac2967f5 100644 --- a/apps/studio/lib/__tests__/theme-overrides.test.ts +++ b/apps/studio/lib/__tests__/theme-overrides.test.ts @@ -1,7 +1,6 @@ import { describe, expect, it } from 'vitest' import { - applyResolvedThemeOverrides, applyThemeOverrides, clearThemeOverridesForMode, getThemeOverrideRange, @@ -142,15 +141,6 @@ describe('theme override application', () => { expect(root.style.getPropertyValue('--contrast')).toBe('') }) - it('removes stored settings from Classic Dark', () => { - const root = document.createElement('html') - root.style.setProperty('--chroma', '0.04') - - applyResolvedThemeOverrides(root, 'classic-dark', 'dark', { chroma: 0.02 }) - - expect(root.style.getPropertyValue('--chroma')).toBe('') - }) - it('reports whether a mode has overrides', () => { expect(hasThemeOverrides({})).toBe(false) expect(hasThemeOverrides({ chroma: 0.02 })).toBe(true) @@ -158,7 +148,6 @@ describe('theme override application', () => { it('resolves classic dark as dark for legacy stored themes', () => { expect(resolveThemeOverrideMode('dark')).toBe('dark') - expect(resolveThemeOverrideMode('classic-dark')).toBe('dark') expect(resolveThemeOverrideMode('light')).toBe('light') expect(resolveThemeOverrideMode(undefined)).toBe('light') }) diff --git a/apps/studio/lib/session-replay.test.ts b/apps/studio/lib/session-replay.test.ts index 88a5cb2b066..06189bb095f 100644 --- a/apps/studio/lib/session-replay.test.ts +++ b/apps/studio/lib/session-replay.test.ts @@ -314,7 +314,7 @@ describe('maskReplayAttribute', () => { expect(maskReplayAttribute(name, value)).toBe(value) }) - it.each(['dark', 'light', 'classic-dark', 'system'])( + it.each(['dark', 'light', 'system'])( 'keeps data-theme=%o, which monaco.css and grid.css select on', (theme) => { expect(maskReplayAttribute('data-theme', theme)).toBe(theme) diff --git a/apps/studio/lib/session-replay.ts b/apps/studio/lib/session-replay.ts index 1fe51331994..51838180295 100644 --- a/apps/studio/lib/session-replay.ts +++ b/apps/studio/lib/session-replay.ts @@ -343,7 +343,7 @@ const RENDER_CRITICAL_ATTRIBUTES = new Set([ * from the SQL editor and the data grid. Gated on the value so the attribute cannot * carry anything else. */ -const THEME_VALUES = new Set(['dark', 'light', 'classic-dark', 'system']) +const THEME_VALUES = new Set(['dark', 'light', 'system']) /** * SVG presentation attributes that take either a plain value (`fill="#fff"`) or a diff --git a/apps/studio/lib/theme-overrides.ts b/apps/studio/lib/theme-overrides.ts index 9ecbe8b741d..d363f6fb072 100644 --- a/apps/studio/lib/theme-overrides.ts +++ b/apps/studio/lib/theme-overrides.ts @@ -195,15 +195,6 @@ export function applyThemeOverrides( }) } -export function applyResolvedThemeOverrides( - root: HTMLElement, - resolvedTheme: string | undefined, - mode: ThemeOverrideMode, - overrides: ThemeOverrides -) { - applyThemeOverrides(root, mode, resolvedTheme === 'classic-dark' ? {} : overrides) -} - export function previewThemeOverride( knob: ThemeOverrideKnob, mode: ThemeOverrideMode, diff --git a/apps/studio/styles/globals.css b/apps/studio/styles/globals.css index d75d9ca72f4..ae8c3e7db6c 100644 --- a/apps/studio/styles/globals.css +++ b/apps/studio/styles/globals.css @@ -1,5 +1,4 @@ @import 'config/tailwind.config.css'; -@import './../../../packages/ui/build/css/themes/classic-dark.css'; @import 'config/typography.css'; @source '../pages/**/*.{js,ts,jsx,tsx}'; diff --git a/apps/studio/tests/components/interfaces/Account/Preferences/ThemeColorSettings.test.tsx b/apps/studio/tests/components/interfaces/Account/Preferences/ThemeColorSettings.test.tsx index acb23e506e8..d0bc1d73299 100644 --- a/apps/studio/tests/components/interfaces/Account/Preferences/ThemeColorSettings.test.tsx +++ b/apps/studio/tests/components/interfaces/Account/Preferences/ThemeColorSettings.test.tsx @@ -4,7 +4,6 @@ import type * as UI from 'ui' import { beforeEach, describe, expect, it, vi } from 'vitest' import { ThemeColorSettings } from '@/components/interfaces/Account/Preferences/ThemeColorSettings' -import { applyResolvedThemeOverrides } from '@/lib/theme-overrides' import { customRender } from '@/tests/lib/custom-render' type SliderProps = ComponentProps @@ -88,14 +87,4 @@ describe('ThemeColorSettings', () => { expect(document.documentElement.style.getPropertyValue('--chroma')).toBe('0.02') }) - - it('does not restore persisted Dark values when switching to Classic Dark', () => { - const { rerender } = customRender() - - fireEvent.click(screen.getByRole('slider', { name: 'Color intensity' })) - applyResolvedThemeOverrides(document.documentElement, 'classic-dark', 'dark', { chroma: 0.02 }) - rerender() - - expect(document.documentElement.style.getPropertyValue('--chroma')).toBe('') - }) }) diff --git a/apps/ui-library/components/command-menu.tsx b/apps/ui-library/components/command-menu.tsx index c93c59d0f84..b618da47911 100644 --- a/apps/ui-library/components/command-menu.tsx +++ b/apps/ui-library/components/command-menu.tsx @@ -96,10 +96,6 @@ export function CommandMenu({ ...props }: DialogProps) { Dark
- runCommand(() => setTheme('classic-dark'))}> - - Classic dark - runCommand(() => setTheme('system'))}> System diff --git a/apps/ui-library/styles/globals.css b/apps/ui-library/styles/globals.css index f1cb2bfd4d2..0427403f9c6 100644 --- a/apps/ui-library/styles/globals.css +++ b/apps/ui-library/styles/globals.css @@ -1,5 +1,4 @@ @import 'config/tailwind.config.css'; -@import './../../../packages/ui/build/css/themes/classic-dark.css'; @source '../app/**/*.{js,ts,jsx,tsx}'; @source '../components/**/*.{js,ts,jsx,tsx}'; @@ -71,8 +70,7 @@ --sidebar-ring: hsl(0 0% 70.8%); } - [data-theme='dark'], - [data-theme='classic-dark'] { + [data-theme='dark'] { --background: hsl(0 0% 14.5%); --foreground: hsl(0 0% 98.5%); --card: hsl(0 0% 0%); diff --git a/apps/www/styles/globals.css b/apps/www/styles/globals.css index 27ec8c88e45..b2893a73bf4 100644 --- a/apps/www/styles/globals.css +++ b/apps/www/styles/globals.css @@ -1,5 +1,4 @@ @import 'config/tailwind.config.css'; -@import './../../../packages/ui/build/css/themes/faux-classic-dark.css'; @source '../_blog/*.mdx'; @source '../components/**/*.tsx'; diff --git a/packages/common/Providers.tsx b/packages/common/Providers.tsx index 6b0aa5b5662..33b93f5539b 100644 --- a/packages/common/Providers.tsx +++ b/packages/common/Providers.tsx @@ -1,16 +1,32 @@ 'use client' -import { ThemeProvider as NextThemesProvider, type ThemeProviderProps } from 'next-themes' +import { ThemeProvider as NextThemesProvider, useTheme, type ThemeProviderProps } from 'next-themes' +import { useEffect } from 'react' + +import { migrateLegacyTheme, THEME_DOM_VALUES } from './theme' + +function LegacyThemeMigration() { + const { theme, setTheme } = useTheme() + + useEffect(() => { + const migratedTheme = migrateLegacyTheme(theme) + if (migratedTheme !== theme && migratedTheme !== undefined) setTheme(migratedTheme) + }, [setTheme, theme]) + + return null +} export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return ( + {children} ) diff --git a/packages/common/theme.test.ts b/packages/common/theme.test.ts new file mode 100644 index 00000000000..6b43584b95e --- /dev/null +++ b/packages/common/theme.test.ts @@ -0,0 +1,17 @@ +import { describe, expect, it } from 'vitest' + +import { migrateLegacyTheme, THEME_DOM_VALUES } from './theme' + +it('renders the legacy preference as Dark before migration', () => { + expect(THEME_DOM_VALUES['classic-dark']).toBe('dark') +}) + +describe('migrateLegacyTheme', () => { + it('migrates Classic Dark to Dark', () => { + expect(migrateLegacyTheme('classic-dark')).toBe('dark') + }) + + it.each(['dark', 'light', 'system', undefined])('leaves %s unchanged', (theme) => { + expect(migrateLegacyTheme(theme)).toBe(theme) + }) +}) diff --git a/packages/common/theme.ts b/packages/common/theme.ts new file mode 100644 index 00000000000..38e517e988a --- /dev/null +++ b/packages/common/theme.ts @@ -0,0 +1,9 @@ +export const THEME_DOM_VALUES = { + dark: 'dark', + light: 'light', + 'classic-dark': 'dark', +} + +export function migrateLegacyTheme(theme: string | undefined) { + return theme === 'classic-dark' ? 'dark' : theme +} diff --git a/packages/ui-patterns/src/CommandMenu/prepackaged/ThemeSwitcher.test.tsx b/packages/ui-patterns/src/CommandMenu/prepackaged/ThemeSwitcher.test.tsx index 75df7aa36cc..c5992ea35d5 100644 --- a/packages/ui-patterns/src/CommandMenu/prepackaged/ThemeSwitcher.test.tsx +++ b/packages/ui-patterns/src/CommandMenu/prepackaged/ThemeSwitcher.test.tsx @@ -245,6 +245,5 @@ describe('useThemeSwitcherCommands', () => { 'Dark theme, Dark mode', 'Light theme, Light mode', ]) - expect(pageCommands.some((command) => command.name === 'Classic dark')).toBe(false) }) }) diff --git a/packages/ui/build/css/themes/classic-dark.css b/packages/ui/build/css/themes/classic-dark.css deleted file mode 100644 index 57e4ff9f334..00000000000 --- a/packages/ui/build/css/themes/classic-dark.css +++ /dev/null @@ -1,40 +0,0 @@ -[data-theme='classic-dark'], -.classic-dark { - --helpers-os-appearance: Dark; - --hue: 159; - --chroma: 0.014; - --surface: 0.205; - --foreground-lightness: 0.97; - --muted-foreground-level: 0.72; - --tertiary-foreground-level: 0.52; - - --code-block-5: 13.8deg 89.7% 69.6%; - --code-block-4: 276.1deg 67.7% 74.5%; - --code-block-3: 83.8deg 61.7% 63.1%; - --code-block-2: 33.2deg 90.3% 75.7%; - --code-block-1: 170.8deg 43.1% 61.4%; - - /* Stepped numeric scales (per-theme literals; mapped in theme.css) */ - --secondary-default: 0deg 0% 100%; - --secondary-400: 0deg 0% 100%; - --secondary-200: 0deg 0% 100%; - --brand-link: 153.1deg 60.2% 52.7%; - --brand-default: 153.1deg 60.2% 52.7%; - --brand-600: 153deg 59.5% 70%; - --brand-500: 153.5deg 61.8% 21.6%; - --brand-400: 153.3deg 65.2% 13.5%; - --brand-300: 153.8deg 69.6% 9%; - --brand-200: 152.5deg 75% 6.3%; - --warning-default: 38.9deg 100% 57.1%; - --warning-600: 38.9deg 89.8% 49.8%; - --warning-500: 34.8deg 90.9% 21.6%; - --warning-400: 33.2deg 100% 14.5%; - --warning-300: 32.3deg 100% 10.2%; - --warning-200: 36.8deg 100% 6.1%; - --destructive-default: 10.2deg 77.9% 53.9%; - --destructive-600: 9.7deg 85.2% 62.9%; - --destructive-500: 7.9deg 71.6% 29%; - --destructive-400: 6.7deg 60% 20.6%; - --destructive-300: 7.5deg 51.3% 15.3%; - --destructive-200: 10.9deg 23.4% 9.2%; -} diff --git a/packages/ui/build/css/themes/faux-classic-dark.css b/packages/ui/build/css/themes/faux-classic-dark.css deleted file mode 100644 index 44d33f82373..00000000000 --- a/packages/ui/build/css/themes/faux-classic-dark.css +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This theme intentionally follows the standard dark theme. It exists for - * apps that expose a classic-dark selector but only ship light/dark imagery. - */ -[data-theme='classic-dark'], -.classic-dark { - --helpers-os-appearance: Dark; - --hue: 159; - --chroma: 0.018; - --surface: 0.16; - --foreground-lightness: 0.985; - --muted-foreground-level: 0.72; - --tertiary-foreground-level: 0.52; - - --code-block-5: 13.8deg 89.7% 69.6%; - --code-block-4: 276.1deg 67.7% 74.5%; - --code-block-3: 83.8deg 61.7% 63.1%; - --code-block-2: 33.2deg 90.3% 75.7%; - --code-block-1: 170.8deg 43.1% 61.4%; - - /* Stepped numeric scales (per-theme literals; mapped in theme.css) */ - --secondary-default: 247.8deg 100% 70%; - --secondary-400: 248.3deg 54.5% 25.9%; - --secondary-200: 248deg 53.6% 11%; - --brand-link: 155deg 100% 38.6%; - --brand-default: 153.1deg 60.2% 52.7%; - --brand-600: 154.9deg 59.5% 70%; - --brand-500: 154.9deg 100% 19.2%; - --brand-400: 155.5deg 100% 9.6%; - --brand-300: 155.1deg 100% 8%; - --brand-200: 162deg 100% 2%; - --warning-default: 38.9deg 100% 42.9%; - --warning-600: 38.9deg 100% 42.9%; - --warning-500: 34.8deg 90.9% 21.6%; - --warning-400: 33.2deg 100% 14.5%; - --warning-300: 32.3deg 100% 10.2%; - --warning-200: 36.6deg 100% 8%; - --destructive-default: 10.2deg 77.9% 53.9%; - --destructive-600: 9.7deg 85.2% 62.9%; - --destructive-500: 7.9deg 71.6% 29%; - --destructive-400: 6.7deg 60% 20.6%; - --destructive-300: 7.5deg 51.3% 15.3%; - --destructive-200: 10.9deg 23.4% 9.2%; -} diff --git a/packages/ui/src/components/ThemeProvider/singleThemes.ts b/packages/ui/src/components/ThemeProvider/singleThemes.ts index bb27c487afc..6640e683a72 100644 --- a/packages/ui/src/components/ThemeProvider/singleThemes.ts +++ b/packages/ui/src/components/ThemeProvider/singleThemes.ts @@ -7,5 +7,4 @@ export const singleThemes = [ { name: 'System', value: 'system' }, // Classic Supabase light { name: 'Dark', value: 'dark' }, // Classic Supabase dark { name: 'Light', value: 'light' }, // Classic Supabase light - { name: 'Classic Dark', value: 'classic-dark' }, // Deep Dark Supabase dark ]