useThemeSandbox.tsx 3.8 KB

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