Workday Canvas

Segmented Control

Segmented Control displays different views of the same content.

v14.2.34
Install
yarn add @workday/canvas-kit-preview-react

Segmented Control (Main) vs. Segmented Control (Preview)

We recommend you use the Segmented Control in the Preview package (@workday/canvas-kit-preview-react) documented here on this page. The Segmented Control in the Main package (@workday/canvas-kit-react) will eventually be replaced with the Segmented Control in the Preview package.

Anatomy

Image of a segmented control with annotation markers describing the anatomy.

  1. Active Button: Only one segment can be active at a time. These mutually exclusive Buttons can appear as icon only, text left, or icon left with text.
  2. Default Buttons: Default control options that can be customized.
  3. Alignment: The Icon Only variant can be arranged horizontally or vertically.

Usage Guidance

  • Only one control option can be selected and active at a time.
  • Once a user selects an option, the results should be displayed immediately.
  • Each Button must clearly identify its purpose. Text variants will already have their labels displayed visually, but tooltips should be used for icon only variants.
  • All the buttons in the group to be the same size regardless of text length. Length of segments is determined by the longest text label.

When to Use

  • Use Segmented Control to switch between alternate views of similar or related content.
  • Use icon-only variant when there is limited space or when the icons are intuitive and easy to understand.

When to Use Something Else

  • Use Tabs to display different, unrelated content.
  • Use Hyperlinks within a paragraph to navigate to another page.
  • Use Checkboxes when the user is able to select multiple values from a predefined list of 7 or less options.
  • Use Multi-select Prompts when the user is able to select multiple values from a predefined list of more than 7 options.
  • Use Radio when the user is able to select a single value from a predefined list of 2 to 7 options that will not alter the page content.
  • Use Single-select Prompts when the user is able to select a single value from a predefined list of more than 7 options that will not alter the page content. Alternatively, Select can also be used when there are 7 to 15 single-select options.

Dos and Don’ts

Image displaying icon only Segmented Controls, using icons indicating a switch between list, bullets, and paragraph view.

Do

Labels should be short and succinct. If a label is too long to fit within its segment, consider using an icon- only variant.

Image displaying a text-based segemnted control with each category taking more than one line or needing to be truncated with an elipses.

Don't

Wrap or truncate text

Image highlighting the margins around a segmented control container on a mobile device.

Do

Segmented buttons should have adequate margins from the edge of the viewport or frame. The container shouldn’t reach the edge of the viewport.

Image showing segmented controls being used as action buttons, something to avoid with segmented control.

Don't

Avoid using a segmented control to enable actions, such as adding, removing, or editing content.

Design Annotations for Accessibility

  • Accessible name for the group describing the purpose of the Segmented Control
  • When using Icon Only Button, an accessible name for each Icon Button in the Segmented Control

Examples

Basic Example

SegmentedControl includes a container SegmentedControl component and the following subcomponents: SegmentedControl.List and SegmentedControl.Item.

The example below contains a SegmentedControl with four icon-only buttons. Each button is rendered using a SegmentedControl.Item and is paired with a tooltip describing the button’s function. Only one button can be active at a time.

Selected: table

import React from 'react';

import {SegmentedControl} from '@workday/canvas-kit-preview-react/segmented-control';
import {BodyText} from '@workday/canvas-kit-react/text';
import {
  gridIcon,
  listViewIcon,
  listDetailIcon,
  pieChartIcon,
} from '@workday/canvas-system-icons-web';

