Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[question] ColorModeProvider with Gatsby #874

Closed
iamskok opened this issue Apr 27, 2020 · 3 comments
Closed

[question] ColorModeProvider with Gatsby #874

iamskok opened this issue Apr 27, 2020 · 3 comments

Comments

@iamskok
Copy link

iamskok commented Apr 27, 2020

I was experimenting with various font loading strategies with Gatsby and ended up coping over the source of gatsby-plugin-theme-ui so I can update theme.fonts values once all the fonts are loaded by the client (by default theme.fonts are set to system fonts). If there is any other way to do it without modifying the original gatsby-plugin-theme-ui please share it.

After doing all of that I installed ColorModeProvider and added it to the wrap-root-element.js inside of the ThemeProvider, but it's not completely working. Only text & background colors are updating, but primary, secondary and muted colors are not changing (default theme colors are being used). I can confirm that CSS variables are getting updated each time I change the theme.

Please let me know what I'm missing here?

  • Demo (Code block colors are not supposed to be changed, because they are handled by another provider)
  • The actual PR

ThemeProvider:

/** @jsx jsx */
import { jsx, ThemeProvider } from 'theme-ui'
import { useState, useEffect, useCallback, Fragment } from 'react'
import theme from '../gatsby-plugin-theme-ui'
import components from '../gatsby-plugin-theme-ui/components'
import useEventListener from '../hooks/use-event-listener'

const themeUI = { ...theme }
const { fonts: { safe: safeFonts } } = { ...theme }
const safeFontsTheme = {
  ...Object.assign(
    {},
    theme,
    { fonts: safeFonts }
  )
}

const ThemeUIProvider = ({ element }) => {
  const [theme, setTheme] = useState(safeFontsTheme)

  const updateTheme = useCallback(
    () => {
      setTheme(themeUI)
      document.documentElement.classList.remove(
        `font-loading-stage-1`,
        `font-loading-stage-2`
      )
    },
    [setTheme],
  )

  useEventListener(
    typeof window !== 'undefined' && window,
    'FONTS_ARE_LOADED',
    updateTheme
  )

  useEffect(() => {
    updateTheme()
    sessionStorage.getItem('areFontsLoaded')
  }, [updateTheme])

  return (
    <Fragment>
      {jsx(ThemeProvider, {
        theme,
        components,
      },
        element
      )}
    </Fragment>
  )
}

export default ThemeUIProvider

wrap-root-element.js:

/** @jsx jsx */
import { jsx } from 'theme-ui'
import { ColorModeProvider } from '@theme-ui/color-modes'
import PrismThemeProvider from './code-block/prism-theme-provider'
import ThemeUIProvider from './theme-ui-provider'

export const wrapRootElement = ({ element }) => {
  return (
    <PrismThemeProvider>
      <ThemeUIProvider element={element}>
        <ColorModeProvider>
          {element}
        </ColorModeProvider>
      </ThemeUIProvider>
    </PrismThemeProvider>
  )
}

ColorModeButton:

/** @jsx jsx */
import { jsx, IconButton, useColorMode } from 'theme-ui'

const ColorModeButton = props => {
  const colorModes = [`default`, `dark`, `deep`, `swiss`]
  const [colorMode, setColorMode] = useColorMode()

  return (
    <IconButton
      {...props}
      aria-label='Toggle website theme'
      onClick={() => {
        const index = colorModes.indexOf(colorMode)
        const next = colorModes[(index + 1) % colorModes.length]
        setColorMode(next)
      }}
      sx={{
        cursor: 'pointer',
        padding: 0,
        width: 40,
        height: 40,
        marginX: 1,
      }}
    >
      <svg
        width='24'
        height='24'
        viewBox='0 0 32 32'
        fill='currentcolor'
        sx={{
          display: 'flex',
          margin: '0 auto',
          transition: 'transform 400ms ease',
        }}
      >
        <circle
          cx='16'
          cy='16'
          r='14'
          fill='none'
          stroke='currentcolor'
          strokeWidth='4'
        ></circle>
        <path d='M 16 0 A 16 16 0 0 0 16 32 z'></path>
      </svg>
    </IconButton>
  )
}

export default ColorModeButton

I have also asked this question on Stackoverflow and Spectrum in case anyone else is interested in checking these threads.

@lachlanjc
Copy link
Member

I might be misunderstanding, but couldn’t you leverage CSS’s loading behavior instead of detecting all this activity with JS? Put your webfont first, then all the system fonts as fallbacks, with font-display: swap on the webfont. That way, the page text will immediately show up in system fonts, then when the webfont loads, will switch to it. If that’s the behavior you’re looking for, I don’t think changing your theme dynamically will be necessary.

@iamskok
Copy link
Author

iamskok commented Apr 27, 2020

@lachlanjc I have to use a tiny bit of JS (CSS font loading API) so I know when all 3 of the web fonts are loaded and only after this I append CSS class with all the custom fonts to the HTML tag. This way we minimize the number of layout repaints from 3 times to just 1.

You can find more information about this technique in this awesome article - Comperhansive webfonts.

@iamskok
Copy link
Author

iamskok commented Apr 28, 2020

OMG this such an embarrassment! 🤣 I have copied over the code from gatsby-theme-ui-plugin and edited it, but forgot to remove the original gatsby-theme-ui-plugin package.

@iamskok iamskok closed this as completed Apr 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants