22
33Generate user story maps from markdown. Write your product spec as a readable
44document — personas, releases, activities, tasks, and stories — and render it
5- as a styled HTML page.
5+ as a styled, interactive HTML page.
66
77## Installation
88
@@ -19,6 +19,9 @@ storymap render mymap.md # → mymap.html (same directory)
1919storymap render mymap.md -o out # → out/mymap.html
2020```
2121
22+ The output is a single self-contained HTML file. Local images referenced in
23+ story descriptions are embedded as base64 data URIs — no external files needed.
24+
2225** PDF output:** open the generated HTML in a browser and use print-to-PDF
2326(Ctrl+P → Save as PDF). The HTML includes print-optimised CSS for landscape
2427layout and color preservation.
@@ -27,7 +30,7 @@ layout and color preservation.
2730
2831A storymap file is a standard markdown document with three reserved top-level
2932sections: ` # Releases ` , ` # Personas ` , and ` # Map ` . Any other ` # ` heading is
30- treated as the document title (first one) or passed through to the output .
33+ treated as the document title (first one) or ignored .
3134
3235``` markdown
3336# My Product
@@ -50,19 +53,15 @@ Margie manages a team of 8 and primarily uses the app on mobile.
5053# Map
5154## User Management
5255### Authentication
53- #### Sign in [ status:: done] [ persona:: Margie the Manager]
56+ #### Sign in [ status:: done] [ persona:: Margie the Manager] [ release:: MVP ]
5457User can log in with email and password.
5558
56- > release Beta
57-
58- #### Password reset [ status:: in-progress] [ deadline:: 2026-03-01]
59+ #### Password reset [ status:: in-progress] [ deadline:: 2026-03-01] [ release:: Beta]
5960
6061### Profile
61- #### Edit profile [ status:: done]
62-
63- > release Beta
62+ #### Edit profile [ status:: done] [ release:: MVP]
6463
65- #### Upload avatar [ status:: blocked]
64+ #### Upload avatar [ status:: blocked] [ release:: Beta ]
6665Blocked pending storage provider decision.
6766```
6867
@@ -84,34 +83,36 @@ Section names are case-insensitive.
8483#### Story (card in a swimlane)
8584```
8685
87- Use ` > release ` on its own line to advance to the next release swimlane within
88- a task. Annotate it for readability — anything after ` release ` is ignored:
86+ ### Assigning stories to releases
87+
88+ Use the ` [release:: name] ` field on each story to assign it to a release
89+ swimlane. The name must match a release defined in the ` # Releases ` section.
90+ Stories without a ` [release::] ` field are parsed but not shown in any swimlane.
8991
9092``` markdown
9193### Authentication
92- #### Sign in ← Release 1 (MVP)
93- #### Remember me ← Release 1 (MVP)
94- > release Beta
95- #### SSO ← Release 2 (Beta)
96- > release GA
97- ← Release 3 empty for this task
94+ #### Sign in [ status:: done] [ release:: MVP]
95+ #### Remember me [ status:: done] [ release:: MVP]
96+ #### SSO [ status:: not-started] [ release:: Beta]
97+ #### SAML [ status:: not-started] [ release:: GA]
9898```
9999
100- Keep ` > release ` count consistent across all tasks — mismatched counts produce
101- misaligned swimlane rows .
100+ This is more explicit than positional separators — each story carries its own
101+ release assignment regardless of order in the file .
102102
103103### Story fields
104104
105105Stories support optional inline fields using ` [key:: value] ` syntax.
106106Fields appear as badges on the rendered story card.
107107
108108``` markdown
109- #### Story name [ status:: done] [ persona:: Margie the Manager] [ deadline:: 2026-03-01]
109+ #### Story name [ status:: done] [ persona:: Margie the Manager] [ deadline:: 2026-03-01] [ release:: MVP ]
110110```
111111
112112| Field | Values | Default |
113113| ---| ---| ---|
114114| ` status ` | ` not-started ` , ` in-progress ` , ` done ` , ` blocked ` | ` not-started ` |
115+ | ` release ` | Release name from ` # Releases ` section | — |
115116| ` persona ` | Any string matching a persona name | — |
116117| ` deadline ` | ISO date ` YYYY-MM-DD ` | — |
117118
@@ -120,8 +121,45 @@ Any other `[key:: value]` field is accepted and rendered as a badge.
120121### Story descriptions
121122
122123Markdown content following a ` #### Story ` heading and before the next heading
123- or ` > release ` separator is treated as the story description. Descriptions
124- support standard markdown: bold, italics, links, lists.
124+ is treated as the story description. Descriptions support standard markdown:
125+ bold, italics, links, lists, and images.
126+
127+ The first paragraph is always visible on the story card. Additional paragraphs
128+ (separated by a blank line) are shown only in Detail zoom level.
129+
130+ ``` markdown
131+ #### Sign in [ status:: done] [ release:: MVP]
132+ User can log in with email and password.
133+
134+ ** Acceptance criteria:**
135+ - Given valid credentials, user is redirected to dashboard
136+ - Given invalid credentials, an error message is shown
137+
138+ ![ wireframe] ( ./screens/sign-in.png )
139+ ```
140+
141+ ### Images
142+
143+ Images in story descriptions and persona descriptions are embedded as base64
144+ data URIs in the output HTML, making the file fully self-contained. Paths are
145+ resolved relative to the source ` .md ` file. Remote URLs are left as-is.
146+
147+ ## Interactive HTML features
148+
149+ The rendered HTML includes controls for navigating large maps:
150+
151+ ** Zoom levels** — three buttons in the sticky header:
152+ - ** Overview** — story names only, compact layout
153+ - ** Map** — story names, status badges, and first-paragraph descriptions
154+ - ** Detail** — full descriptions and acceptance criteria expanded
155+
156+ ** Release focus** — a dropdown to highlight one release swimlane and dim
157+ the rest. Works independently of the zoom level.
158+
159+ ** Story Lens** — a toggle that enables click-to-zoom on individual story cards.
160+ When active, clicking a card expands it to ~ 40% of the viewport width with full
161+ details visible. Clicking outside collapses it. Stories in the focused release
162+ (if any) are the only ones that can be expanded.
125163
126164## CLI reference
127165
@@ -197,6 +235,8 @@ The template receives:
197235| ` status_colors ` | ` dict[str, str] ` | Resolved status → hex color |
198236| ` ui_colors ` | ` dict[str, str] ` | Resolved UI element → hex color |
199237| ` render_md ` | ` callable ` | Render a markdown string to HTML |
238+ | ` render_md_intro ` | ` callable ` | Render first paragraph only |
239+ | ` render_md_rest ` | ` callable ` | Render everything after first paragraph |
200240
201241The ` darken ` filter is also available: ` {{ color | darken }} ` .
202242
@@ -217,7 +257,7 @@ just test
217257storymap/
218258├── model.py — dataclasses and default color constants
219259├── parser.py — markdown-it-py state machine parser
220- ├── renderer.py — Jinja2 HTML renderer
260+ ├── renderer.py — Jinja2 HTML renderer with base64 image embedding
221261├── cli.py — click CLI entry point
222262└── templates/
223263 └── default.html.j2
0 commit comments