chore: Convert sx(deprecated) to style

Michael Smith committed Jan 8, 2026 at 13:30 UTC a9fd12fa43c9d73e7202c07028e2d70c276fd7c5
32 files changed +853 -390
src/components/breadcrumbs.js
+3 -1
@@ -4,6 +4,8 @@ import {Link as GatsbyLink} from 'gatsby'
4 import * as getNav from '../util/get-nav'
5 import usePage from '../hooks/use-page'
6
7 +import * as styles from './breadcrumbs.module.css'
8 +
9 const BreadcrumbItem = ({item, path}) => {
10 const selected = getNav.isPathForItem(path, getNav.getItem(item.url))
11
@@ -23,7 +25,7 @@ const Breadcrumbs = () => {
25 }
26
27 return (
26 - <PrimerBreadcrumbs sx={{mb: 4}}>
28 + <PrimerBreadcrumbs className={styles.PrimerBreadcrumbs}>
29 {items.map(item => (
30 <BreadcrumbItem key={item.url} item={item} path={pathname} />
31 ))}
src/components/breadcrumbs.module.css new
+3
@@ -0,0 +1,3 @@
1 +.PrimerBreadcrumbs {
2 + margin-bottom: 24px;
3 +}
src/components/header.js
+12 -24
@@ -5,11 +5,13 @@ import * as Search from './search'
5 import NavDrawer from './nav-drawer'
6 import Link from './link'
7 import useSearch from '../hooks/use-search'
8 -import {HEADER_HEIGHT, HEADER_BAR, Z_INDEX} from '../constants'
8 +import {HEADER_BAR, Z_INDEX} from '../constants'
9 import headerNavItems from '../../content/header-nav.yml'
10 import {DarkTheme} from '../theme'
11 import SiteTitle from './site-title'
12
13 +import * as styles from './header.module.css'
14 +
15 const NpmHeaderBar = styled(Box)`
16 height: ${HEADER_BAR}px;
17 background-image: linear-gradient(139deg, #fb8817, #ff4b01, #c12127, #e02aff);
@@ -19,38 +21,24 @@ function Header() {
21 const search = useSearch()
22
23 return (
22 - <DarkTheme sx={{top: 0, position: 'sticky', zIndex: Z_INDEX.HEADER}}>
24 + <DarkTheme style={{zIndex: Z_INDEX.HEADER}} className={styles.DarkTheme}>
25 <NpmHeaderBar />
24 - <Box
25 - as="header"
26 - sx={{
27 - display: 'flex',
28 - height: `${HEADER_HEIGHT}px`,
29 - px: [3, null, null, 4],
30 - alignItems: 'center',
31 - justifyContent: 'space-between',
32 - bg: 'canvas.default',
33 - border: '1px solid',
34 - borderLeftWidth: 0,
35 - borderRightWidth: 0,
36 - borderColor: 'border.muted',
37 - }}
38 - >
39 - <Box sx={{display: 'flex', alignItems: 'center'}}>
40 - <SiteTitle logo={true} sx={{mr: 4}} />
41 - <Box sx={{display: ['none', null, null, 'block'], ml: 4}}>
26 + <Box as="header" className={styles.headerBox}>
27 + <Box className={styles.Box}>
28 + <SiteTitle logo={true} className={styles.SiteTitle} />
29 + <Box className={styles.searchDesktop}>
30 <Search.Desktop {...search} />
31 </Box>
32 </Box>
45 - <Box sx={{display: 'flex'}}>
46 - <Box sx={{display: ['none', null, null, 'flex'], alignItems: 'center'}}>
33 + <Box className={styles.Box_1}>
34 + <Box className={styles.navDesktop}>
35 {headerNavItems.map((item, index) => (
48 - <Link key={index} href={item.url} sx={{display: 'block', ml: 4, color: 'fg.default'}}>
36 + <Link key={index} href={item.url} className={styles.Link}>
37 {item.title}
38 </Link>
39 ))}
40 </Box>
53 - <Box sx={{display: ['flex', null, null, 'none']}}>
41 + <Box className={styles.navMobile}>
42 <Search.Mobile {...search} />
43 <NavDrawer />
44 </Box>
src/components/header.module.css new
+82
@@ -0,0 +1,82 @@
1 +.Box {
2 + display: flex;
3 + align-items: center;
4 +}
5 +
6 +.Box_1 {
7 + display: flex;
8 +}
9 +
10 +.DarkTheme {
11 + top: 0;
12 + position: sticky;
13 +}
14 +
15 +/* mr: 4 = 24px */
16 +.SiteTitle {
17 + margin-right: 24px;
18 +}
19 +
20 +/* ml: 4 = 24px */
21 +/* color: 'fg.default' in dark_dimmed = #E1E4E8 */
22 +.Link {
23 + display: block;
24 + margin-left: 24px;
25 + color: #e1e4e8;
26 +}
27 +
28 +/* px: [3, null, null, 4] = 16px base, 24px at lg */
29 +/* bg: 'canvas.default' in dark_dimmed theme = #333333 */
30 +.headerBox {
31 + display: flex;
32 + height: 56px;
33 + padding-left: 16px;
34 + padding-right: 16px;
35 + align-items: center;
36 + justify-content: space-between;
37 + background-color: #333333;
38 + border: 1px solid;
39 + border-left-width: 0;
40 + border-right-width: 0;
41 + border-color: #444d56;
42 +}
43 +
44 +@media (min-width: 1012px) {
45 + .headerBox {
46 + padding-left: 24px;
47 + padding-right: 24px;
48 + }
49 +}
50 +
51 +/* ml: 4 = 24px */
52 +.searchDesktop {
53 + display: none;
54 + margin-left: 24px;
55 +}
56 +
57 +@media (min-width: 1012px) {
58 + .searchDesktop {
59 + display: block;
60 + }
61 +}
62 +
63 +.navDesktop {
64 + display: none;
65 + align-items: center;
66 +}
67 +
68 +@media (min-width: 1012px) {
69 + .navDesktop {
70 + display: flex;
71 + }
72 +}
73 +
74 +.navMobile {
75 + display: flex;
76 +}
77 +
78 +@media (min-width: 1012px) {
79 + .navMobile {
80 + display: none;
81 + }
82 +}
src/components/link.js
+18 -21
@@ -3,6 +3,10 @@ import {Link as PrimerLink} from '@primer/react'
3 import {Link as GatsbyLink} from 'gatsby'
4 import omit from '../util/omit'
5
6 +import * as styles from './link.module.css'
7 +
8 +import {clsx} from 'clsx'
9 +
10 const FALLBACK = `http://_${Math.random().toString().slice(2)}._${Math.random().toString().slice(2)}`
11
12 const getLocalPath = href => {
@@ -26,34 +30,27 @@ const GatsbyLinkWithoutSxProps = React.forwardRef(function GatsbyLinkWithoutSxPr
30 return <GatsbyLink ref={ref} {...omit(props, 'sx', 'underline', 'hoverColor', 'muted')} />
31 })
32
29 -const Link = React.forwardRef(function Link({to, href, showUnderline = false, sx, ...props}, ref) {
33 +const Link = React.forwardRef(function Link({to, href, showUnderline = false, className, ...props}, ref) {
34 const localPath = getLocalPath(href)
31 -
32 - const linkStyles = {
33 - ...sx,
34 - ...(showUnderline && {textDecoration: 'underline'}),
35 - }
35 + const combinedClassName = showUnderline ? clsx(className, styles.showUnderline) : className
36
37 if (to || localPath !== null) {
38 - return <PrimerLink ref={ref} as={GatsbyLinkWithoutSxProps} to={to || localPath} sx={linkStyles} {...props} />
38 + return (
39 + <PrimerLink
40 + ref={ref}
41 + as={GatsbyLinkWithoutSxProps}
42 + to={to || localPath}
43 + className={combinedClassName}
44 + {...props}
45 + />
46 + )
47 }
48
41 - return <PrimerLink ref={ref} href={href} sx={linkStyles} {...props} />
49 + return <PrimerLink ref={ref} href={href} className={combinedClassName} {...props} />
50 })
51
44 -export const LinkNoUnderline = React.forwardRef(function LinkNoUnderline({sx, ...props}, ref) {
45 - return (
46 - <Link
47 - ref={ref}
48 - sx={{
49 - ...sx,
50 - '&:hover, &:focus': {
51 - textDecoration: 'none',
52 - },
53 - }}
54 - {...props}
55 - />
56 - )
52 +export const LinkNoUnderline = React.forwardRef(function LinkNoUnderline({className, ...props}, ref) {
53 + return <Link ref={ref} className={clsx(styles.Link, className)} {...props} />
54 })
55
56 export default Link
src/components/link.module.css new
+10
@@ -0,0 +1,10 @@
1 +.Link {
2 + &:hover,
3 + &:focus {
4 + text-decoration: none;
5 + }
6 +}
7 +
8 +.showUnderline {
9 + text-decoration: underline;
10 +}
src/components/nav-drawer.js
+10 -60
@@ -9,6 +9,8 @@ import {FocusOn} from 'react-focus-on'
9 import {HEADER_BAR, HEADER_HEIGHT} from '../constants'
10 import SiteTitle from './site-title'
11
12 +import * as styles from './nav-drawer.module.css'
13 +
14 const Drawer = ({isOpen, onDismiss, children}) => (
15 <AnimatePresence>
16 {isOpen ? (
@@ -24,14 +26,6 @@ const Drawer = ({isOpen, onDismiss, children}) => (
26 >
27 <FocusOn returnFocus={true} onEscapeKey={onDismiss}>
28 <Box
27 - sx={{
28 - position: 'fixed',
29 - top: 0,
30 - right: 0,
31 - bottom: 0,
32 - left: 0,
33 - bg: 'overlay.backdrop',
34 - }}
29 key="overlay"
30 as={motion.div}
31 initial={{opacity: 0}}
@@ -39,22 +33,17 @@ const Drawer = ({isOpen, onDismiss, children}) => (
33 exit={{opacity: 0}}
34 transition={{type: 'tween'}}
35 onClick={onDismiss}
36 + className={styles.Box}
37 />
38 <Box
44 - sx={{
45 - position: 'fixed',
46 - top: `${HEADER_BAR}px`,
47 - right: 0,
48 - bottom: 0,
49 - width: 300,
50 - zIndex: 1,
51 - }}
39 + style={{top: `${HEADER_BAR}px`}}
40 key="drawer"
41 as={motion.div}
42 initial={{x: '100%'}}
43 animate={{x: 0}}
44 exit={{x: '100%'}}
45 transition={{type: 'tween', duration: 0.2}}
46 + className={styles.Box_1}
47 >
48 {children}
49 </Box>
@@ -76,58 +65,19 @@ function NavDrawer() {
65
66 return (
67 <>
79 - <Button
80 - aria-label="Menu"
81 - aria-expanded={open}
82 - onClick={() => setOpen(true)}
83 - sx={{
84 - ml: 3,
85 - '&:focus-visible': {
86 - outline: '2px solid',
87 - outlineColor: '-webkit-focus-ring-color',
88 - outlineOffset: '1px',
89 - },
90 - }}
91 - >
68 + <Button aria-label="Menu" aria-expanded={open} onClick={() => setOpen(true)} className={styles.Button}>
69 <ThreeBarsIcon />
70 </Button>
71 <LightTheme as={Drawer} isOpen={open} onDismiss={() => setOpen(false)}>
95 - <Box
96 - sx={{
97 - display: 'flex',
98 - flexDirection: 'column',
99 - height: '100%',
100 - bg: 'canvas.backdrop',
101 - overflow: 'auto',
102 - }}
103 - style={{WebkitOverflowScrolling: 'touch'}}
104 - >
105 - <Box
106 - sx={{
107 - display: 'flex',
108 - flexDirection: 'column',
109 - flex: '1 0 auto',
110 - color: 'fg.default',
111 - bg: 'canvas.default',
112 - }}
113 - >
114 - <DarkTheme
115 - sx={{
116 - color: 'fg.default',
117 - bg: 'canvas.default',
118 - height: `${HEADER_HEIGHT}px`,
119 - px: 3,
120 - alignItems: 'center',
121 - justifyContent: 'space-between',
122 - display: 'flex',
123 - }}
124 - >
72 + <Box style={{WebkitOverflowScrolling: 'touch'}} className={styles.Box_2}>
73 + <Box className={styles.Box_3}>
74 + <DarkTheme style={{height: `${HEADER_HEIGHT}px`}} className={styles.DarkTheme}>
75 <SiteTitle />
76 <Button aria-label="Close" onClick={() => setOpen(false)}>
77 <XIcon />
78 </Button>
79 </DarkTheme>
130 - <Box sx={{display: 'flex', flexDirection: 'column'}}>
80 + <Box className={styles.Box_4}>
81 <NavItems />
82 </Box>
83 </Box>
src/components/nav-drawer.module.css new
+57
@@ -0,0 +1,57 @@
1 +.Box {
2 + position: fixed;
3 + top: 0;
4 + right: 0;
5 + bottom: 0;
6 + left: 0;
7 + background: rgba(140, 149, 159, 0.15);
8 +}
9 +
10 +.Box_1 {
11 + position: fixed;
12 + right: 0;
13 + bottom: 0;
14 + width: 300px;
15 + z-index: 1;
16 +}
17 +
18 +.Box_2 {
19 + display: flex;
20 + flex-direction: column;
21 + height: 100%;
22 + background: #333333;
23 + overflow: auto;
24 +}
25 +
26 +.Box_3 {
27 + display: flex;
28 + flex-direction: column;
29 + flex: 1 0 auto;
30 + color: #e1e4e8;
31 + background: #333333;
32 +}
33 +
34 +.Box_4 {
35 + display: flex;
36 + flex-direction: column;
37 +}
38 +
39 +.Button {
40 + margin-left: 16px;
41 +}
42 +
43 +.Button:focus-visible {
44 + outline: 2px solid;
45 + outline-color: -webkit-focus-ring-color;
46 + outline-offset: 1px;
47 +}
48 +
49 +.DarkTheme {
50 + color: #e1e4e8;
51 + background: #333333;
52 + padding-left: 16px;
53 + padding-right: 16px;
54 + align-items: center;
55 + justify-content: space-between;
56 + display: flex;
57 +}
src/components/nav-items.js
+5 -3
@@ -8,6 +8,8 @@ import headerNavItems from '../../content/header-nav.yml'
8 import usePage from '../hooks/use-page'
9 import useSiteMetadata from '../hooks/use-site-metadata'
10
11 +import * as styles from './nav-items.module.css'
12 +
13 const NavItem = ({item, path, depth}) => {
14 const isCurrent = getNav.isActiveUrl(path, item.url)
15 const items = getNav.getHierarchy(item, item.url, {hideVariants: true})
@@ -18,7 +20,7 @@ const NavItem = ({item, path, depth}) => {
20 to={item.url}
21 defaultOpen={items && isCurrent}
22 aria-current={isCurrent ? 'page' : null}
21 - sx={{fontSize: depth === 1 ? 2 : 1}}
23 + className={depth === 1 ? styles.NavList_Item_depth1 : styles.NavList_Item_depth2}
24 >
25 {item.title}
26 {items ? (
@@ -43,7 +45,7 @@ const NavItems = ({items, path, depth = 1}) => (
45 )
46
47 const ExternalNavItem = ({title, ...props}) => (
46 - <NavList.Item sx={{fontSize: 2}} {...props}>
48 + <NavList.Item className={styles.NavList_Item} {...props}>
49 {title}
50 <NavList.TrailingVisual>
51 <LinkExternalIcon />
@@ -63,7 +65,7 @@ const Navigation = () => {
65 <NavItems items={items} path={pathname} />
66 <NavList.Divider />
67 {headerNavItems.map(item => (
66 - <Box key={item.title} sx={{display: ['flex', null, null, 'none']}}>
68 + <Box key={item.title} className={styles.headerNavItem}>
69 <ExternalNavItem title={item.title} href={item.url} />
70 </Box>
71 ))}
src/components/nav-items.module.css new
+21
@@ -0,0 +1,21 @@
1 +.NavList_Item {
2 + font-size: 16px;
3 +}
4 +
5 +.NavList_Item_depth1 {
6 + font-size: 16px;
7 +}
8 +
9 +.NavList_Item_depth2 {
10 + font-size: 14px;
11 +}
12 +
13 +.headerNavItem {
14 + display: flex;
15 +}
16 +
17 +@media (min-width: 1012px) {
18 + .headerNavItem {
19 + display: none;
20 + }
21 +}
src/components/page-footer.js
+9 -17
@@ -6,6 +6,8 @@ import {PencilIcon} from '@primer/octicons-react'
6 import Link from './link'
7 import usePage from '../hooks/use-page'
8
9 +import * as styles from './page-footer.module.css'
10 +
11 const months = [
12 'January',
13 'February',
@@ -32,20 +34,20 @@ const Contributors = ({contributors = [], latestCommit}) => {
34
35 return (
36 <>
35 - <Box sx={{display: 'flex', alignItems: 'center', flexWrap: 'wrap'}}>
36 - <Text sx={{mr: 2}}>
37 + <Box className={styles.Box}>
38 + <Text className={styles.Text}>
39 {contributors.length} {pluralize('contributor', contributors.length)}
40 </Text>
41 {contributors.map(login => (
42 <Tooltip key={login} text={login}>
41 - <Link href={`https://github.com/${login}`} sx={{lineHeight: 'condensedUltra', mr: 2}}>
43 + <Link href={`https://github.com/${login}`} className={styles.Link}>
44 <Avatar src={`https://github.com/${login}.png?size=40`} alt={login} />
45 </Link>
46 </Tooltip>
47 ))}
48 </Box>
49 {latestCommit ? (
48 - <Text sx={{fontSize: 1, mt: 1}}>
50 + <Text className={styles.Text_1}>
51 Last edited by{' '}
52 <Link href={`https://github.com/${latestCommit.login}`} showUnderline={true}>
53 {latestCommit.login}
@@ -68,21 +70,11 @@ const PageFooter = () => {
70 }
71
72 return (
71 - <Box
72 - sx={{
73 - borderWidth: 0,
74 - borderTopWidth: 1,
75 - borderRadius: 0,
76 - mt: 8,
77 - py: 5,
78 - borderStyle: 'solid',
79 - borderColor: 'border.default',
80 - }}
81 - >
82 - <Box sx={{display: 'grid', gap: 4}}>
73 + <Box className={styles.Box_1}>
74 + <Box className={styles.Box_2}>
75 {editUrl ? (
76 <Link href={editUrl}>
85 - <Octicon icon={PencilIcon} sx={{mr: 2}} />
77 + <Octicon icon={PencilIcon} className={styles.Text} />
78 Edit this page on GitHub
79 </Link>
80 ) : null}
src/components/page-footer.module.css new
+35
@@ -0,0 +1,35 @@
1 +.Box {
2 + display: flex;
3 + align-items: center;
4 + flex-wrap: wrap;
5 +}
6 +
7 +.Box_1 {
8 + border-width: 0;
9 + border-top-width: 1px;
10 + border-radius: 0;
11 + margin-top: 64px;
12 + padding-top: 32px;
13 + padding-bottom: 32px;
14 + border-style: solid;
15 + border-color: #444d56;
16 +}
17 +
18 +.Box_2 {
19 + display: grid;
20 + gap: 16px;
21 +}
22 +
23 +.Text {
24 + margin-right: 8px;
25 +}
26 +
27 +.Link {
28 + line-height: 1.25;
29 + margin-right: 8px;
30 +}
31 +
32 +.Text_1 {
33 + font-size: 14px;
34 + margin-top: 4px;
35 +}
src/components/search.js
+26 -84
@@ -12,12 +12,14 @@ import * as getNav from '../util/get-nav'
12 import omit from '../util/omit'
13 import {announce} from '../util/aria-live'
14
15 +import * as styles from './search.module.css'
16 +
17 const SearchResults = ({results, getItemProps, highlightedIndex}) => {
18 const siteMetadata = useSiteMetadata()
19 if (!results || results.length === 0) {
20 announce('No results')
21 return (
20 - <Box sx={{fontSize: 2, px: 3, py: 3}} aria-live="polite">
22 + <Box aria-live="polite" className={styles.Box}>
23 No results
24 </Box>
25 )
@@ -46,10 +48,10 @@ const SearchResults = ({results, getItemProps, highlightedIndex}) => {
48 active={highlightedIndex === index}
49 >
50 <Box
49 - sx={{display: 'flex', flexDirection: 'column', flex: '0 0 auto'}}
51 aria-label={`${item.title}${hierarchy.length ? ` in ${hierarchy.map(s => s.shortName || s.title).join(' / ')}` : ` in ${siteMetadata.shortName}`}, ${index + 1} of ${results.length}`}
52 + className={styles.Box_1}
53 >
52 - <Text sx={{fontSize: 0}}>
54 + <Text className={styles.Text}>
55 {hierarchy.length ? hierarchy.map(s => s.shortName || s.title).join(' / ') : siteMetadata.shortName}
56 </Text>
57 <Text>{item.title}</Text>
@@ -66,28 +68,16 @@ export const Desktop = props => {
68 const {getInputProps, getMenuProps, resultsOpen, ...rest} = props
69
70 return (
69 - <Box sx={{position: 'relative'}}>
71 + <Box className={styles.Box_2}>
72 <TextInput
71 - sx={{width: '240px'}}
73 placeholder={`Search ${siteMetadata.title}`}
74 aria-label={`Search ${siteMetadata.title}`}
75 + className={styles.TextInput}
76 {...getInputProps()}
77 />
76 - <Box sx={{position: 'absolute', left: 0, right: 0, pt: 1}} {...getMenuProps()}>
78 + <Box className={styles.Box_3} {...getMenuProps()}>
79 {resultsOpen ? (
78 - <LightTheme
79 - sx={{
80 - overflow: 'auto',
81 - minWidth: 300,
82 - maxHeight: '70vh',
83 - boxShadow: 'shadow.large',
84 - borderColor: 'border.muted',
85 - bg: 'canvas.overlay',
86 - borderRadius: 2,
87 - borderWidth: 1,
88 - borderStyle: 'solid',
89 - }}
90 - >
80 + <LightTheme className={styles.LightTheme}>
81 <SearchResults {...rest} />
82 </LightTheme>
83 ) : null}
@@ -120,67 +110,43 @@ export const Mobile = ({
110 aria-label="Search"
111 aria-expanded={isMobileSearchOpen}
112 onClick={handleSearchToggle}
123 - sx={{
124 - '&:focus-visible': {
125 - outline: '2px solid',
126 - outlineColor: '-webkit-focus-ring-color',
127 - outlineOffset: '1px',
128 - },
129 - }}
113 + className={styles.Button}
114 >
115 <SearchIcon />
116 </Button>
117 )}
134 -
118 <AnimatePresence>
119 {isMobileSearchOpen ? (
120 <FocusOn returnFocus={true} onEscapeKey={() => resetAndClose(true)}>
121 <Box
139 - sx={{
140 - position: 'fixed',
122 + style={{
123 top: `${HEADER_BAR}px`,
142 - left: 0,
143 - right: 0,
144 - bottom: 0,
124 zIndex: Z_INDEX.SEARCH_OVERLAY,
125 }}
126 + className={styles.Box_4}
127 >
128 <Box
149 - sx={{
150 - position: 'absolute',
151 - top: 0,
152 - left: 0,
153 - right: 0,
154 - bottom: 0,
155 - bg: 'overlay.backdrop',
156 - zIndex: -1,
157 - }}
129 key="search-backdrop"
130 as={motion.div}
131 initial={{opacity: 0}}
132 animate={{opacity: 1}}
133 transition={{type: 'tween'}}
134 onClick={() => resetAndClose(true)}
135 + className={styles.Box_5}
136 {...getCloseAnimation({opacity: 0})}
137 />
166 - <Box sx={{display: 'flex', flexDirection: 'column', height: resultsOpen ? '100%' : 'auto'}}>
138 + <Box
139 + style={{
140 + height: resultsOpen ? '100%' : 'auto',
141 + }}
142 + className={styles.Box_6}
143 + >
144 <Box
168 - sx={{
169 - display: 'flex',
170 - color: 'fg.default',
145 + style={{
146 height: `${HEADER_HEIGHT}px`,
172 - flex: '0 0 auto',
173 - px: 3,
174 - alignItems: 'center',
175 - border: '1px solid',
176 - borderTopWidth: 0,
177 - borderLeftWidth: 0,
178 - borderRightWidth: 0,
179 - borderColor: 'border.muted',
180 - position: 'relative',
181 - bg: 'canvas.default',
147 zIndex: Z_INDEX.SEARCH_OVERLAY + 1,
148 }}
149 + className={styles.Box_7}
150 >
151 <motion.div
152 key="search-box"
@@ -190,22 +156,12 @@ export const Mobile = ({
156 transition={{type: 'tween', ease: 'easeOut', duration: 0.2}}
157 style={{width: '100%', originX: '100%'}}
158 >
193 - <Box
194 - sx={{
195 - position: 'absolute',
196 - top: 0,
197 - bottom: 0,
198 - left: 0,
199 - width: '70px',
200 - bg: 'canvas.default',
201 - zIndex: '-1',
202 - }}
203 - />
159 + <Box className={styles.Box_8} />
160 <TextInput
161 leadingVisual={SearchIcon}
162 placeholder={`Search ${siteMetadata.title}`}
163 aria-label={`Search ${siteMetadata.title}`}
208 - sx={{width: '100%'}}
164 + className={styles.TextInput_1}
165 {...getInputProps()}
166 />
167 </motion.div>
@@ -216,15 +172,7 @@ export const Mobile = ({
172 animate={{opacity: 1}}
173 {...getCloseAnimation({scaleX: 0})}
174 transition={{type: 'tween', ease: 'easeOut', duration: 0.2}}
219 - sx={{
220 - position: 'absolute',
221 - top: 0,
222 - bottom: 0,
223 - right: 0,
224 - width: '70px',
225 - bg: 'canvas.default',
226 - zIndex: '-1',
227 - }}
175 + className={styles.Box_9}
176 />
177 <Box
178 key="button"
@@ -234,22 +182,16 @@ export const Mobile = ({
182 {...getCloseAnimation({opacity: 0})}
183 transition={{type: 'tween', ease: 'easeOut', duration: 0.2}}
184 >
237 - <Button sx={{ml: 3}} aria-label="Cancel" onClick={() => resetAndClose(false)}>
185 + <Button aria-label="Cancel" onClick={() => resetAndClose(false)} className={styles.Button_1}>
186 <XIcon />
187 </Button>
188 </Box>
189 </Box>
190 <LightTheme
243 - sx={{
244 - display: 'flex',
245 - bg: 'canvas.default',
246 - flexDirection: 'column',
247 - flex: '1 1 auto',
248 - overflow: 'auto',
249 - }}
191 style={{
192 WebkitOverflowScrolling: 'touch',
193 }}
194 + className={styles.LightTheme_1}
195 {...getMenuProps()}
196 >
197 {resultsOpen ? <SearchResults {...rest} /> : null}
src/components/search.module.css new
+121
@@ -0,0 +1,121 @@
1 +.Box {
2 + font-size: 16px;
3 + padding: 16px;
4 +}
5 +
6 +.Box_1 {
7 + display: flex;
8 + flex-direction: column;
9 + flex: 0 0 auto;
10 +}
11 +
12 +.Box_2 {
13 + position: relative;
14 +}
15 +
16 +.Box_3 {
17 + position: absolute;
18 + left: 0;
19 + right: 0;
20 + padding-top: 4px;
21 +}
22 +
23 +.Box_4 {
24 + position: fixed;
25 + left: 0;
26 + right: 0;
27 + bottom: 0;
28 +}
29 +
30 +.Box_5 {
31 + position: absolute;
32 + top: 0;
33 + left: 0;
34 + right: 0;
35 + bottom: 0;
36 + background: rgba(140, 149, 159, 0.15);
37 + z-index: -1;
38 +}
39 +
40 +.Box_6 {
41 + display: flex;
42 + flex-direction: column;
43 +}
44 +
45 +.Box_7 {
46 + display: flex;
47 + color: #1f2328;
48 + flex: 0 0 auto;
49 + padding-left: 16px;
50 + padding-right: 16px;
51 + align-items: center;
52 + border: 1px solid;
53 + border-top-width: 0;
54 + border-left-width: 0;
55 + border-right-width: 0;
56 + border-color: #d0d7de;
57 + position: relative;
58 + background: #ffffff;
59 +}
60 +
61 +.Box_8 {
62 + position: absolute;
63 + top: 0;
64 + bottom: 0;
65 + left: 0;
66 + width: 70px;
67 + background: #ffffff;
68 + z-index: -1;
69 +}
70 +
71 +.Box_9 {
72 + position: absolute;
73 + top: 0;
74 + bottom: 0;
75 + right: 0;
76 + width: 70px;
77 + background: #ffffff;
78 + z-index: -1;
79 +}
80 +
81 +.Text {
82 + font-size: 12px;
83 +}
84 +
85 +.TextInput {
86 + width: 240px;
87 +}
88 +
89 +.LightTheme {
90 + overflow: auto;
91 + min-width: 300px;
92 + max-height: 70vh;
93 + box-shadow: 0 8px 24px rgba(140, 149, 159, 0.2);
94 + border-color: #d0d7de;
95 + background: #ffffff;
96 + border-radius: 6px;
97 + border-width: 1px;
98 + border-style: solid;
99 +}
100 +
101 +.Button:focus-visible {
102 + outline: 2px solid;
103 + outline-color: -webkit-focus-ring-color;
104 + outline-offset: 1px;
105 +}
106 +
107 +.TextInput_1 {
108 + width: 100%;
109 +}
110 +
111 +.Button_1 {
112 + margin-left: 16px;
113 +}
114 +
115 +.LightTheme_1 {
116 + display: flex;
117 + background: #ffffff;
118 + flex-direction: column;
119 + flex: 1 1 auto;
120 + overflow: auto;
121 +}
src/components/sidebar.js
+6 -15
@@ -3,6 +3,8 @@ import React from 'react'
3 import NavItems from './nav-items'
4 import {FULL_HEADER_HEIGHT} from '../constants'
5
6 +import * as styles from './sidebar.module.css'
7 +
8 function usePersistentScroll(id) {
9 const ref = React.useRef()
10
@@ -30,25 +32,14 @@ function usePersistentScroll(id) {
32 const Sidebar = () => (
33 <Box
34 role="navigation"
33 - sx={{
34 - position: 'sticky',
35 + style={{
36 top: `${FULL_HEADER_HEIGHT}px`,
37 height: `calc(100vh - ${FULL_HEADER_HEIGHT}px)`,
37 - width: 270,
38 }}
39 + className={styles.Box}
40 >
40 - <Box
41 - {...usePersistentScroll('sidebar')}
42 - sx={{
43 - overflow: 'auto',
44 - borderWidth: 0,
45 - borderRightWidth: 1,
46 - height: '100%',
47 - borderStyle: 'solid',
48 - borderColor: 'border.subtle',
49 - }}
50 - >
51 - <Box sx={{display: 'flex', flexDirection: 'column'}}>
41 + <Box {...usePersistentScroll('sidebar')} className={styles.Box_1}>
42 + <Box className={styles.Box_2}>
43 <NavItems />
44 </Box>
45 </Box>
src/components/sidebar.module.css new
+18
@@ -0,0 +1,18 @@
1 +.Box {
2 + position: sticky;
3 + width: 270px;
4 +}
5 +
6 +.Box_1 {
7 + overflow: auto;
8 + border-width: 0;
9 + border-right-width: 1px;
10 + height: 100%;
11 + border-style: solid;
12 + border-color: #444d56;
13 +}
14 +
15 +.Box_2 {
16 + display: flex;
17 + flex-direction: column;
18 +}
src/components/site-title.js
+8 -15
@@ -3,8 +3,11 @@ import {Box} from '@primer/react'
3 import Link from './link'
4 import useSiteMetadata from '../hooks/use-site-metadata'
5
6 -const NpmLogo = ({size, sx}) => (
7 - <Box sx={{...sx, color: 'logoBg'}}>
6 +import * as styles from './site-title.module.css'
7 +import {clsx} from 'clsx'
8 +
9 +const NpmLogo = ({size, className}) => (
10 + <Box className={clsx(styles.Box, className)}>
11 <svg height={size} width={size} viewBox="0 0 700 700" fill="currentColor" aria-hidden="true">
12 <polygon fill="currentColor" points="0,700 700,700 700,0 0,0" />
13 <polygon fill="#ffffff" points="150,550 350,550 350,250 450,250 450,550 550,550 550,150 150,150 " />
@@ -12,22 +15,12 @@ const NpmLogo = ({size, sx}) => (
15 </Box>
16 )
17
15 -const SiteTitle = ({logo, sx}) => {
18 +const SiteTitle = ({logo, className}) => {
19 const siteMetadata = useSiteMetadata()
20
21 return (
19 - <Link
20 - to="/"
21 - sx={{
22 - fontWeight: 'bold',
23 - fontSize: 2,
24 - color: 'fg.default',
25 - display: 'flex',
26 - alignItems: 'center',
27 - ...sx,
28 - }}
29 - >
30 - {logo ? <NpmLogo size="32" sx={{display: 'flex', mr: 3}} /> : null}
22 + <Link to="/" className={clsx(styles.Link, className)}>
23 + {logo ? <NpmLogo size="32" className={styles.NpmLogo} /> : null}
24 {siteMetadata.title}
25 </Link>
26 )
src/components/site-title.module.css new
+16
@@ -0,0 +1,16 @@
1 +.Box {
2 + color: #cb3837;
3 +}
4 +
5 +.Link {
6 + font-weight: bold;
7 + font-size: 16px;
8 + color: #e1e4e8;
9 + display: flex;
10 + align-items: center;
11 +}
12 +
13 +.NpmLogo {
14 + display: flex;
15 + margin-right: 16px;
16 +}
src/components/table-of-contents.js
+12 -53
@@ -1,9 +1,11 @@
1 import React from 'react'
2 import {Heading, Box, Details, useDetails, Button, NavList} from '@primer/react'
3 import {ChevronDownIcon, ChevronRightIcon} from '@primer/octicons-react'
4 -import {SCROLL_MARGIN_TOP} from '../constants'
4 import usePage from '../hooks/use-page'
5
6 +import * as styles from './table-of-contents.module.css'
7 +import {clsx} from 'clsx'
8 +
9 const TableOfContentsItems = ({items, depth}) => (
10 <>
11 {items.map((item, index) => (
@@ -25,15 +27,8 @@ const TableOfContentsItems = ({items, depth}) => (
27 </>
28 )
29
28 -const TableOfContents = ({'aria-labelledby': ariaLabelledBy, items, depth = 1, ...props}) => (
29 - <NavList
30 - aria-labelledby={ariaLabelledBy}
31 - {...props}
32 - sx={{
33 - textDecoration: 'underline',
34 - ...props.sx,
35 - }}
36 - >
30 +const TableOfContents = ({'aria-labelledby': ariaLabelledBy, items, depth = 1, className, ...props}) => (
31 + <NavList aria-labelledby={ariaLabelledBy} {...props} className={clsx(styles.NavList, className)}>
32 <TableOfContentsItems items={items} depth={depth} />
33 </NavList>
34 )
@@ -49,32 +44,12 @@ const withTableOfContents = Component => {
44 export const Mobile = withTableOfContents(({items}) => {
45 const {getDetailsProps, open} = useDetails({defaultOpen: true})
46 return (
52 - <Box sx={{display: ['block', null, 'none'], mb: 3, mt: 4}}>
53 - <Details
54 - {...getDetailsProps()}
55 - sx={{
56 - borderStyle: 'solid',
57 - borderWidth: 1,
58 - borderColor: 'border.muted',
59 - borderRadius: 2,
60 - }}
61 - >
47 + <Box className={styles.tocMobile}>
48 + <Details {...getDetailsProps()} className={styles.Details}>
49 <Button
50 as="summary"
64 - sx={{
65 - borderTopWidth: 0,
66 - borderLeftWidth: 0,
67 - borderRightWidth: 0,
68 - borderBottomWidth: open ? 1 : 0,
69 - borderBottomLeftRadius: open ? 0 : 2,
70 - borderBottomRightRadius: open ? 0 : 2,
71 - '&:focus-visible': {
72 - outline: '2px solid',
73 - outlineColor: '-webkit-focus-ring-color',
74 - outlineOffset: '1px',
75 - },
76 - }}
51 leadingIcon={open ? ChevronDownIcon : ChevronRightIcon}
52 + className={`${styles.Button} ${open ? styles.buttonOpen : styles.buttonClosed}`}
53 >
54 Table of contents
55 </Button>
@@ -85,28 +60,12 @@ export const Mobile = withTableOfContents(({items}) => {
60 })
61
62 export const Desktop = withTableOfContents(({items}) => (
88 - <Box
89 - sx={{
90 - width: 220,
91 - flex: '0 0 auto',
92 - marginLeft: [null, 7, 8, 9],
93 - display: ['none', null, 'block'],
94 - position: 'sticky',
95 - top: SCROLL_MARGIN_TOP,
96 - maxHeight: `calc(100vh - ${SCROLL_MARGIN_TOP}px)`,
97 - }}
98 - >
99 - <Heading as="h2" sx={{fontSize: 1, display: 'inline-block', fontWeight: 'bold'}} id="toc-heading">
63 + <Box className={styles.tocDesktop}>
64 + <Heading as="h2" id="toc-heading" className={styles.Heading}>
65 Table of contents
66 </Heading>
102 - <Box
103 - sx={{
104 - // extra pixels to account for table of contents title height
105 - maxHeight: `calc(100% - 24px)`,
106 - overflowY: 'auto',
107 - }}
108 - >
109 - <TableOfContents aria-labelledby="toc-heading" items={items} sx={{ml: -2}} />
67 + <Box className={styles.Box}>
68 + <TableOfContents aria-labelledby="toc-heading" items={items} className={styles.TableOfContents} />
69 </Box>
70 </Box>
71 ))
src/components/table-of-contents.module.css new
+97
@@ -0,0 +1,97 @@
1 +.Box {
2 + overflow-y: auto;
3 + /* extra pixels to account for table of contents title height */
4 + max-height: calc(100% - 24px);
5 +}
6 +
7 +.NavList {
8 + text-decoration: underline;
9 +}
10 +
11 +/* borderRadius: 2 = 6px */
12 +.Details {
13 + border-style: solid;
14 + border-width: 1px;
15 + border-color: #444d56;
16 + border-radius: 6px;
17 +}
18 +
19 +.Button {
20 + border-top-width: 0;
21 + border-left-width: 0;
22 + border-right-width: 0;
23 +}
24 +
25 +.Button:focus-visible {
26 + outline: 2px solid;
27 + outline-color: -webkit-focus-ring-color;
28 + outline-offset: 1px;
29 +}
30 +
31 +.buttonOpen {
32 + border-bottom-width: 1px;
33 + border-bottom-left-radius: 0;
34 + border-bottom-right-radius: 0;
35 +}
36 +
37 +/* borderRadius: 2 = 6px, but since 0 border, it's effectively a smaller radius for consistency */
38 +.buttonClosed {
39 + border-bottom-width: 0;
40 + border-bottom-left-radius: 6px;
41 + border-bottom-right-radius: 6px;
42 +}
43 +
44 +/* fontSize: 1 = 14px */
45 +.Heading {
46 + font-size: 14px;
47 + display: inline-block;
48 + font-weight: bold;
49 +}
50 +
51 +/* ml: -2 = -8px */
52 +.TableOfContents {
53 + margin-left: -8px;
54 +}
55 +
56 +/* mb: 3 = 16px, mt: 4 = 24px */
57 +.tocMobile {
58 + display: block;
59 + margin-bottom: 16px;
60 + margin-top: 24px;
61 +}
62 +
63 +@media (min-width: 768px) {
64 + .tocMobile {
65 + display: none;
66 + }
67 +}
68 +
69 +/* marginLeft: [null, 7, 8, 9] = 48px, 64px, 80px */
70 +.tocDesktop {
71 + width: 220px;
72 + flex: 0 0 auto;
73 + margin-left: 48px;
74 + display: none;
75 + position: sticky;
76 + top: 90px;
77 + max-height: calc(100vh - 90px);
78 +}
79 +
80 +@media (min-width: 544px) {
81 + .tocDesktop {
82 + margin-left: 48px;
83 + }
84 +}
85 +
86 +@media (min-width: 768px) {
87 + .tocDesktop {
88 + display: block;
89 + margin-left: 64px;
90 + }
91 +}
92 +
93 +@media (min-width: 1012px) {
94 + .tocDesktop {
95 + margin-left: 80px;
96 + }
97 +}
src/components/variant-select.js
+6 -13
@@ -6,6 +6,8 @@ import {LinkNoUnderline} from './link'
6 import useLocationChange from '../hooks/use-location-change'
7 import styled from 'styled-components'
8
9 +import * as styles from './variant-select.module.css'
10 +
11 const StyledOverlay = styled(ActionMenu.Overlay)`
12 background-color: var(--bgColor-default, #ffffff) !important;
13 border-color: var(--borderColor-default, #d0d7de);
@@ -45,25 +47,16 @@ const VariantMenu = ({title, latest, current, prerelease, legacy}) => {
47 if (locationChange.change && getNav.didVariantChange(locationChange.previous, locationChange.current)) {
48 setOpen(false)
49 }
50 + // eslint-disable-next-line react-hooks/exhaustive-deps -- granular deps are intentional; locationChange.current is a URL string, not a ref
51 }, [locationChange.change, locationChange.current, locationChange.previous])
52
53 return (
54 <>
52 - <Box as="p" sx={{m: 0}} id={labelId}>
55 + <Box as="p" id={labelId} className={styles.Box}>
56 Select CLI Version:
57 </Box>
58 <ActionMenu anchorRef={anchorRef} open={open} onOpenChange={setOpen}>
56 - <ActionMenu.Button
57 - aria-describedby={labelId}
58 - sx={{
59 - width: ['100%', null, 'auto'],
60 - '&:focus-visible': {
61 - outline: '2px solid',
62 - outlineColor: '-webkit-focus-ring-color',
63 - outlineOffset: '1px',
64 - },
65 - }}
66 - >
59 + <ActionMenu.Button aria-describedby={labelId} className={styles.menuButton}>
60 {title}
61 </ActionMenu.Button>
62 <StyledOverlay width="auto" onEscape={() => setOpen(false)}>
@@ -133,7 +126,7 @@ const useVariants = () => {
126 const VariantSelect = () => {
127 const variants = useVariants()
128 return variants ? (
136 - <Box sx={{mt: 2, mb: 3}}>
129 + <Box className={styles.Box_1}>
130 <VariantMenu {...variants} />
131 </Box>
132 ) : null
src/components/variant-select.module.css new
+24
@@ -0,0 +1,24 @@
1 +.Box {
2 + margin: 0;
3 +}
4 +
5 +.Box_1 {
6 + margin-top: 8px;
7 + margin-bottom: 16px;
8 +}
9 +
10 +.menuButton {
11 + width: 100%;
12 +}
13 +
14 +.menuButton:focus-visible {
15 + outline: 2px solid;
16 + outline-color: -webkit-focus-ring-color;
17 + outline-offset: 1px;
18 +}
19 +
20 +@media (min-width: 768px) {
21 + .menuButton {
22 + width: auto;
23 + }
24 +}
src/layout.js
+13 -19
@@ -10,25 +10,28 @@ import usePage from './hooks/use-page'
10 import {DarkTheme} from './theme'
11 import {SkipNav} from './components/skip-nav'
12
13 -const Container = ({sx, ...props}) => <Box sx={{width: '100%', maxWidth: '960px', ...sx}} {...props} />
13 +import * as styles from './layout.module.css'
14 +import {clsx} from 'clsx'
15 +
16 +const Container = ({className, ...props}) => <Box className={clsx(styles.Box, className)} {...props} />
17
18 const HeroLayout = ({children}) => {
19 const {title, description} = useSiteMetadata()
20
21 return (
19 - <Box as="main" sx={{width: '100%'}}>
20 - <DarkTheme sx={{bg: 'canvas.inset', py: [0, 4, 5, 6]}}>
21 - <Container sx={{p: 5, mx: 'auto'}}>
22 - <Heading as="h1" sx={{color: 'fg.default', fontSize: 7, m: 0}}>
22 + <Box as="main" className={styles.Box_1}>
23 + <DarkTheme className={styles.DarkTheme}>
24 + <Container className={styles.Container}>
25 + <Heading as="h1" className={styles.Heading}>
26 {title}
27 </Heading>
25 - <Text as="p" sx={{m: 0, color: 'fg.onEmphasis', fontSize: 4}}>
28 + <Text as="p" className={styles.Text}>
29 {description}
30 </Text>
31 </Container>
32 </DarkTheme>
33 <SkipNav />
31 - <Container sx={{p: 5, mx: 'auto'}}>{children}</Container>
34 + <Container className={styles.Container}>{children}</Container>
35 </Box>
36 )
37 }
@@ -36,21 +39,12 @@ const HeroLayout = ({children}) => {
39 const DefaultLayout = ({children}) => {
40 const {title, description} = usePage().frontmatter
41 return (
39 - <Box
40 - sx={{
41 - justifyContent: 'center',
42 - display: 'flex',
43 - maxWidth: '1200px',
44 - mx: 'auto',
45 - width: '100%',
46 - p: [4, 5, 6, 7],
47 - }}
48 - >
42 + <Box className={styles.Box_2}>
43 <Container as="main">
50 - <Box sx={{mb: 4}}>
44 + <Box className={styles.Box_3}>
45 <Breadcrumbs />
46 <H1 autolink={false}>{title}</H1>
53 - {description ? <Box sx={{fontSize: 3, mb: 3}}>{description}</Box> : null}
47 + {description ? <Box className={styles.Box_4}>{description}</Box> : null}
48 </Box>
49 <SkipNav />
50 <VariantSelect />
src/layout.module.css new
+98
@@ -0,0 +1,98 @@
1 +.Box {
2 + width: 100%;
3 + max-width: 960px;
4 +}
5 +
6 +.Box_1 {
7 + width: 100%;
8 +}
9 +
10 +/* DefaultLayout wrapper - p: [4, 5, 6, 7] = 24px, 32px, 40px, 48px */
11 +.Box_2 {
12 + justify-content: center;
13 + display: flex;
14 + max-width: 1200px;
15 + margin-left: auto;
16 + margin-right: auto;
17 + width: 100%;
18 + padding: 24px;
19 +}
20 +
21 +@media (min-width: 544px) {
22 + .Box_2 {
23 + padding: 32px;
24 + }
25 +}
26 +
27 +@media (min-width: 768px) {
28 + .Box_2 {
29 + padding: 40px;
30 + }
31 +}
32 +
33 +@media (min-width: 1012px) {
34 + .Box_2 {
35 + padding: 48px;
36 + }
37 +}
38 +
39 +/* mb: 4 = 24px */
40 +.Box_3 {
41 + margin-bottom: 24px;
42 +}
43 +
44 +/* fontSize: 3 = 20px, mb: 3 = 16px */
45 +.Box_4 {
46 + font-size: 20px;
47 + margin-bottom: 16px;
48 +}
49 +
50 +/* bg: 'canvas.inset', py: [0, 4, 5, 6] = 0, 24px, 32px, 40px */
51 +/* canvas.inset in dark mode - slightly darker than #333333 */
52 +.DarkTheme {
53 + background-color: #292929;
54 + padding-top: 0;
55 + padding-bottom: 0;
56 +}
57 +
58 +@media (min-width: 544px) {
59 + .DarkTheme {
60 + padding-top: 24px;
61 + padding-bottom: 24px;
62 + }
63 +}
64 +
65 +@media (min-width: 768px) {
66 + .DarkTheme {
67 + padding-top: 32px;
68 + padding-bottom: 32px;
69 + }
70 +}
71 +
72 +@media (min-width: 1012px) {
73 + .DarkTheme {
74 + padding-top: 40px;
75 + padding-bottom: 40px;
76 + }
77 +}
78 +
79 +/* p: 5 = 32px, mx: auto */
80 +.Container {
81 + padding: 32px;
82 + margin-left: auto;
83 + margin-right: auto;
84 +}
85 +
86 +/* color: 'fg.default' in dark_dimmed = #E1E4E8, fontSize: 7 = 48px, m: 0 */
87 +.Heading {
88 + color: #e1e4e8;
89 + font-size: 48px;
90 + margin: 0;
91 +}
92 +
93 +/* m: 0, color: 'fg.onEmphasis' = white, fontSize: 4 = 24px */
94 +.Text {
95 + margin: 0;
96 + color: rgba(255, 255, 255, 0.9);
97 + font-size: 24px;
98 +}
src/mdx/code.js
+11 -48
@@ -6,6 +6,8 @@ import styled from 'styled-components'
6 import {CheckIcon, CopyIcon} from '@primer/octicons-react'
7 import copyToClipboard from 'copy-to-clipboard'
8 import {announce} from '../util/aria-live'
9 +import * as styles from './code.module.css'
10 +import {clsx} from 'clsx'
11 ;(typeof global !== 'undefined' ? global : window).Prism = Prism
12 require('prismjs/components/prism-bash')
13
@@ -24,23 +26,16 @@ const ClipboardCopy = ({value, ...props}) => {
26
27 return (
28 <Button
27 - {...props}
29 aria-label="Copy to clipboard"
30 + size="small"
31 onClick={() => {
32 copyToClipboard(value)
33 setCopied(true)
34 announce(`Copied to clipboard`)
35 }}
34 - sx={{
35 - ...props.sx,
36 - '&:focus-visible': {
37 - outline: '2px solid',
38 - outlineColor: '-webkit-focus-ring-color',
39 - outlineOffset: '1px',
40 - },
41 - }}
36 + className={clsx(styles.Button, props.className)}
37 >
43 - <Octicon icon={copied ? CheckIcon : CopyIcon} sx={{color: copied ? 'success.fg' : 'fg.muted'}} />
38 + <Octicon icon={copied ? CheckIcon : CopyIcon} style={{color: copied ? '#1a7f37' : '#656d76'}} />
39 </Button>
40 )
41 }
@@ -60,46 +55,14 @@ const colorMap = {
55 'token string': '#db1068',
56 }
57
63 -const MonoText = props => <Text sx={{fontFamily: 'mono', fontSize: 1}} {...props} />
58 +const MonoText = props => <Text className={styles.Text} {...props} />
59
60 const CodeBlock = ({children, code, className, style}) => (
66 - <Box
67 - sx={{
68 - // Make <pre> adjust to the width of the container
69 - // https://stackoverflow.com/a/14406386
70 - display: 'table',
71 - tableLayout: 'fixed',
72 - width: '100%',
73 - mb: 3,
74 - }}
75 - >
76 - <Box
77 - style={style}
78 - sx={{
79 - ...(code ? {display: 'flex', justifyContent: 'space-between', flexDirection: 'row-reverse'} : {}),
80 - borderRadius: 2,
81 - borderStyle: 'solid',
82 - borderWidth: 1,
83 - borderColor: 'border.muted',
84 - }}
85 - >
86 - {code ? (
87 - <ClipboardCopy
88 - value={code}
89 - sx={{
90 - borderRadius: 0,
91 - borderStyle: 'solid',
92 - borderWidth: 1,
93 - borderColor: 'border.muted',
94 - marginTop: '-1px',
95 - marginRight: '-1px',
96 - borderTopRightRadius: 2,
97 - borderBottomLeftRadius: 2,
98 - }}
99 - />
100 - ) : null}
101 - <Box sx={{m: 0, p: 3, overflowX: 'auto'}}>
102 - <Box as="pre" className={className} tabIndex={0} sx={{m: 0}}>
61 + <Box className={styles.Box}>
62 + <Box style={style} className={styles.Box_1}>
63 + {code ? <ClipboardCopy value={code} className={styles.ClipboardCopy} /> : null}
64 + <Box className={styles.Box_2}>
65 + <Box as="pre" className={clsx(className, styles.Box_3)} tabIndex={0}>
66 {children}
67 </Box>
68 </Box>
src/mdx/code.module.css new
+60
@@ -0,0 +1,60 @@
1 +.Box {
2 + display: table;
3 + table-layout: fixed;
4 + width: 100%;
5 + margin-bottom: 16px;
6 +}
7 +
8 +.Box_1 {
9 + position: relative;
10 + border-radius: 6px;
11 + border-style: solid;
12 + border-width: 1px;
13 + border-color: #d0d7de;
14 +}
15 +
16 +.Box_2 {
17 + margin: 0;
18 + padding: 16px;
19 + overflow-x: auto;
20 +}
21 +
22 +.Box_3 {
23 + margin: 0;
24 +}
25 +
26 +.Button {
27 + position: absolute;
28 + top: 0;
29 + right: 0;
30 +}
31 +
32 +.Button:focus-visible {
33 + outline: 2px solid;
34 + outline-color: -webkit-focus-ring-color;
35 + outline-offset: 1px;
36 +}
37 +
38 +.Text {
39 + font-family:
40 + ui-monospace,
41 + SFMono-Regular,
42 + SF Mono,
43 + Menlo,
44 + Consolas,
45 + Liberation Mono,
46 + monospace;
47 + font-size: 14px;
48 +}
49 +
50 +.ClipboardCopy {
51 + position: absolute;
52 + top: -1px;
53 + right: -1px;
54 + border-radius: 0;
55 + border-style: solid;
56 + border-width: 1px;
57 + border-color: #d0d7de;
58 + border-top-right-radius: 6px;
59 + border-bottom-left-radius: 6px;
60 +}
src/mdx/components.js
+7 -12
@@ -10,6 +10,10 @@ import usePage from '../hooks/use-page'
10 import SiteLink, {LinkNoUnderline} from '../components/link'
11 import Code from './code'
12
13 +import * as styles from './components.module.css'
14 +
15 +import {clsx} from 'clsx'
16 +
17 export {Code}
18 export {default as Index} from './nav-hierarchy'
19
@@ -42,18 +46,9 @@ const StyledHeading = styled(Heading)`
46
47 const HeaderLink = ({autolink, children, ...props}) =>
48 autolink ? (
45 - <LinkNoUnderline {...props} sx={{color: 'inherit'}}>
49 + <LinkNoUnderline {...props} className={styles.LinkNoUnderline}>
50 {children}
47 - <Octicon
48 - icon={LinkIcon}
49 - className="octicon-link"
50 - sx={{
51 - ml: 2,
52 - color: 'fg.muted',
53 - // !important is needed here to override default icon styles
54 - verticalAlign: 'middle !important',
55 - }}
56 - />
51 + <Octicon icon={LinkIcon} className={clsx('octicon-link', styles.Octicon)} />
52 </LinkNoUnderline>
53 ) : (
54 children
@@ -304,10 +299,10 @@ export const Screenshot = styled(RequiredImage)`
299 export const YouTube = ({id}) => (
300 <Box
301 as="iframe"
307 - sx={{aspectRatio: '16 / 9', width: '100%'}}
302 title="YouTube video"
303 src={`https://www.youtube.com/embed/${id}`}
304 frameBorder="0"
305 allowFullScreen
306 + className={styles.Box}
307 />
308 )
src/mdx/components.module.css new
+14
@@ -0,0 +1,14 @@
1 +.Box {
2 + aspect-ratio: 16 / 9;
3 + width: 100%;
4 +}
5 +
6 +.LinkNoUnderline {
7 + color: inherit;
8 +}
9 +
10 +.Octicon {
11 + margin-left: 8px;
12 + color: #656d76;
13 + vertical-align: middle !important;
14 +}
src/mdx/nav-hierarchy.js
+3 -1
@@ -4,6 +4,8 @@ import Link from '../components/link'
4 import * as getNav from '../util/get-nav'
5 import usePage from '../hooks/use-page'
6
7 +import * as styles from './nav-hierarchy.module.css'
8 +
9 const HierarchyItem = ({item, maxDepth, depth, hideVariants}) => {
10 const hierarchy = getNav.getHierarchy(item, null, {hideVariants})
11
@@ -12,7 +14,7 @@ const HierarchyItem = ({item, maxDepth, depth, hideVariants}) => {
14 <Link key={item.title} to={item.url}>
15 {item.title}
16 </Link>
15 - {item.description ? <Box sx={{fontSize: 1, mb: 1}}>{item.description}</Box> : null}
17 + {item.description ? <Box className={styles.Box}>{item.description}</Box> : null}
18 {hierarchy ? (
19 <Hierarchy items={hierarchy} maxDepth={maxDepth} depth={depth + 1} hideVariants={hideVariants} />
20 ) : null}
src/mdx/nav-hierarchy.module.css new
+4
@@ -0,0 +1,4 @@
1 +.Box {
2 + font-size: 14px;
3 + margin-bottom: 4px;
4 +}
src/page.js
+22 -4
@@ -5,12 +5,30 @@ import Slugger from 'github-slugger'
5 import Header from './components/header'
6 import Sidebar from './components/sidebar'
7 import {SkipBox, SkipLink} from './components/skip-nav'
8 -import {SKIP_TO_CONTENT_ID, SKIP_TO_SEARCH_ID} from './constants'
8 +import {
9 + SKIP_TO_CONTENT_ID,
10 + SKIP_TO_SEARCH_ID,
11 + HEADER_HEIGHT,
12 + HEADER_BAR,
13 + FULL_HEADER_HEIGHT,
14 + SCROLL_MARGIN_TOP,
15 + Z_INDEX,
16 +} from './constants'
17
18 import {PageProvider} from './hooks/use-page'
19 import Layout from './layout'
20
21 +import * as styles from './page.module.css'
22 +
23 const GlobalStyles = createGlobalStyle`
24 + :root {
25 + --header-height: ${HEADER_HEIGHT}px;
26 + --header-bar: ${HEADER_BAR}px;
27 + --full-header-height: ${FULL_HEADER_HEIGHT}px;
28 + --scroll-margin-top: ${SCROLL_MARGIN_TOP}px;
29 + --z-index-header: ${Z_INDEX.HEADER};
30 + --z-index-search-overlay: ${Z_INDEX.SEARCH_OVERLAY};
31 + }
32 body {
33 color: ${themeGet('colors.fg.default')};
34 background-color: ${themeGet('colors.canvas.default')};
@@ -33,10 +51,10 @@ const PageElement = ({element, props}) => {
51 <SkipLink href={`#${SKIP_TO_CONTENT_ID}`}>Skip to content</SkipLink>
52 </SkipBox>
53 <PageProvider value={page}>
36 - <Box sx={{display: 'flex', flexDirection: 'column', minHeight: '100vh'}}>
54 + <Box className={styles.Box}>
55 <Header />
38 - <Box sx={{zIndex: 0, display: 'flex', flex: '1 1 auto', flexDirection: 'row'}}>
39 - <Box sx={{display: ['none', null, null, 'block']}}>
56 + <Box className={styles.Box_1}>
57 + <Box className={styles.sidebarContainer}>
58 <Sidebar />
59 </Box>
60 <Layout>{element}</Layout>
src/page.module.css new
+22
@@ -0,0 +1,22 @@
1 +.Box {
2 + display: flex;
3 + flex-direction: column;
4 + min-height: 100vh;
5 +}
6 +
7 +.Box_1 {
8 + z-index: 0;
9 + display: flex;
10 + flex: 1 1 auto;
11 + flex-direction: row;
12 +}
13 +
14 +.sidebarContainer {
15 + display: none;
16 +}
17 +
18 +@media (min-width: 1012px) {
19 + .sidebarContainer {
20 + display: block;
21 + }
22 +}