Layout

Layouts help to create a visually consistent experience across Workday products. At the foundation of layouts is the 12 column Canvas grid. Use this to guide your decisions when laying out content within your product.

GitHubStorybook
yarn add @workday/canvas-kit-react
Sources
GitHubStorybook
Install
yarn add @workday/canvas-kit-react

Grid Anatomy

Image of a Grid with annotation markers.

  1. Margins: The space at the outer edges of the grid, should always be of equal fixed width.
  2. Columns: Span the width of the content, use this to align your content correctly.
  3. Gutters: The space between columns that help separate content.

12 Column Grid

The Canvas grid system uses 12 columns. We chose the 12 column grid system because it is easy to divide 12 columns into halves, thirds, fourths, and sixths when designing for different screen sizes. We recommend reducing the number of columns as the screen width reduces as illustrated below. Check out our Responsive Layouts for recommended sizes.

Usage Guidance

The Grid system should be used when designing any layout within your product. If you think of your UI elements like blocks then these blocks can span across the 12 column grid in any number of ways as illustrated below. The 12 column grid system gives you a lot of flexibility when arranging UI elements.

Image of blocks overlaying the Grid to indicate how the grid can be divided into different layouts.

Not all UI elements within your layout need to align with the column. As long as the parent element aligns to the assigned number of columns, that’s okay!

Image of Cards aligning to the Layout Grid.

The cards within the above container do not need to align with the layout columns. You can use the 8pt grid to align the cards.

Responsive Layouts

Responsive layouts change the appearance of a layout, depending on the screen size and orientation of the device being used to view it. We recommend using the following grids to cater for these different screen sizes below.

Small

320px - 767px

  • Number of columns: 4 Margins: 16px Gutter: 16px

  • Used for mobile size screens

To accommodate a 3 panel layout on mobile you can add a separate 12 column grid with a margin of 16px and gutter of 16px

Example for space.small
BreakpointValueColumnsMarginGutter
Small320px416px16px
Medium768px840px24px
Large1024px1240px32px
Extra Large1440px1280px40px

Layout Regions

Grids should always be used to layout your designs, even when working with different layout regions. Grids give you greater control over different regions and how you can position UI elements within your screen layout.

Image of main content region spanning across 12 columns.

Main content region spanning across 12 columns

Image of main page content with grid and left and right side panel grids.

Multiple grids used to accommodate layouts with side panel regions

Image of left and right side panel grids overlaying main page content grid.

Grids used to accommodate layouts with left and right side panels regions that overlays the main page content

In the cases illustrated above, side panels can reduce the width of the main page content or overlay the page content. Grids of varying columns e.g 4, 8 or 12 can be used to accommodate your layout, but we recommend reducing the number of columns as the width decreases in order to prevent the layout becoming cluttered.

Density

The grid system can help you design for both high and low density layouts. The important thing to remember when working with density is to increase the margin and gutter space on high density layouts and decrease the margin and gutter space on low density layouts. This allows the user to easily scan groups of content.

Do

Increase the width of the gutters when laying out components with high density content.

Don't

Decrease the width of the gutters when laying out components with high density content.

When to Use

  • To help you make decisions when designing high or low density layouts.
  • Create visual hierarchy and rhythm in your layouts.
  • Create consistent layouts across your product.

Flex Examples

Basic Example

Canvas Principles

Empower over Enforce

Encourage our user's expression. Stay out of the way and provide them with the tools and resources to build their vision.

Evolution over Perfection

Nothing is ever perfect – embrace that. Make educated assumptions, validate and test our decisions, then iterate! Aim of continous rather than perfect solutions.

Simple over Clever

Simple solutions invite the user in - clever solutions invite complexity. Make the system easy and predictable, and progressively disclose advanced functionality.

Everyone over Every One

Each piece of the system is designs and built to be accessible, while still providing the best experience for all consumers. But not if something is focused on a single use case and negates the usability for others.

Rows and Columns

You can nest Flex components to build layouts with rows and columns.

1
2
3
4
5
6
7
8
9

Small Containers

You can also use Flex to create layouts within smaller containers, such as this card example below:

Learn about Flex

Complete this task when you have a functional understanding of Flex.

Flex vs. Box

Flex is built with Box and has access to all BoxProps. The main differences between Box and Flex are:

  • Flex is set to display: flex by default
  • The display prop is limited to flex and inline-flex
  • Flex has access to flex container properties.

In short, use Flex when you need a flex container to build layouts with CSS Flexbox, and use Box when you need a simple container. It's also important to note that Flex has no real opinion about how to build layouts outside of the CSS Flexbox spec. Its unopinonated nature allows it to be composed into many different components.

