How to create attractive, interactive docs

Technical documentation has a bad reputation. Too often technical docs and blog posts are dry, linear, and static. They read more like an instruction manual than a narrative.

When I see how data journalism has impacted the calibre of storytelling for news publications, packing articles with rich visualizations and interactivity, it makes me scratch my head. Why aren’t devs making attractive, immersive documents like that?

I believe that there is unrealised potential for making stories about code more expressive and edifying. Tutorials and tech blogs can be a lot better.

What does great technical storytelling look like?

The first example I saw of a blog post that took an abstract technical subject - designing systems - and delivered a visually rich and interactive experience was Up and Down the Ladder of Abstraction by Bret Victor. For this subject, it could easily have been a snorefest. Instead, Bret created a beautiful and engaging exploration of the subject, which he called a visual essay.

screenshot of Up and Down the Ladder of Abstraction visual essay

Bret takes an example of designing a control system for a simple car simulation, and references it throughout the essay with visuals and interactive widgets accompanying the narrative.

Even the hero section of the article has a car that can be controlled and can jump up a ladder! It is peculiar that this was written in 2011, and even now, you don’t see many articles of this calibre!

If you are not a designer or a particular type of frontend developer, you may think I can’t create visuals like that!

That is a legitimate concern!

I want to show you a few more examples now, and later I will show you that there are some simple, common patterns that can be extrapolated from these examples that can empower everyone to create better technical documents. It does not have to be a “visual essay”, but we can draw inspiration from them.

Nowadays, the most common source of visual essays is from the data visualization (data viz) community. Publications such as The Pudding cover a varierty of subjects. However, the subject matter is not web development. But in order to produce the visual essays, the writers must know web development, right? Well, why don’t they turn their attention to explaining web development topics in a similar way, wouldn’t that be great?

Well, Amelia Wattenberger is from the data viz school, she was a contributor to The Pudding, and she has explored some web development topics on her blog. Take her article on The CSS Cascade. She literally embeds a cartoon cascade in the article as a sticky section! The cascade is a river, and has a kayaker that paddles downriver as you scroll through the article. 🚣

screenshot of The CSS Cascade visual essay

While it is cool to have a kayaker accompany you while you read, a lot of value is delivered by a simple question and answer widget. It seeks to challenge the reader and test comprehension along the way. It is just 2 code blocks, and an answer block with a blurry foreground that can be clicked to reveal the answer.

screenshot of the question and answer widget from The CSS Cascade visual essay

As a design-challenged dev, you could pull that off, right?

Really, it is simple touches like this that can elevate an article or technical document. A tenet worth following is what Confucius said: “Tell me and I will forget, show me and I may remember; involve me and I will understand.”

Here are some more examples of great interactive articles related to web development:

Delba Oliveira has a list of inspirational interactive articles too. In her post on Interactive playgrounds, she says:

One under-utilised opportunity for active learning the web allows is interaction. Creating interactive content is both more time consuming and more technically challenging. But if active learning is more effective, I think it’s worth a shot.

That covers one-off articles. What about technical documentation (docs)?

A pioneer in technical documentation is Stripe. Their docs have exemplary code walkthroughs. Stripe are smart to recognize this as an opportunity. Their core audience is developers. Any friction they can remove to make it easier for devs to understand their SDK and APIs will boost adoption of their product. And that should boost their revenue. 🤑

Take a look at the below video to understand how the Stripe docs step you through a big example.

Example of Stripe's interactive docs, building a checkout page on the client

Lastly, a great example of how to make technical documentation more approachable is Yoksel’s flex cheatsheet. She took the CSS Flexible Box Layout Module Level 1 W3C specification, and turned the static examples into interactive examples.

Would you prefer this version?

screenshot of Yoksel's flexbox cheatsheet showing the flex direction with an interactive example
Flex direction section in flex cheatsheet.

Or the original?

screenshot of the flex direction section from the CSS Flexible Box Layout Module Level 1 Specification
Flex direction section from CSS Flexible Box Layout Module Level 1 specification.

Why isn’t this the norm? 😃

There is a funny side note to this. At the moment, Eric Meyer and Estelle Weyl are finishing a new edition of the well-established book - CSS: The Definitive Guide. Estelle was on an episode of the JS Party podcast recently, and she said that a suggestion for the subtitle of the book was “We read the specs, so you don’t have to”, but it was rejected as an idea!

We’re finishing it up right now. Eric Meyer is writing most of it, but I’m helping him, and I wanted to subtitle it “We read the specs, so you don’t have to.”

O’Reilly put the kibosh on that, but they did say they would put it on the back cover.

While I appreciate that the specs are primarily written for browser vendors, I have read some of the specs, and refer to them from time to time. So why not make them a more approachable resource?

Now, I want to explore the new wave of dev documentation tools that are offering the potential to create better documentation.

Use themes and language grammars from VS Code with Shiki

Syntax highlighting is ubquitious in tech blogs and docs. You can help developers to understand the code by highlighting the syntax, exactly like their favourite text editor does.

The thing is though that most syntax highlighting libaries do not highlight code like your favourite text editor does. Some libraries do a rough job of picking out semantic tokens. Many libraries lack support for frameworks. You may have to hunt down a CSS file to bring your favourite theme to the browser.

Back in June, I did a short review of syntax highlighting libraries to see how well they support modern frontend frameworks. None of them have been able to keep up properly!

That led me to the following query:

I wonder if the Language Server Protocol (LSP), which powers some of the features for code editors now, provides a simple way to abstract out the semantic highlighting bit. It seems a shame that the different libraries are all doing the same thing and they do not quite cover the bases.

In other words, why not reuse what is in text editors like VS Code?

The outcome I was (mostly) hoping for has been done by Shiki library! I take no credit for the idea btw, I am just glad that someone else (Pine) took it on!

Shiki is a syntax highlighting library based on VS Code’s syntax highlighting functionality.

The first big payoff of this decision is that you can use any VS Code theme for syntax highlighting code blocks on your website.

Just change this line to switch between the bundled themes:

theme: 'nord'

Nice and easy!

warning icon

Shiki is geared towards being used at build time or on the server. The package size is 8.42MB unpacked, so this is not something you want to use in the browser unless you want your users to suffer! 🤣

If you want to use a libary in the browser, a lightweight library like Prism is more suitable.

The second payoff is that Shiki supports well over 100 langauges and frameworks out of the box.

Also, you are wayyyy more likely to be able to highlight your favourite frontend framework without needing to figure out how to get done by yourself. This is because Shiki uses the TextMate grammar for language definitions, which many of the most popular text editors support including VS Code, Sublime Text, and Atom. Basically if there is syntax highlighting in one of those text editors for a framework or language, then you can take its grammar file and load it in Shiki direcly to highlight the syntax of your code.

Another thing that I like about Shiki is that the colors for the semantic tokens are added as an inline styles by default. Skipping a stylesheet will improve the rendering performance of your page.

If you take the hello world example in written in Svelte below:

let name = 'world';

<h1>Hello {name.toUpperCase()}!</h1>

This is the HTML output:

<pre class="shiki" style="background-color: #282a36">
<span class="line">
<span style="color: #F8F8F2">&lt;</span>
<span style="color: #FF79C6">script</span>
<span style="color: #F8F8F2">&gt;</span>
<span class="line">
<span style="color: #F8F8F2"> </span>
<span style="color: #FF79C6">let</span>
<span style="color: #F8F8F2"> name </span>
<span style="color: #FF79C6">=</span>
<span style="color: #F8F8F2"> </span>
<span style="color: #E9F284">'</span>
<span style="color: #F1FA8C">world</span>
<span style="color: #E9F284">'</span>
<span style="color: #F8F8F2">;</span>
<span class="line">
<span style="color: #F8F8F2">&lt;/</span>
<span style="color: #FF79C6">script</span>
<span style="color: #F8F8F2">&gt;</span>
<span class="line"></span>
<span class="line">
<span style="color: #F8F8F2">&lt;</span>
<span style="color: #FF79C6">h1</span>
<span style="color: #F8F8F2">&gt;Hello </span>
<span style="color: #FF79C6">{</span>
<span style="color: #F8F8F2">name.</span>
<span style="color: #50FA7B">toUpperCase</span>
<span style="color: #F8F8F2">()</span>
<span style="color: #FF79C6">}</span>
<span style="color: #F8F8F2">!&lt;/</span>
<span style="color: #FF79C6">h1</span>
<span style="color: #F8F8F2">&gt;</span>

And last but not least, leveraging VS Code’s functionality means you get highly accurate syntax highlighting.

Kudos to Pine for making this. Consider sponsoring him if you use this.

What about other features?

It is still early days for Shiki. The current release is v0.11.0. I hope that there is more to come from it.

Some of the most commmon features that other syntax highlighting libraries offer are missing, such as:

  1. Showing line numbers to reference the code you’re discussing,
  2. Highlight lines or words to draw attention to specific parts of code,
  3. The ability to add custom classes and attributes for styling and displaying additional information such as filenames.

Since the HTML markup is well structured, you could show line numbers with some CSS.

code {
counter-reset: step;
counter-increment: step 0;

code .line::before {
content: counter(step);
counter-increment: step;
width: 1rem;
margin-right: 1.5rem;
display: inline-block;
text-align: right;
color: rgba(115,138,148,.4)

The rest of these features need to be built. You can check the requested features in the GitHub issues to see how it is progressing.

Some people are already building some of these features independently. One example is a fork of Shiki called Shiki-Twoslash (Twoslash) that has added features such as line highlighting and typescript metadata annotation (say what!).

Let’s take a look at Twoslash’s typescript metadata annotations and see how that can elevate code blocks in the browser.

Embed TypeScript compiler metadata in code blocks with Twoslash Shiki

Shiki Twoslash (Twoslash) is a “polite but hard fork of the Shiki code rendering engine”. Very considerate! 💕

Twoslash brings two major additions.

Firstly, it brings metadata. It can show types as hover information, accurate error information, and other callouts on your JavaScript and TypeScript code courtesy of the TypeScript compiler.

twoslash shiki hover with type information for an import statement

Anything your text editor can tell you about your code, Twoslash can include in a code block for you.

For example, here is the typeahead dialog when working on an express app:

twoslash shiki example showing hover popup demonstrating the autocomplete popup for an expresss reference

Secondly, Two Slash gives you the ability to create interrelated code blocks. It has a custom include directive where you can reference previous code blocks. So, you can write a code block and explain a little, then write another code block that carries on from previous block and explain more. It has echoes of a Jupyter Notebook.

Side by side demonstration of twoslash's interrelated code block syntax.

To get a deeper understanding of Twoslash, peruse the examples on its homepage.

There are already integrations with popular JavaScript tooling such as eleventy, Gatsby, and Docusaurus.

Fatih Kalifa wrote a walkthrough of how he added build-time type annotation and syntax highlighting using Twoslash to his Gatsby website.

Kudos to Orta for making this. Consider sponsoring him if you use this.

Interactive code walkthroughs with Code Hike

Code Hike is a remark plugin for MDX. It was made to provide more compelling ways to tell stories with code.

Currently, Code Hike can only be used with React. 😒 I hope that support improves in the future.

Code Hike has a couple of neat features that I would like to show you.

Code Hike has a custom <CH.Spotlight> component that allows you to focus the reader’s attention to different parts of the code block. It can dim or animate away surrounding code, and introduce new sections or files. It is reminscint of the Stripe docs.

Check the demo to see the source MDX file and output side-by-side.

demon of spotlight component
Demonstration of <CH.Spotlight> component

You create a <CH.Spotlight> component, and place markdown inside with some custom annotations. The authoring experience is pleasant.

When you combine this spotlight functionality with scroll triggered events, you get scrollycoding. It is a beautiful way to step through code with laser-like focus. Check the demo to see the source MDX file and output side-by-side.

You can watch the video below to see it in action.

Rodrigo Pombo is the creator of Code Hike. He has been making tools to make code and technical data more comprehensible for some time. Previously, he applied a similar technique to make your git history easier to navigate and comprehend with git-history.

Rodrigo did a talk related to Code Hike titled “Stop Writing Dead Fish”, if you would like to see more of the capabilities of Code Hike. The title of the talk is a hat tip to a talk done by Bret Victor, so it gets my seal of approval!

Kudos to Rodrigo for creating this. Consider sponsoring the project if you use it.

UI Patterns

There are some UI patterns emerging in technical documentation that we can apply to any document with technical content. Let’s shine the light on some of these patterns in the hope that they recieve wider application.

Tabbed view to show snippets for different languages and package managers

It sounds obvious to show different files in a tabbed view, but it’s not as widely used as you would expect. It is usually used in docs to show instructions for different package managers, or to show code for the same example in different programming languages.

For example, with the eleventy (11ty) static site generator, you can use use many different template languages to create webpages. The explanations in the docs demonstrate concepts using the 4 most popular template choices in a tabbed pane, as below.

screenshot of eleventy docs for collections, it shows how to create a blog using collections using the 4 most popular template languages. The code excerpt for the lnguages are in tabbed pane.
The Collections (Using Tags) section of 11ty docs. A tabbed pane show how to use collections to make a blog with the 4 most popular template languages

The Astro framework takes this further in their docs, by coordinating the selection of their tabbed instances in the same page. For example, in the guide on Integrations, they have instructions on how to add the correct packages for the integrations. They have a tabbed view with a tab for each of the most popular package managers: npm, pnpm, and yarn. Selecting ‘pnpm’ in the first instance, switches the active tab to ‘pnpm’ for all tabbed instances in the page.

screenshot of astro docs for installing a UI framework using a package manager. Selecting 'pnpm' in the first instance, switches the active pane to 'pnpm' for all tabbed instances

Stripe take a slightly different approach in their docs. They have a tabbed preview pane with the source files, accompanied by 3 lists to select the platform, frontend environment, and backend langauge. Changing these selections will swap out the source files in the tabbed preview pane.

screenshot of astro docs demonstrating the project structure in a plaintext code block. Some files are highlighted to aid in explanation.

There is no HTML tabbed view element, so this can led to folks creating their own implementation unfortunately. The open ui project is researching a proposal for a tabs element. Fingers crossed that it becomes a reality some time soon! 🤞

File tree to demonstrate project structure

Showing a file tree to explain project structure is a common requirement for static site generators and frameworks. This is becoming more important with frameworks basing routing and other tasks on where you put your files.

The most common method to show a file tree is to use a plaintext code block, and use slashes and indentation to show the folder structures. Kind of like ASCII art!

This is an example from the Astro docs:

screenshot of astro docs demonstrating the project structure in a plaintext code block

The ability to highlight folders and add tooltips would help with a walkthrough.

Below is an excerpt from the Astro docs where they have an example talking about page routes, they highlight some files in the file tree.

screenshot of astro docs demonstrating the project structure in a plaintext code block. Some files are highlighted to aid in explanation.

I haven’t seen this formalized into a feature anywhere.

Integrated view of source and output

To explain syntax for a library or language, it is helpful to have an integrated view to show the source and the output side-by-side.

We saw this in Shiki Twoslash docs for the explanation of their annotation syntax.

twoslash interrelated code blocks

In articles, it is common to see a complete example embedded as a codepen or using some external code sandbox environment. The output (preview or result) is a rendering of the source files. The problem with this is that it takes people outside of the authoring experience of the document.

As an UI pattern, you should be able to define sources and an ouput. From an authoring perspective, it shouldn’t matter if it is done in a pre-rendered way, or it is editable like a code sandbox.

Selective focus to step through code

Drawing attention to different sections of the code can help step a reader through the code in a measured manner. Rodrigo Pombo calls this pattern - the code spotlight.

In its simplest form, you can highlight lines of a code block. It is prevalent in syntax highlighting libraries to have custom annotations for markdown code blocks to highlight lines.

For example, Twoslash permits highlighting of individual lines, and ranges of lines, via a list in a codefence comment. The comment is a set of curly braces enclosing a list of numbers e.g. {1, 3} will highlight lines 1 and 3. A range is designated by two numbers concatenated by a hyphen e.g. {3-5} designates lines 3 to 5 to be highlighted. See below for an example of this.

twoslash interrelated code blocks

There are variations of this annotation syntax in different libraries.

The more sophisticated form of this is to permit the user to change the focus in a code block. You can see this in the wild in the Stripe docs.

For example, on the custom payment flow page, you can click on the steps in a side panel to jump to a focused section of the code.


Scrollytelling uses the scroll position to control what is shown to the reader. This is very helpful for code walkthroughs.

Again, you can visit the custom payment flow page of the Stripe docs, and scroll through it, to see a good exposition of this pattern. As you scroll, the preview pane animates code into focus to tie the narrative to a specifc section of code.

Most of the people that I have seen doing this are using MDX and React such as Varun Vachhar and Code Hike. I would like to see a more general purpose approach, the Intersection Observer API is the key ingredient.

Question and answer view

Again, this sounds so simple, but it is rare. Asking a question of the reader can involve a reader more. You can have a pane for the question, and another pane for the answer which can be revealed interactively.

As mentioned earlier, Amelia Wattenberger does this in her post - The CSS Cascade.

screenshot of the question and answer widget from The CSS Cascade visual essay

Closing thoughts

Creating great, interactive content takes time and effort. I hope that through the examples, tools, and UI patterns that I have shown in this article, that you can see that this is something that is attainable for anyone with some dev skills.

There are promising signs that there is an appetite to create more compelling narratives with technical content, and that there is tooling emerging to facilitate this. It still feels like early days though, and we can do with more help!

It is interesting to analyse Stripe’s moves in this area. They created their own Markdown-based document format called Markdoc. This was designed to meet the needs of their user-facing product documentation. They said the following on why they created Markdoc:

We chose Markdown as a starting point because it is easy to read and reason about, already familiar to many engineers and technical writers, and widely supported by a large ecosystem of existing tools. Markdown by itself, however, isn’t ideally suited for writing complex, highly-structured content like documentation.

Markdoc provides an extensible system for defining custom tags that can be used seamlessly in Markdown content. Using the custom tag syntax, we’re able to express more elaborate document hierarchy, insert interactive components, and support features like conditional content, content inclusion, and variable interpolation. Markdoc’s extensions to the Markdown syntax are designed to be composable and minimally intrusive, providing crucial functionality without compromising the readability of prose.

At its core it is an extension to the popular open-source Markdown library markdown-it. Plugins for markdown parsers and preprocesseors are an avenue for more innovation of this nature.

There is a rise in markdown-based formats to bring capacity for integrating interactive content into documents, namely using web components in markdown. MDX enables using JSX with markdown, the chief aim to bring React components to markdown. Mdsvex brings svelte components to markdown.

It may not seem like it, but Markdoc and MDX are two different approaches. Stripe summarises this well in their answer to the question: Why not MDX?

MDX is a Markdown variant that allows users to embed content written in JavaScript and React’s JSX templating syntax. Like Markdoc, MDX makes it possible to incorporate React components into a piece of documentation. The key difference is that MDX supports arbitrarily-complex JavaScript logic (think: docs as code) while Markdoc enforces a strict separation between code and content (think: docs as data).

Markdoc uses a fully declarative approach to composition and flow control, where MDX relies on JavaScript and React. This means MDX affords users more power and flexibility, but at the cost of complexity–content can quickly become as complex as regular code, which can lead to maintainability complications or a more difficult authoring environment.

One of the key motivations for creating Markdoc at Stripe was to create a format that is optimized for writing rather than programming so that we could overcome the challenges that resulted from mixing code and content in our legacy documentation platform.

I don’t totally understand the dichotomy that they mention between docs as code versus docs as data, but I agree with the sentiment that the authoring experience should be optimized for writing content, not code. I will need to look into Markdoc and MDX in more detail to grasp the nuances of this distinction.

Also, I think it is important not to get bogged down with tools. Tools should not dictate the message. You should not feel like you are embedding content into code. Also, placing code-like snippets into documents should not lead to maintenance tangles.

Separating concerns in the right way is challenging. The goal is to create an authoring experience that simplifies the addition of interactivity.

I hope to see more innovation in this space.

Thanks for reading!