From c9a70f2f91697b0935446df8bd51bc1e9a92e951 Mon Sep 17 00:00:00 2001 From: Laine Hallot Date: Thu, 13 Feb 2025 13:07:16 -0600 Subject: [PATCH 1/5] feat: put colorMode preference in local storage --- src/hooks/use-color-preference.js | 20 ++++++++++++++++++++ src/theme.js | 4 +++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/hooks/use-color-preference.js 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/theme.js b/src/theme.js index 0c9df6fbfe3..0d07647e47b 100644 --- a/src/theme.js +++ b/src/theme.js @@ -4,6 +4,8 @@ import deepmerge from 'deepmerge' export const NPM_RED = '#cb3837' +const colorModePreference = localStorage.getItem('root-color-mode') ?? '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 ( From e4a2f34527ae9d0f1268d3dc0af8a160fcc5a787 Mon Sep 17 00:00:00 2001 From: Laine Hallot Date: Thu, 13 Feb 2025 13:08:16 -0600 Subject: [PATCH 2/5] feat: base code block theme on primer colorMode --- src/hooks/use-prism-theme.js | 17 +++++++++++++++++ src/mdx/code.js | 8 +++++--- 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 src/hooks/use-prism-theme.js 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) => ( From e7f2d35afe1d26df822837b5db5a2444e4a82602 Mon Sep 17 00:00:00 2001 From: Laine Hallot Date: Thu, 13 Feb 2025 13:09:03 -0600 Subject: [PATCH 3/5] feat: light/dark mode control --- src/components/color-preference-picker.js | 32 +++++++++++++++++++++++ src/components/header.js | 5 ++++ 2 files changed, 37 insertions(+) create mode 100644 src/components/color-preference-picker.js 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..7ee9b1c75a7 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) => ( From ba5301229adf679d58878657c2fc208f7a9adbfb Mon Sep 17 00:00:00 2001 From: Laine Hallot Date: Thu, 13 Feb 2025 14:28:37 -0600 Subject: [PATCH 4/5] fix: dont check color prefrence is ssr --- src/theme.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/theme.js b/src/theme.js index 0d07647e47b..d73e8492ffa 100644 --- a/src/theme.js +++ b/src/theme.js @@ -4,7 +4,7 @@ import deepmerge from 'deepmerge' export const NPM_RED = '#cb3837' -const colorModePreference = localStorage.getItem('root-color-mode') ?? 'auto' +const colorModePreference = (typeof window !== `undefined` ? localStorage.getItem('root-color-mode') : null) ?? 'auto' export const npmTheme = deepmerge(theme, { colors: { From f1e714e3b85ec1a8c8105dc8a58a466e9752c5e1 Mon Sep 17 00:00:00 2001 From: Laine Hallot Date: Thu, 13 Feb 2025 14:29:23 -0600 Subject: [PATCH 5/5] fix: add margin between color mode and search on mobile --- src/components/header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/header.js b/src/components/header.js index 7ee9b1c75a7..c24a5dbc970 100644 --- a/src/components/header.js +++ b/src/components/header.js @@ -55,7 +55,7 @@ function Header() { ))} - +