Skip to content

Commit ea7c913

Browse files
authored
docs: color scheme switcher (adobe#9403)
* add settings dialog for color scheme, package manager, styling solution, and css theme * add icons for color scheme * fix RAC examples to use correct color scheme * add color scheme options to markdown menu * typescript * use color scheme toggle in Header (for React Spectrum) * fix lint * cleanup * fix search menu background color * add switcher to mobile header * remove cursor-pointer * add divider * add transition to icon change
1 parent 245a703 commit ea7c913

9 files changed

Lines changed: 448 additions & 107 deletions

File tree

packages/dev/s2-docs/pages/s2/home/Header.tsx

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,88 @@
11
'use client';
22

33
import {CSSProperties, useId, useRef, useState} from 'react';
4+
import {Button} from 'react-aria-components';
5+
import Contrast from '@react-spectrum/s2/icons/Contrast';
6+
import Lighten from '@react-spectrum/s2/icons/Lighten';
7+
import {Divider, pressScale} from '@react-spectrum/s2';
48
import SearchMenuTrigger, {preloadSearchMenu} from '@react-spectrum/s2-docs/src/SearchMenuTrigger';
59
import {useLayoutEffect} from '@react-aria/utils';
10+
import {useSettings} from '@react-spectrum/s2-docs/src/SettingsContext';
611
import { HeaderLink, Link } from '@react-spectrum/s2-docs/src/Link';
7-
import { space, style } from '@react-spectrum/s2/style' with {type: 'macro'};
12+
import { focusRing, iconStyle, space, style } from '@react-spectrum/s2/style' with {type: 'macro'};
813
import { getBaseUrl } from '@react-spectrum/s2-docs/src/pageUtils';
914
import GithubLogo from '@react-spectrum/s2-docs/src/icons/GithubLogo';
1015
import { NpmLogo } from '@react-spectrum/s2-docs/src/icons/NpmLogo';
1116

17+
const colorSchemeToggleStyles = style({
18+
...focusRing(),
19+
outlineColor: 'white',
20+
font: 'ui',
21+
color: {
22+
default: 'white'
23+
},
24+
textDecoration: 'none',
25+
transition: 'default',
26+
backgroundColor: {
27+
default: 'transparent',
28+
isHovered: 'white/15',
29+
isFocusVisible: 'white/15'
30+
},
31+
size: 32,
32+
display: 'flex',
33+
alignItems: 'center',
34+
justifyContent: 'center',
35+
borderRadius: 'lg',
36+
borderWidth: 0
37+
});
38+
39+
const whiteIconStyle = iconStyle({color: 'white'});
40+
41+
const iconContainerStyles = style({
42+
position: 'relative',
43+
size: 20
44+
});
45+
46+
function ColorSchemeToggle() {
47+
let {colorScheme, toggleColorScheme, systemColorScheme} = useSettings();
48+
let isOverriding = colorScheme !== systemColorScheme;
49+
let label = isOverriding
50+
? `Using ${colorScheme} mode (press to follow system)`
51+
: `Using system ${systemColorScheme} mode (press to switch)`;
52+
let ref = useRef<HTMLButtonElement>(null);
53+
let isDark = colorScheme === 'dark';
54+
55+
return (
56+
<Button
57+
ref={ref}
58+
aria-label={label}
59+
onPress={toggleColorScheme}
60+
className={renderProps => colorSchemeToggleStyles(renderProps)}
61+
style={pressScale(ref)}>
62+
<span className={iconContainerStyles}>
63+
<Contrast
64+
styles={whiteIconStyle}
65+
UNSAFE_style={{
66+
position: 'absolute',
67+
inset: 0,
68+
opacity: isDark ? 0 : 1,
69+
transform: isDark ? 'rotate(-90deg) scale(0.5)' : 'rotate(0deg) scale(1)',
70+
transition: 'opacity 200ms ease-out, transform 200ms ease-out'
71+
}} />
72+
<Lighten
73+
styles={whiteIconStyle}
74+
UNSAFE_style={{
75+
position: 'absolute',
76+
inset: 0,
77+
opacity: isDark ? 1 : 0,
78+
transform: isDark ? 'rotate(0deg) scale(1)' : 'rotate(90deg) scale(0.5)',
79+
transition: 'opacity 200ms ease-out, transform 200ms ease-out'
80+
}} />
81+
</span>
82+
</Button>
83+
);
84+
}
85+
1286
export default function HomeHeader() {
1387
const [searchOpen, setSearchOpen] = useState(false);
1488
const searchMenuId = useId();
@@ -150,6 +224,8 @@ export default function HomeHeader() {
150224
<HeaderLink staticColor="white" href={getBaseUrl('react-aria') + '/blog/'} rel="noopener noreferrer" styles={style({display: {default: 'none', sm: 'flex'}})}>Blog</HeaderLink>
151225
<HeaderLink staticColor="white" aria-label="GitHub" href="https://github.com/adobe/react-spectrum" target="_blank" rel="noopener noreferrer"><GithubLogo /></HeaderLink>
152226
<HeaderLink staticColor="white" aria-label="npm" href="https://npmjs.com/@react-spectrum/s2" target="_blank" rel="noopener noreferrer"><NpmLogo /></HeaderLink>
227+
<Divider orientation="vertical" staticColor="white" UNSAFE_style={{marginBlock: 4}} />
228+
<ColorSchemeToggle />
153229
</div>
154230
</nav>
155231
);

packages/dev/s2-docs/pages/s2/index.mdx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Home} from './home/Home';
33
import {Provider} from '@react-spectrum/s2';
44
import rspFavicon from 'url:../../assets/rsp-favicon.svg';
55
import {RouterWrapperServer} from "@react-spectrum/s2-docs/src/SearchMenuWrapperServer";
6+
import {SettingsContextProvider} from '../../src/SettingsProvider';
67
import {getBaseUrl} from '../../src/pageUtils';
78

89
export const section = 'Overview';
@@ -49,6 +50,8 @@ export const hideFromSearch = true;
4950
}
5051
)}} />
5152
</head>
52-
<Home currentPage={props.currentPage} />
53+
<SettingsContextProvider>
54+
<Home currentPage={props.currentPage} />
55+
</SettingsContextProvider>
5356
</Provider>
5457
</RouterWrapperServer>