export default () => {
  const [viewType, setViewType] = React.useState('table');

  return (
    <>
      <SegmentedControl initialValue={viewType} onSelect={data => setViewType(data.id)}>
        <SegmentedControl.List aria-label="View type">
          <SegmentedControl.Item data-id="table" icon={gridIcon} tooltipProps={{title: 'Table'}} />
          <SegmentedControl.Item
            data-id="list-view"
            icon={listViewIcon}
            tooltipProps={{title: 'List'}}
          />
          <SegmentedControl.Item
            data-id="list-detail"
            icon={listDetailIcon}
            tooltipProps={{title: 'Detail'}}
          />
          <SegmentedControl.Item
            data-id="diagrams"
            icon={pieChartIcon}
            tooltipProps={{title: 'Diagram'}}
          />
        </SegmentedControl.List>
      </SegmentedControl>
      <BodyText size="small" marginTop="s">
        Selected: {viewType}
      </BodyText>
    </>
  );
};

We strongly discourage including more than four buttons in a single SegmentedControl.

Accessibility

Our SegmentedControl component renders semantic HTML <button> elements to the browser DOM, wrapped inside of a <div> with an explicit ARIA role="group". This is equivalent to an HTML <fieldset> element, and useful for screen readers to describe the relationship between the buttons.

  • Each button is a 2-state toggle button with aria-pressed={true | false} to indicate the current state to screen readers.
  • Providing your own aria-labelstring to SegmentedControl.List is recommended for describing the purpose of the component.

Screen Reader Experience

When users interact with a SegmentedControl using screen readers:

  • The group context is announced (e.g., “View options, group” when using aria-label="View options")
  • Each button announces its text/label, “toggle button” role, and pressed/unpressed state (e.g., “List view, toggle button, pressed” or “Grid view, toggle button, not pressed”)
  • For icon-only buttons with tooltips, the tooltip text is announced along with the button role and state
  • When a button is activated, screen readers should announce the new state

Refer to Button for more information about accessibility of these components.

Variations

SegmentedControl supports three variations based on whether or not its SegmentedControl.Item components have an icon prop and/or text content: icon-only, text-only, and text-and-icon.

All SegmentedControl.Item components within a given SegmentedControl must be of the same variation.

Icon-Only

To render an icon-only SegmentedControl, apply the icon prop to SegmentedControl.Item and do not provide it with text content. Refer to the basic example above for an instance of an icon-only SegmentedControl.

The icon-only variation is the only variation which supports a vertical orientation in addition to the default horizontal orientation. Set the orientation prop of SegmentedControl to vertical to configure the component to render vertically.

import {SegmentedControl} from '@workday/canvas-kit-preview-react/segmented-control';
import {
  gridIcon,
  listViewIcon,
  listDetailIcon,
  pieChartIcon,
} from '@workday/canvas-system-icons-web';

export default () => (
  <SegmentedControl orientation="vertical">
    <SegmentedControl.List aria-label="View type">
      <SegmentedControl.Item data-id="table" icon={gridIcon} tooltipProps={{title: 'Table'}} />
      <SegmentedControl.Item
        data-id="list-view"
        icon={listViewIcon}
        tooltipProps={{title: 'List'}}
      />
      <SegmentedControl.Item
        data-id="list-detail"
        icon={listDetailIcon}
        tooltipProps={{title: 'Detail'}}
      />
      <SegmentedControl.Item
        data-id="diagram"
        icon={pieChartIcon}
        tooltipProps={{title: 'Diagram'}}
      />
    </SegmentedControl.List>
  </SegmentedControl>
);

Text-Only

To render a text-only SegmentedControl, omit the icon prop from SegmentedControl.Item and provide it with text content.

import React from 'react';
import {SegmentedControl} from '@workday/canvas-kit-preview-react/segmented-control';

export default () => (
  <SegmentedControl>
    <SegmentedControl.List aria-label="View type">
      <SegmentedControl.Item data-id="table">Table</SegmentedControl.Item>
      <SegmentedControl.Item data-id="list">List</SegmentedControl.Item>
      <SegmentedControl.Item data-id="diagram">Diagram</SegmentedControl.Item>
    </SegmentedControl.List>
  </SegmentedControl>
);

Text-and-Icon

To render a text-and-icon SegmentedControl, apply the icon prop to SegmentedControl.Item and provide it with text content.

