useThemeSandbox.tsx117 lines · main
| 1 | 'use client' |
| 2 | |
| 3 | import { useEffect, useState } from 'react' |
| 4 | |
| 5 | import { IS_PROD } from '../constants' |
| 6 | |
| 7 | const defaultDark: { [name: string]: string } = { |
| 8 | '--brand-accent': '160deg 100% 50%', |
| 9 | '--brand-default': '159.9deg 100% 38.6%', |
| 10 | '--brand-600': '136deg 59.5% 70%', |
| 11 | '--brand-500': '160.4deg 100% 19.2%', |
| 12 | '--brand-400': '160.4deg 100% 9.6%', |
| 13 | '--brand-300': '158.4deg 100% 4.9%', |
| 14 | '--brand-200': '162deg 100% 2%', |
| 15 | '--border-stronger': '200deg 16.1% 22%', |
| 16 | '--border-strong': '200deg 16.5% 17.8%', |
| 17 | '--border-alternative': '200deg 16.4% 13.1%', |
| 18 | '--border-control': '200deg 10% 14%', |
| 19 | '--border-overlay': '200deg 20% 9%', |
| 20 | '--border-secondary': '200deg 21.6% 10%', |
| 21 | '--border-muted': '200deg 14.9% 9.2%', |
| 22 | '--border-default': '200deg 10% 11%', |
| 23 | '--background-muted': '200deg 9.1% 10.8%', |
| 24 | '--background-overlay-hover': '200deg 20% 11%', |
| 25 | '--background-overlay-default': '200deg 14.3% 6.9%', |
| 26 | '--background-surface-300': '200deg 20% 10%', |
| 27 | '--background-surface-200': '200deg 20% 7%', |
| 28 | '--background-surface-100': '200deg 20% 6%', |
| 29 | '--background-control': '200deg 9% 11%', |
| 30 | '--background-selection': '200deg 9.8% 10%', |
| 31 | '--background-alternative': '200deg 20% 2.9%', |
| 32 | '--background-default': '200deg 20% 4%', |
| 33 | '--foreground-muted': '200deg 10% 35%', |
| 34 | '--foreground-lighter': '200deg 8% 55%', |
| 35 | '--foreground-light': '200deg 5% 69%', |
| 36 | '--foreground-default': '200deg 0% 93%', |
| 37 | } |
| 38 | |
| 39 | /** |
| 40 | * Shows a GUI to test color themes in dev and preview env. |
| 41 | * |
| 42 | * To access sandbox mode: |
| 43 | * - switch theme to dark mode |
| 44 | * - append "#theme-sandbox" to the url |
| 45 | * - select "Apply Theme" to apply preset (localStorage will keep track of changes so you don't lose new values) |
| 46 | * - select "Reset localStorage" and refresh page to restart |
| 47 | */ |
| 48 | export const useThemeSandbox = (): any => { |
| 49 | const isWindowUndefined = typeof window === 'undefined' |
| 50 | if (isWindowUndefined || IS_PROD) return null |
| 51 | const hash = window.location.hash |
| 52 | const defaultConfig = defaultDark // use dark default tokens |
| 53 | // const defaultConfig = defaultLight // use light default tokens |
| 54 | const localPreset = localStorage.getItem('theme-sandbox') |
| 55 | const isSandbox = hash.includes('#theme-sandbox') || localPreset !== null |
| 56 | const [themeConfig, setThemeConfig] = useState( |
| 57 | localPreset ? JSON.parse(localPreset) : defaultConfig |
| 58 | ) |
| 59 | const styles = document.querySelector(':root') as any |
| 60 | |
| 61 | const handleSetThemeConfig = (name: string, value: any) => { |
| 62 | updateCSSVariables() |
| 63 | setThemeConfig((prevConfig: any) => ({ ...prevConfig, [name]: value })) |
| 64 | } |
| 65 | |
| 66 | const updateCSSVariables = () => { |
| 67 | Object.entries(themeConfig).map(([key, value]) => styles.style.setProperty(key, value)) |
| 68 | localStorage.setItem('theme-sandbox', JSON.stringify(themeConfig)) |
| 69 | } |
| 70 | |
| 71 | const init = async () => { |
| 72 | if (!isSandbox) return |
| 73 | const dat = await import('dat.gui') |
| 74 | const gui = new dat.GUI() |
| 75 | |
| 76 | gui.width = 500 |
| 77 | |
| 78 | Object.entries(defaultConfig).map(([key, _value]) => { |
| 79 | if (!themeConfig[key]) return localStorage.removeItem('theme-sandbox') |
| 80 | const folderName = key.split('-')[2] |
| 81 | const folder = gui.__folders[folderName] ?? gui.addFolder(folderName) |
| 82 | |
| 83 | return folder |
| 84 | .add(themeConfig, key) |
| 85 | .name(key) |
| 86 | .onChange((newValue) => { |
| 87 | handleSetThemeConfig(key, newValue) |
| 88 | }) |
| 89 | }) |
| 90 | |
| 91 | var obj = { |
| 92 | 'Apply Theme': function () { |
| 93 | updateCSSVariables() |
| 94 | }, |
| 95 | 'Exit Sandbox': function () { |
| 96 | gui.destroy() |
| 97 | }, |
| 98 | 'Reset localStorage': function () { |
| 99 | localStorage.removeItem('theme-sandbox') |
| 100 | setThemeConfig(defaultConfig) |
| 101 | }, |
| 102 | } |
| 103 | |
| 104 | gui.add(obj, 'Apply Theme') |
| 105 | gui.add(obj, 'Reset localStorage') |
| 106 | gui.add(obj, 'Exit Sandbox') |
| 107 | gui.load |
| 108 | } |
| 109 | |
| 110 | useEffect(() => { |
| 111 | init() |
| 112 | }, []) |
| 113 | |
| 114 | return { themeConfig, handleSetThemeConfig, isSandbox } |
| 115 | } |
| 116 | |
| 117 | export default useThemeSandbox |