packages/dev/s2-docs/src/Header.tsx

Lines changed: 74 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
'use client';
22

33
import {baseColor, focusRing, space, style} from '@react-spectrum/s2/style' with { type: 'macro' };
4+
import {Button, Link} from 'react-aria-components';
5+
import Contrast from '@react-spectrum/s2/icons/Contrast';
6+
import {Divider, pressScale} from '@react-spectrum/s2';
47
import {getBaseUrl} from './pageUtils';
58
import {getLibraryFromPage, getLibraryIcon, getLibraryLabel} from './library';
69
import GithubLogo from './icons/GithubLogo';
710
import {HeaderLink} from './Link';
8-
// @ts-ignore
9-
import {Link} from 'react-aria-components';
11+
import Lighten from '@react-spectrum/s2/icons/Lighten';
1012
import {NpmLogo} from './icons/NpmLogo';
11-
import {pressScale} from '@react-spectrum/s2';
1213
import React, {useId, useRef, useState} from 'react';
1314
import SearchMenuTrigger, {preloadSearchMenu} from './SearchMenuTrigger';
1415
import {useLayoutEffect} from '@react-aria/utils';
1516
import {useRouter} from './Router';
17+
import {useSettings} from './SettingsContext';
1618

1719
function getButtonText(currentPage) {
1820
return getLibraryLabel(getLibraryFromPage(currentPage));
@@ -44,6 +46,69 @@ const libraryStyles = style({
4446
marginStart: space(26)
4547
});
4648

49+
const colorSchemeToggleStyles = style({
50+
...focusRing(),
51+
font: 'ui',
52+
color: 'neutral',
53+
textDecoration: 'none',
54+
transition: 'default',
55+
backgroundColor: {
56+
default: {
57+
...baseColor('gray-100'),
58+
default: 'transparent'
59+
}
60+
},
61+
size: 32,
62+
display: 'flex',
63+
alignItems: 'center',
64+
justifyContent: 'center',
65+
borderRadius: 'lg',
66+
borderWidth: 0
67+
});
68+
69+
const iconContainerStyles = style({
70+
position: 'relative',
71+
size: 20
72+
});
73+
74+
function ColorSchemeToggle() {
75+
let {colorScheme, toggleColorScheme, systemColorScheme} = useSettings();
76+
let isOverriding = colorScheme !== systemColorScheme;
77+
let label = isOverriding
78+
? `Using ${colorScheme} mode (press to use system)`
79+
: `Using system ${systemColorScheme} mode (press to switch)`;
80+
let ref = useRef(null);
81+
let isDark = colorScheme === 'dark';
82+
83+
return (
84+
<Button
85+
ref={ref}
86+
aria-label={label}
87+
onPress={toggleColorScheme}
88+
className={renderProps => colorSchemeToggleStyles(renderProps)}
89+
style={pressScale(ref)}>
90+
<span className={iconContainerStyles}>
91+
<Contrast
92+
UNSAFE_style={{
93+
position: 'absolute',
94+
inset: 0,
95+
opacity: isDark ? 0 : 1,
96+
transform: isDark ? 'rotate(-90deg) scale(0.5)' : 'rotate(0deg) scale(1)',
97+
transition: 'opacity 200ms ease-out, transform 200ms ease-out'
98+
}} />
99+
<Lighten
100+
UNSAFE_style={{
101+
position: 'absolute',
102+
inset: 0,
103+
opacity: isDark ? 1 : 0,
104+
transform: isDark ? 'rotate(0deg) scale(1)' : 'rotate(90deg) scale(0.5)',
105+
transition: 'opacity 200ms ease-out, transform 200ms ease-out'
106+
}} />
107+
</span>
108+
</Button>
109+
);
110+
}
111+
47112
export default function Header() {
48113
const {currentPage} = useRouter();
49114
const [searchOpen, setSearchOpen] = useState(false);
@@ -166,6 +231,12 @@ export default function Header() {
166231
<HeaderLink href={blog} target={subdirectory === 's2' ? '_blank' : ''} rel="noopener noreferrer">Blog</HeaderLink>
167232
<HeaderLink aria-label="GitHub" href="https://github.com/adobe/react-spectrum" target="_blank" rel="noopener noreferrer" ><GithubLogo /></HeaderLink>
168233
<HeaderLink aria-label="npm" href={`https://npmjs.com/${npm}`} target="_blank" rel="noopener noreferrer"><NpmLogo /></HeaderLink>
234+
{library !== 'react-aria' && (
235+
<>
236+
<Divider orientation="vertical" UNSAFE_style={{marginBlock: 4}} />
237+
<ColorSchemeToggle />
238+
</>
239+
)}
169240
</div>
170241
</div>
171242
</header>

0 commit comments

Comments
 (0)