Skip to Content

KBD

KBD visually represents keyboard input such as keys or keyboard shortcuts.

Interaction Modes

Reviewing

Scanning

Install

yarn add @workday/canvas-kit-labs-react

Component Type

Indicator

Platform

Web

Component

Sana Canvas

Delivery Channels

Web

Version

16.1.7

Experience Surfaces

Page Body Inline

Component Type

Indicator

Platform

Web

Component

Sana Canvas

Delivery Channels

Web

Version

16.1.7

Experience Surfaces

Page Body Inline

Labs package

KBD is in the Labs package (@workday/canvas-kit-labs-react). It's safe to use, with the caveat that Labs components are experimental and may receive significant changes or be removed entirely. See the Packages Glossary for details on Main, Preview, and Labs.

Anatomy

Image of a KBD component with annotation markers.
  1. Container: Houses the contents of the KBD component.
  2. Keycode: Label for a single key.

Examples

Basic Example

KBD is a container that wraps one or more KBD.Item components. Each KBD.Item represents a single keyboard key.

Important: When displaying a group of keyboard keys (such as Ctrl + C), do not place that combination as a text into a single KBD.Item (for example, <KBD.Item>Ctrl + C</KBD.Item>). Doing so will cause issues with RTL (right-to-left) positioning and keyboard shortcut display. Instead, wrap each key in its own KBD.Item and group them inside a KBD container. This ensures correct rendering and layout in both LTR and RTL environments.

PressFto pay respects.

⌘CShift+PShift+P
import {KBD} from '@workday/canvas-kit-labs-react/kbd';
import {BodyText} from '@workday/canvas-kit-react/text';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const containerStyles = createStyles({
  gap: system.gap.md,
  display: 'flex',
  flexDirection: 'column',
});

export default () => {
  return (
    <div className={containerStyles}>
      <BodyText size="small" cs={{marginBlock: 0}}>
        Press
        <KBD cs={{marginInline: system.padding.xxs}}>
          <KBD.Item>F</KBD.Item>
        </KBD>
        to pay respects.
      </BodyText>
      <KBD>
        <KBD.Item aria-label="Command">⌘</KBD.Item>
        <KBD.Item>C</KBD.Item>
      </KBD>
      <KBD aria-keyshortcuts="Shift+P">
        <KBD.Item>Shift</KBD.Item>
        <span>+</span>
        <KBD.Item>P</KBD.Item>
      </KBD>
      <KBD aria-keyshortcuts="Shift+P">
        <KBD.Item>
          <KBD variant="plain">
            <KBD.Item>Shift</KBD.Item>
            <span>+</span>
            <KBD.Item>P</KBD.Item>
          </KBD>
        </KBD.Item>
      </KBD>
    </div>
  );
};

Dynamic Items

KBD is built on the Collection API. Instead of rendering KBD.Item components statically, you can pass an array of strings to the items prop and provide a render prop as the children. Each string represents the label of a single keyboard key.

⌘ShiftP
import {KBD} from '@workday/canvas-kit-labs-react/kbd';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const containerStyles = createStyles({
  gap: system.gap.md,
  display: 'flex',
  flexDirection: 'column',
});

const items = ['⌘', 'Shift', 'P'];

export default () => {
  return (
    <div className={containerStyles}>
      <KBD items={items}>{item => <KBD.Item>{item}</KBD.Item>}</KBD>
    </div>
  );
};

RTL Example

You can also use the KBD component in right-to-left (RTL) layouts. This is helpful for languages that are read from right to left.

F⌘CShift+PShift+P
import {KBD} from '@workday/canvas-kit-labs-react/kbd';
import {createStyles} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const containerStyles = createStyles({
  gap: system.gap.md,
  display: 'flex',
  flexDirection: 'column',
});

export default () => {
  return (
    <div className={containerStyles} dir="rtl">
      <KBD>
        <KBD.Item>F</KBD.Item>
      </KBD>
      <KBD>
        <KBD.Item>⌘</KBD.Item>
        <KBD.Item>C</KBD.Item>
      </KBD>
      <KBD>
        <KBD.Item>Shift</KBD.Item>
        <span>+</span>
        <KBD.Item>P</KBD.Item>
      </KBD>
      <KBD as="span">
        <KBD.Item>
          <KBD variant="plain">
            <KBD.Item>Shift</KBD.Item>
            <span>+</span>
            <KBD.Item>P</KBD.Item>
          </KBD>
        </KBD.Item>
      </KBD>
    </div>
  );
};

Size

The KBD component supports different sizes. You can adjust the size using the size prop with options like small, medium, and large to suit various UI needs. The size is shared with each KBD.Item through the model.

Large Size

⌘C

Medium Size

Shift+A

Small Size

CtrlV
import {KBD} from '@workday/canvas-kit-labs-react/kbd';
import {Subtext} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const containerStyles = createStyles({
  gap: system.gap.md,
  display: 'flex',
  flexDirection: 'column',
});

