import {PageLayout} from '@primer/react'
See storybook for fullscreen examples.
<PageLayout><PageLayout.Header><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Pane><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
<PageLayout><PageLayout.Header divider="line"><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content><Placeholder label="Content" height={240} /></PageLayout.Content>{/* You can change the divider based on the viewport width */}<PageLayout.Pane divider={{narrow: 'line'}}><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer divider="line"><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
<PageLayout padding="none" rowGap="none" columnGap="none"><PageLayout.Header padding="normal" divider="line"><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content padding="normal"><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Pane padding="normal" divider="line"><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer padding="normal" divider="line"><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
<PageLayout><PageLayout.Header><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Pane position="start"><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
<PageLayout><PageLayout.Header><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Pane position="start" hidden={{narrow: true}}><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
<PageLayout padding="condensed" rowGap="condensed" columnGap="condensed"><PageLayout.Header><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Pane><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
<PageLayout><PageLayout.Content><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Pane><Placeholder label="Pane" height={120} /></PageLayout.Pane></PageLayout>
<Box sx={{height: 320, overflowY: 'auto', border: '1px solid', borderColor: 'border.default'}}><PageLayout><PageLayout.Header><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content><Placeholder label="Content" height={420} /></PageLayout.Content><PageLayout.Pane sticky><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout></Box>
Add offsetHeader
prop to specify the height of the custom sticky header along with sticky
prop
<Box sx={{height: 320, overflowY: 'auto', border: '1px solid', borderColor: 'border.default'}}><Boxsx={{position: 'sticky',top: 0,height: 64,display: 'grid',placeItems: 'center',backgroundColor: 'canvas.subtle',borderBottom: '1px solid',borderColor: 'border.default',zIndex: 1,}}>Custom sticky header</Box><PageLayout><PageLayout.Content><Placeholder label="Content" height={320} /></PageLayout.Content><PageLayout.Pane position="start" sticky offsetHeader={64}><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout></Box>
NavList
PageLayout.Pane
by itself does not specify or provide landmark roles. Using
components that define their own landmark roles, such as NavList
, will provide
clear structure to the page that is not present by default in PageLayout.Pane
.
It's important to note that multiple landmark roles, such as multiple
<nav>
elements, should be uniquely labelled in order to clarify what the
navigation container is used for.
<PageLayout><PageLayout.Header><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Pane position="start" aria-label="Secondary navigation"><NavList><NavList.Item href="/" aria-current="page">Home</NavList.Item><NavList.Item href="/about">About</NavList.Item><NavList.Item href="/contact">Contact</NavList.Item></NavList></PageLayout.Pane><PageLayout.Content><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Footer><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
aria-label
Using aria-label
along with PageLayout.Header
, PageLayout.Content
, or PageLayout.Footer
creates a unique label for that landmark role that can make it easier to navigate between sections of content on a page.
<PageLayout><PageLayout.Header aria-label="header"><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content aria-label="content"><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Pane><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer aria-label="footer"><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
aria-labelledby
Using aria-labelledby
along with PageLayout.Header
, PageLayout.Content
, or PageLayout.Footer
creates a unique label for each landmark role by using the given id
to associate the landmark with the content with the corresponding id
. This is helpful when you have a visible item that visually communicates the type of content which you would like to associate to the landmark itself.
<PageLayout><PageLayout.Header aria-labelledby="header-label"><Placeholder id="header-label" label="Header" height={64} /></PageLayout.Header><PageLayout.Content aria-labelledby="main-label"><Placeholder id="main-label" label="Content" height={240} /></PageLayout.Content><PageLayout.Pane><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer aria-labelledby="footer-label"><Placeholder id="footer-label" label="Footer" height={64} /></PageLayout.Footer></PageLayout>
<PageLayout><PageLayout.Header><Placeholder label="Header" height={64} /></PageLayout.Header><PageLayout.Content><Placeholder label="Content" height={240} /></PageLayout.Content><PageLayout.Pane resizable><Placeholder label="Pane" height={120} /></PageLayout.Pane><PageLayout.Footer><Placeholder label="Footer" height={64} /></PageLayout.Footer></PageLayout>
The PageLayout
component uses landmark roles for PageLayout.Header
, PageLayout.Content
, and PageLayout.Footer
in order to make it easier for screen reader users to navigate between sections of the page.
Component | Landmark role |
---|---|
PageLayout.Header | banner |
PageLayout.Content | main |
PageLayout.Footer | contentinfo |
Each component may be labeled through either aria-label
or aria-labelledby
in order to provide a unique label for the landmark. This can be helpful when there are multiple landmarks of the same type on the page.
PageLayout.Pane
The PageLayout.Pane
component does not provide a default landmark role as the
content of this component may have different roles. When using this component,
consider the type of content being rendered inside of PageLayout.Pane
and if
it requires a landmark role. Common landmark roles include:
Some components, such as NavList
may already include a landmark role. In these
situation, these components use an appropriate landmark role and no further
action is needed when using PageLayout.Pane
.
Most screen readers provide a mechanism through which you can navigate between landmarks on the page. Typically, this is done through a specific keyboard shortcut or through an option in a rotor.
JAWS supports landmark regions and the details in which it presents them depends on the Web Verbosity Level setting. You can navigate to the next landmark on the page by pressing R
and the previous landmark by pressing Shift-R
.
NVDA supports landmark regions and you can navigate to the next landmark by using pressing D
and to the previous landmark by pressing Shift-D
. You may also list out the landmarks by pressing Insert-F7
.
VoiceOver supports assigning landmark roles to areas of a page. In order to navigate between landmarks, you can use the rotor.
On macOS, you can open the VoiceOver rotor by pressing VO-U
. You can navigate between lists to find the Landmarks
list by using the Right Arrow or Left Arrow key. From that list, you can use the Down Arrow and Up Arrow keys to navigate between landmarks identified on the page.
Name | Type | Default | Description |
---|---|---|---|
containerWidth | | 'full' | 'medium' | 'large' | 'xlarge' | 'xlarge' | The maximum width of the page container. |
padding | | 'none' | 'condensed' | 'normal' | 'normal' | The spacing between the outer edges of the page container and the viewport |
columnGap | | 'none' | 'condensed' | 'normal' | 'normal' | |
rowGap | | 'none' | 'condensed' | 'normal' | 'normal' | |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. |
Name | Type | Default | Description |
---|---|---|---|
aria-label | string | undefined | A unique label for the rendered banner landmark | |
aria-labelledby | string | undefined | An id to an element which uniquely labels the rendered banner landmark | |
padding | | 'none' | 'condensed' | 'normal' | 'none' | The amount of padding inside the header. |
divider | | 'none' | 'line' | { narrow?: | 'none' | 'line' | 'filled' regular?: | 'none' | 'line' wide?: | 'none' | 'line' } | 'none' | |
dividerWhenNarrow Deprecated | | 'inherit' | 'none' | 'line' | 'filled' | 'inherit' | Use the divider prop with a responsive value instead. |
hidden | | boolean | { narrow?: boolean regular?: boolean wide?: boolean } | false | Whether the header is hidden. |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. |
Name | Type | Default | Description |
---|---|---|---|
aria-label | string | undefined | A unique label for the rendered main landmark | |
aria-labelledby | string | undefined | An id to an element which uniquely labels the rendered main landmark | |
width | | 'full' | 'medium' | 'large' | 'xlarge' | 'full' | The maximum width of the content region. |
padding | | 'none' | 'condensed' | 'normal' | 'none' | The amount of padding inside the content. |
hidden | | boolean | { narrow?: boolean regular?: boolean wide?: boolean } | false | Whether the content is hidden. |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. |
Name | Type | Default | Description |
---|---|---|---|
position | | 'start' | 'end' | { narrow?: | 'start' | 'end' regular?: | 'start' | 'end' wide?: | 'start' | 'end' } | 'end' | |
positionWhenNarrow Deprecated | | 'inherit' | 'start' | 'end' | 'inherit' | Use the position prop with a responsive value instead. |
width | | 'small' | 'medium' | 'large' | 'medium' | The width of the pane. |
resizable | boolean | false | When true, the pane may be resized by the user. |
widthStorageKey | string | 'paneWidth' | Provide a key used by localStorage to persist the size of the pane on the client. |
sticky | boolean | false | Whether the pane should stick to the top of the screen while the content scrolls. |
offsetHeader | number | string | 0 | Use offsetHeader along with the sticky prop to push the sticky pane down to make room for a sticky header if necessary. Use the type `string` to specify the height with a unit i.e. 5rem; otherwise the type `number` will be taken as px. |
padding | | 'none' | 'condensed' | 'normal' | 'none' | The amount of padding inside the pane. |
divider | | 'none' | 'line' | { narrow?: | 'none' | 'line' | 'filled' regular?: | 'none' | 'line' wide?: | 'none' | 'line' } | 'none' | |
dividerWhenNarrow Deprecated | | 'inherit' | 'none' | 'line' | 'filled' | 'inherit' | Use the divider prop with a responsive value instead. |
hidden | | boolean | { narrow?: boolean regular?: boolean wide?: boolean } | false | Whether the pane is hidden. |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. | |
ref | React.RefObject<HTMLDivElement> | A ref to the element rendered by this component. |
Name | Type | Default | Description |
---|---|---|---|
aria-label | string | undefined | A unique label for the rendered contentinfo landmark | |
aria-labelledby | string | undefined | An id to an element which uniquely labels the rendered contentinfo landmark | |
padding | | 'none' | 'condensed' | 'normal' | 'none' | The amount of padding inside the footer. |
divider | | 'none' | 'line' | { narrow?: | 'none' | 'line' | 'filled' regular?: | 'none' | 'line' wide?: | 'none' | 'line' } | 'none' | |
dividerWhenNarrow Deprecated | | 'inherit' | 'none' | 'line' | 'filled' | 'inherit' | Use the divider prop with a responsive value instead. |
hidden | | boolean | { narrow?: boolean regular?: boolean wide?: boolean } | false | Whether the footer is hidden. |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. |