import React from 'react';
import {gridIcon, listViewIcon, pieChartIcon} from '@workday/canvas-system-icons-web';
import {SegmentedControl} from '@workday/canvas-kit-preview-react/segmented-control';

export default () => (
  <SegmentedControl>
    <SegmentedControl.List aria-label="View type">
      <SegmentedControl.Item data-id="table" icon={gridIcon}>
        Table
      </SegmentedControl.Item>
      <SegmentedControl.Item data-id="list" icon={listViewIcon}>
        List
      </SegmentedControl.Item>
      <SegmentedControl.Item data-id="diagram" icon={pieChartIcon}>
        Diagram
      </SegmentedControl.Item>
    </SegmentedControl.List>
  </SegmentedControl>
);

Sizes

SegmentedControl accepts a size prop which supports the following values:

  • small
  • medium (Default)
  • large

Small

Medium

Large

import React from 'react';
import {Box} from '@workday/canvas-kit-react/layout';
import {
  gridIcon,
  listViewIcon,
  listDetailIcon,
  pieChartIcon,
} from '@workday/canvas-system-icons-web';
import {SegmentedControl} from '@workday/canvas-kit-preview-react/segmented-control';
import {BodyText} from '@workday/canvas-kit-react/text';

export default () => (
  <>
    <Box>
      <BodyText size="medium" fontWeight="bold" marginTop={0}>
        Small
      </BodyText>
      <SegmentedControl size="small">
        <SegmentedControl.List aria-label="View type">
          <SegmentedControl.Item data-id="table" icon={gridIcon}>
            Table
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="list-view" icon={listViewIcon}>
            List
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="list-detail" icon={listDetailIcon}>
            Detail
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="diagrams" icon={pieChartIcon}>
            Diagram
          </SegmentedControl.Item>
        </SegmentedControl.List>
      </SegmentedControl>
    </Box>
    <Box>
      <BodyText size="medium" fontWeight="bold">
        Medium
      </BodyText>
      <SegmentedControl size="medium">
        <SegmentedControl.List aria-label="View type">
          <SegmentedControl.Item data-id="table" icon={gridIcon}>
            Table
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="list-view" icon={listViewIcon}>
            List
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="list-detail" icon={listDetailIcon}>
            Detail
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="diagrams" icon={pieChartIcon}>
            Diagram
          </SegmentedControl.Item>
        </SegmentedControl.List>
      </SegmentedControl>
    </Box>
    <Box>
      <BodyText size="medium" fontWeight="bold">
        Large
      </BodyText>
      <SegmentedControl size="large">
        <SegmentedControl.List aria-label="Content view type">
          <SegmentedControl.Item data-id="table" icon={gridIcon}>
            Table
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="list-view" icon={listViewIcon}>
            List
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="list-detail" icon={listDetailIcon}>
            Detail
          </SegmentedControl.Item>
          <SegmentedControl.Item data-id="diagrams" icon={pieChartIcon}>
            Diagram
          </SegmentedControl.Item>
        </SegmentedControl.List>
      </SegmentedControl>
    </Box>
  </>
);

Disabled

Set the disabled prop of SegmentedControl to disable the entire component including its buttons.

import {SegmentedControl} from '@workday/canvas-kit-preview-react/segmented-control';
import {
  gridIcon,
  listViewIcon,
  listDetailIcon,
  pieChartIcon,
} from '@workday/canvas-system-icons-web';

export default () => (
  <SegmentedControl disabled>
    <SegmentedControl.List aria-label="View type">
      <SegmentedControl.Item data-id="table" icon={gridIcon} tooltipProps={{title: 'Table'}} />
      <SegmentedControl.Item
        data-id="list-view"
        icon={listViewIcon}
        tooltipProps={{title: 'List'}}
      />
      <SegmentedControl.Item
        data-id="list-detail"
        icon={listDetailIcon}
        tooltipProps={{title: 'Detail'}}
      />
      <SegmentedControl.Item
        data-id="diagrams"
        icon={pieChartIcon}
        tooltipProps={{title: 'Diagram'}}
      />
    </SegmentedControl.List>
  </SegmentedControl>
);

