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 (
+ <>
+