Menu


Board column


A lane of cards on a board: a title with a capacity meter and an action, then groups of cards, each with a label and a count.

Customize the preview


Show
View

Agents

Waiting on you6
In progress1

Options


The board column has no choices: it is drawn one way.

Examples


Usage

A title with a capacity meter and an add button, then two groups of cards, each with a status dot, a label and a count.

Website

Waiting on you6
In progress1

With a summary line

A line of text in place of the meter.

Docs

2 open, 1 waiting on you

Waiting on you1

Scrolling inside

Given a height, the column scrolls inside itself and becomes a stop for the Tab key.

Design system

Waiting on you6
In progress1

Mobile

In a narrow space the column fills the width.

Shop

Waiting on you6
In progress1

Groups that fold on a phone

Each group's label is a heading in the page's outline. On a phone the finished and failed groups fold under their headings, closed at first; a press on a heading opens it. In a wide space they are plain groups.

Store

Waiting on you

1

Finished

2

Failed

1

A group that always folds

A group that folds on any screen, open at first: its caret turns as it closes.

Guidelines


Purpose

One lane of a board: the work for one project, one person or one stage. Its head says what the lane is and how full it is; under it, its cards are grouped by where each one stands, such as "Waiting on you" and "In progress".

When to use

  • Inside a board, one column per lane.
  • On its own, for one lane of work in a narrow side panel.

When not to use

  • For a list that is not split into lanes. Use a list.
  • For content that is not a set of comparable items. Use a card or a section.

Usage rules

Do

  • Give each column a short title of its own.
  • Order the groups by what needs the reader first.

Don't

  • Use a bare "Add" on every column.
  • Let a group run on for dozens of cards; show a few and offer the rest.

Blog

