Generate user story maps from markdown
Project description
storymap
Generate user story maps from markdown. Write your product spec as a readable document — personas, releases, activities, tasks, and stories — and render it as a styled, interactive HTML page.
Installation
pip install storymap
Quick start
storymap init # create a skeleton storymap.md
storymap init myproduct.md # create a named skeleton
storymap render mymap.md # → mymap.html (same directory)
storymap render mymap.md -o out # → out/mymap.html
The output is a single self-contained HTML file. Local images referenced in story descriptions are embedded as base64 data URIs — no external files needed.
PDF output: open the generated HTML in a browser and use print-to-PDF (Ctrl+P → Save as PDF). The HTML includes print-optimised CSS for landscape layout and color preservation.
Document format
A storymap file is a standard markdown document with three reserved top-level
sections: # Releases, # Personas, and # Map. Any other # heading is
treated as the document title (first one) or ignored.
# My Product
Short description.
# Releases
## MVP
First public release.
## Beta
Invite-only beta with selected users.
# Personas
## Margie the Manager
- **Age:** 45–55
- **Tech level:** Low
Margie manages a team of 8 and primarily uses the app on mobile.
# Map
## User Management
### Authentication
#### Sign in [status:: done] [persona:: Margie the Manager] [release:: MVP]
User can log in with email and password.
#### Password reset [status:: in-progress] [deadline:: 2026-03-01] [release:: Beta]
### Profile
#### Edit profile [status:: done] [release:: MVP]
#### Upload avatar [status:: blocked] [release:: Beta]
Blocked pending storage provider decision.
Sections
| Section | Required | Purpose |
|---|---|---|
# Releases |
Yes | Defines release swimlanes |
# Personas |
No | UX persona descriptions |
# Map |
Yes | The story map itself |
Section names are case-insensitive.
Map hierarchy
## Activity (column group)
### Task (column)
#### Story (card in a swimlane)
Assigning stories to releases
Use the [release:: name] field on each story to assign it to a release
swimlane. The value must match a release name (or id, see below) defined in
# Releases. Stories without a [release::] field are parsed but not shown
in any swimlane.
### Authentication
#### Sign in [status:: done] [release:: MVP]
#### SSO [status:: not-started] [release:: Beta]
Release identifiers
If a release has a long display name, add an [id:: short-name] field to the
release heading. Stories then use the short id instead of the full name.
storymap will warn if a release name contains spaces and has no [id::].
# Releases
## Minimum Viable Product [id:: mvp]
## Private Beta [id:: beta]
# Map
## User Management
### Authentication
#### Sign in [status:: done] [release:: mvp]
#### SSO [status:: not-started] [release:: beta]
This decouples the display name from the identifier — you can rename the release heading freely without updating every story.
Story fields
Stories support optional inline fields using [key:: value] syntax.
Fields appear as badges on the rendered story card.
#### Story name [status:: done] [persona:: Margie the Manager] [deadline:: 2026-03-01] [release:: mvp]
| Field | Values | Default |
|---|---|---|
status |
not-started, in-progress, done, blocked |
not-started |
release |
Release name or id from # Releases section |
— |
persona |
Any string matching a persona name | — |
deadline |
ISO date YYYY-MM-DD |
— |
Any other [key:: value] field is accepted and rendered as a badge.
Story descriptions
Markdown content following a #### Story heading and before the next heading
is treated as the story description. Descriptions support standard markdown:
bold, italics, links, lists, and images.
The first paragraph is always visible on the story card. Additional paragraphs are shown only in Detail zoom level.
Images
Images in story and persona descriptions are embedded as base64 data URIs in
the output HTML, making the file fully self-contained. Paths are resolved
relative to the source .md file. Remote URLs are left as-is.
Interactive HTML features
The rendered HTML includes controls for navigating large maps:
Zoom levels — three buttons in the sticky header:
- Overview — story names only, compact layout
- Map — story names, status badges, and first-paragraph descriptions
- Detail — full descriptions expanded
Release focus — a dropdown to highlight one release swimlane and dim the rest. Works independently of the zoom level.
Story Lens — a toggle that enables click-to-zoom on individual story cards. When active, clicking a card expands it to ~40% of the viewport width. Clicking outside collapses it.
CLI reference
Usage: storymap [OPTIONS] COMMAND [ARGS]...
Generate user story maps from markdown.
Options:
--version Show the version and exit.
--help Show this message and exit.
Commands:
init Create a skeleton storymap markdown file to get started.
render Render a story map markdown INPUT_FILE to HTML.
storymap render
Usage: storymap render [OPTIONS] INPUT_FILE
Options:
-o, --output DIR Output directory. Defaults to the input
file's directory.
-t, --template FILE Path to a custom Jinja2 template (.html.j2).
--status-colors KEY=COLOR,... Override status colors.
Example: done=#00FF00,blocked=#FF0000
--ui-colors KEY=COLOR,... Override UI colors.
Example: activity=#1565C0,task=#90CAF9
--help Show this message and exit.
storymap init
Usage: storymap init [OUTPUT_FILE]
OUTPUT_FILE defaults to storymap.md in the current directory.
Refuses to overwrite an existing file.
Customisation
Color overrides
storymap render mymap.md \
--status-colors "done=#27AE60,in-progress=#2980B9,blocked=#E74C3C" \
--ui-colors "activity=#2C3E50,task=#34495E"
Default status colors:
| Status | Color |
|---|---|
not-started |
#E0E0E0 grey |
in-progress |
#90CAF9 blue |
done |
#A5D6A7 green |
blocked |
#EF9A9A red |
Custom templates
storymap render mymap.md --template my-template.html.j2
The template receives:
| Variable | Type | Description |
|---|---|---|
document |
StorymapDocument |
The full parsed document |
status_colors |
dict[str, str] |
Resolved status → hex color |
ui_colors |
dict[str, str] |
Resolved UI element → hex color |
render_md |
callable |
Render a markdown string to HTML |
render_md_intro |
callable |
Render first paragraph only |
render_md_rest |
callable |
Render everything after first paragraph |
The darken filter is also available: {{ color | darken }}.
Development
git clone https://github.com/mozaicworks/storymap
cd storymap
just install
just test
Project structure
storymap/
├── model.py — dataclasses and default color constants
├── parser.py — markdown-it-py state machine parser
├── renderer.py — Jinja2 HTML renderer with base64 image embedding
├── cli.py — click CLI entry point
└── templates/
└── default.html.j2
tests/
├── test_model.py
├── test_parser.py
├── test_renderer.py
└── test_cli.py
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file storymap-0.1.4.tar.gz.
File metadata
- Download URL: storymap-0.1.4.tar.gz
- Upload date:
- Size: 30.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a20963dfd4e5d62a418e421a248b5b3e869fb063299045d5a577090f712a44b
|
|
| MD5 |
8119473575279b5c43f0bcd36498db59
|
|
| BLAKE2b-256 |
c2a92a191524f24d467c7f2226d4e835c0ec96fd468cf7e958d8cf6e026a6616
|
File details
Details for the file storymap-0.1.4-py3-none-any.whl.
File metadata
- Download URL: storymap-0.1.4-py3-none-any.whl
- Upload date:
- Size: 20.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
34587104ed9a7da1fd39ad153e1dae6e4601c03952cc3585da334b9c07a968fc
|
|
| MD5 |
9b34e47a96d09ff1594699d7d4d2471e
|
|
| BLAKE2b-256 |
4e19f2a52da978f9d5857ff9ca9d551602f951ce8587972b4727c591a410fa9c
|