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

feat(v2): Add <Root> theme element #3932

Merged
merged 2 commits into from
Dec 17, 2020
Merged

feat(v2): Add <Root> theme element #3932

merged 2 commits into from
Dec 17, 2020

Conversation

slorber
Copy link
Collaborator

@slorber slorber commented Dec 17, 2020

Motivation

Fixes #3919

We need a way to add state components (often providers) at the very top of the tree, and avoid unmount/remount when navigating.

This <Root> theme component is at the very top of the app, even above the <Layout> component, and unlike the layout (that is applied on a per-page basic), the Root component is rendered consistently, never unmounts, and can hold stateful logic.

Example: put this in website/src/theme/Root.js

import React from 'react';

function StatefulButton() {
  const [value, setValue] = React.useState(false);
  return (
    <div style={{padding: 50, border: 'solid'}}>
      <button onClick={() => setValue((v) => !v)}>
        Value = {value ? 'true' : 'false'}
      </button>
    </div>
  );
}

function Root({children}) {
  return (
    <>
      <StatefulButton />
      {children}
    </>
  );
}

export default Root; 

image

Note: we might still provide a wrapRootElement API, like Gatsby does here, but this Root theme element can be useful in the meantime.

Have you read the Contributing Guidelines on pull requests?

yes

Test Plan

Not sure how, but theme aliases are already tested

@slorber slorber added the pr: new feature This PR adds a new API or behavior. label Dec 17, 2020
@slorber slorber requested a review from lex111 as a code owner December 17, 2020 18:02
@facebook-github-bot facebook-github-bot added the CLA Signed Signed Facebook CLA label Dec 17, 2020
@netlify
Copy link

netlify bot commented Dec 17, 2020

✔️ Deploy preview for docusaurus-2 ready!

🔨 Explore the source changes: 2f0914b

🔍 Inspect the deploy logs: https://app.netlify.com/sites/docusaurus-2/deploys/5fdba1ed4c5d6200087a5200

😎 Browse the preview: https://deploy-preview-3932--docusaurus-2.netlify.app

@github-actions
Copy link

github-actions bot commented Dec 17, 2020

⚡️ Lighthouse report for the changes in this PR:

Category Score
🟢 Performance 92
🟢 Accessibility 99
🟢 Best practices 100
🟢 SEO 100
🟢 PWA 95

Lighthouse ran on https://deploy-preview-3932--docusaurus-2.netlify.app/classic/

@github-actions
Copy link

github-actions bot commented Dec 17, 2020

Size Change: +20 B (0%)

Total Size: 152 kB

ℹ️ View Unchanged
Filename Size Change
website/build/blog/2017/12/14/introducing-docusaurus/index.html 20.7 kB +1 B
website/build/docs/introduction/index.html 180 B 0 B
website/build/index.html 5.8 kB -1 B
website/build/main.********.js 108 kB +20 B (0%)
website/build/styles.********.css 17.6 kB 0 B

compressed-size-action

@slorber slorber merged commit df47c17 into master Dec 17, 2020
@dayhaysoos
Copy link

Wow that was fast!!!

@guanghechen
Copy link

@slorber Hey, thank you for your hard work!

It seems that the useThemeContext API cannot be used in the Root.js. I want to get whether it is currently in dark mode in Root.js. Is there any way to do it?

Looking forward to your reply, thank you!

@slorber
Copy link
Collaborator Author

slorber commented Aug 12, 2021

@guanghechen , Root is a "core" component, not a theme component. It is one layer above the theme, and thus can't access anything to the theme.

The concept of dark mode does not exist in the context of the Docusaurus core, it's a theme-specific feature. Theoretically, some theme might decide to not implement dark mode, or you can even use multiple themes at the same time.

You can still access the theme by reading <html data-theme="dark"> attribute in Root in a useEffect but this is a bit hacky. We probably need to introduce some kind of ThemeRoot component to make this possible.

Can you share your use-case for this? maybe there's a workaround I can suggest.

@guanghechen
Copy link

@slorber thank you for your quick reply.

I use the material-ui component library, so I hope to use the ThemeProvider provided by @material-ui/core in a global place, so that all components imported from @material-ui/core naturally support switching light / dark modes.

We probably need to introduce some kind of ThemeRoot component to make this possible.

This is a great idea, if possible, I hope it could be implemented! I think this is very useful for the case of using use of mature third-party component libraries.

You can still access the theme by reading <html data-theme="dark"> attribute in Root in a useEffect but this is a bit hacky.

At this stage, I think this method is enough, thank you for your suggestion, it is very useful!

@guanghechen
Copy link

You can still access the theme by reading <html data-theme="dark"> attribute in Root in a useEffect but this is a bit hacky.

@slorber I have encountered a new problem, how can I trigger a component update when switching the theme, if I just simply get the theme through the following code?

function Root({ children }) {
  const [isDarkTheme, setIsDarkTheme] = useState(false)
  useEffect(() => {
    const theme = document.documentElement.getAttribute('data-theme')
    setIsDarkTheme(theme === 'dark')
  }, [])

  return (
    <ThemeProvider theme={isDarkTheme ? muiDarkTheme : muiLightTheme}>
      {children}
    </ThemeProvider>
  )
}

@guanghechen
Copy link

Okay, this works!

function Root({ children }) {
  const [isDarkTheme, setIsDarkTheme] = useState(false)

  useEffect(() => {
    const update = () => {
      console.log('clicked')
      const theme = document.documentElement.getAttribute('data-theme')
      setIsDarkTheme(theme === 'dark')
    }
    update()

    const observer = new MutationObserver(function (mutations) {
      for (const mutation of mutations) {
        if (mutation.type == 'attributes') update()
      }
    })
    observer.observe(document.documentElement, { attributes: true })

    return () => observer.disconnect()
  }, [])

  return (
    <ThemeProvider theme={isDarkTheme ? muiDarkTheme : muiLightTheme}>
      {children}
    </ThemeProvider>
  )
}

@slorber
Copy link
Collaborator Author

slorber commented Aug 12, 2021

We don't have proper CSS-in-JS support on the server, so it's possible you end-up with FOUC after building your site (and there's no good workaround for that).

We'll likely add a way for CSS-in-JS plugins to add such a provider without using swizzling.

@guanghechen
Copy link

Excuse me, since I am not a native English speaker, I would like to ask what FOUC means. My dictionary tells me it is Flash Of Unstyled Content, is it correct?

@slorber
Copy link
Collaborator Author

slorber commented Aug 12, 2021

Yes, it means that some css will only inserted/applied after React hydration, so on the first milliseconds of the static page load, your UI would look broken (because of the missing CSS), and only later it would be fixed (because that CSS would be inserted by your CSS-in-JS lib). To prevent these issues, CSS-in-JS libs provide server-side system so we can inline critical CSS directly in the static HTML files, ensuring there's no missing CSS rule on the first render.

@guanghechen
Copy link

Oh, I see! Thank you for your patient explanation, you are the best! In fact, I did encounter this problem. So, there will be FOUC problems in all the libraries that use CSS-in-JS in docusaurus at this stage?

@slorber
Copy link
Collaborator Author

slorber commented Aug 12, 2021

Yes, unfortunately, until we implement #3236 (we really want this to happen, just a matter of time)

@guanghechen
Copy link

Okay, I get it. (A year flies so fast)

@slorber slorber deleted the slorber/root-element branch August 17, 2021 17:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed Signed Facebook CLA pr: new feature This PR adds a new API or behavior.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Root Provider support
4 participants