Skip to content

Technical Documentation

Technical documentation allows you to document how your app works and the architecture of your app. In the case of Epic Fantasy Forge, the technical documentation is used to document how Epic Fantasy Forge is created. This guide is the technical documentation of Epic Fantasy Forge.

Documentation Software

The recommended documentation software to use for technical documentation is Material for MkDocs. Material for MkDocs is free to use and comes with many features useful for technical documentation such as support for code snippets. The documentation is written in Markdown and can be easily added to your version control system.

Installation

Material for MkDocs requires you to have Python installed. Normally Python is already pre-installed on Fedora Linux.

To install Material for MkDocs, run the below command:

pip install mkdocs-material

Version Control

You need to store your technical documentation somewhere. It is recommended to store this documentation in the same Git repository where your app code resides. You can use the Git repository you created earlier in the Creating a Git Repository section of this guide.

Navigate to wherever you cloned your Git repository, for example:

cd ~/Development/epic-fantasy-forge

The Material for MkDocs build generates some files/directories which we don't want to add to our Git repository. To prevent them from being added by accident add the below content to the ".gitignore" file in the root of your Git repository. Create the ".gitignore" file if it does not exist yet.

.gitignore
.cache
public

Create a directory in your Git repository where your technical documentation will go. You might name it "technical-documentation" however in the case of Epic Fantasy Forge I named it "development-guide" since that is what this guide primarily is:

mkdir development-guide

Now enter your new directory:

cd development-guide

Generate MkDocs Project

You can now create an MkDocs project by running:

mkdocs new .

Running MkDocs Project

You can now run your MkDocs project:

mkdocs serve

Your MkDocs project should now be accessible in the browser from localhost:8000:

MkDocs 1

Configuration

Material for MkDocs is configured through a file named "mkdocs.yml" located in the root directory of the MkDocs project. You can open this file in Visual Studio Code or Neovim and configure Material for MkDocs to suit your preferences.

As an example configuration file, you can refer to the Epic Fantasy Forge mkdocs.yml. The Epic Fantasy Forge mkdocs.yml contains some additional configuration not explained below. The full reference for configuring mydocs.yml can be found in the official reference available on the Material for MkDocs website.

Site Name

To change the site name:

mkdocs.yml
site_name: Full Stack Development Guide

The site name should now be updated:

MkDocs Configuration Site Name

Site URL

Configure the site URL to the URL from where you would like your documentation to be available from. For example, for Epic Fantasy Forge we want this development guide to be available from development.epicfantasyforge.com:

mkdocs.yml
site_url: https://development.epicfantasyforge.com

Theme

To use the Material theme:

mkdocs.yml
theme:
  name: material

The site should now be using the Material theme:

MkDocs Configuration Theme

Dark Mode

To use dark mode:

mkdocs.yml
theme:
  palette:
    scheme: slate

The site should now look like this:

MkDocs Configuration Dark Mode

Colors

To change the accent and primary colors add/modify the below configuration. On Epic Fantasy Forge indigo is used as the accent color and deep purple as the primary color.

mkdocs.yml
theme:
  palette:
    accent: indigo
    primary: deep-purple

With these colors your site should now look like this:

MkDocs Configuration Colors

Font

To use a custom font add/modify the configuration item below. On Epic Fantasy Forge the font "Overpass" is used. It is based on the font used on US highway signs.

mkdocs.yml
theme:
  font:
    text: Overpass

With the custom font set your site should now look like this:

MkDocs Configuration Font

Since we don't have a logo yet, the default logo can be kept for now. In a later section of this guide we will create a logo.

Privacy

To improve the privacy of your documentation, enable the privacy plugin:

mkdocs.yml
plugins:
  - privacy

Enabling this plugin allows your documentation site to be completely self-hosted. For example, when you earlier in this guide set a custom font, the font will be downloaded from Google Fonts by your users every time they access your site. By enabling the privacy plugin MkDocs will download its own copy of the font and host it alongside your documentation. In this way users download your custom font from your website and not Google Fonts.

The recommended navigation features to enable are:

mkdocs.yml
theme:
  features:
    - navigation.footer
    - navigation.sections
The navigation.footer feature adds a next page link and a previous page link to the footer:

MkDocs Configuration Next Page

As your site does not have much content yet the navigation sections feature is not yet visible. This feature makes top-level navigation items stand out from sub-items. However, on Epic Fantasy Forge, where there already is structure and content, the page index on the left is displayed as follows when the navigation.sections feature is enabled:

MkDocs Configuration Sections

You can see that the top-level items such as "Development Environment" and "DevOps" are visually distinct from non-top-level items.

Warning

Using the navigation.instant feature may interfere with custom analytics solutions if you use any. This guide later in the Analytics section recommends using the custom analytics solution Pirsch Analytics, therefore it is recommended to leave the navigation.instant feature turned off.

To enable searching of your documentation add the below configuration:

mkdocs.yml
plugins:
  - search
theme:
  features:
    - search.highlight
    - search.suggest

This configuration adds a search bar to your documentation site. Search terms are automatically highlighted in found pages. Whilst typing the search bar will make suggestions.

MkDocs Configuration Search

Table of Contents

Material for MkDocs should automatically add table of contents for each of your pages. Use the Markdown header syntax to create entries in the table of contents.

On Epic Fantasy Forge, a table of contents on any particular page can look as follows:

MkDocs Configuration TOC

Tip

If the table of contents is not showing for a particular page, make sure you have at most one top-most header. More than one top-most header is not supported. See this GitHub issue for more details about this behaviour.

Code Snippets

To enable code snippets, add the below configuration:

mkdocs.yml
markdown_extensions:
  - pymdownx.inlinehilite
  - pymdownx.snippets
  - pymdownx.superfences
theme:
  features:
    - content.code.copy

The feature content.code.copy will allow users to copy your code snippets. Code snippets with the above configuration look like this:

MkDocs Configuration Code Snippets

Images

To allow users to click on images to enlarge them add the below configuration:

mkdocs.yml
plugins:
  - glightbox

Git Repository

You can integrate a Git repository link into your documentation site by adding the below configuration:

mkdocs.yml
repo_url: https://gitlab.com/brusecke/epic-fantasy-forge

Set the repo_url to the Git repository you would like to link. With the above configuration your Git repository should now be visible from the navigation bar on the top right:

MkDocs Configuration Git

With the additional below footer configuration you can get a footer that looks something like this:

MkDocs Configuration Footer

For a copyright notice, add the below configuration:

mkdocs.yml
copyright: Copyright © 2025 Henrik Brüsecke

Replace the above example text with your custom copyright text.

You can link to your social media accounts in the footer. For example, to link to GitLab and LinkedIn, add the below configuration:

mkdocs.yml
extra:
  social:
    - icon: fontawesome/brands/gitlab
      link: https://gitlab.com/brusecke/epic-fantasy-forge
    - icon: fontawesome/brands/linkedin
      link: https://www.linkedin.com/in/henrik-bruesecke-07961791

Customize the above example to your needs. You can for example change the icons.

Warning

None of the information provided by Epic Fantasy Forge should be considered legal advice. Always make your own judgements and consult a lawyer if necessary.

Unfortunately Material for MkDocs does not have native support for adding links to legal documents in the footer. We will want to add a privacy policy and terms & conditions.

If you do not have the budget for a lawyer, you could use a legal document generator instead. The legal document generator service recommended by this guide is GetTerms. The service is available at several price points. The pricing plan recommended by this guide is "Compliance Pro" for $69 per website per year. This plan includes a privacy policy, cookie policy, terms of service policy, acceptable use policy and return policy.

A privacy policy is mandatory to have in many jurisdictions, including the European Union. Terms & Conditions may not be mandatory for a non-commercial website, however it may still be advisable to have one. Terms & Conditions may protect you against potential lawsuits by including clauses regarding liability, accuracy of materials and limitations of use.

To get started, create an account on GetTerms. Once you have an account click on "Create Compliance Pack":

GetTerms 1

In total, we will generate three compliance packs. One for the technical documentation, one for the Epic Fantasy Forge web app and one for the Epic Fantasy Forge desktop and mobile apps. Since we are now only generating a compliance pack for the technical documentation, choose "Website" for the "Select the primary use for your policies" question.

Select "Comprehensive Privacy Policy" and "Terms of Service Policy" for the "Select your policies & inclusions" question and any other policies inclusions you may need. In the case of Epic Fantasy Forge, we do not need a Cookie Policy as no non-essential cookies are used on Epic Fantasy Forge.

GetTerms 2

Fill out the rest of the fields and questions on the form. Finally you should have a privacy policy and terms of service. For both of them select "Markdown" from the dropdown menu and then choose "Copy":

