Building project documentation from Markdown files

Documentation, Please

© Lead Image © stokkete, 123RF.com

© Lead Image © stokkete, 123RF.com

Article from Issue 263/2022
Author(s):

MkDocs, a static site generator, lets you easily transform Markdown files into ready-to-use, user-friendly project documentation.

Documentation: Everybody needs it, but not everyone wants to deal with it, especially for smaller projects where time and resources are limited. Even if you manage to find time to create technical content, turning it into user-friendly, searchable, and easy-to-navigate documentation is no mean feat – unless you use MkDocs [1]. This unassuming tool is manna from heaven for anyone looking for a straightforward and low effort way to publish and maintain documentation (Figure 1). You can also use MkDocs for any content that needs to be presented in a structured and easily searchable format, from research notes to a knowledge base.

Figure 1: MkDocs allows you to build documentation for your projects with a minimum of effort.

First Steps

If you have Python 3 and pip installed on your machine, you can deploy MkDocs by running the following command as root:

pip3 install mkdocs

While you are at it, you can also install the Material theme [2] with:

pip3 install mkdocs-material

Although technically correct, to call Material a theme would be doing it a great disservice. Instead of merely giving MkDocs a new look, Material adds an entire new level of functionality, making it worth installing for that reason alone. While certain capabilities of Material are available only to paying supporters, all the features described in this article are free.

When you are ready to create your first documentation project, run the command

mkdocs new documentation

(replace documentation with your project name). This creates a project skeleton that includes two items: the mkdocs.yml configuration file and the docs directory for storing the actual content. The content in this case is Markdown-formatted files, and it's up to you how to organize them inside the directory. For example, you can create a separate file for each topic and then group the files into subdirectories, such as Getting started, Tutorials, References, etc. Want to quickly preview the documentation you're working on? Switch to the created project directory, run the mkdocs serve command, and point the browser to 127.0.0.1:8000. The clever part is that whenever you edit the files in the project directory, the server automatically rebuilds the content to reflect the changes.

While the built-in server allows you to preview the documentation site, you still need to convert raw source into a self-contained publishable static site. When you're ready to publish the documentation, run the mkdocs build command, and MkDocs creates the site directory containing the static pages and all the required components. You can then either upload the contents of the site directory to your server or use MkDocs to deploy documentation on GitHub (more about that later). That's what the most basic MkDocs workflow looks like: Create a project skeleton, populate the docs directory with Markdown-formatted text files, preview the result in the browser, and use the mkdocs build command to generate ready-to-publish static documentation.

Configuration

MkDoc's default configuration does the job, but it only uses a fraction of the available features. To make use of these features lurking beneath the surface, you need to enable and configure them by editing the mkdocs.yml configuration file. Initially, the file contains a single configuration parameter: site_name: My Docs. Listing 1 shows a real world configuration file. The first four parameters (site_name, site_description, site_author, and site_url) are pretty much self-explanatory.

Listing 1

Sample Configuration File

site_name: KOReader compendium
site_description: KOReader knowledgebase
site_url: https://dmpop.github.io/koreader-compendium/
site_author: Dmitri Popov
repo_url: https://github.com/dmpop/koreader-compendium/
edit_uri: blob/main/docs/
copyright: Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0)
plugins:
  - search
  - git-revision-date-localized:
      enable_creation_date: true
      type: date
theme:
  name: material
  logo: logo.png
  favicon: logo.png
  palette:
    - scheme: default
      primary: green
      accent: teal
      toggle:
        icon: material/lightbulb-outline
        name: Switch to dark mode
    - scheme: slate
      primary: green
      accent: teal
      toggle:
        icon: material/lightbulb
        name: Switch to light mode
  font:
    text: Lato
    code: JetBrains Mono
markdown_extensions:
  - admonition
  - pymdownx.details
  - pymdownx.highlight:
      linenums: true
  - pymdownx.superfences
  - pymdownx.inlinehilite
  - pymdownx.keys

Things get a bit more interesting starting with the repo_url parameter. MkDocs assumes that you manage your documentation project's source code using Git. You don't have to use Git, but if you choose this approach and opt to use GitHub or GitLab public or private instances for hosting the source code, you can specify the URL of the repository. This adds the link in the upper-right corner of the finished documentation site. If you use GitHub, MkDocs even adds Stars and Forks counters. But that's not all. If you want to encourage readers to contribute to the documentation, specify edit_uri to add an edit link to each page. This link points to the page in the GitHub repository where the reader can make edits and create pull requests. The exact URL depends on the platform you're using. For GitHub, it's blob/main/docs/; for GitLab, it's -/blob/main/docs (where main is the name of the branch).

The copyright parameter requires no explanation, so I'll move on to the plug--ins section. MkDocs' functionality can be extended by installing plugins. I'll discuss third-party plugins later, but what you need to know now is that to enable a plugin you must specify it in the plugins list. The search plugin is enabled by default, but you must add it to the list if you use other plugins.

