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

refactor(v2): refactor color mode system to enable all possibilities #2939

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 24 additions & 16 deletions packages/docusaurus-theme-classic/src/index.js
Expand Up @@ -19,39 +19,47 @@ const ContextReplacementPlugin = requireFromDocusaurusCore(
// Need to be inlined to prevent dark mode FOUC
// Make sure that the 'storageKey' is the same as the one in `/theme/hooks/useTheme.js`
const storageKey = 'theme';
const noFlash = (defaultDarkMode) => `(function() {
const noFlash = ({defaultDarkMode = false, respectUserPreference = false}) => {
return `(function() {
var defaultDarkMode = ${defaultDarkMode};
var respectUserPreference = ${respectUserPreference};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to more explicitly describe in the option itself that what exactly is this preference. respectUserDarkModeSystemPreference (being elaborate here)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://drafts.csswg.org/mediaqueries-5/#prefers-color-scheme

I don't like the idea to have dark mode in the attribute name. As it's specified, the preferred color scheme may be extended in the future to other values like "sepia" (wouldn't be surprised to see new values being created for color blind people in the future).

Also not fan of "system". It's not part of the spec where this preference is configured exactly. It can be your OS, it can be your browser, it can be a browser plugin...


function setDataThemeAttribute(theme) {
document.documentElement.setAttribute('data-theme', theme);
}

function getPreferredTheme() {
function getStoredTheme() {
var theme = null;
try {
theme = localStorage.getItem('${storageKey}');
} catch (err) {}

return theme;
}

var darkQuery = window.matchMedia('(prefers-color-scheme: dark)');

var preferredTheme = getPreferredTheme();
if (preferredTheme !== null) {
setDataThemeAttribute(preferredTheme);
} else if (darkQuery.matches || defaultDarkMode) {
setDataThemeAttribute('dark');
var storedTheme = getStoredTheme();
if (storedTheme !== null) {
setDataThemeAttribute(storedTheme);
}
else {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to run prettier here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as it's a string literal it would be hard to run prettier here :)

if ( respectUserPreference && window.matchMedia('(prefers-color-scheme: dark)').matches ) {
setDataThemeAttribute('dark');
}
else if ( respectUserPreference && window.matchMedia('(prefers-color-scheme: light)').matches ) {
setDataThemeAttribute('light');
}
else {
setDataThemeAttribute(defaultDarkMode ? 'dark' : 'light');
}
}
})();`;
};

module.exports = function (context, options) {
const {
siteConfig: {themeConfig},
} = context;
const {
disableDarkMode = false,
defaultDarkMode = false,
colorMode: {defaultDarkMode = false, respectUserPreference = false} = {},
prism: {additionalLanguages = []} = {},
} = themeConfig || {};
const {customCss} = options || {};
Expand Down Expand Up @@ -92,17 +100,17 @@ module.exports = function (context, options) {
},

injectHtmlTags() {
if (disableDarkMode) {
return {};
}
return {
preBodyTags: [
{
tagName: 'script',
attributes: {
type: 'text/javascript',
},
innerHTML: noFlash(defaultDarkMode),
innerHTML: noFlash({
defaultDarkMode,
respectUserPreference,
}),
},
],
};
Expand Down
6 changes: 3 additions & 3 deletions packages/docusaurus-theme-classic/src/theme/Navbar/index.js
Expand Up @@ -175,7 +175,7 @@ function Navbar() {
siteConfig: {
themeConfig: {
navbar: {title, links = [], hideOnScroll = false} = {},
disableDarkMode = false,
colorMode: {disableSwitch: disableColorModeSwitch = false} = {},
},
},
isClient,
Expand Down Expand Up @@ -265,7 +265,7 @@ function Navbar() {
{rightLinks.map((linkItem, i) => (
<NavItem {...linkItem} key={i} />
))}
{!disableDarkMode && (
{!disableColorModeSwitch && (
<Toggle
className={styles.displayOnlyInLargeViewport}
aria-label="Dark mode toggle"
Expand Down Expand Up @@ -303,7 +303,7 @@ function Navbar() {
<strong className="navbar__title">{title}</strong>
)}
</Link>
{!disableDarkMode && sidebarShown && (
{!disableColorModeSwitch && sidebarShown && (
<Toggle
aria-label="Dark mode toggle in sidebar"
checked={isDarkTheme}
Expand Down
53 changes: 30 additions & 23 deletions packages/docusaurus-theme-classic/src/theme/hooks/useTheme.js
Expand Up @@ -10,59 +10,66 @@ import {useState, useCallback, useEffect} from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';

const themes = {
light: '',
light: 'light',
dark: 'dark',
};

// Ensure to always return a valid theme even if input is invalid
const coerceToTheme = (theme) => {
return theme === themes.dark ? themes.dark : themes.light;
};

const getInitialTheme = () => {
if (typeof document === 'undefined') {
return themes.light; // SSR: we don't care
}
return coerceToTheme(document.documentElement.getAttribute('data-theme'));
};

const storeTheme = (newTheme) => {
try {
localStorage.setItem('theme', coerceToTheme(newTheme));
} catch (err) {
console.error(err);
}
};

const useTheme = () => {
const {
siteConfig: {themeConfig: {disableDarkMode}} = {},
siteConfig: {themeConfig: {colorMode: {disableSwitch = false} = {}}} = {},
} = useDocusaurusContext();
const [theme, setTheme] = useState(
typeof document !== 'undefined'
? document.documentElement.getAttribute('data-theme')
: themes.light,
);
const setThemeSyncWithLocalStorage = useCallback(
(newTheme) => {
try {
localStorage.setItem('theme', newTheme);
} catch (err) {
console.error(err);
}
},
[setTheme],
);
const [theme, setTheme] = useState(getInitialTheme);

const setLightTheme = useCallback(() => {
setTheme(themes.light);
setThemeSyncWithLocalStorage(themes.light);
storeTheme(themes.light);
}, []);
const setDarkTheme = useCallback(() => {
setTheme(themes.dark);
setThemeSyncWithLocalStorage(themes.dark);
storeTheme(themes.dark);
}, []);

useEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
document.documentElement.setAttribute('data-theme', coerceToTheme(theme));
}, [theme]);

useEffect(() => {
if (disableDarkMode) {
if (disableSwitch) {
return;
}

try {
const localStorageTheme = localStorage.getItem('theme');
if (localStorageTheme !== null) {
setTheme(localStorageTheme);
setTheme(coerceToTheme(localStorageTheme));
}
} catch (err) {
console.error(err);
}
}, [setTheme]);

useEffect(() => {
if (disableDarkMode) {
if (disableSwitch) {
return;
}

Expand Down
6 changes: 6 additions & 0 deletions website/docusaurus.config.js
Expand Up @@ -81,6 +81,12 @@ module.exports = {
],
],
themeConfig: {
colorMode: {
defaultDarkMode: false,
respectUserPreference: true,
disableSwitch: false,
},

announcementBar: {
id: 'supportus',
content:
Expand Down