diff --git a/src/components/color-preference-picker.js b/src/components/color-preference-picker.js
new file mode 100644
index 00000000000..8820bb547e1
--- /dev/null
+++ b/src/components/color-preference-picker.js
@@ -0,0 +1,32 @@
+import React, {useCallback} from 'react'
+import {SegmentedControl} from '@primer/react'
+import {DeviceDesktopIcon, MoonIcon, SunIcon} from '@primer/octicons-react'
+
+const MODE_ICONS = [
+ {id: 'auto', name: 'System', icon: DeviceDesktopIcon},
+ {id: 'day', name: 'Day', icon: SunIcon},
+ {id: 'night', name: 'Night', icon: MoonIcon},
+]
+
+export const ColorPreferencePicker = ({preferredColorMode, setColorPreference}) => {
+ const handleColorModeChange = useCallback(
+ modeIndex => {
+ const mode = MODE_ICONS[modeIndex].id
+ setColorPreference(mode)
+ },
+ [setColorPreference],
+ )
+
+ return (
+
+ {MODE_ICONS.map((mode, index) => (
+
+ ))}
+
+ )
+}
diff --git a/src/components/header.js b/src/components/header.js
index 404072cbbef..c24a5dbc970 100644
--- a/src/components/header.js
+++ b/src/components/header.js
@@ -9,6 +9,8 @@ import {HEADER_HEIGHT, HEADER_BAR} from '../constants'
import headerNavItems from '../../content/header-nav.yml'
import {DarkTheme} from '../theme'
import SiteTitle from './site-title'
+import {ColorPreferencePicker} from './color-preference-picker'
+import {useColorPreference} from '../hooks/use-color-preference'
const NpmHeaderBar = styled(Box)`
height: ${HEADER_BAR}px;
@@ -16,6 +18,8 @@ const NpmHeaderBar = styled(Box)`
`
function Header() {
+ const {preferredColorMode, setColorPreference} = useColorPreference()
+
const search = useSearch()
return (
@@ -43,6 +47,7 @@ function Header() {
+
{headerNavItems.map((item, index) => (
@@ -50,7 +55,7 @@ function Header() {
))}
-
+
diff --git a/src/hooks/use-color-preference.js b/src/hooks/use-color-preference.js
new file mode 100644
index 00000000000..a9343b7086e
--- /dev/null
+++ b/src/hooks/use-color-preference.js
@@ -0,0 +1,20 @@
+import {useCallback, useMemo} from 'react'
+import {useTheme} from '@primer/react'
+
+export const useColorPreference = (themeContextId = 'root') => {
+ const {colorMode, setColorMode} = useTheme()
+ const setColorPreference = useCallback(
+ mode => {
+ localStorage.setItem(`${themeContextId}-color-mode`, mode)
+ setColorMode(mode)
+ },
+ [setColorMode, themeContextId],
+ )
+
+ const preferredColorMode = useMemo(
+ () => colorMode ?? localStorage.getItem(`${themeContextId}-color-mode`) ?? 'auto',
+ [colorMode, themeContextId],
+ )
+
+ return {preferredColorMode, setColorPreference}
+}
diff --git a/src/hooks/use-prism-theme.js b/src/hooks/use-prism-theme.js
new file mode 100644
index 00000000000..020b4dcb481
--- /dev/null
+++ b/src/hooks/use-prism-theme.js
@@ -0,0 +1,17 @@
+import {useTheme} from '@primer/react'
+import {themes} from 'prism-react-renderer'
+import {useMemo} from 'react'
+
+const colorModeToThemeMap = {
+ light: themes.github,
+ dark: themes.vsDark,
+ day: themes.github,
+ night: themes.vsDark,
+}
+
+export const usePrismTheme = () => {
+ const {resolvedColorMode} = useTheme()
+
+ const theme = useMemo(() => colorModeToThemeMap[resolvedColorMode ?? 'day'], [resolvedColorMode])
+ return {theme}
+}
diff --git a/src/mdx/code.js b/src/mdx/code.js
index fcac8056c32..1dca439e7ce 100644
--- a/src/mdx/code.js
+++ b/src/mdx/code.js
@@ -1,11 +1,12 @@
import React from 'react'
import {Box, Text, Button, themeGet} from '@primer/react'
import {Octicon} from '@primer/react/deprecated'
-import {Highlight, themes, Prism} from 'prism-react-renderer'
+import {Highlight, Prism} from 'prism-react-renderer'
import styled from 'styled-components'
import {CheckIcon, CopyIcon} from '@primer/octicons-react'
import copyToClipboard from 'copy-to-clipboard'
import {announce} from '../util/aria-live'
+import {usePrismTheme} from '../hooks/use-prism-theme'
;(typeof global !== 'undefined' ? global : window).Prism = Prism
require('prismjs/components/prism-bash')
@@ -99,9 +100,10 @@ const CodeBlock = ({children, code, className, style}) => (
)
function Code({className = '', prompt, children}) {
+ const {theme: codeTheme} = usePrismTheme()
if (prompt) {
return (
-
+
{children}
)
@@ -115,7 +117,7 @@ function Code({className = '', prompt, children}) {
}
return (
-
+
{({className: highlightClassName, style, tokens, getLineProps, getTokenProps}) => (
{tokens.map((line, i) => (
diff --git a/src/theme.js b/src/theme.js
index 0c9df6fbfe3..d73e8492ffa 100644
--- a/src/theme.js
+++ b/src/theme.js
@@ -4,6 +4,8 @@ import deepmerge from 'deepmerge'
export const NPM_RED = '#cb3837'
+const colorModePreference = (typeof window !== `undefined` ? localStorage.getItem('root-color-mode') : null) ?? 'auto'
+
export const npmTheme = deepmerge(theme, {
colors: {
logoBg: NPM_RED,
@@ -37,7 +39,7 @@ export const npmTheme = deepmerge(theme, {
},
})
-export const ThemeProvider = props =>
+export const ThemeProvider = props =>
export const Theme = React.forwardRef(function Theme({theme: colorMode, as = Box, ...props}, ref) {
return (