const rowStyles = createStyles({
  display: 'flex',
  alignItems: 'center',
  gap: system.gap.md,
  p: {
    minWidth: px2rem(104),
    margin: 0,
  },
});

export default () => {
  return (
    <div className={containerStyles}>
      <div className={rowStyles}>
        <Subtext size="large">Large Size</Subtext>
        <KBD size="large">
          <KBD.Item>⌘</KBD.Item>
          <KBD.Item>C</KBD.Item>
        </KBD>
      </div>
      <div className={rowStyles}>
        <Subtext size="medium">Medium Size</Subtext>
        <KBD size="medium">
          <KBD.Item>Shift</KBD.Item>
          <span>+</span>
          <KBD.Item>A</KBD.Item>
        </KBD>
      </div>
      <div className={rowStyles}>
        <Subtext size="small">Small Size</Subtext>
        <KBD size="small">
          <KBD.Item>Ctrl</KBD.Item>
          <KBD.Item>V</KBD.Item>
        </KBD>
      </div>
    </div>
  );
};

Variant

The KBD component supports different variants through the variant prop. Use default for the standard style or plain for a more minimal appearance. The variant is shared with each KBD.Item through the model.

Default Variant

⌘C

Plain Variant

Shift+A
import {KBD} from '@workday/canvas-kit-labs-react/kbd';
import {Subtext} from '@workday/canvas-kit-react/text';
import {createStyles, px2rem} from '@workday/canvas-kit-styling';
import {system} from '@workday/canvas-tokens-web';

const containerStyles = createStyles({
  gap: system.gap.md,
  display: 'flex',
  flexDirection: 'column',
  p: {
    minWidth: px2rem(104),
    margin: 0,
  },
});

const rowStyles = createStyles({
  display: 'flex',
  alignItems: 'center',
  gap: system.gap.md,
});

export default () => {
  return (
    <div className={containerStyles}>
      <div className={rowStyles}>
        <Subtext size="large">Default Variant</Subtext>
        <KBD variant="default">
          <KBD.Item>⌘</KBD.Item>
          <KBD.Item>C</KBD.Item>
        </KBD>
      </div>
      <div className={rowStyles}>
        <Subtext size="large">Plain Variant</Subtext>
        <KBD variant="plain">
          <KBD.Item>Shift</KBD.Item>
          <span>+</span>
          <KBD.Item>A</KBD.Item>
        </KBD>
      </div>
    </div>
  );
};

In Tooltip

KBD can be used inside a Tooltip to communicate the keyboard shortcut associated with an interactive control. Pair it with descriptive text so the shortcut is easy to understand.

import {KBD} from '@workday/canvas-kit-labs-react/kbd';
import {SecondaryButton} from '@workday/canvas-kit-react/button';
import {Subtext} from '@workday/canvas-kit-react/text';
import {Tooltip} from '@workday/canvas-kit-react/tooltip';
import {createStyles} from '@workday/canvas-kit-styling';
import {copyIcon} from '@workday/canvas-system-icons-web';
import {system} from '@workday/canvas-tokens-web';

const flexContainer = createStyles({
  display: 'flex',
  alignItems: 'center',
  gap: system.gap.xs,
  p: {
    margin: 0,
  },
});

export default () => {
  return (
    <Tooltip
      title={
        <div className={flexContainer}>
          <Subtext size="large">Copy to clipboard</Subtext>
          <KBD>
            <KBD.Item>
              <KBD variant="plain">
                <KBD.Item aria-label="Command">⌘</KBD.Item>
                <KBD.Item>C</KBD.Item>
              </KBD>
            </KBD.Item>
          </KBD>
        </div>
      }
    >
      <SecondaryButton icon={copyIcon} aria-keyshortcuts="Command+C" />
    </Tooltip>
  );
};

Accessibility

KBD is presentational and semantic text. It renders a kbd element so assistive technology can recognize the content as keyboard input, but it does not make a keyboard shortcut work or expose it to the browser. It is the visual/semantic representation of a key, not the accessibility contract for a functioning shortcut.

  • aria-keyshortcuts isn’t supposed to go on the presentational KBD wrapper, it should be on the interactive control with the key listener.
  • The <KBD.Item> components that use glyphs need translated aria-label strings. (E.g. “Command”, “Control”, etc.)

Provide spoken labels for symbolic or abbreviated keys

Glyphs and abbreviations such as ⌘, ⌥, ⇧, and arrow keys are not announced reliably across screen reader, browser, and OS combinations. When a KBD.Item contains a symbol or abbreviation, add an aria-label with the spoken name of the key so it is announced consistently:

<KBD> <KBD.Item aria-label="Command">⌘</KBD.Item> <KBD.Item>C</KBD.Item> </KBD>

Plain alphanumeric keys (such as C above) read correctly on their own and don’t need an aria-label.

Expose the actual shortcut on the control it triggers

KBD only documents a shortcut visually. If the shortcut is functional, also expose it on the relevant interactive control using aria-keyshortcuts so assistive technology can communicate it to users:

