From cf31fa627551cbeaf33f7c8cc36801f003bd2f2d Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Tue, 1 Jul 2025 15:40:55 -0700 Subject: [PATCH] feat(clerk-js): Remove CSS variables when modern color isn't supported --- .../__tests__/parseVariables.spec.ts | 58 +++++++++++++++++++ .../src/ui/customizables/parseVariables.ts | 46 ++++++++++++++- 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 packages/clerk-js/src/ui/customizables/__tests__/parseVariables.spec.ts diff --git a/packages/clerk-js/src/ui/customizables/__tests__/parseVariables.spec.ts b/packages/clerk-js/src/ui/customizables/__tests__/parseVariables.spec.ts new file mode 100644 index 00000000000..d72ba57e2de --- /dev/null +++ b/packages/clerk-js/src/ui/customizables/__tests__/parseVariables.spec.ts @@ -0,0 +1,58 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +// Mock cssSupports +vi.mock('../../utils/cssSupports', () => ({ + cssSupports: { + modernColor: vi.fn(), + }, +})); + +import { cssSupports } from '@/ui/utils/cssSupports'; + +import { removeInvalidValues } from '../parseVariables'; + +const mockModernColorSupport = vi.mocked(cssSupports.modernColor); + +describe('removeInvalidValues', () => { + beforeEach(() => { + vi.clearAllMocks(); + mockModernColorSupport.mockReturnValue(false); + }); + + it('should return the variables as-is if modern color support is present', () => { + mockModernColorSupport.mockReturnValue(true); + const variables = { + colorPrimary: 'var(--color-primary)', + }; + + const result = removeInvalidValues(variables); + expect(result).toEqual({ colorPrimary: 'var(--color-primary)' }); + }); + + it('should remove invalid string values if modern color support is not present', () => { + mockModernColorSupport.mockReturnValue(false); + const variables = { + colorPrimary: 'var(--color-primary)', + colorDanger: '#ff0000', + }; + + const result = removeInvalidValues(variables); + expect(result).toEqual({ colorDanger: '#ff0000' }); + }); + + it('should remove invalid object values if modern color support is not present', () => { + mockModernColorSupport.mockReturnValue(false); + const variables = { + colorPrimary: { + 400: 'var(--color-primary-500)', + 500: '#fff', + }, + colorDanger: { + 500: '#ff0000', + }, + }; + + const result = removeInvalidValues(variables); + expect(result).toEqual({ colorDanger: { 500: '#ff0000' } }); + }); +}); diff --git a/packages/clerk-js/src/ui/customizables/parseVariables.ts b/packages/clerk-js/src/ui/customizables/parseVariables.ts index db660335073..fab13f448de 100644 --- a/packages/clerk-js/src/ui/customizables/parseVariables.ts +++ b/packages/clerk-js/src/ui/customizables/parseVariables.ts @@ -4,11 +4,12 @@ import { spaceScaleKeys } from '../foundations/sizes'; import type { fontSizes, fontWeights } from '../foundations/typography'; import { colors } from '../utils/colors'; import { colorOptionToThemedAlphaScale, colorOptionToThemedLightnessScale } from '../utils/colors/scales'; +import { cssSupports } from '../utils/cssSupports'; import { fromEntries } from '../utils/fromEntries'; import { removeUndefinedProps } from '../utils/removeUndefinedProps'; export const createColorScales = (theme: Theme) => { - const variables = theme.variables || {}; + const variables = removeInvalidValues(theme.variables || {}); const dangerScale = colorOptionToThemedLightnessScale(variables.colorDanger, 'danger'); const primaryScale = colorOptionToThemedLightnessScale(variables.colorPrimary, 'primary'); @@ -43,6 +44,49 @@ export const createColorScales = (theme: Theme) => { }); }; +export const removeInvalidValues = (variables: NonNullable): NonNullable => { + // Check for modern color support. If present, we can simply return the variables as-is since we support everything + // CSS supports. + if (cssSupports.modernColor()) { + return variables; + } + + // If not, we need to remove any values that are specified with CSS variables, as our color scale generation only + // supports CSS variables using modern CSS functionality. + const validVariables: Theme['variables'] = Object.fromEntries( + Object.entries(variables).filter(([key, value]) => { + if (typeof value === 'string') { + const isValid = !value.startsWith('var('); + if (!isValid) { + console.warn( + `Invalid color value: ${value} for key: ${key}, using default value instead. Using CSS variables is not supported in this browser.`, + ); + } + return isValid; + } + + if (typeof value === 'object') { + return Object.entries(value).every(([key, value]) => { + if (typeof value !== 'string') return true; + + const isValid = !value.startsWith('var('); + if (!isValid) { + console.warn( + `Invalid color value: ${value} for key: ${key}, using default value instead. Using CSS variables is not supported in this browser.`, + ); + } + + return isValid; + }); + } + + return false; + }), + ); + + return validVariables; +}; + export const createRadiiUnits = (theme: Theme) => { const { borderRadius } = theme.variables || {}; if (borderRadius === undefined) {