Flex Props

As previously mentioned, Flex is built on top of Box and has access to all its props, including flex item properties. But Flex adds flex container properties through style props as well. There are seven flex container props:

NameTypeDefaultDescription
alignItemsPropertyAlignItems | undefinedsets `align-items` property
alignContentPropertyAlignContent | undefinedsets `align-content` property
display"flex" | "inline-flex" | undefined'flex'sets `display` property
justifyItemsPropertyJustifyItems | undefinedsets `justify-items` property
justifyContentPropertyJustifyContent | undefinedsets `justify-content` property
flexWrapPropertyFlexWrap | undefinedsets `flex-wrap` property
flexDirectionPropertyFlexDirection | undefinedsets `flex-direction` property

Flex Props (Exhaustive List)

NameTypeDefaultDescription
alignItemsPropertyAlignItems | undefinedsets `align-items` property
alignContentPropertyAlignContent | undefinedsets `align-content` property
justifyItemsPropertyJustifyItems | undefinedsets `justify-items` property
justifyContentPropertyJustifyContent | undefinedsets `justify-content` property
flexWrapPropertyFlexWrap | undefinedsets `flex-wrap` property
flexDirectionPropertyFlexDirection | undefinedsets `flex-direction` property
refReact.Ref<any>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`).
as"symbol" | "object" | "small" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | ... 156 more ... | React.ComponentType<any>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.forwardRef` and spread extra props to a root element.

Stack Examples

Stack is a higher-level layout component, meaning:

  1. It's built on top of lower-level components (Box and Flex)
  2. It's opinionated about how it builds layouts.

Stack has three core opinions that guide its API:

  • Space should be equal between child elements
  • Space should only exist between child elements
  • Children should be semantic elements

Much of layout design is founded on these opinions. Whether its creating lists of navigation links, groups of cards, or collections of buttons, once you think in stacks, you'll see them everywhere.

Basic Example

Stack is a composable component that's helpful for creating layouts with equal spacing between elements. It can create horizontal or vertical stacks of elements.

The most important distinction to make with Stack is that it is not a grid. Grid components are able to align items along two dimensions (rows and columns), and Stack is only built to support one dimension (rows or columns). That said, you can nest horizontal and vertical Stack components to create grid-like layouts.

Stack uses CSS pseudo-classes to apply margin to child elements. This makes it really simple to get consistent spacing between child elements without much manual effort. Below is a basic example. Note that Stack does not add any space above or below the child elements. This helps your element fit nicely within their containers.

One
Two
Three

Use this approach when:

  • You want consistent space between child elements
  • You don't need to apply custom margin to child elements
    • Specifically marginLeft/marginInlineStart for horizontal stacks and marginTop for vertical stacks
  • You don't want to add extra markup to wrap child elements

Setting Direction

The direction of the child elements is set with the flexDirection prop. This prop supports four directions: column, column-reverse, row, and row-reverse. By default, it will set the direction to row as is consistent with the CSS Flexbox spec.

Note: flex-direction and other CSS Flexbox attributes support bidirectionality (LTR and RTL) automatically. You don't need to think about switching directions for localization.

Note: spacing also supports bidirectionality automatically. This means you don't need to think about it for localization as long as you properly pass the direction to the theme object un

Setting Space

The space between child elements is set with the spacing prop, which supports all space token and string values. Stack intentionally only sets space between elements and never outside. For example: a vertical stack of three elements would only have margin added to the top of the second and third child elements. Keeping space between elements makes building layouts more block-like and predictable.

Managing Child Elements

There are three ways to manage space between child elements with Stack.

  • Implicit spacing applied directly to child elements (default behavior)
  • Implicit wrapping child elements with shouldWrapChildren
  • Explicit wrapping child elements with Stack.Items.

Each approach has its own benefits and limitations, which are discussed in further detail below. We chose to implicitly apply spacing to child elements as the default behavior because it requires the least overhead to implement and should support most use cases. The other approaches act as escape hatches for when the default behavior becomes forced. Should you find none of these approaches work for your use case, we recommend using Flex instead of Stack, which will provide you with more flexibility.

Valid Children

Because Stack applies styles to children, it requires its immediate children to be valid child elements. For example, text outside of an HTML tag would not render if it was an immediate Stack child. Here's an example snippet:

const ValidChildrenExample = () => {
return (
<Stack flexDirection="row" spacing="s">
This text will not render. Don't do this.
<p>This text will render.</p>
<span>This text will also render.</span>
</Stack>
);
};

Grid-like Layouts