MkDocs comes with a handful of themes, and you can specify the desired theme in the theme section. Because you've installed Material, it makes sense to enable it for use with MkDocs. It also makes sense to replace the default logo and favicon with your own. To do this, specify their paths and file names as the logo and favicon parameters. In Listing 1, the logo.png file is used both as a logo and a favicon, and the file itself is stored in the docs directory.

Material comes with its own features that can be enabled by adding them to the features list. For example, if you want the header bar to automatically hide when the user scrolls past a certain threshold, enable header.autohide as follows:

features:
    - header.autohide

Material makes it possible to adjust its color palette by specifying primary and accent colors using the appropriate entries under the palette parameter. To quickly figure out what colors you want to use, open the Changing the colors page [3] and pick the desired primary and accent colors to see the result in real time. A dark mode with the ability to toggle it makes the reading experience more comfortable. To enable this feature in Material, you have to specify a dark mode theme in addition to the default (light) one and then define toggles for each theme. In Listing 1, the toggle for the default theme is as follows:

- scheme: default
  <...>
  toggle:
    icon: material/lightbulb-outline
    name: Switch to dark mode

And the toggle for the dark theme looks like this:

- scheme: slate
  <...>
  toggle:
    icon: material/lightbulb
    name: Switch to light mode

In the example, slate is used as the dark theme.

You can also replace the default font. Using the font list, you can specify any font available through Google Fonts:

font:
  text: Lato

And if you want to use a specific monospaced font for code, you can do that too:

font:
  text: Lato
  code: JetBrains Mono

If you want to bypass Google Fonts and use the fonts you include with your documentation, you can do that by specifying a custom CSS stylesheet:

extra_css:
  - css/extra.css

Then add a font definition to the extra.css file:

@font-face {
  font-family: "Lato";
  src: "fonts/Lato.ttf";
}

What makes Material particularly suitable for technical writing is the fact that it supports a wide range of Python Markdown extensions. The admonition extension, for example, makes it possible to add notes, tips, examples, quotes, warnings, etc. To enable the desired extension, add it to the markdown_extensions list:

markdown_extensions:
  - admonition

The extensions you choose to enable depend entirely on your needs. The Python Markdown Extensions page [4] provides a list of all the supported extensions, with detailed descriptions and usage examples. The extensions listed in Listing 1 are the bare minimum that you would want to include. In addition to admonition, Listing 1 enables syntax highlighting (pymdownx.highlight), both inline (pymdownx.inlinehilite) and fenced code blocks (pymdownx.superfences), as well the ability to render keys and keyboard shortcuts (pymdownx.keys).

MkDocs Plugins

While MkDocs and Material make a rather powerful combination, you can also add other features and capabilities using MkDocs plugins. Need to add an RSS feed to your documentation site? There is a plugin for that. Want to include charts? There is a plugin for that, too. Similar to MkDocs itself, most plugins are available through the PyPI package repository [5]; to see a list of MkDocs plugins, simply search for mkdocs. To deploy a plugin, install it using the pip package manager. For example, the following command installs the plugin that adds the localized date of the last modification date to every page:

sudo pip3 install mkdocs-git-revision-date-localized-plugin

Once the plugin has been installed, enable and configure it in the configuration file:

plugins:
  - git-revision-date-localized:
    enable_creation_date: true
    type: date

Another plugin worth adding right from the start is Awesome Pages. By default, MkDocs arranges pages alphabetically, which is rarely how you'd want to organize your content. An ugly workaround is to prepend pages with numbers (e.g., 01_quickstart.md, 02_advanced_config.md). But if you have a lot of pages and often add new content, keeping the content structured can quickly become a time-consuming and laborious process. The Awesome Pages plugin provides an elegant solution to the problem. Install the plugin using the command

sudo pip3 install mkdocs-awesome-pages-plugin

and add awesome-pages to the plugins list in mkdocs.yml. Then create a .pages file in the docs directory and add the pages in the order you want them to appear in the documentation as follows:

nav:
  - quickstart.md
  - advanced_config.md

Check the plugin's documentation for other options you can use [6].

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Static Website Generators

    If you only want to put a blog, technical documentation, or a web business card online, static website generators can save you a lot of work.

  • Tutorials – Markdown

    Create attractive and structured documents from the comfort of your text editor – and convert them to a huge array of formats.

  • Workspace: Markdown Tools

    From note-taking applications to wikis – there are plenty of handy tools for working with Markdown-formatted content. Here are a few worth adding to your toolbox.

  • Zim

    Organize your ideas, notes, and shopping lists with Zim, a handy tool that enshrines the principle of the wiki on your Linux, Mac OS, or Windows desktop.

  • Personal Knowledge Managers

    Obsidian helps you think and work more effectively by giving you a tool to record, connect, and catalog your ideas and related notes.

comments powered by Disqus
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters

Support Our Work

Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.

Learn More

News