Right-to-Left (RTL)

SegmentedControl supports right-to-left languages when specified in the CanvasProvider theme.

import {CanvasProvider} from '@workday/canvas-kit-react/common';
import {
  gridIcon,
  listViewIcon,
  listDetailIcon,
  pieChartIcon,
} from '@workday/canvas-system-icons-web';
import {SegmentedControl} from '@workday/canvas-kit-preview-react/segmented-control';

export default () => (
  <CanvasProvider dir="rtl">
    <SegmentedControl initialValue="list-detail">
      <SegmentedControl.List aria-label="View type">
        <SegmentedControl.Item data-id="table" icon={gridIcon}>
          שולחן
        </SegmentedControl.Item>
        <SegmentedControl.Item data-id="list-view" icon={listViewIcon}>
          רשימה
        </SegmentedControl.Item>
        <SegmentedControl.Item data-id="list-detail" icon={listDetailIcon}>
          פרטים
        </SegmentedControl.Item>
        <SegmentedControl.Item data-id="diagrams" icon={pieChartIcon}>
          תרשים
        </SegmentedControl.Item>
      </SegmentedControl.List>
    </SegmentedControl>
  </CanvasProvider>
);

Dynamic Items

SegmentedControl supports a dynamic API where instead of statically providing the JSX for each SegmentedControl.Item, you pass an array of items in the model state and provide a render function to display the items.

import React from 'react';
import {
  SegmentedControl,
  useSegmentedControlModel,
} from '@workday/canvas-kit-preview-react/segmented-control';
import {
  gridIcon,
  listViewIcon,
  listDetailIcon,
  pieChartIcon,
} from '@workday/canvas-system-icons-web';

export default () => {
  const [viewType, setViewType] = React.useState('table');

  const model = useSegmentedControlModel({
    items: [
      {id: 'table', icon: gridIcon, label: 'Table'},
      {id: 'list', icon: listViewIcon, label: 'List'},
      {id: 'detail', icon: listDetailIcon, label: 'Detail'},
      {id: 'diagram', icon: pieChartIcon, label: 'Diagram'},
    ],
    size: 'small',
    initialValue: viewType,
    onSelect: data => {
      console.log(`${data.id} is selected`);
      setViewType(data.id);
    },
  });

  return (
    <SegmentedControl model={model}>
      <SegmentedControl.List aria-label="View type">
        {item => (
          <SegmentedControl.Item data-id={item.id} icon={item.icon}>
            {item.label}
          </SegmentedControl.Item>
        )}
      </SegmentedControl.List>
    </SegmentedControl>
  );
};

Component API

SegmentedControl

SegmentedControl is a container component that is responsible for creating a {@link SegmentedControlModel } and sharing it with its subcomponents using React context. It does not represent a real element.

<SegmentedControl items={[]}>{Child components}</SegmentedControl>

Alternatively, you may pass in a model using the hoisted model pattern.

const model = useSegmentedControlModel({
  items: [],
});

<SegmentedControl model={model}>{Child components}</SegmentedControl>;

Props

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

NameTypeDescriptionDefault
childrenReactNode
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.

SegmentedControl.List

SegmentedControl.List renders {@link Grid } under the hood. It is a container for subcomponents.

<SegmentedControl.List>{SegmentedControl.Items}</SegmentedControl.List>

Layout Component

SegmentedControl.List supports all props from thelayout component.

Props

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

NameTypeDescriptionDefault
aria-labelstring
children ((item: T) => ReactNode) ReactNode
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 {@link mergeStyles } 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.

div
refReact.Ref<R = div>

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.

SegmentedControl.Item