<button aria-keyshortcuts="Command+C"> Copy <KBD> <KBD.Item aria-label="Command">⌘</KBD.Item> <KBD.Item>C</KBD.Item> </KBD> </button>

Component API

KBD

Props

Props extend from kbd. Changing the as prop will change the element interface.

Props extend from . If a model is passed, props from KBDModelConfig are ignored.

NameTypeDescriptionDefault
children ReactNode | ((item: string) => ReactNode)

The children of the KBD container. This should contain one or more KBD.Item components that each represent a single keyboard key. If items are provided to the model, this should be a render prop that returns a KBD.Item for each item.

cs

The cs prop takes in a single value or an array of values. You can pass the CSS class name returned by , or the result of and . If you're extending a component already using cs, you can merge that prop in as well. Any style that is passed to the cs prop will override style props. If you wish to have styles that are overridden by the css prop, or styles added via the styled API, use wherever elemProps is used. If your component needs to also handle style props, use instead.

import {handleCsProp} from '@workday/canvas-kit-styling'; import {mergeStyles} from '@workday/canvas-kit-react/layout'; // ... // `handleCsProp` handles compat mode with Emotion's runtime APIs. `mergeStyles` has the same // function signature, but adds support for style props. return ( <Element {...handleCsProp(elemProps, [ myStyles, myModifiers({ size: 'medium' }), myVars({ backgroundColor: 'red' }) ])} > {children} </Element> )
asReact.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 React.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

kbd
refReact.Ref<R = kbd>

Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If as is set to an element, it will be that element. If as is a component, the reference will be to that component (or element if the component uses React.forwardRef).

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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

KBD.Item

KBD.Item renders a single keyboard key as a semantic kbd element.

Props

Props extend from kbd. Changing the as prop will change the element interface.

NameTypeDescriptionDefault
childrenReactNode

The content of the key. This should be the label of a single keyboard key (e.g. Ctrl, ⌘, Enter).

cs

The cs prop takes in a single value or an array of values. You can pass the CSS class name returned by , or the result of and . If you're extending a component already using cs, you can merge that prop in as well. Any style that is passed to the cs prop will override style props. If you wish to have styles that are overridden by the css prop, or styles added via the styled API, use wherever elemProps is used. If your component needs to also handle style props, use instead.

import {handleCsProp} from '@workday/canvas-kit-styling'; import {mergeStyles} from '@workday/canvas-kit-react/layout'; // ... // `handleCsProp` handles compat mode with Emotion's runtime APIs. `mergeStyles` has the same // function signature, but adds support for style props. return ( <Element {...handleCsProp(elemProps, [ myStyles, myModifiers({ size: 'medium' }), myVars({ backgroundColor: 'red' }) ])} > {children} </Element> )
asReact.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 React.forwardRefand spread extra props to a root element.

Note: Not all elements make sense and some elements may cause accessibility issues. Change this value with care.

kbd
refReact.Ref<R = kbd>

Optional ref. If the component represents an element, this ref will be a reference to the real DOM element of the component. If as is set to an element, it will be that element. If as is a component, the reference will be to that component (or element if the component uses React.forwardRef).

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(
  model: ,
  elemProps: TProps
) => HTML Attributes

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.

useListItemRegister

This elemProps hook is the base of all item component hooks. It registers an item with a collection and sets the data-id that is used by other hooks. It should always be the last defined hook when using composeHooks (composeHooks executes hooks right to left and merges props left to right). It is used by ListBox.Item and all *.Item subcomponents.

const useMyItem = composeHooks( useListItemSelect, // additional hooks go here useListItemRegister // always last );
(
  model: ,
  elemProps: {
    data-id: string;
    data-text: string;
    data-has-children: boolean;
    children: ReactNode;
    index: number;
    disabled: boolean;
  },
  ref: React.Ref
) => {
  ref: (instance:  | null) => void;
  data-id: string;
  disabled:  true | undefined;
  aria-setsize:  number | undefined;
  aria-posinset:  number | undefined;
  data-index:  number | undefined;
  style: ;
  id: string;
}

Model

useKBDModel

The KBD model extends the Collection System. It tracks the keyboard keys (items) and shares the size and variant of the keys with the KBD.Item subcomponents.

const model = useKBDModel({ size: 'large', variant: 'plain', items: ['⌘', 'C'], }); <KBD model={model}>{item => <KBD.Item>{item}</KBD.Item>}</KBD>
useKBDModel (config: ):

Model

KBD uses a useKBDModel to track the keyboard keys (items) and to share the size and variant with each KBD.Item. If you need direct access to the model’s state, you can create it yourself with useKBDModel and pass it to KBD via the model prop.

const model = useKBDModel({ size: 'large', variant: 'plain', items: ['⌘', 'C'], }); <KBD model={model}>{item => <KBD.Item>{item}</KBD.Item>}</KBD>;

useKBDModel

useKBDModel (config: ):