diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json
index c0167d8f9073..5255b67606bc 100644
--- a/packages/docusaurus-theme-classic/package.json
+++ b/packages/docusaurus-theme-classic/package.json
@@ -20,6 +20,7 @@
"clsx": "^1.1.1",
"copy-text-to-clipboard": "^2.2.0",
"infima": "0.2.0-alpha.12",
+ "lodash": "^4.17.19",
"parse-numeric-range": "^0.0.2",
"prism-react-renderer": "^1.1.0",
"prismjs": "^1.20.0",
diff --git a/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.js b/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.js
new file mode 100644
index 000000000000..44531fe179c9
--- /dev/null
+++ b/packages/docusaurus-theme-classic/src/__tests__/validateThemeConfig.test.js
@@ -0,0 +1,86 @@
+/**
+ * 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.
+ */
+import merge from 'lodash/merge';
+
+const {
+ validateThemeConfig,
+ DEFAULT_COLOR_MODE_CONFIG,
+} = require('../validateThemeConfig');
+
+const mergeDefault = (config) => merge({}, DEFAULT_COLOR_MODE_CONFIG, config);
+
+function testValidateThemeConfig(themeConfig) {
+ function validate(schema, cfg) {
+ const {value, error} = schema.validate(cfg, {
+ convert: false,
+ });
+ if (error) {
+ throw error;
+ }
+ return value;
+ }
+
+ return validateThemeConfig({themeConfig, validate});
+}
+
+describe('color mode config', () => {
+ test('minimal config', () => {
+ const colorMode = {
+ switchConfig: {
+ darkIcon: '🌙',
+ },
+ };
+ expect(testValidateThemeConfig({colorMode})).toEqual({
+ colorMode: mergeDefault(colorMode),
+ });
+ });
+
+ test('max config', () => {
+ const colorMode = {
+ defaultMode: 'dark',
+ disableSwitch: false,
+ respectPrefersColorScheme: true,
+ switchConfig: {
+ darkIcon: '🌙',
+ darkIconStyle: {
+ marginTop: '1px',
+ marginLeft: '2px',
+ },
+ lightIcon: '☀️',
+ lightIconStyle: {
+ marginLeft: '1px',
+ },
+ },
+ };
+ expect(testValidateThemeConfig({colorMode})).toEqual({
+ colorMode: mergeDefault(colorMode),
+ });
+ });
+
+ test('undefined config', () => {
+ const colorMode = undefined;
+ expect(testValidateThemeConfig({colorMode})).toEqual({
+ colorMode: mergeDefault(colorMode),
+ });
+ });
+
+ test('empty config', () => {
+ const colorMode = {};
+ expect(testValidateThemeConfig({colorMode})).toEqual({
+ colorMode: mergeDefault(colorMode),
+ });
+ });
+
+ test('empty switch config', () => {
+ const colorMode = {
+ switchConfig: {},
+ };
+ expect(testValidateThemeConfig({colorMode})).toEqual({
+ colorMode: mergeDefault(colorMode),
+ });
+ });
+});
diff --git a/packages/docusaurus-theme-classic/src/index.js b/packages/docusaurus-theme-classic/src/index.js
index cff72869155f..8d0653830b34 100644
--- a/packages/docusaurus-theme-classic/src/index.js
+++ b/packages/docusaurus-theme-classic/src/index.js
@@ -7,7 +7,7 @@
const path = require('path');
const Module = require('module');
-const ThemeConfigSchema = require('./themeConfigSchema');
+const {validateThemeConfig} = require('./validateThemeConfig');
const createRequire = Module.createRequire || Module.createRequireFromPath;
const requireFromDocusaurusCore = createRequire(
@@ -120,6 +120,4 @@ module.exports = function (context, options) {
};
};
-module.exports.validateThemeConfig = ({validate, themeConfig}) => {
- return validate(ThemeConfigSchema, themeConfig);
-};
+module.exports.validateThemeConfig = validateThemeConfig;
diff --git a/packages/docusaurus-theme-classic/src/theme/Toggle/index.tsx b/packages/docusaurus-theme-classic/src/theme/Toggle/index.tsx
index a73e95e05551..3ab914e4a242 100644
--- a/packages/docusaurus-theme-classic/src/theme/Toggle/index.tsx
+++ b/packages/docusaurus-theme-classic/src/theme/Toggle/index.tsx
@@ -13,17 +13,40 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import clsx from 'clsx';
import styles from './styles.module.css';
-const Moon = () => ;
-const Sun = () => ;
+const Dark = ({icon, style}) => (
+
+ {icon}
+
+);
+const Light = ({icon, style}) => (
+
+ {icon}
+
+);
export default function (props: ComponentProps): JSX.Element {
- const {isClient} = useDocusaurusContext();
+ const {
+ siteConfig: {
+ themeConfig: {
+ colorMode: {
+ switchConfig: {
+ darkIcon,
+ darkIconStyle,
+ lightIcon,
+ lightIconStyle,
+ },
+ },
+ },
+ },
+ isClient
+ } = useDocusaurusContext();
+
return (
,
- unchecked: ,
+ checked: ,
+ unchecked: ,
}}
{...props}
/>
diff --git a/packages/docusaurus-theme-classic/src/theme/Toggle/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Toggle/styles.module.css
index 42619f0251f6..1ac128ce8cf1 100644
--- a/packages/docusaurus-theme-classic/src/theme/Toggle/styles.module.css
+++ b/packages/docusaurus-theme-classic/src/theme/Toggle/styles.module.css
@@ -16,12 +16,6 @@
.toggle::before {
position: absolute;
}
-.moon::before {
- content: '\1F31C';
-}
-.sun::before {
- content: '\1F31E';
-}
/**
* styles for React Toggle
diff --git a/packages/docusaurus-theme-classic/src/themeConfigSchema.js b/packages/docusaurus-theme-classic/src/validateThemeConfig.js
similarity index 81%
rename from packages/docusaurus-theme-classic/src/themeConfigSchema.js
rename to packages/docusaurus-theme-classic/src/validateThemeConfig.js
index 7f847f4d6ca1..9cfe86602c87 100644
--- a/packages/docusaurus-theme-classic/src/themeConfigSchema.js
+++ b/packages/docusaurus-theme-classic/src/validateThemeConfig.js
@@ -7,6 +7,19 @@
const Joi = require('@hapi/joi');
+const DEFAULT_COLOR_MODE_CONFIG = {
+ defaultMode: 'light',
+ disableSwitch: false,
+ respectPrefersColorScheme: false,
+ switchConfig: {
+ darkIcon: '🌜',
+ darkIconStyle: {},
+ lightIcon: '🌞',
+ lightIconStyle: {},
+ },
+};
+exports.DEFAULT_COLOR_MODE_CONFIG = DEFAULT_COLOR_MODE_CONFIG;
+
const NavbarItemPosition = Joi.string().equal('left', 'right').default('left');
// TODO we should probably create a custom navbar item type "dropdown"
@@ -102,14 +115,28 @@ const NavbarItemSchema = Joi.object().when('type', {
*/
const ColorModeSchema = Joi.object({
- defaultMode: Joi.string().equal('dark', 'light').default('light'),
- disableSwitch: Joi.bool().default(false),
- respectPrefersColorScheme: Joi.bool().default(false),
-}).default({
- defaultMode: 'light',
- disableSwitch: false,
- respectPrefersColorScheme: false,
-});
+ defaultMode: Joi.string()
+ .equal('dark', 'light')
+ .default(DEFAULT_COLOR_MODE_CONFIG.defaultMode),
+ disableSwitch: Joi.bool().default(DEFAULT_COLOR_MODE_CONFIG.disableSwitch),
+ respectPrefersColorScheme: Joi.bool().default(
+ DEFAULT_COLOR_MODE_CONFIG.respectPrefersColorScheme,
+ ),
+ switchConfig: Joi.object({
+ darkIcon: Joi.string().default(
+ DEFAULT_COLOR_MODE_CONFIG.switchConfig.darkIcon,
+ ),
+ darkIconStyle: Joi.object().default(
+ DEFAULT_COLOR_MODE_CONFIG.switchConfig.darkIconStyle,
+ ),
+ lightIcon: Joi.string().default(
+ DEFAULT_COLOR_MODE_CONFIG.switchConfig.lightIcon,
+ ),
+ lightIconStyle: Joi.object().default(
+ DEFAULT_COLOR_MODE_CONFIG.switchConfig.lightIconStyle,
+ ),
+ }).default(DEFAULT_COLOR_MODE_CONFIG.switchConfig),
+}).default(DEFAULT_COLOR_MODE_CONFIG);
const FooterLinkItemSchema = Joi.object({
to: Joi.string(),
@@ -178,4 +205,6 @@ const ThemeConfigSchema = Joi.object({
}),
});
-module.exports = ThemeConfigSchema;
+exports.validateThemeConfig = ({validate, themeConfig}) => {
+ return validate(ThemeConfigSchema, themeConfig);
+};
diff --git a/website/docs/docusaurus.config.js.md b/website/docs/docusaurus.config.js.md
index e15c83d4d124..20a0914c822a 100644
--- a/website/docs/docusaurus.config.js.md
+++ b/website/docs/docusaurus.config.js.md
@@ -158,6 +158,22 @@ Example:
```js title="docusaurus.config.js"
module.exports = {
themeConfig: {
+ colorMode: {
+ defaultMode: 'light',
+ disableSwitch: false,
+ respectPrefersColorScheme: true,
+ switchConfig: {
+ darkIcon: '🌙',
+ darkIconStyle: { // Style object passed to inline CSS
+ // For more information about styling options visit: https://reactjs.org/docs/dom-elements.html#style
+ marginLeft: '2px',
+ },
+ lightIcon: '\u2600',
+ lightIconStyle: {
+ marginLeft: '1px',
+ },
+ },
+ },
navbar: {
title: 'Site Title',
logo: {