Side Panel
Side Panels are containers that anchor to the left or right side of the screen.
Component Type
Container
Platform
Web
Component
Sana Canvas
Delivery Channels
Web
Version
16.1.7Experience Surfaces
Page Body Inline
Side Panel (Main) vs. Side Panel (Preview)
@workday/canvas-kit-react) documented here on this page. The Side Panel in the Preview package (@workday/canvas-kit-preview-react) will eventually be removed.Anatomy

- Tooltip (Required if using Expand/Collapse Button): Tooltip used to provide additional visual affordance for the Expand/Collapse Button.
- Expand/Collapse Button (Optional): Icon only Tertiary Button variant used to open or close the Side Panel.
- Container: Rectangular container that houses the contents of the Side Panel. The container spans the full height of the viewport and are flush to the left or right edge of the screen.
Usage Guidance
- Side Panels can either push and resize content as it expands within a page or float over page content. See the Expandable pattern for more detailed information on horizontal animation.
- When the content of the Side Panel exceeds the height of the viewport, overflow behavior such as a scrollbar is introduced.
- Consider the behavior of Side Panels at different responsive breakpoints and in different use cases. In use cases where the Side Panel is used to edit content within the page, keeping the Side Panel open and resizing the page content may be ideal. For use cases where a Side Panel is not required to remain open, enabling a Side Panel to automatically collapse when it reaches smaller screen sizes will prevent the panel from taking up too much of the screen until the user wants to take action on it.
- When using the Expand/Collapse Button within the Side Panel, use a Tooltip to provide additional affordance that the icon is interactive and to improve accessibility for the Side Panel. When the Side Panel is expanded, tooltip text reads “Collapse” and when collapsed, the tooltip reads “Expand.”
When to Use
Although the elements within a Side Panel are highly configurable to support various use cases, they are commonly used in the following ways:
Local Page Navigation

- Provides users with a way to navigate within an area of your product.
- Typically tied to the main content region.
- Often collapsible but not closeable, meaning the Panel remains on the page and cannot be dismissed.
Editing and Displaying Additional Information

- Ideal for editing specific content within the page or displaying additional information that supports the main content area.
- Can be temporary, meaning the Panel may disappear when the associated content on the main page is no longer in focus.
Panel Overlays


- When Panels open over an overlay, the user cannot interact with the main page. The overlay helps users focus attention on the contents of the Panel, making it ideal for higher-level navigation and editing or displaying additional information while minimizing distractions.
- A Side Panel that opens over an overlay has a close Button but not a collapse. Activating the Button closes the Panel and the Overlay so the user can return focus to the main page.
Do’s and Don’ts

Caution
Consider screen real estate when multiple Side Panels are present within a page. When multiple Side Panels are open at the same time, it may be overwhelming to users as their page content shrinks.