GetTerms 3

In the next section we will paste both policies into Markdown files.

To add custom links to your footer, we need to override the default html for the copyright message. To do so start by creating the overrides directory structure if it doesn't exist yet in your project. From the root of your Material for MkDocs project, run the below command:

mkdir -p overrides/partials

Now create a file named "copyright.html" in the docs/overrides/partials directory of your Material for MkDocs project. Populate this file with the below content:

copyright.html
{# base partial from:
  github.com/squidfunk/mkdocs-material/material/templates/partials/copyright.html
#}
<div class="md-copyright">
  <div class="md-copyright__highlight">
    {% if config.copyright %}
      {{ config.copyright }}
    {% endif %}
  </div>

  <div class="md-copyright__links">
    <a href="{{ base_url }}/legal/privacy-policy/">Privacy Policy</a>
    &nbsp;|&nbsp;
    <a href="{{ base_url }}/legal/terms-and-conditions/">Terms & Conditions</a>
  </div>

  {% if not config.extra.generator == false %}
    <div class="md-copyright__generator">
      Made with
      <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
        Material for MkDocs
      </a>
    </div>
  {% endif %}
</div>

We need to put the legal Markdown files somewhere. Create a new directory for these legal files in the "docs" directory for your Material for MkDocs project:

mkdir legal

In the "legal" directory, create two new files and name them "privacy-policy.md" and "terms-and-conditions.md". Paste the contents of the privacy policy and terms of service you generated with GetTerms earlier into these two new files. Adjust the wording of these texts to your preferences. For example, for Epic Fantasy Forge the following modifications were made:

  • Any instances of "Terms of Service" were replaced with "Terms & Conditions"
  • Any wording applicable to businesses were adjusted or removed since Epic Fantasy Forge is not a business

You should now have links to your privacy policy and terms & conditions in your footer:

Legal 1

Both pages should contain the content you copied/pasted and potentially modified earlier.

Analytics

Analytics allow you to see how many visitors your documentation site gets, what pages are popular, where your visitors are from, what devices they are using to access your website and more. Integration with Google Analytics is natively supported by Material for MkDocs. However, in my opinion, it is hard to recommend Google Analytics since the UI is not very intuitive and you would also need to ask your users for consent since Google Analytics uses cookies.

The Analytics platform recommended to use in this guide is Pirsch Analytics. It is a privacy-focused and cookieless analytics solution. No cookie consent is required from users of your site to use Pirsch Analytics.

Account Creation

Start by creating an account on the Pirsch Analytics website. There is no free version of Pirsch Analytics. Three pricing tiers are offered:

  • Standard
  • Plus
  • Enterprise

The tier recommended in this guide is "Standard". It comes with the most essential features and starts at $6 per month.

Add Website

Once you have an account with Pirsch Analytics go to your "Overview" page and click on "Add Website":

Analytics Add Website 1

Fill in the form with your website domain details. The technical documentation for Epic Fantasy Forge is hosted on development.epicfantasyforge.com. Therefore we need to put the value "development.epicfantasyforge.com" in the "Hostname" field and not "epicfantasyforge.com".

Optionally choose a custom timezone. The subdomain field has an arguably ambiguous name. It refers to the subdomain under which your Pirsch Analytics dashboard will be hosted on Pirsch Analytics. It is not actually related to your real domain, i.e. you can use any identifier there that you want as long as it hasn't been taken already by another Pirsch Analytics customer. Since we will later in this guide host the Pirsch Analytics Dashboard using a custom domain the value you put here for the subdomain is not that important unless you opt to not host your analytics dashboard on a custom domain later. Once you have filled in the form click on "Create Dashboard":

Analytics Add Website 2

Tip

If you use subdomains on your website, then you need to create separate "websites" in Pirsch Analytics for each subdomain. That is why in the above screenshot we use the subdomain "development.epicfantasyforge.com" and not "epicfantasyforge.com" since collecting analytics from websites hosted on subdomains does not work when the subdomain is not configured in Pirsch Analytics.

On the next screen open the "Website Integration" accordion and click the "Copy to Clipboard" button to copy the JavaScript code snippet which you will need later. Now press the "View Dashboard" button to view your newly created dashboard.

Analytics Add Website 3

You should now see your newly created dashboard. Naturally it will be quite empty initially since your website has not had any visitors yet since it is not using Pirsch Analytics yet.

Analytics Add Website 4

Integration

To use Pirsch Analytics with Material for MkDocs we need to make the following configuration changes to mkdocs.yml:

mkdocs.yml
extra:
  analytics:
    provider: pirsch-analytics
theme:
  custom_dir: overrides

Now create the override directory structure we need to use custom analytics. Run the below command from the root directory of your Material for MkDocs project:

mkdir -p overrides/partials/integrations/analytics

HTML template

Either using Visual Studio Code or Neovim, create a new file named "pirsch-analytics.html.j2" and populate it with the below content:

<script
  defer src="https://api.pirsch.io/pa.js"
  id="pianjs"
  data-code="{{ pirsch_analytics_code }}">
</script>

You might notice that this JavaScript snippet looks very similar to the JavaScript snippet copied from the Pirsch Analytics website earlier. The only difference between the two is that the Pirsch Analytics snippet contained your actual unique data-code to identify your website whereas the more generic snippet above does not.

It is better to not hardcode your website's unique Pirsch Analytics identifier directly into your code. Whilst your unique code is not secret, anyone with a web browser inspecting the source of your website can see it, it is better to not share it in a public repository. Anyone who pulls your project's open source code or forks your project will by default use the same Pirsch Analytics code as you which may caused your Pirsch Analytics data to contain traffic originating from someone else's website.

For this reason we use what is known as a HTML template to later insert your actual Pirsch Analytics code with a template engine. The template engine we will use for this task is Jinja.

Template Engine

In the root of your Material for MkDocs project create a scripts folder:

mkdir scripts

Inside the scripts folder create a file named "render_template.py" and populate it with the following content:

render_template.py
import os
from jinja2 import Environment, FileSystemLoader

pirsch_analytics_code = os.getenv('PIRSCH_ANALYTICS_CODE')

env = Environment(loader=FileSystemLoader('overrides/partials/integrations/analytics'))
template = env.get_template('pirsch-analytics.html.j2')

rendered_html = template.render(pirsch_analytics_code=pirsch_analytics_code)

with open('overrides/partials/integrations/analytics/pirsch-analytics.html', 'w') as f:
    f.write(rendered_html)

We will use this script to set the Pirsch Analytics code in your HTML file. The script will read your Pirsch Analytics code from an environment variable.

Environment Variable

Set an environment variable named "PIRSCH_ANALYTICS_CODE" with the "data-code" value you copied from the Pirsch Analytics website earlier. To set an environment variable run the below command:

export PIRSCH_ANALYTICS_CODE="Your unique data-code"

Naturally replace the string "Your unique data-code" in the example above with your actual unique data-code from the Pirsch Analytics website. The data-code is the string inside the red rectangle in the below image:

Pirsch Analytics Data Code

Build & Execution

Now everything is in place to render your actual custom HTML file and to then run Material for MkDocs as usual. To render the custom HTML run the below command from the root directory of your Material for MkDocs project:

python scripts/render_template.py

You should now see that your custom HTML file has been created at the following location relative to the root directory of your Material for MkDocs project:

overrides/partials/integrations/analytics/pirsch-analytics.html

Warning

Make sure not to add the rendered HTML file to your Git repository. Only pirsch-analytics.html.j2 should be added to your repository but not pirsch-analytics.html. You should not add files to the repository that are generated during build time.

Tip

To ensure you don't accidentally add the rendered HTML file to your Git repository you can add the rendered HTML file name to the .gitignore file in the root of your Git repository. Add the following content to your .gitignore file:

.gitignore
pirsch-analytics.html

Now run MkDocs as usual:

mkDocs serve

By default, Pirsch Analytics does not send any analytics data if you are running Material for MkDocs on your local machine. In the Hosting section below we will host your documentation on a remote server. So until then you will not see any analytics data appearing on your dashboard in Pirsch Analytics.

Tip

If you want to verify your Pirsch Analytics integration is working properly without hosting it on a remote server you can manually edit the generated HTML file and manually insert the attribute "data-dev". The value of this attribute should be set to your domain name used in production, e.g. epicfantasyforge.com for Epic Fantasy Forge:

Analytics Data Dev

Hosting

So far we have only run the documentation site on our local machine. To publish your documentation and make it available by the general public you need to host it somewhere. You can host it alongside your main website on your own server, however to keep things simple it is recommended to outsource the hosting of your documentation site. The third-party host for your documentation site recommended by this guide is GitLab Pages. GitLab Pages allows you to host any static site for free.

GitLab Pages

In the earlier section Creating a Git Repository you should have already created an account on GitLab.

To host your Material for MkDocs documentation on GitLab Pages, create a new file named ".gitlab-ci.yml" in the root directory of your Git repository. Populate this file with the below content:

.gitlab-ci.yml
stages:
  - documentation

include:
  - local: "ci/documentation.yml"

Now create a new directory named "ci" in the root directory of your Git repository:

mkdir ci

Inside the "ci" directory create a file named "documentation.yml" and populate it with the following content:

documentation.yml
documentation:
  artifacts:
      paths:
        - public
  image: python:latest
  pages: true
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
  script:
    - cd development-guide
    - pip install mkdocs-glightbox
    - pip install jinja2
    - pip install mkdocs-material
    - python scripts/render_template.py
    - mkdocs build --site-dir ../public
  stage: documentation

CI Variables

The "render_template.py" script above depends on the environment variable "PIRSCH_ANALYTICS_CODE" being set. To automatically set this environment variable inside the Docker instance running in the CI you need to use GitLab CI variables. To create a GitLab CI variable to store your Pirsch analytics code, click on "Settings" and select "CI/CD":

CI Variable 1

Open the "Variables" accordion and click on "Add variable":

CI Variable 2

Set the "Visibility" to "Masked". Despite your Pirsch Analytics code not being a true secret, it's better to hide it in job logs in case someone uses it by accident on another website, e.g. when using your open source project as a reference how to make their own website. Depending on your GitLab settings, the CI job logs may be publicly visible. Optionally your CI variable a "Description" and set the "Key" to "PIRSCH_ANALYTICS_CODE". Set the "Value" to your Pirsch Analytics code which you can retrieve from the Pirsch Analytics website. For more details see Environment Variable above. Leave all the other settings on the default value. Now click "Add variable":

CI Variable 3

Your Pirsch Analytics code should now be stored as a CI variable in GitLab:

CI Variable 4

CI variables are automatically injected into any Docker instances you run on the CI.

Deployment

The CI script we created above will now always execute when you merge a merge request to your default branch. The default branch is usually "main". If you have been working on a development branch, which is the recommended practice in this guide, then you can now create a merge request and merge the merge request to the main branch. Your CI pipeline will now run and after some delay your GitLab hosted documentation should come online. To check your GitLab Pages, click on "Deploy" and select "Pages":

GitLab Pages 1

Your documentation page should now be listed. To access your publicly accessible documentation site click on the link list under "Access pages":

GitLab Pages 2

Your public documentation site should now be visible:

GitLab Pages 3

Custom Domain

Your public documentation site is currently accessible under a hard to remember subdomain, e.g. "epic-fantasy-forge-03b3ba.gitlab.io". To use your own custom domain click on "New domain" under the "Domains" section:

GitLab Custom Domain 1

In the form fill in your custom domain. Remember to use the same domain you used when you configured Material for MkDocs in the Site URL section above. For Epic Fantasy Forge the subdomain development.epicfantasyforge.com is used for the technical documentation, i.e. this development guide. Leave the "Automatic certificate management using Let's Encrypt" ticked. Click on "Create new domain":

GitLab Custom Domain 2

You should now see two custom DNS records you should add in your domain registrar, e.g. Cloudflare:

GitLab Custom Domain 3

Keep the above page open for reference and now go to Cloudflare and configure a CNAME record to point to GitLab. Please refer to the Custom Domain section in the User Facing Documentation guide. There we already covered configuring DNS records in Cloudflare in more detail. Use the details from the last page on GitLab that you kept open for reference to fill the DNS record fields appropriately:

GitLab Custom Domain 4

Now add a TXT DNS record to verify ownership of your domain. Use the details from the last page on GitLab that you kept open for reference to fill the DNS record fields appropriately:

GitLab Custom Domain 5

Now on the still open GitLab page press the refresh icon beside "Verification status" until the verification status becomes "Verified". Depending on DNS delays this may take from several minutes to several hours. Once the verification status becomes verified click the "Save changes" button:

GitLab Custom Domain 6

Your documentation site should now be available from your custom domain, e.g. development.epicfantasyforge.com in the case of Epic Fantasy Forge. Note that due to DNS delays, it might take up to several hours until your site becomes available from the custom domain but usually it only takes a few minutes.

GitLab Custom Domain 7

GitLab Custom Domain 8