story-map

Horizontal user story map with activity columns and drag-and-drop between columns.

Overview

A web component that renders a horizontal user story map organized by activity columns. Each column contains a <drag-surface> enabling drag-and-drop reorder within a column and transfer between columns. Ideal for sprint planning, backlog grooming, and mapping user activities to deliverable stories.

Attributes

Attribute Type Default Description
src string (URL) Path to a JSON file containing activity and story data
compact boolean Reduces column widths, padding, and header font sizes for denser layouts
title string Optional heading displayed above the story map

Child Attributes

The <story-map> component expects direct <section> children, each defining an activity column. Story items go inside each section.

Section Attributes (Activity Columns)

Attribute Type Default Description
data-activity string Activity column identifier. Used in event details and as the internal key.
data-activity-label string Title-cased data-activity Display label for the column header. Falls back to a title-cased version of the activity ID.
data-journey-phase string Optional link to a <user-journey> phase name for cross-referencing

Item Attributes (Story Cards)

Items inside each section become draggable story cards. Use <article> elements for simple text or <user-story> elements for rich story cards.

Attribute Type Description
draggable boolean Must be set to "true" for drag capability
data-id string Stable identifier for the item, included in event details

Column Structure

Each activity column consists of a sticky header and a drag surface area:

  • Header — displays the activity label in uppercase with a count badge showing the number of stories in that column. The badge updates automatically when items are transferred.
  • Drag surface — a <drag-surface> element that holds the story items. All surfaces share the same group identifier, enabling cross-column transfers.
  • Empty state — when a column has no stories, an italic "No stories yet" placeholder appears. The placeholder is removed when an item is dropped in, and re-added when the last item is dragged out.

The columns sit inside a horizontally scrollable container. Each column has a minimum width of 14rem (11rem in compact mode) and a maximum of 22rem, using flex: 1 0 14rem to fill available space while allowing horizontal scroll when columns exceed the viewport.

Drag Interaction

Items can be reordered within a column and transferred between columns:

  • Reorder — drag an item up or down within the same column. Fires a story-map:reorder event with the old and new indices.
  • Transfer — drag an item from one column and drop it onto another. The count badges on both columns update automatically. Fires a story-map:transfer event.
  • Keyboard<drag-surface> provides keyboard reorder and cross-surface transfer using its built-in keyboard shortcuts.

JSON Loading

Load the entire story map from a JSON file using the src attribute. The component creates activity columns with <user-story> cards automatically.

Using with User Stories

For richer story cards, use <user-story> elements as children instead of plain <article> elements. Add the compact attribute on each story for a tighter layout within the columns.

Events

Event Detail When
story-map:reorder { itemId, activity, oldIndex, newIndex } Fires when an item is reordered within the same activity column
story-map:transfer { itemId, fromActivity, toActivity, newIndex } Fires when an item is dragged from one activity column to another
story-map:ready { activityCount, storyCount } Fires after the component initializes with the total number of activity columns and stories

Accessibility

  • The scroll container uses role="region" with aria-label="Story map" and is keyboard-focusable via tabindex="0"
  • Each <drag-surface> has an aria-label describing the activity column (e.g., "Discovery stories")
  • A hidden live region with aria-live="polite" announces item transfers between columns
  • Keyboard navigation is provided by the underlying <drag-surface> components
  • Column headers are sticky so the activity label remains visible while scrolling down through long columns
  • Respects prefers-reduced-motion: reduce by disabling scroll behavior transitions
  • Uses container-type: inline-size and stacks columns vertically at narrow viewports (<30rem)
  • Print styles remove sticky positioning, enable wrapping columns, and set break-inside: avoid on columns

Related