SegmentedControl.Item is a button element built on BaseButton. SegmentedControl.Item has a data-id prop to handle onSelect properly.

<SegmentedControl.Item data-id="table">Table</SegmentedControl.Item>

Layout Component

SegmentedControl.Item supports all props from thelayout component.

Props

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

NameTypeDescriptionDefault
indexnumber

Optionally pass index to the item. This should be done if SegmentedControl.Item components were created via a [Array::map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) function. This index will ensure keyboard navigation works even if items are inserted out of order.

childrenReactNode

The contents of the item. This is text used as the accessible name of the button for screen readers.

data-idstring

The identifier of the item. This identifier will be used in change events and for initialTab. If this property is not provided, it will default to a string representation of the the zero-based index of the item when it was initialized.

idstring

Optional id. If not set, it will inherit the ID passed to the SegmentedControl component and append the index at the end. Only set this for advanced cases.

aria-pressedboolean

Part of the ARIA specification for buttons. Lets screen readers know which button is active. This should either be true or false. This is automatically set by the component and should only be used in advanced cases.

icon

The icon of the button.

tooltipProps<, 'children'>

Tooltip Props

colors

Override default colors of a button. The default will depend on the button type

size

There are four button sizes: extraSmall, small, medium, and large. If no size is provided, it will default to medium.

fillIconboolean

Whether the icon should received filled (colored background layer) or regular styles. Corresponds to toggled in ToolbarIconButton

iconPosition

Button icon positions can either be start or end. If no value is provided, it defaults to start.

'start'
accentstring

The accent color of the SystemIcon. This overrides color.

accentHoverstring

The accent color of the SystemIcon on hover. This overrides colorHover.

backgroundstring

The background color of the SystemIcon.

backgroundHoverstring

The background color of the SystemIcon on hover.

colorstring

The color of the SystemIcon. This defines accent and fill. color may be overwritten by accent and fill.

colorHoverstring

The hover color of the SystemIcon. This defines accentHover and fillHover. colorHover may be overwritten by accentHover and fillHover.

fillstring

The fill color of the SystemIcon. This overrides color.

fillHoverstring

The fill color of the SystemIcon on hover. This overrides colorHover.

shouldMirrorboolean

If set to true, transform the SVG's x-axis to mirror the graphic. Use this if you want to always mirror the icon regardless of the content direction. If the SVG should mirror only when in an right-to-left language, use shouldMirrorInRTL instead.

false
shouldMirrorInRTLboolean

If set to true, transform the SVG's x-axis to mirror the graphic when the content direction is rtl. Icons don't have enough context to know if they should be mirrored in all cases. Setting this to true indicates the icon should be mirrored in right-to-left languages.

false
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 {@link mergeStyles } 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>
)
growboolean

True if the component should grow to its container's width. False otherwise.

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.

button
refReact.Ref<R = button>

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.

useSegmentedControlItem

(
  ,
  (
    model: ,
    elemProps: {
      data-id: string;
      children: ReactNode;
    },
    ref: React.Ref
  ) => {
    id: any;
    aria-pressed: boolean;
  },
  ,
  (
    model: ,
    elemProps: {},
    ref: React.Ref
  ) => {
    disabled:  true undefined;
  }
)

Model

useSegmentedControlModel

useSegmentedControlModel (config: ):

Specifications

Content Guidelines

  • If the content exceeds the specified max-width, consider making your labels more concise, or use an icon-only variant.
  • Text in a segmented control should never truncate or wrap.
  • See the Buttons and Calls to Action page in the UI Text section of the Content Style Guide for button language guidelines.
  • When writing Tooltips to pair with Segmented Control, refer to the Tooltips section of the Content Style Guide.

Can't Find What You Need?

Check out our FAQ section which may help you find the information you're looking for. For further information, contact the #ask-canvas-design or #ask-canvas-kitchannels on Slack.

On this Page: