All work

Strivelabs · Design engineer · Sep 2026

Diagrams in Strive chat

“Adding mermaid charts to easily explain stuff.” A one-line tip on WhatsApp from a friend, after his sales team saw it in another tool. Strive’s chat could already draw one by accident. The real work was deciding when it should, and what it shouldn’t. I landed on two rules: draw only when it helps, and never show a broken one.

My part
Designed end to end, built pairing with Claude Code
Shipped
29 Sep 2026
Status
Live for every tenant
Link
strivelabs.ai
Live today: an answer that draws, then expand, zoom, download and copy the source.

The ask

A tip, not a spec

  • The tip was one line, so before saying yes I asked three things: does it fit Strive, what’s worth building, and what isn’t?
  • It fit. Strive’s chat could already draw a Mermaid block, but only by accident: our markdown renderer happened to support it, and nothing told the model to write one. That path had rough edges, so I started with the two rules, and everything else serves them. I designed it end to end and built it pairing with Claude Code.
One line of feedback, three questions before building.
Drawing a diagram was solved. Drawing it on purpose wasn’t.

Design call · layers

One concern per layer

  • I split it three ways: a tool that decides when, a skill that teaches how, and a validator that’s the real gate. The tool is always in the model’s context. The skill loads only before the first diagram in a conversation.
  • Why not just a skill? Our router picks one best-matching skill per request, and prefers the narrower one. A diagram skill would either never load, or match every question and over-draw. So the when lives in the tool’s description, which is always there.
When, how, and a gate that isn’t a prompt.

Design call · the call

A tool call, not a code fence

  • The cheapest version was one prompt line: “use Mermaid code blocks when helpful”. But nothing can check a code block before it shows, and it streams in half-written.
  • A tool call hands over a title, the source and a caption, which can be parsed and rejected before anyone sees them. Code blocks stay as a safety net: if the model writes one anyway, chat waits until it settles, draws it with the same component, and counts the bypass.
If you can’t check it, you can’t promise it.

Design call · Mermaid again

Same team, opposite answer

  • In July I’d designed the routine “logic workflow” diagram, and said no to Mermaid on evidence. Plain Mermaid got about 50% of the way to the Figma mock, and getting near 90% meant HTML inside labels. So that one became a JSON graph, drawn with React Flow.
  • For chat, the answer flipped. Free-form answers need sequences, journeys, mindmaps and timelines: shapes a node-and-edge graph can’t express. And Mermaid is the diagram language models already know. Both stay, each for its own job.
The job decides, not the tool.

The gate

A broken diagram never shows

  • Every call goes through the real Mermaid parser on the server. It only parses inside a browser-like window, and putting one on the server’s globals would fool other libraries into thinking they run in a browser. So it runs in its own worker thread, and fails open: if the worker is down, chat doesn’t wait on it.
  • A rejected diagram goes back to the model with a targeted hint, like “quote labels with punctuation”, and one retry. Then prose. The browser draws from the tool’s arguments and parses them again, so a failed attempt just isn’t there, live or on reload.
Fail open on the server, hide failures in the browser.

Design call · the look

Meaning, not colours

  • My note on the first render: don’t make them boring. Out of the box, Mermaid looks generated. So every diagram gets a clean finish from the platform’s live tokens, in light and dark and in each tenant’s brand colour. Any styling the model writes is stripped, so the theme always wins.
  • Instead, the model gets a tiny vocabulary of meaning, plus a few taste rules in the skill: stadiums for start and end, decisions phrased as questions, sentence-case labels, at most one diagram per answer.
  • ==>The one main path, bold in the brand colour
  • -.->Optional steps, fallbacks and loop-backs
  • :::accentThe step the answer is about
  • :::success / warning / dangerGood, at-risk and bad outcomes
  • :::mutedOut of scope or deprioritised
The model says what a step means. The theme decides how it looks.

Around the diagram

Expand, download, copy

  • Chat is narrow, and diagrams get big. So each one has a toolbar on hover: expand into a dialog with zoom from 50 to 300%, download a PNG at 2×, or copy the source.
  • Mermaid is about 1 MB, so it loads only when a diagram mounts. A chat that never draws never pays for it.
  1. A title, always. Each diagram says what it shows.
  2. Expand, download, copy. On hover, out of the way.
  3. The step that matters. A brand wash, on the bold main path.
  4. Out of scope, muted. Negatives in quiet, dashed boxes.
  5. A caption with the takeaway. One line under the diagram.
A campaign’s structure, answered as a diagram. The account name is blurred.

Measure

Measure the gate, don’t guess

  • I didn’t guess whether it would draw at the right moments. A 10-prompt eval, six that should draw and four that shouldn’t, ran through the real chat API and model.
  • Rewording the tool moved nothing. The blocker was two older system-prompt lines that sent every explanation to markdown. I reworded both, shorter to fit a 12,700-token budget, and matched the skill’s description to “walk me through…” questions. Recall went from 2/6 to 3/6, and false positives stayed at 0/4.
Recall crept up. False positives never left zero.

What dried

What I’d keep

  • MissReworded the tool’s description first. Recall didn’t move.

    LessonMeasure prompt changes. The real blocker sat in an older line I hadn’t touched.

  • MissPlanned a fixed list of diagram types in the tool’s schema.

    LessonLet code hold the policy. Allowing a type is one line, not a prompt rewrite and another eval.

  • MissGave the parser one timeout for starting and parsing. Slow cold starts were killed, respawned and killed again.

    LessonSeparate budgets for separate jobs: 30 s to start, 8 s to parse. Code review caught this one.

  • MissTrusted Mermaid 11.11’s mindmap. Every node rendered black.

    LessonVisual QA catches what tests can’t. A Storybook pass over every type found it, and a version bump fixed it.

Drawing a diagram is the easy half. Knowing when to draw one is the product.

Back to all work