3

I got stuck at enabling dark mode for my react/typescript element. I created a Context.Provider to switch light/dark mode for the entire app, but the toggle does not work at all. If anybody knows how to fix it, please help.

This is the ThemeContext and ContextProvider

import { createContext, useState, useEffect } from 'react'

type ThemeName = 'light' | 'dark' | string
type ThemeContextType = {
  theme: ThemeName
  setTheme: (name: ThemeName) => void
}

const getInitialTheme = () => {
  if (typeof window !== 'undefined' && window.localStorage) {
    const storedPrefs = window.localStorage.getItem('color-theme')
    if (typeof storedPrefs === 'string') {
      return storedPrefs
    }

    const userMedia = window.matchMedia('(prefers-color-scheme:dark)')
    if (userMedia.matches) {
      return 'dark'
    }
  }
  // returning default theme here
  return 'light'
}

export const ThemeContext = createContext<ThemeContextType>({} as ThemeContextType)

export const ThemeProvider = ({ initialTheme, children }) => {
  const [theme, setTheme] = useState(getInitialTheme)


  const rawSetTheme = theme => {
//Updated rawSetTheme to theme above//
    const root = window.document.documentElement
    const isDark = theme === 'dark'

    root.classList.remove(isDark ? 'light' : 'dark')
    root.classList.add(theme)

    localStorage.setItem('color-theme', theme)
  }

  if (initialTheme) {
    rawSetTheme(initialTheme)
  }

  useEffect(() => {
    rawSetTheme(theme)
  }, [theme])

  return <ThemeContext.Provider value={{ theme, setTheme }}>{children}</ThemeContext.Provider>
}

And this is the index.tsx.

ReactDOM.render(
  <ThemeProvider>
    <App />, document.getElementById('root')
  </ThemeProvider>
)

And this is the Toggle

export const DarkModeToggle: VFC<Props> = memo(() => {
  const { theme, setTheme } = useContext(ThemeContext)

  function isDark() {
    return theme === 'dark'
  }

  function toggleTheme(e) {
    setTheme(e.target.checked ? 'dark' : 'light')
  }
  return (
    <div className='flex flex-col'>
      <label htmlFor='unchecked' className='mt-3 inline-flex items-center cursor-pointer'>
        <span className='relative'>
          <span className='block w-10 h-6 bg-gray-200 rounded-full shadow-inner'></span>
          <span
            className={`${
              theme === 'dark' ? 'bg-indigo-400 transform translate-x-full' : 'bg-white'
            } absolute block w-4 h-4 mt-1 ml-1  rounded-full shadow inset-y-0 left-0 focus-within:shadow-outline transition-transform duration-300 ease-in-out`}
          >
            <input
              id='darkmode'
              onClick={e => toggleTheme(e)}
              type='checkbox'
              checked={isDark()}
              className='absolute opacity-0 w-0 h-0'
            />
          </span>
        </span>
        <span className='ml-3 text-sm'>{theme === 'dark' ? 'ON' : 'OFF'}</span>
      </label>
    </div>
  )
})

Updated: I changed the 'rawSetTheme' to 'theme' for variable, but it returns an error" in App.tsx as below. If you have any suggestions, it would be very appreciated.

Property 'initialTheme' is missing in type '{ children: Element; }' but required in type '{ initialTheme: any; children: any; }'.  TS2741

     7 | export default function App() {
     8 |   return (
  >  9 |     <ThemeProvider>
       |      ^
    10 |       <Router />
    11 |     </ThemeProvider>
    12 |   )
8
  • What's the CSS you have to apply this theme? Commented Oct 4, 2021 at 23:54
  • Assuming rawSetTheme is hit every time and the class values changes on the HTML tag and local storage. Commented Oct 4, 2021 at 23:55
  • Here is the index.css (omitted excess for characters limit) :root { @apply light; } .dark { --color-bg-primary: #1F2937; } .light { --color-bg-primary: #F3F4F6; } Commented Oct 5, 2021 at 4:50
  • Why do you have sass in css file? Commented Oct 5, 2021 at 15:38
  • This is tailwind. I added dark/light color scheme so I can switch. darkMode: 'class', theme: { extend: { backgroundColor: { primary: 'var(--color-bg-primary)', secondary: 'var(--color-bg-secondary)', }, textColor: { accent: 'var(--color-text-accent)', primary: 'var(--color-text-primary)', secondary: 'var(--color-text-secondary)', },``` Commented Oct 5, 2021 at 16:33

1 Answer 1

1

Thank you for the comment. I could fix it. It was just a small error of Toggle.tsx. The tailwind extends setting is also working fine.

export const DarkModeToggle: VFC<Props> = memo(() => {
  const { theme, setTheme } = useContext(ThemeContext)
  
  return (

    <div className='flex flex-col'>
      <label className='mt-3 inline-flex items-center cursor-pointer'>
        <span className='relative'>
          <span className='block w-10 h-6 bg-gray-200 rounded-full shadow-inner'></span>
          <span
            className={`${
              theme === 'dark' ? 'bg-indigo-400 transform translate-x-full' : 'bg-white'
            } absolute block w-4 h-4 mt-1 ml-1  rounded-full shadow inset-y-0 left-0 focus-within:shadow-outline transition-transform duration-300 ease-in-out`}
          >
            <input onClick={()=>setTheme(theme==='dark'?'light':'dark')} className='absolute opacity-0 w-0 h-0' />
          </span>
        </span>
        <span className='ml-3 text-sm'>{theme === 'dark' ? 'ON' : 'OFF'}</span>
      </label>
    </div>
  )
})
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.