diff --git a/src/theme/ColorModeToggle/index.tsx b/src/theme/ColorModeToggle/index.tsx new file mode 100644 index 0000000..53d51e8 --- /dev/null +++ b/src/theme/ColorModeToggle/index.tsx @@ -0,0 +1,150 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// Ejected from @docusaurus/theme-classic to invert which icon the toggle +// shows: the icon now represents the mode you'd switch TO (moon on a light +// background, sun on a dark background). The component logic is unchanged from +// upstream; only the co-located styles.module.css swaps the light/dark mapping. + +import React, {type ReactNode} from 'react'; +import clsx from 'clsx'; +import useIsBrowser from '@docusaurus/useIsBrowser'; +import {translate} from '@docusaurus/Translate'; +import IconLightMode from '@theme/Icon/LightMode'; +import IconDarkMode from '@theme/Icon/DarkMode'; +import IconSystemColorMode from '@theme/Icon/SystemColorMode'; +import type {Props} from '@theme/ColorModeToggle'; +import type {ColorMode} from '@docusaurus/theme-common'; + +import styles from './styles.module.css'; + +// The order of color modes is defined here, and can be customized with swizzle +function getNextColorMode( + colorMode: ColorMode | null, + respectPrefersColorScheme: boolean, +) { + // 2-value transition + if (!respectPrefersColorScheme) { + return colorMode === 'dark' ? 'light' : 'dark'; + } + + // 3-value transition + switch (colorMode) { + case null: + return 'light'; + case 'light': + return 'dark'; + case 'dark': + return null; + default: + throw new Error(`unexpected color mode ${colorMode}`); + } +} + +function getColorModeLabel(colorMode: ColorMode | null): string { + switch (colorMode) { + case null: + return translate({ + message: 'system mode', + id: 'theme.colorToggle.ariaLabel.mode.system', + description: 'The name for the system color mode', + }); + case 'light': + return translate({ + message: 'light mode', + id: 'theme.colorToggle.ariaLabel.mode.light', + description: 'The name for the light color mode', + }); + case 'dark': + return translate({ + message: 'dark mode', + id: 'theme.colorToggle.ariaLabel.mode.dark', + description: 'The name for the dark color mode', + }); + default: + throw new Error(`unexpected color mode ${colorMode}`); + } +} + +function getColorModeAriaLabel(colorMode: ColorMode | null) { + return translate( + { + message: 'Switch between dark and light mode (currently {mode})', + id: 'theme.colorToggle.ariaLabel', + description: 'The ARIA label for the color mode toggle', + }, + { + mode: getColorModeLabel(colorMode), + }, + ); +} + +function CurrentColorModeIcon(): ReactNode { + // 3 icons are always rendered for technical reasons + // We use "data-theme-choice" to render the correct one + // This must work even before React hydrates + return ( + <> + + + + + ); +} + +function ColorModeToggle({ + className, + buttonClassName, + respectPrefersColorScheme, + value, + onChange, +}: Props): ReactNode { + const isBrowser = useIsBrowser(); + return ( +
+ +
+ ); +} + +export default React.memo(ColorModeToggle); diff --git a/src/theme/ColorModeToggle/styles.module.css b/src/theme/ColorModeToggle/styles.module.css new file mode 100644 index 0000000..9eea7ce --- /dev/null +++ b/src/theme/ColorModeToggle/styles.module.css @@ -0,0 +1,45 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +.toggle { + width: 2rem; + height: 2rem; +} + +.toggleButton { + -webkit-tap-highlight-color: transparent; + align-items: center; + display: flex; + justify-content: center; + width: 100%; + height: 100%; + border-radius: 50%; + transition: background var(--ifm-transition-fast); +} + +.toggleButton:hover { + background: var(--ifm-color-emphasis-200); +} + +.toggleIcon { + display: none; +} + +/* + * Inverted from upstream: the toggle shows the icon of the mode you'd switch TO, + * not the current mode. Light background -> moon (dark icon); dark background -> + * sun (light icon). System choice still shows the system icon. + */ +[data-theme-choice='system'] .systemToggleIcon, +[data-theme-choice='light'] .darkToggleIcon, +[data-theme-choice='dark'] .lightToggleIcon { + display: initial; +} + +.toggleButtonDisabled { + cursor: not-allowed; +}