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.
.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:
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:
site_name: Full Stack Development Guide
The site name should now be updated:
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:
site_url: https://development.epicfantasyforge.com
Theme
To use the Material theme:
theme:
name: material
The site should now be using the Material theme:
Dark Mode
To use dark mode:
theme:
palette:
scheme: slate
The site should now look like this:
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.
theme:
palette:
accent: indigo
primary: deep-purple
With these colors your site should now look like this:
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.
theme:
font:
text: Overpass
With the custom font set your site should now look like this:
Logo
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:
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.
Navigation
The recommended navigation features to enable are:
theme:
features:
- navigation.footer
- navigation.sections
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:
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.
Search
To enable searching of your documentation add the below configuration:
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.
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:
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:
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:
Images
To allow users to click on images to enlarge them add the below configuration:
plugins:
- glightbox
Git Repository
You can integrate a Git repository link into your documentation site by adding the below configuration:
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:
Footer
With the additional below footer configuration you can get a footer that looks something like this:
Copyright
For a copyright notice, add the below configuration:
copyright: Copyright © 2025 Henrik Brüsecke
Replace the above example text with your custom copyright text.
Social Media Links
You can link to your social media accounts in the footer. For example, to link to GitLab and LinkedIn, add the below configuration:
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.
Legal
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.
Legal Document Generators
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":
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.
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":
In the next section we will paste both policies into Markdown files.
Links in Footer
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:
{# 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>
|
<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:
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":
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":
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.
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.
Integration
To use Pirsch Analytics with Material for MkDocs we need to make the following configuration changes to 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:
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:
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:
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:
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:
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:
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":
Open the "Variables" accordion and click on "Add variable":
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":
Your Pirsch Analytics code should now be stored as a CI variable in GitLab:
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":
Your documentation page should now be listed. To access your publicly accessible documentation site click on the link list under "Access pages":
Your public documentation site should now be visible:
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:
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":
You should now see two custom DNS records you should add in your domain registrar, e.g. Cloudflare:
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:
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:
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:
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.