Drafts0
    Do

    Name the add button for what it adds and where, since every column has one.

    News

    Drafts0
      Don't

      A bare "Add" on every column leaves a screen reader user guessing which one they are on.

      Anatomy

      • The block, a lane pressed into the page: the canvas surface with the strong inset shadow, inside a subtle hairline, with the medium container corners.
      • The head part: the heading part on the left, holding the title part in a small heading style and, under it, the meter part, and the action part at the end.
      • The meter part holds a capacity meter, or a line of text that sums the lane up.
      • The action part holds a small ghost icon button that adds to the lane.
      • The group part, one per group: the group head part (the dot part, a status dot; the group label part in a small label style and the subtle text color; the count part, a small soft badge), then the cards part, a list with one card part per item, then the more part, a small ghost button that shows the rest. A group that folds is a disclosure: its group head is the row that opens and closes it, with the caret part at its end, and the group body part holds the cards and the more part.

      Behavior

      On a wide screen a column is the large column width; in a narrow space it fills the width it is given. It is as tall as its cards. Given a height by the page, it scrolls inside itself with the system's scrollbar: a thin, see-through bar over its cards that takes no room and shows while the mouse is over it or it scrolls.

      A group shows the cards it is given; to keep a long group short, show a few and put a "Show all" button under them.

      A group can fold under its head (fold): always, or narrow, only in a narrow space such as a phone, where the columns stack and a finished or failed group can wait closed under the work still to do. It starts closed unless defaultExpanded is set, or the page holds it with expanded and onExpandedChange. Pressing the head opens or closes it: the cards unfold with the expand motion as an accordion's item does, and the caret turns. A group that folds only in a narrow space is a plain group in a wide one, its cards always shown, and closed or open again as it was when the space turns narrow. Written by hand it is a details element with the smbk-board-column__group--fold-always or --fold-narrow class, its head the summary; in a wide space the stylesheet shows a narrow-only group's cards and hides its caret, where the browser lets a page show a closed disclosure's content.

      Accessibility


      What it does for you

      • Each column is a region named by its title, so a screen reader can list the lanes and jump between them.
      • The title is a third-level heading by default.
      • Each group is a list named by its label and its count, such as "Waiting on you 6", one item per card.
      • Given headingLevel, a group's label is a heading at that level, so the group sits in the page's outline under its column.
      • A group that folds is a native disclosure: its head is one row that says whether it is open, and opens and closes with Enter and Space, with no script.
      • The group's dot is decoration: the label says what the group is.
      • While a column with a height has more to show than fits, it is a stop for the Tab key, so the arrow keys can scroll it, and shows the focus ring inside its edge.

      What you must do

      • Give each column a title of its own.
      • Set headingLevel to fit the page's outline, one below the heading above the board. A group's headingLevel goes one below its column's, and the cards' titles one below that.
      • Name the add button for what it adds and where, such as "Add an agent to the blog", and the "Show all" button for what it shows, such as "Show all 6 waiting".

      How to test

      • Tab through a column: focus goes through the head, the cards and "Show all" in reading order, with the focus ring showing in both themes and in a forced-color mode.
      • Give a column a height it cannot fit in: it takes focus as a stop, and the arrow keys scroll it.
      • With a screen reader, each column is found as a region by its title, and each group as a list with its label and count.
      • On a phone, Tab to a folding group's head and press Enter or Space: it opens and says it is expanded; press again and it closes.
      • With text at 200% and the page 320 pixels wide, the title and cards wrap and nothing is cut off.

      Keyboard

      Board column keys
      KeyWhat it does
      Tab
      Moves focus through the head's buttons, the cards that are links or buttons, and "Show all". A column that scrolls is a stop of its own.
      Up ArroworDown Arrow
      Scrolls a column that has more to show than fits, while the column has focus.
      Enter
      Follows a focused link, presses a focused button, or opens or closes a folding group from its head.
      Space
      Presses a focused button, or opens or closes a folding group from its head.

      Source


      The files the board column is made of. Its folder on GitHub (opens in a new tab) holds them with its wrapper and its document.

      Stylesheet
      board-column.css
      /* smbk-board-column — Board column and Board group. Figma "Board column" 7118:731 (Meter and Action
       * booleans) and "Board group" 7118:654 (Count, Dot and Show all booleans).
       * A lane of cards pressed into the page: the canvas surface with the strong inset shadow inside a subtle
       * hairline, the medium container corners, 12 all round. Its head holds the title in Heading XS, the
       * capacity meter under it and an action at the end; then its groups, each a label with a status dot and a
       * count, the cards in a list and an optional "Show all". On a wide screen a column is the large column
       * width; in a narrow space it fills the width. A column given a height scrolls inside itself. A group can fold
       * under its head, always or only in a narrow space: it is then a native details element, its head the summary
       * with a caret at its end, its cards and "Show all" what it discloses (see the folding rules at the end). */
      
      .smbk-board-column {
        box-sizing: border-box;
        display: flex;
        flex: none;
        flex-direction: column;
        gap: var(--smbk-space-stack-xl);
        width: var(--smbk-size-column-min-lg);
        max-width: 100%;
        padding: var(--smbk-space-inset-lg);
        overflow-y: auto;
        border: var(--smbk-border-width-hairline) solid var(--smbk-color-border-subtle);
        border-radius: var(--smbk-radius-container-md);
        background: var(--smbk-color-surface-canvas);
        color: var(--smbk-color-text-default);
        box-shadow: var(--smbk-elevation-inset-strong);
        /* The system's scrollbar: thin, in the subtle border color, on a clear track. */
        scrollbar-width: thin;
        scrollbar-color: var(--smbk-color-border-subtle) transparent;
      }
      
      /* A column that scrolls is a region the keyboard reaches: its ring is drawn inside its edge. */
      .smbk-board-column:focus-visible {
        outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
        outline-offset: calc(-1 * var(--smbk-focus-ring-width));
      }
      
      .smbk-board-column__head {
        display: flex;
        align-items: flex-start;
        gap: var(--smbk-space-inline-md);
      }
      
      .smbk-board-column__heading {
        display: flex;
        flex: 1 1 auto;
        flex-direction: column;
        gap: var(--smbk-space-stack-sm);
        min-width: 0;
      }
      
      .smbk-board-column__title {
        margin: 0;
        color: var(--smbk-color-text-default);
        font-family: var(--smbk-typography-heading-xs-font-family);
        font-size: var(--smbk-typography-heading-xs-font-size);
        font-weight: var(--smbk-typography-heading-xs-font-weight);
        line-height: var(--smbk-typography-heading-xs-line-height);
        letter-spacing: var(--smbk-typography-heading-xs-letter-spacing);
        text-transform: var(--smbk-typography-heading-xs-text-transform);
        overflow-wrap: anywhere;
      }
      
      .smbk-board-column__meter {
        display: flex;
      }
      
      .smbk-board-column__action {
        display: flex;
        flex-shrink: 0;
        gap: var(--smbk-space-inline-xs);
      }
      
      /* ---- a group: its label row, its cards and "Show all" ---- */
      
      .smbk-board-column__group {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: var(--smbk-space-stack-md);
      }
      
      .smbk-board-column__group-head {
        display: flex;
        align-items: center;
        gap: var(--smbk-space-inline-sm);
      }
      
      .smbk-board-column__group-label {
        margin: 0;
        color: var(--smbk-color-text-subtle);
        font-family: var(--smbk-typography-label-sm-font-family);
        font-size: var(--smbk-typography-label-sm-font-size);
        font-weight: var(--smbk-typography-label-sm-font-weight);
        line-height: var(--smbk-typography-label-sm-line-height);
        letter-spacing: var(--smbk-typography-label-sm-letter-spacing);
        text-transform: var(--smbk-typography-label-sm-text-transform);
      }
      
      .smbk-board-column__cards {
        display: flex;
        flex-direction: column;
        gap: var(--smbk-space-stack-md);
        align-self: stretch;
        margin: 0;
        padding: 0;
        list-style: none;
      }
      
      .smbk-board-column__card {
        display: block;
      }
      
      /* ---- a group that folds ----
         A group given a fold is a details element: its head is the summary, so it opens and closes with Enter and Space and
         says whether it is open with no script, and its cards and "Show all" sit in the body it discloses, the same room
         above them as in a group that does not fold. The head is at least the extra-small control height, the least a press
         needs, with a caret at its end in the extra-small icon size, the label's, that turns when the group is open; pointing at it darkens its
         label and caret, and keyboard focus rings it. A group that folds only in a narrow space is a plain group in a wide
         one: the React component draws it so, and in markup written by hand the stylesheet shows its body whatever the
         open state and hides the caret. */
      
      .smbk-board-column__group--fold-always,
      .smbk-board-column__group--fold-narrow {
        display: block;
      }
      
      .smbk-board-column__group-body {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: var(--smbk-space-stack-md);
        padding-top: var(--smbk-space-stack-md);
      }
      
      :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow) > .smbk-board-column__group-head {
        box-sizing: border-box;
        min-height: var(--smbk-size-control-xs);
        border-radius: var(--smbk-radius-control-sm);
        list-style: none;
        cursor: pointer;
        -webkit-tap-highlight-color: transparent;
        touch-action: manipulation;
      }
      
      :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow) > .smbk-board-column__group-head::-webkit-details-marker {
        display: none;
      }
      
      :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow) > .smbk-board-column__group-head:focus-visible {
        outline: var(--smbk-focus-ring-width) solid var(--smbk-color-border-focus);
        outline-offset: var(--smbk-focus-ring-offset);
      }
      
      @media (hover: hover) {
        :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow) > .smbk-board-column__group-head:hover .smbk-board-column__group-label {
          color: var(--smbk-color-text-default);
        }
      
        :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow) > .smbk-board-column__group-head:hover .smbk-board-column__caret {
          color: var(--smbk-color-icon-default);
        }
      }
      
      .smbk-board-column__caret {
        flex-shrink: 0;
        color: var(--smbk-color-icon-subtle);
        font-size: var(--smbk-size-icon-xs);
        transition: transform var(--smbk-motion-expand-duration) var(--smbk-motion-expand-easing);
      }
      
      /* A group the React component is closing (data-closing) is still open until its motion ends; its caret turns back as
         the motion starts. */
      .smbk-board-column__group[open]:not([data-closing]) > .smbk-board-column__group-head .smbk-board-column__caret {
        transform: rotate(180deg);
      }
      
      /* ---- motion: the body unfolds with the expand purpose and folds back the same way, as an accordion's item does.
         The group's height grows from its head to its own while the body fades in from the purpose's slight blur, and the
         caret turns with it. The React component moves it, the same in every browser, and marks the group data-motion
         once it runs; while it moves (data-moving) it is clipped, with room for the head's focus ring. Markup written by
         hand is moved by the stylesheet alone where the browser can animate to a natural height, and opens at once
         elsewhere. Under reduced motion the height lands at once and the fade still plays. ---- */
      
      .smbk-board-column__group[data-moving] {
        overflow: clip;
        overflow-clip-margin: calc(var(--smbk-focus-ring-width) + var(--smbk-focus-ring-offset));
      }
      
      @supports (interpolate-size: allow-keywords) {
        :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow):not([data-motion]) {
          interpolate-size: allow-keywords;
        }
      
        :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow):not([data-motion])::details-content {
          block-size: 0;
          opacity: 0;
          filter: blur(var(--smbk-motion-expand-blur));
          overflow: clip;
          overflow-clip-margin: calc(var(--smbk-focus-ring-width) + var(--smbk-focus-ring-offset));
          transition-property: block-size, opacity, filter, content-visibility;
          transition-duration: var(--smbk-motion-expand-duration);
          transition-timing-function: var(--smbk-motion-expand-easing);
          transition-behavior: allow-discrete;
        }
      
        :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow):not([data-motion])[open]::details-content {
          block-size: auto;
          opacity: 1;
          filter: none;
        }
      
        /* Under reduced motion the height steps: at once as it opens, and only once it has faded as it closes. */
        @media (prefers-reduced-motion: reduce) {
          :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow):not([data-motion])::details-content {
            transition-duration: 0s, var(--smbk-motion-expand-duration), var(--smbk-motion-expand-duration), var(--smbk-motion-expand-duration);
            transition-delay: var(--smbk-motion-expand-duration), 0s, 0s, 0s;
          }
      
          :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow):not([data-motion])[open]::details-content {
            transition-delay: 0s;
          }
        }
      }
      
      @media (prefers-reduced-motion: reduce) {
        .smbk-board-column__caret {
          transition: none;
        }
      }
      
      /* A group that folds only in a narrow space, written by hand, in a wide one: its body shows whatever the open state,
         its head takes no press look and no caret, so it reads as a group that does not fold. */
      @container style(--smbk-layout: wide) {
        .smbk-board-column__group--fold-narrow > .smbk-board-column__group-head {
          min-height: 0;
          cursor: auto;
        }
      
        .smbk-board-column__group--fold-narrow .smbk-board-column__caret {
          display: none;
        }
      
        .smbk-board-column__group.smbk-board-column__group--fold-narrow:not([data-motion])::details-content {
          content-visibility: visible;
          block-size: auto;
          opacity: 1;
          filter: none;
        }
      }
      
      @media (hover: hover) {
        @container style(--smbk-layout: wide) {
          .smbk-board-column__group--fold-narrow > .smbk-board-column__group-head:hover .smbk-board-column__group-label {
            color: var(--smbk-color-text-subtle);
          }
        }
      }
      
      /* ---- touch: a folding group's head takes an invisible press area ----
         On a touch screen an invisible area, centred on it, takes the press out to the comfortable target in each direction
         it falls short of it; its look, its focus ring and its place stay as drawn. */
      
      @media (pointer: coarse) {
        :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow) > .smbk-board-column__group-head {
          position: relative;
        }
      
        :is(.smbk-board-column__group--fold-always, .smbk-board-column__group--fold-narrow) > .smbk-board-column__group-head::after {
          content: '';
          position: absolute;
          inset: min(0%, (100% - var(--smbk-size-target-comfortable)) / 2);
        }
      }
      
      /* ---- the narrow form: the column fills the width it is given ---- */
      
      @container style(--smbk-layout: narrow) {
        .smbk-board-column {
          width: 100%;
        }
      }
      Tokens it reads
      Board column tokens
      Token and CSSValuePreviewFoundation
      space.stack.xl--smbk-space-stack-xlcomfortable: 24pxcompact: 16pxchanges with densitySpacing
      size.column.min.lg--smbk-size-column-min-lg400pxSize
      space.inset.lg--smbk-space-inset-lgcomfortable: 12pxcompact: 8pxchanges with densitySpacing
      border-width.hairline--smbk-border-width-hairline1pxBorder width
      color.border.subtle--smbk-color-border-subtlelight: #c4cdd8dark: #2d3237changes with themeColor
      radius.container.md--smbk-radius-container-md12pxRadius
      color.surface.canvas--smbk-color-surface-canvaslight: #e7ebf0dark: #111213changes with themeColor
      color.text.default--smbk-color-text-defaultlight: #232527dark: #e7ebf0changes with themeColor
      elevation.inset-strong--smbk-elevation-inset-stronglight: inset -1px 2px 2px 0px #373d4333dark: inset -1px 2px 2px 0px #0000004dElevation
      focus.ring.width--smbk-focus-ring-width2pxFocus
      color.border.focus--smbk-color-border-focuslight: #cc0d0ddark: #f34336changes with themeColor
      space.inline.md--smbk-space-inline-mdcomfortable: 8pxcompact: 6pxchanges with densitySpacing
      space.stack.sm--smbk-space-stack-smcomfortable: 8pxcompact: 6pxchanges with densitySpacing
      typography.heading-xs--smbk-typography-heading-xs-font-family--smbk-typography-heading-xs-font-size--smbk-typography-heading-xs-font-weight--smbk-typography-heading-xs-line-height--smbk-typography-heading-xs-letter-spacing--smbk-typography-heading-xs-text-transformfontFamily: Funnel Display, Funnel Display Fallback, system-ui, sans-seriffontSize: min: 20px · max: 28pxfontWeight: 400lineHeight: 1.2letterSpacing: 0px

      The quick brown fox

      Typography
      space.inline.xs--smbk-space-inline-xs4pxSpacing
      space.stack.md--smbk-space-stack-mdcomfortable: 12pxcompact: 8pxchanges with densitySpacing
      space.inline.sm--smbk-space-inline-smcomfortable: 6pxcompact: 4pxchanges with densitySpacing
      color.text.subtle--smbk-color-text-subtlelight: #4e555edark: #9ba5b1changes with themeColor
      typography.label-sm--smbk-typography-label-sm-font-family--smbk-typography-label-sm-font-size--smbk-typography-label-sm-font-weight--smbk-typography-label-sm-line-height--smbk-typography-label-sm-letter-spacing--smbk-typography-label-sm-text-transformfontFamily: Geist, Geist Fallback, system-ui, sans-seriffontSize: 12pxfontWeight: 500lineHeight: 1.4letterSpacing: 0px

      The quick brown fox

      Typography
      size.control.xs--smbk-size-control-xs24pxSize
      radius.control.sm--smbk-radius-control-sm4pxRadius
      focus.ring.offset--smbk-focus-ring-offset2pxFocus
      color.icon.default--smbk-color-icon-defaultlight: #232527dark: #e7ebf0changes with themeColor
      color.icon.subtle--smbk-color-icon-subtlelight: #4e555edark: #c4cdd8changes with themeColor
      size.icon.xs--smbk-size-icon-xs12pxSize
      motion.expand.duration--smbk-motion-expand-duration320msMotion
      motion.expand.easing--smbk-motion-expand-easingcubic-bezier(0.215, 0.61, 0.355, 1)Motion
      motion.expand.blur--smbk-motion-expand-blur2pxMotion
      size.target.comfortable--smbk-size-target-comfortable44pxSize
      Manifest
      board-column.manifest.json
      {
        "name": "board-column",
        "block": "smbk-board-column",
        "summary": "A lane of cards on a board: a title with a capacity meter and an action, then groups of cards, each with a label and a count.",
        "adapt": "css",
        "axes": {},
        "flags": {},
        "parts": [
          "head",
          "heading",
          "title",
          "meter",
          "action",
          "group",
          "group-head",
          "dot",
          "group-label",
          "count",
          "caret",
          "group-body",
          "cards",
          "card",
          "more"
        ],
        "figmaNodeId": "7118:731"
      }