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 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
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 passed through to the output.
# 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]
User can log in with email and password.
> release Beta
#### Password reset [status:: in-progress] [deadline:: 2026-03-01]
### Profile
#### Edit profile [status:: done]
> release Beta
#### Upload avatar [status:: blocked]
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)
Use > release on its own line to advance to the next release swimlane within
a task. Annotate it for readability — anything after release is ignored:
### Authentication
#### Sign in ← Release 1 (MVP)
#### Remember me ← Release 1 (MVP)
> release Beta
#### SSO ← Release 2 (Beta)
> release GA
← Release 3 empty for this task
Keep > release count consistent across all tasks — mismatched counts produce
misaligned swimlane rows.
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]
| Field | Values | Default |
|---|---|---|
status |
not-started, in-progress, done, blocked |
not-started |
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
or > release separator is treated as the story description. Descriptions
support standard markdown: bold, italics, links, lists.
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 |
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
├── 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.1.tar.gz.
File metadata
- Download URL: storymap-0.1.1.tar.gz
- Upload date:
- Size: 22.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4648984deef8210b0cb4b88aafd0a16a4df325d020c53075eba67219fe356521
|
|
| MD5 |
77769cdff31e9a56cf254b9dc5ad5248
|
|
| BLAKE2b-256 |
974c1a1e4b077944dd2d79f17504132e9b040f3e6aa892cdde4d6444b95b3611
|
File details
Details for the file storymap-0.1.1-py3-none-any.whl.
File metadata
- Download URL: storymap-0.1.1-py3-none-any.whl
- Upload date:
- Size: 13.5 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 |
efeed37e4950507b81d0421cb346b21e0de64edabef67fd41345ef85b385101a
|
|
| MD5 |
a8416e802edd7cef4cc6634f40347ae3
|
|
| BLAKE2b-256 |
1a313a47ab034e974a3704d4dee46ed5f48c54df8119e84a738c2c40ffef420e
|