You can nest vertical and horizontal Stacks to create grid-like layouts. Here are three horizontal Stacks nested within a vertical Stack. The fourth row is a single Flex component.

1
2
3
4
5
6
7
8
9

Small Containers

You can also use Stack to manage smaller layouts, such as within Cards.

Stack

Stack provides a simple API for managing consistent space between elements in one-dimensional layouts. In this Card example, we are setting extra-small spacing the heading, body text, and button elements.

HStack

HStack works identically to Stack but is limited to only row and row-reverse directions. It defaults to row if no flexDirection is provided.

Note: HStack supports bidirectionality automatically. This means that you won't need to switch the direction or spacing for localization. The direction switching is handled by the CSS Flexbox spec, and the spacing will flip direction based on the direction provided in the theme object.

In this example, HStack is placing three cards in a row with small spacing between them.

Diavola

sauce, smoked mozzarella, pepperoni, basil, chili flake

Four Cheese

mozzarella, gorgonzola, smoked mozzarella, parmesan, garlic oil

Veggie

sauce, mozzarella, artichokes, roasted red peppers, broccolini

VStack

VStack works identically to Stack but is limited to only column and column-reverse directions. It defaults to column if no flexDirection is provided.

In this example, VStack is placing three cards in a column with small spacing between them.

Diavola

sauce, smoked mozzarella, pepperoni, basil, chili flake

Four Cheese

mozzarella, gorgonzola, smoked mozzarella, parmesan, garlic oil

Veggie

sauce, mozzarella, artichokes, roasted red peppers, broccolini

shouldWrapChildren and Stack.Item

A side-effect of Stack's pseudo-classes is an increased style specificity. This specificity can override the child element's margin styles. If that creates a problem, there are two recommended options available:

  • Use the shouldWrapChildren prop (implicit option)
  • Use Stack.Item (explicit option)

shouldWrapChildren

The shouldWrapChildren prop will implicitly wrap each child in a Stack.Item component (which is a Box) and apply the margin to it instead of the child element provided. This allows you to control the margin of the child elements you provide and avoiding style collisions. The trade-off is that you do not have direct access to the Stack.Item being rendered. If having access to that element is necessary, using Stack.Item explicitly (as described below) could be the best solution.

The example below is a bit contrived but has enough information to convey the point. In this scenario, we have a horizontal stack of SecondaryButtons that render an icon, but they are visually divided into separate groupings. The navigational buttons are on the left, and the additional actions are spaced slightly further to the right. You could use two separate horizontal stacks here to achieve the same result, but you could also use shouldWrapChildren and then put additional margin only where needed. Either approach would be fine; it's a matter of personal preference.

Use this approach when:

  • You want consistent space between child elements
  • You need to apply custom margin to particular children
  • Wrapping each child element won't create issues with other semantic elements
  • You don't want to explicitly add extra markup to wrap child elements

Stack.Item

Sometimes inserting implicit wrapper elements can be problematic when you need access to those elements. In those situations, shouldWrapChildren is not the best choice, and you should opt for wrapping children in Stack.Items. A Stack.Item is a Box with some preset styles which can be overridden if needed.

In the example below, we wanted to keep our li elements as direct children of the ul stack. So we're wrapping each of the links with Stack.Items, casting them as lis and applying custom styles to each. This would not be possible with shouldWrapChildren.

Use this approach when:

  • You want consistent space between child elements
  • You need to have direct access to Stack.Item wrappers
  • Wrapping each child element won't create issues with other semantic elements

Stack Props

NameTypeDefaultDescription
alignItemsPropertyAlignItems | undefinedsets `align-items` property
alignContentPropertyAlignContent | undefinedsets `align-content` property
justifyItemsPropertyJustifyItems | undefinedsets `justify-items` property
justifyContentPropertyJustifyContent | undefinedsets `justify-content` property
flexWrapPropertyFlexWrap | undefinedsets `flex-wrap` property
flexDirection"column" | "column-reverse" | "row" | "row-reverse" | undefined"row"sets `flex-direction` property sets the direction for the stack
spacing*StackSpacingsets space values between child elements (bidirectional support)
shouldWrapChildrenboolean | undefinedfalsewhen `true` wraps each child element in a `Stack.Item`
refReact.Ref<any>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`).
as"symbol" | "object" | "small" | "a" | "abbr" | "address" | "area" | "article" | "aside" | "audio" | "b" | "base" | "bdi" | "bdo" | "big" | "blockquote" | "body" | "br" | "button" | ... 156 more ... | React.ComponentType<any>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.forwardRef` and spread extra props to a root element.

Can't Find What You Need?

Check out our FAQ section which may help you find the information you're looking for.

FAQ Section