Do
When using the Expand / Collapse Button with the Side Panel, provide a Tooltip to label the icon only Tertiary Button variant. When the Side Panel is expanded, the Tooltip contains the text "Collapse" and when collapsed, the Tooltip reads "Expand."
Examples
Basic Example
SidePanel is composed of three parts:
- The panel container (with an optional
modelprop) - A heading (
SidePanel.Heading) for the panel that is visually hidden when the panel is collapsed - A toggle button (
SidePanel.ToggleButton) to control the expand / collapse states
Bidirectional support is built into SidePanel. As seen in the example below, CSS Flexbox flips the
page layout and the panel’s contents. SidePanel also has logic to flip the position and direction
of the ToggleButton as well as the direction of the expand / collapse animation. If you’re using
CSS Flexbox for layouts and using the provided components, you shouldn’t have to provide any custom
logic or styling for bidirectional support.
Tasks Panel
import {rocketIcon} from '@workday/canvas-expressive-icons-web';
import {ExpressiveIcon} from '@workday/canvas-kit-react/icon';
import {Flex} from '@workday/canvas-kit-react/layout';
import {SidePanel} from '@workday/canvas-kit-react/side-panel';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
const flexHeadingStyles = createStyles({
alignItems: 'center',
gap: system.gap.sm,
});
const viewPortStyles = createStyles({
height: px2rem(320),
});
export default () => {
return (
<Flex cs={viewPortStyles}>
<SidePanel>
<SidePanel.Heading>
<Flex cs={flexHeadingStyles}>
<ExpressiveIcon icon={rocketIcon} size="xs" />
Tasks Panel
</Flex>
</SidePanel.Heading>
<SidePanel.ToggleButton aria-label="Collapse View" />
</SidePanel>
</Flex>
);
};
Hidden Name
SidePanel’s <section> element container should always have an accessible name to help screen
reader users understand the purpose of the panel. For this reason, we recommend using the
SidePanel.Heading component and setting the hidden prop to true. This will visually hide the
heading while keeping it accessible to screen readers.
Tasks Panel
Side Panel with a hidden title text.
import * as React from 'react';
import {Flex} from '@workday/canvas-kit-react/layout';
import {SidePanel, useSidePanelModel} from '@workday/canvas-kit-react/side-panel';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
const stylesOverride = {
viewport: createStyles({
height: px2rem(320),
}),
main: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
flex: 1,
flexBasis: 'auto',
}),
};
export default () => {
const model = useSidePanelModel({
onStateTransition: state => {
console.log(`state is: ${state}`);
},
});
return (
<Flex cs={stylesOverride.viewport}>
<SidePanel model={model}>
<SidePanel.ToggleButton aria-label="Collapse View" />
<SidePanel.Heading hidden size="small">
Tasks Panel
</SidePanel.Heading>
</SidePanel>
<Flex as="main" cs={stylesOverride.main}>
<Text as="p" typeLevel="body.large">
Side Panel with a hidden title text.
</Text>
</Flex>
</Flex>
);
};
Variants
SidePanel supports three variants, which you supply as a top-level variant prop. They differ
only in surface color and depth — the layout, animation, and accessibility behavior is identical.
| Variant | Surface | Depth | Use for |
|---|---|---|---|
standard | system.legacy.color.surface.navigation | None | The default. Panels that are part of the page layout, such as navigation. |
alternative | system.legacy.color.surface.raised | None | Panels that need to stand out from the page background while staying in-flow. |
overlay | system.legacy.color.surface.default | 6 | Panels that need to look lifted off the page. Styling only — see below. |
Alternative
The alternative variant uses a raised surface color with no depth. Because it doesn’t cast a
shadow, it remains part of the page layout and is a good fit when the panel sits next to content on
a tinted or gray background.
Alternative Panel
import {Flex} from '@workday/canvas-kit-react/layout';
import {SidePanel} from '@workday/canvas-kit-react/side-panel';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
const viewportStyles = createStyles({
height: px2rem(320),
});
export default () => {
return (
<Flex cs={viewportStyles}>
<SidePanel variant="alternative">
<SidePanel.ToggleButton aria-label="Collapse View" />
<SidePanel.Heading size="small">Alternative Panel</SidePanel.Heading>
</SidePanel>
</Flex>
);
};
Overlay
The overlay variant is a visual treatment only. It gives the panel the default surface color
and depth 6 so it reads as floating above the page, which is the same elevation Modal.Card uses.
The name describes how the panel looks; it does not turn SidePanel into a dialog. This variant is
the alternate variant from @workday/canvas-kit-preview-react, renamed — it has never carried any
behavior beyond the surface and shadow.
Reach for it when a panel needs to look lifted off the page — for example an overlay menu or a
filter panel you position over content with your own CSS. SidePanel still renders a <section>
that participates in the page and the tab order exactly like standard and alternative do.
Setting variant="overlay" does not add any of the following, and SidePanel has no props to
opt into them:
role="dialog"oraria-modal— the element stays a plain<section>named by its heading- A focus trap, initial focus, or return focus
- A backdrop / overlay element
- Close on Escape or close on outside click
aria-hiddenon sibling content, or body scroll locking- Positioning — the panel is in normal flow until you position it yourself
Accessibility Note: If you need a panel that genuinely blocks the rest of the page, use
Modalinstead of assembling that behavior aroundSidePanel.ModalcomposesuseInitialFocus,useReturnFocus,useFocusTrap,useCloseOnEscape,useCloseOnOverlayClick,useAssistiveHideSiblings, anduseDisableBodyScrolland is tested as a unit. Those hooks are built onusePopupModeland cannot be added touseSidePanelModel, so a hand-rolled version on top ofSidePanelwill not get you the same guarantees.
Keyboard and focus behavior: because the variant adds no dialog semantics, keyboard behavior is identical to the other two variants and no focus management is expected of you:
- Tab / Shift + Tab move through the panel’s focusable elements in DOM order and then continue into the rest of the page. Focus is not trapped.
- Focus does not move when the panel expands or collapses. It stays on
SidePanel.ToggleButton, which reports the new state througharia-pressed. - Escape does nothing. Collapsing happens only through the toggle button or
model.events.collapse(). - Collapsing hides the panel’s content visually but does not remove it from the DOM. Keep the toggle button as the first focusable element in the panel — see Accessibility — so keyboard users reach the control before the content it hides.
If you do position this variant over other content, treat it as a non-modal region: the content
behind it stays reachable by keyboard and by screen reader, and users can tab out of the panel while
it visually covers the page. That mismatch between what the panel looks like and how it behaves is
the reason to prefer Modal for anything the user must dismiss before continuing.
Overlay Panel
Toggle the content direction
import {SecondaryButton} from '@workday/canvas-kit-react/button';
import {CanvasProvider} from '@workday/canvas-kit-react/common';
import {Flex} from '@workday/canvas-kit-react/layout';
import {SidePanel} from '@workday/canvas-kit-react/side-panel';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
// local helper hook for setting content direction;
import {useDirection} from './useDirection';
const stylesOverride = {
viewport: createStyles({
height: px2rem(320),
backgroundColor: system.color.bg.alt.default,
}),
main: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
flex: 1,
flexBasis: 'auto',
}),
};
export default () => {
const {direction, toggleDirection} = useDirection();
return (
<CanvasProvider dir={direction}>
<Flex cs={stylesOverride.viewport}>
<SidePanel variant="overlay">
<SidePanel.ToggleButton aria-label="Collapse View" />
<SidePanel.Heading size="small">Overlay Panel</SidePanel.Heading>
</SidePanel>
<Flex as="main" cs={stylesOverride.main}>
<Text as="p" typeLevel="body.large">
Toggle the content direction
</Text>
<SecondaryButton onClick={toggleDirection}>
Set to {direction === 'ltr' ? 'Right-to-Left' : 'Left-to-Right'}
</SecondaryButton>
</Flex>
</Flex>
</CanvasProvider>
);
};
External Control
Sometimes you’ll want to control SidePanel’s expand / collapse behavior from outside the
component. You can use the model’s events (model.events.expand() and model.events.collapse()) to
programmatically control the panel.
Notes about accessibility
When using external controls, be mindful of accessibility:
- Use
aria-pressedon toggle buttons to indicate the current state - The
SidePanel.ToggleButtoninside the panel automatically receives the correct ARIA attributes - External buttons should have their own accessible labels (don’t rely on
aria-labelledbypointing to the panel’s label)
In the following example, we use the model’s transitionState to determine the button’s pressed
state and call model.events.expand() or model.events.collapse() on click.
Task Panel
Control the panel externally
import {SecondaryButton} from '@workday/canvas-kit-react/button';
import {Flex} from '@workday/canvas-kit-react/layout';
import {SidePanel, useSidePanelModel} from '@workday/canvas-kit-react/side-panel';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
const stylesOverride = {
viewport: createStyles({
height: px2rem(320),
}),
panel: createStyles({
alignItems: 'center',
padding: system.padding.md,
}),
panelHeading: createStyles({
color: system.color.fg.muted.strong,
}),
main: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
flex: 1,
flexBasis: 'auto',
}),
};
export default () => {
const model = useSidePanelModel({
initialTransitionState: 'collapsed',
labelId: 'tasks-panel-label',
});
return (
<Flex cs={stylesOverride.viewport}>
<SidePanel model={model}>
<SidePanel.ToggleButton aria-label="Collapse View" />
<SidePanel.Heading size="small" cs={stylesOverride.panelHeading}>
Task Panel
</SidePanel.Heading>
{model.state.transitionState === 'expanded' && (
<Flex cs={stylesOverride.panel}>Contents</Flex>
)}
</SidePanel>
<Flex as="main" cs={stylesOverride.main}>
<Text as="p" typeLevel="body.large">
Control the panel externally
</Text>
<SecondaryButton
onClick={
model.state.transitionState === 'expanded' ? model.events.collapse : model.events.expand
}
aria-pressed={model.state.transitionState === 'expanded'}
>
{model.state.transitionState === 'expanded' ? 'Hide Side Panel' : 'Show Side Panel'}
</SecondaryButton>
</Flex>
</Flex>
);
};
Right Origin
By default, SidePanel uses a start origin (left in LTR, right in RTL). This sets the
ToggleButton’s position and direction as well as the direction of the animation. You can set the
origin to "end" to flip these. The origin uses logical properties (start/end) for proper
bidirectional support.
Toggle the content direction
Tasks Panel
import {SecondaryButton} from '@workday/canvas-kit-react/button';
import {CanvasProvider} from '@workday/canvas-kit-react/common';
import {Flex} from '@workday/canvas-kit-react/layout';
import {SidePanel, useSidePanelModel} from '@workday/canvas-kit-react/side-panel';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
// local helper hook for setting content direction;
import {useDirection} from './useDirection';
const stylesOverride = {
viewport: createStyles({
height: px2rem(320),
}),
panelContainer: createStyles({
marginInlineStart: 'auto',
}),
panel: createStyles({
alignItems: 'center',
justifyContent: 'flex-end',
}),
main: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
flex: 1,
flexBasis: 'auto',
}),
};
const RightPanel = () => {
const model = useSidePanelModel({
origin: 'end',
});
return (
<SidePanel model={model} className={stylesOverride.panelContainer}>
<SidePanel.ToggleButton aria-label="Collapse View" />
<Flex cs={stylesOverride.panel}>
<SidePanel.Heading size="small">Tasks Panel</SidePanel.Heading>
</Flex>
</SidePanel>
);
};
export default () => {
const {direction, toggleDirection} = useDirection();
return (
<CanvasProvider dir={direction}>
<Flex cs={stylesOverride.viewport}>
<Flex as="main" cs={stylesOverride.main}>
<Text as="p" typeLevel="body.large">
Toggle the content direction
</Text>
<SecondaryButton onClick={toggleDirection}>
Set to {direction === 'ltr' ? 'Right-to-Left' : 'Left-to-Right'}
</SecondaryButton>
</Flex>
<RightPanel />
</Flex>
</CanvasProvider>
);
};
Always Open
If you do not need SidePanel’s expand / collapse behavior, you can simply omit the ToggleButton.
Tasks Panel
This is the main content section.
import {rocketIcon} from '@workday/canvas-expressive-icons-web';
import {ExpressiveIcon} from '@workday/canvas-kit-react/icon';
import {Flex} from '@workday/canvas-kit-react/layout';
import {SidePanel} from '@workday/canvas-kit-react/side-panel';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';
const stylesOverride = {
accentIcon: createStyles({
marginInlineEnd: system.gap.md,
}),
pageContainer: createStyles({
gap: system.gap.md,
height: px2rem(320),
}),
panelContainer: createStyles({
alignItems: 'center',
padding: system.padding.md,
}),
panelHeading: createStyles({
color: system.color.fg.default,
}),
mainContent: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexBasis: 'auto',
flex: 1,
}),
};
export default () => {
return (
<Flex cs={stylesOverride.pageContainer}>
<SidePanel initialTransitionState="expanded">
<Flex cs={stylesOverride.panelContainer}>
<ExpressiveIcon icon={rocketIcon} cs={stylesOverride.accentIcon} />
<SidePanel.Heading size="small" cs={stylesOverride.panelHeading}>
Tasks Panel
</SidePanel.Heading>
</Flex>
</SidePanel>
<Flex as="main" cs={stylesOverride.mainContent}>
<Text as="p" typeLevel="body.large">
This is the main content section.
</Text>
</Flex>
</Flex>
);
};
Deriving Expanded State
If you need a simple boolean expanded state (similar to the preview-react onExpandedChange
callback), you can derive it from the transitionState using the onStateTransition callback on
the model.
onStateTransition
The onStateTransition callback is called whenever the panel’s transition state changes. This
includes all four states: expanding, expanded, collapsing, and collapsed. You can pass this
callback directly to the SidePanel component or to the useSidePanelModel hook.
The transition flow is:
- Collapsing:
expanded→collapsing→collapsed - Expanding:
collapsed→expanding→expanded
This is useful for:
- Triggering side effects when the panel state changes
- Syncing the panel state with external state management
- Animating child components based on the transition state
Hidden Title
Side panel is expanded.
import * as React from 'react';
import {AccessibleHide} from '@workday/canvas-kit-react/common';
import {Flex} from '@workday/canvas-kit-react/layout';
import {
SidePanel,
SidePanelTransitionStates,
useSidePanelModel,
} from '@workday/canvas-kit-react/side-panel';
import {Text} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
const stylesOverride = {
viewport: createStyles({
height: px2rem(320),
}),
main: createStyles({
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
flex: 1,
flexBasis: 'auto',
}),
};
export default () => {
const [transitionState, setTransitionState] =
React.useState<SidePanelTransitionStates>('expanded');
const model = useSidePanelModel({
onStateTransition: state => {
setTransitionState(state);
console.log('Expanded changed to:', state);
},
});
return (
<Flex cs={stylesOverride.viewport}>
<SidePanel model={model}>
<SidePanel.ToggleButton aria-label="Collapse View" />
<SidePanel.Heading hidden size="small">
Hidden Title
</SidePanel.Heading>
</SidePanel>
<Flex as="main" cs={stylesOverride.main}>
<Text as="p" typeLevel="body.large">
Side panel is {transitionState}.
</Text>
</Flex>
</Flex>
);
};
Accessibility
SidePanel renders a <section> element with an accessible name provided by aria-labelledby,
which references the SidePanel.Heading component. This ensures screen reader users understand the
purpose of the panel.
Panel and Heading
- The
SidePanel.Headingprovides the accessible name for the panel viaaria-labelledby - When the panel is collapsed, the heading is automatically hidden visually but remains accessible to screen readers
- Use the
hiddenprop onSidePanel.Headingif you want the heading always visually hidden
Toggle Button
SidePanel.ToggleButtonautomatically includesaria-controls(references the panel’sid),aria-pressed(indicates current state), andaria-describedby(references the panel’s heading)- Developers must provide a static
aria-labelstring onSidePanel.ToggleButtonto describe the button’s purpose (e.g., “Collapse View”). Avoid using ambiguous terms like “Toggle” in the label. Sincearia-pressedcommunicates the state, avoid dynamically updatingaria-label - The button includes a Tooltip with customizable text via
tooltipTextExpandandtooltipTextCollapseprops (defaults: “Expand View” and “Collapse View”) - For optimal keyboard navigation, place
SidePanel.ToggleButtonas the first focusable element in the panel
Component API
SidePanel
Props
Props extend from section. Changing the as prop will change the element interface.
Props extend from . If a model is passed, props from SidePanelModelConfig are ignored.
| Name | Type | Description | Default |
|---|---|---|---|
collapsedWidth | number | string | The width of the component (in | 64 |
expandedWidth | number | string | The width of the component (in | 320 |
variant | | The style variant of the side panel.
| 'standard' |
children | ReactNode | ||
cs | | The | |
as | React.ElementType | Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care. | section |
ref | React.Ref<R = section> | Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If | |
model | | Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context. | |
elemPropsHook | ( | Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one. |
useSidePanelContainer
Adds the necessary props to the SidePanel container element.
This includes the id and aria-labelledby attributes for accessibility.
(
model: ,
elemProps: {},
ref: React.Ref
) => {
id: string;
aria-labelledby: string;
onTransitionEnd: (event: <>) => <>;
}SidePanel.ToggleButton
SidePanel.ToggleButton is a control that toggles between expanded and collapsed states.
It must be used within the SidePanel component as a child. For accessibility purposes,
it should be the first focusable element in the panel.
The button automatically receives aria-controls (the panel's id), aria-pressed (true
when the panel is collapsed), and aria-describedby (the heading's id) from the model.
Provide a static aria-label for the button's accessible name — aria-pressed already
conveys the state, so the label should not change between states.
Props
Props extend from button. Changing the as prop will change the element interface.
| Name | Type | Description | Default |
|---|---|---|---|
model | | Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context. | |
elemPropsHook | ( | Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one. |
useSidePanelToggleButton
Adds the necessary ARIA attributes to the SidePanel's toggle button.
This includes aria-controls pointing at the panel, aria-pressed to convey the collapsed
state, and aria-describedby pointing at the panel's heading.
(
model: ,
elemProps: {},
ref: React.Ref
) => {
aria-controls: string;
aria-pressed: boolean;
aria-describedby: string;
}SidePanel.Heading
SidePanel.Heading is a styled heading that provides the accessible name for the SidePanel.
The heading's id is automatically linked to the panel's aria-labelledby attribute.
By default, the heading is hidden when the panel is collapsed.
Layout Component
SidePanel.Heading supports all props from thelayout component.
Props
Props extend from . Changing the as prop will change the element interface.
| Name | Type | Description | Default |
|---|---|---|---|
size | 'large' | 'medium' | 'small' | The size of the heading. | 'small' |
children | ReactNode | ||
cs | | The | |
variant | 'error' | 'hint' | 'inverse' | Type variant token names: | |
as | React.ElementType | Optional override of the default element used by the component. Any valid tag or Component. If you provided a Component, this component should forward the ref using Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care. | |
ref | React.Ref<R = > | Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If | |
model | | Optional model to pass to the component. This will override the default model created for the component. This can be useful if you want to access to the state and events of the model, or if you have nested components of the same type and you need to override the model provided by React Context. | |
elemPropsHook | ( | Optional hook that receives the model and all props to be applied to the element. If you use this, it is your responsibility to return props, merging as appropriate. For example, returning an empty object will disable all elemProps hooks associated with this component. This allows finer control over a component without creating a new one. |
useSidePanelHeading
Adds the necessary props to the SidePanelHeading subcomponent.
This sets the id to the labelId from the model for accessibility purposes,
and hides the heading when the panel is not expanded.
(
model: ,
elemProps: {},
ref: React.Ref
) => {
id: string;
hidden: boolean;
}Model
Hooks
useSidePanelModel
The useSidePanelModel hook creates a model for managing the SidePanel’s state and events. You can
pass this model to the SidePanel component, or let the component create one internally.
import {useSidePanelModel} from '@workday/canvas-kit-react/side-panel';
// Create a model with custom configuration
const model = useSidePanelModel({
initialTransitionState: 'collapsed',
origin: 'end',
onStateTransition: state => console.log('State:', state),
});
// Access state
model.state.transitionState; // 'expanded' | 'expanding' | 'collapsed' | 'collapsing'
model.state.panelId; // unique ID for the panel
model.state.labelId; // unique ID for the label
// Trigger events
model.events.expand(); // Set to expanded (no animation)
model.events.collapse(); // Set to collapsed (no animation)
model.events.handleAnimationStart(); // Start expand/collapse animationuseSidePanelContainer
The useSidePanelContainer elemProps hook provides the necessary props for the SidePanel container
element, including id, aria-labelledby, and onTransitionEnd.
useSidePanelContainer
Adds the necessary props to the SidePanel container element.
This includes the id and aria-labelledby attributes for accessibility.
(
model: ,
elemProps: {},
ref: React.Ref
) => {
id: string;
aria-labelledby: string;
onTransitionEnd: (event: <>) => <>;
}useSidePanelToggleButton
The useSidePanelToggleButton elemProps hook provides ARIA attributes for the toggle button,
including aria-controls, aria-pressed, and aria-describedby.
useSidePanelToggleButton
Adds the necessary ARIA attributes to the SidePanel's toggle button.
This includes aria-controls pointing at the panel, aria-pressed to convey the collapsed
state, and aria-describedby pointing at the panel's heading.
(
model: ,
elemProps: {},
ref: React.Ref
) => {
aria-controls: string;
aria-pressed: boolean;
aria-describedby: string;
}Accessibility Guidelines
How Side Panels Impact the Accessible Experience
One of the most important aspects of Side Panel is understanding when the Side Panel’s content begins and ends in the context of the holistic design. Side Panel uses a “landmark region” to help establish such boundaries of the Side Panel’s content for non-visual screen reader users. Including semantic heading text at the top of the Side Panel is recommended to help reinforce the beginning of Side Panel content, and convey the intended purpose of the section. Finally, users must be able to understand whether the Side Panel content is expanded or collapsed on the screen.
Keyboard Interaction
Each interactive component inside Side Panel must have a focus indicator that is highly visible against the background and against the non-focused state. Refer to Accessible Colors for more information.
Side Panel must support the following keyboard interactions:
Tab: focus the Side Panel toggle button, and any other interactive components inside Side PanelEnterorSpace: activates Side Panel toggle button
Screen Reader Interaction
Side Panel must communicate the following to users:
- The Side Panel is a landmark region, named by the Side Panel’s heading text
- The “expanded” or “collapsed” state of the Side Panel
Design Annotations Needed
- Specify when the Side Panel is used for navigation
- Specify heading level at the top of SIde Panel
Implementation Markup Needed
- Use semantic heading text at the top of Side Panel to describe the purpose of the content included inside of Side Panel.
- [Included in component] Use a semantic
<section>element and anaria-labelledbyreference to create a landmark region for screen readers. - When Side Panel is used for navigation purposes, use the
asprop to change the rendered element from the default<section>to a<nav>element. - [Included in component] An accessible
Tooltipcomponent is included on the Side Panel toggle button describing what the icon button will do when activated. - [Included in component] The toggle button must have an accessible name using either an
aria-labelledbyreference or anaria-labelstring. - [Included in component] The toggle button must convey the Side Panel state using the
aria-expandedproperty.