Skip to content

Commit e388aee

Browse files
feat: adding a skip to search anchor (#850)
Co-authored-by: Luke Karrys <luke@lukekarrys.com>
1 parent 298c866 commit e388aee

File tree

4 files changed

+51
-34
lines changed

4 files changed

+51
-34
lines changed

gatsby-browser.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
import {SKIP_TO_SEARCH_ID} from './src/constants'
2+
13
export {default as wrapPageElement} from './src/page'
24
export {default as wrapRootElement} from './src/root'
35

46
export const shouldUpdateScroll = ({routerProps}) => {
57
const {scrollUpdate = true} = routerProps.location.state ?? {}
68
return scrollUpdate
79
}
10+
11+
export const onRouteUpdate = ({location, prevLocation}) => {
12+
if (location.hash === `#${SKIP_TO_SEARCH_ID}` && prevLocation?.hash !== `#${SKIP_TO_SEARCH_ID}`) {
13+
document.getElementById(SKIP_TO_SEARCH_ID)?.focus()
14+
}
15+
}

src/components/skip-nav.js

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,50 @@
11
import React from 'react'
2-
import {Box} from '@primer/react'
2+
import {Box, themeGet} from '@primer/react'
33
import styled from 'styled-components'
44
import Link from './link'
5-
import {SCROLL_MARGIN_TOP} from '../constants'
5+
import {SCROLL_MARGIN_TOP, SKIP_TO_CONTENT_ID} from '../constants'
66

7-
const ID = 'skip-nav'
8-
9-
const SkipLinkBase = props => (
10-
<Link
11-
{...props}
12-
href={`#${ID}`}
13-
sx={{
14-
p: 3,
15-
color: 'fg.onEmphasis',
16-
backgroundColor: 'accent.emphasis',
17-
fontSize: 1,
18-
}}
19-
>
20-
Skip to content
21-
</Link>
22-
)
7+
export const SkipLink = styled(Link)`
8+
color: ${themeGet('colors.accent.emphasis')};
9+
padding: ${themeGet('space.1')};
10+
&:focus {
11+
text-decoration: underline;
12+
}
13+
`
2314

2415
// The following rules are to ensure that the element is visually hidden, unless
2516
// it has focus. This is the recommended way to hide content from:
2617
// https://webaim.org/techniques/css/invisiblecontent/#techniques
27-
export const SkipLink = styled(SkipLinkBase)`
18+
export const SkipBox = styled.div`
19+
display: inline-flex;
2820
z-index: 20;
29-
width: auto;
30-
height: auto;
31-
clip: auto;
32-
position: absolute;
33-
overflow: hidden;
3421
left: 10px;
22+
gap: 3px;
23+
position: absolute;
24+
transform: translateY(-100%);
25+
transition: transform 0.3s;
26+
padding: ${themeGet('space.2')};
27+
background-color: ${themeGet('colors.canvas.default')};
28+
border: 1px solid ${themeGet('colors.accent.emphasis')};
29+
border-top: 0;
30+
font-size: ${themeGet('fontSizes.1')};
31+
border-radius: 0 0 ${themeGet('radii.2')} ${themeGet('radii.2')};
32+
33+
34+
&:focus-within {
35+
transform: translateY(0%);
36+
}
37+
38+
& > * {
39+
margin-right: ${themeGet('space.1')};
40+
}
3541
36-
&:not(:focus) {
37-
clip: rect(1px, 1px, 1px, 1px);
38-
clip-path: inset(50%);
39-
height: 1px;
40-
width: 1px;
41-
margin: -1px;
42-
padding: 0;
42+
& > *:last-child {
43+
margin-right: 0;
4344
}
4445
`
4546

46-
const SkipNavBase = props => <Box id={ID} {...props} />
47+
const SkipNavBase = props => <Box id={SKIP_TO_CONTENT_ID} {...props} />
4748

4849
export const SkipNav = styled(SkipNavBase)`
4950
scroll-margin-top: ${SCROLL_MARGIN_TOP}px;

src/constants.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,8 @@ export const FULL_HEADER_HEIGHT = HEADER_HEIGHT + HEADER_BAR
66

77
export const SCROLL_MARGIN_TOP = FULL_HEADER_HEIGHT + 24
88

9+
export const SKIP_TO_CONTENT_ID = 'skip-to-content'
10+
11+
export const SKIP_TO_SEARCH_ID = 'search-box-input'
12+
913
export const CLI_PATH = '/cli'

src/page.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import {createGlobalStyle} from 'styled-components'
44
import Slugger from 'github-slugger'
55
import Header from './components/header'
66
import Sidebar from './components/sidebar'
7-
import {SkipLink} from './components/skip-nav'
7+
import {SkipBox, SkipLink} from './components/skip-nav'
8+
import {SKIP_TO_CONTENT_ID, SKIP_TO_SEARCH_ID} from './constants'
89

910
import {PageProvider} from './hooks/use-page'
1011
import Layout from './layout'
@@ -27,7 +28,10 @@ const PageElement = ({element, props}) => {
2728
return (
2829
<BaseStyles>
2930
<GlobalStyles />
30-
<SkipLink />
31+
<SkipBox>
32+
<SkipLink href={`#${SKIP_TO_SEARCH_ID}`}>Skip to search</SkipLink>
33+
<SkipLink href={`#${SKIP_TO_CONTENT_ID}`}>Skip to content</SkipLink>
34+
</SkipBox>
3135
<PageProvider value={page}>
3236
<Box sx={{display: 'flex', flexDirection: 'column', minHeight: '100vh'}}>
3337
<Header />

0 commit comments

Comments
 (0)