diff --git a/.changeset/cyan-taxis-float.md b/.changeset/cyan-taxis-float.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/cyan-taxis-float.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/packages/clerk-js/sandbox/app.ts b/packages/clerk-js/sandbox/app.ts index 56d2624b11d..3be79d71860 100644 --- a/packages/clerk-js/sandbox/app.ts +++ b/packages/clerk-js/sandbox/app.ts @@ -1,5 +1,6 @@ import { PageMocking, type MockScenario } from '@clerk/msw'; import * as l from '../../localizations'; +import { dark, neobrutalism, shadcn, shadesOfPurple } from '../../ui/src/themes'; import type { Clerk as ClerkType } from '../'; import * as scenarios from './scenarios'; @@ -313,6 +314,84 @@ function otherOptions() { return { updateOtherOptions }; } +const themes: Record = { + dark, + shadesOfPurple, + neobrutalism, + shadcn, +}; + +function themeSelector() { + assertClerkIsLoaded(Clerk); + + const themeSelect = document.getElementById('themeSelect') as HTMLSelectElement; + + const savedTheme = sessionStorage.getItem('baseTheme') ?? ''; + themeSelect.value = savedTheme; + + const updateTheme = () => { + const themeName = themeSelect.value; + sessionStorage.setItem('baseTheme', themeName); + + const currentAppearance = Clerk.__internal_getOption('appearance') ?? {}; + void Clerk.__internal_updateProps({ + appearance: { + ...currentAppearance, + theme: themeName ? themes[themeName] : undefined, + }, + }); + }; + + themeSelect.addEventListener('change', updateTheme); + + return { updateTheme }; +} + +type Preset = { elements: Record; options?: Record; variables?: Record }; + +function presetToAppearance(preset: Preset | undefined) { + if (!preset) return {}; + return { + elements: preset.elements, + ...(preset.options ? { options: preset.options } : {}), + ...(preset.variables ? { variables: preset.variables } : {}), + }; +} + +const presets: Record = {}; + +function presetSelector() { + assertClerkIsLoaded(Clerk); + + const presetSelect = document.getElementById('presetSelect') as HTMLSelectElement; + + // Populate dropdown from presets map + for (const name of Object.keys(presets)) { + presetSelect.add(new Option(name, name)); + } + + const savedPreset = sessionStorage.getItem('preset') ?? ''; + presetSelect.value = savedPreset; + + const updatePreset = () => { + const presetName = presetSelect.value; + sessionStorage.setItem('preset', presetName); + + const currentAppearance = Clerk.__internal_getOption('appearance') ?? {}; + void Clerk.__internal_updateProps({ + appearance: { + ...currentAppearance, + elements: {}, + ...presetToAppearance(presetName ? presets[presetName] : undefined), + }, + }); + }; + + presetSelect.addEventListener('change', updatePreset); + + return { updatePreset }; +} + const urlParams = new URL(window.location.href).searchParams; for (const [component, encodedProps] of urlParams.entries()) { if (AVAILABLE_COMPONENTS.includes(component as AvailableComponent)) { @@ -328,6 +407,8 @@ void (async () => { assertClerkIsLoaded(Clerk); fillLocalizationSelect(); const { updateVariables } = appearanceVariableOptions(); + const { updateTheme } = themeSelector(); + const { updatePreset } = presetSelector(); const { updateOtherOptions } = otherOptions(); const sidebars = document.querySelectorAll('[data-sidebar]'); @@ -452,14 +533,29 @@ void (async () => { await mocking.initialize(route, { scenario }); } + const initialThemeName = sessionStorage.getItem('baseTheme') ?? ''; + const initialTheme = initialThemeName ? themes[initialThemeName] : undefined; + const initialPresetName = sessionStorage.getItem('preset') ?? ''; + const initialPreset = initialPresetName ? presets[initialPresetName] : undefined; + await Clerk.load({ ...(componentControls.clerk.getProps() ?? {}), signInUrl: '/sign-in', signUpUrl: '/sign-up', ui: { ClerkUI: window.__internal_ClerkUICtor }, + appearance: { + ...(initialTheme ? { theme: initialTheme } : {}), + ...presetToAppearance(initialPreset), + }, }); renderCurrentRoute(); - updateVariables(); + updateTheme(); + updatePreset(); + // Only apply sandbox variable overrides when using the default theme. + // Prebuilt themes (raw, dark, etc.) define their own variables. + if (!initialTheme) { + updateVariables(); + } updateOtherOptions(); } else { console.error(`Unknown route: "${route}".`); diff --git a/packages/clerk-js/sandbox/template.html b/packages/clerk-js/sandbox/template.html index d8ff8041392..bc7fefcc617 100644 --- a/packages/clerk-js/sandbox/template.html +++ b/packages/clerk-js/sandbox/template.html @@ -7,7 +7,31 @@ name="viewport" content="width=device-width,initial-scale=1" /> - +
+
+
+ Theme +
+ + +
+
+
+ Page +
+ + +
Other options