diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 43881eb98b8aab6424bcec2d2fb27f5a5f1c16e9..9eb53ea82bae755ad23a03385aaf44e1a98ebf1e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,19 +1,33 @@
 image: lycantropos/cmake
 
+variables:
+  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
+
+# Global caching directive for pip
+cache:
+  key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
+  paths:
+    - .cache/pip
+
 stages:
   - build
   - test
+  - deploy
 
 before_script:
+  - apt-get update -qq && apt-get -qq -y install python3-pip && python3 -m pip install --upgrade pip
+  - python3 -V
+  - python3 -m pip --version
   - g++ --version
   - cat /etc/*-release
+  - python3 -m pip install gcovr
 
 build:
   stage: build
   variables:
-      GIT_SUBMODULE_STRATEGY: normal
-      GCC_COLORS: "error=31;1:warning=35;1:note=36;1:range1=32:range2=34:locus=39;1:quote=39;1:fixit-insert=32:fixit-delete=31:diff-filename=39;1:diff-hunk=32:diff-delete=31:diff-insert=32:type-diff=32;1"
-      CLICOLOR_FORCE: 1 # Necessary for cmake to output colours
+    GIT_SUBMODULE_STRATEGY: normal
+    GCC_COLORS: "error=31;1:warning=35;1:note=36;1:range1=32:range2=34:locus=39;1:quote=39;1:fixit-insert=32:fixit-delete=31:diff-filename=39;1:diff-hunk=32:diff-delete=31:diff-insert=32:type-diff=32;1"
+    CLICOLOR_FORCE: 1 # Necessary for cmake to output colours
   script:
     - cmake . -DCMAKE_CXX_FLAGS="-Werror -fdiagnostics-color=always"
     - make -j4
@@ -23,15 +37,17 @@ build:
 
 tests:
   stage: test
+  coverage: '/^TOTAL.*\s+(\d+\%)$/'
   variables:
     GIT_SUBMODULE_STRATEGY: normal
   script:
     - cmake .
     - make tests -j4
-    - ./tests --use-colour yes
+    - ./tests --use-colour yes  # Run the tests
   after_script:
     - ./tests -r junit -o junit.xml
   artifacts:
+    expire_in: 1 week
     reports:
       junit: junit.xml
 
@@ -75,4 +91,24 @@ clang-tidy:
     - clang-tidy-7 --version
   script:
     # Running with `script` to give clang a tty so that it outputs colours
-    - script -ec "bash -x ci/clang-tidy.sh"
+    - script -c "bash -x ci/clang-tidy.sh"
+
+pages:
+  stage: deploy
+  when: always  # Deploy always and on build or test failure, generate just the documentation
+  cache:
+    key: "$CI_JOB_NAME"
+    paths:
+      - public
+  variables:
+    GIT_SUBMODULE_STRATEGY: normal
+  script:
+    - apt-get install -qq -y doxygen graphviz lcov
+  after_script:
+    - ./ci/pages_deploy.sh
+    - echo -e "\e[1;36mPublic directory contents\e[0m" && ls -l public/coverage  # Print directory contents for debugging
+  artifacts:
+    paths:
+      - public # Upload the resulting website
+  only:
+    - branches # Deploy on all branches
diff --git a/.gitmodules b/.gitmodules
index d2fb166c6a97657b49df5c5ade5002c3b97e1092..2fc4ae0960502c851fe5afb7c4ffb3290db9f120 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,6 @@
 [submodule "lib/etl"]
 	path = lib/etl
 	url = https://github.com/ETLCPP/etl.git
+[submodule "ci/page_style/doxygen_dark_theme"]
+	path = ci/page_style/doxygen_dark_theme
+	url = https://github.com/MaJerle/doxygen_dark_theme.git
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index fea94c457eb70a79c73b917a8993a9fd83f68f71..b361e80b9ac2795e185ae4f8a0ebeac8becdd851 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,6 +2,7 @@
 <project version="4">
   <component name="VcsDirectoryMappings">
     <mapping directory="$PROJECT_DIR$" vcs="Git" />
+    <mapping directory="$PROJECT_DIR$/ci/page_style/doxygen_dark_theme" vcs="Git" />
     <mapping directory="$PROJECT_DIR$/lib/Catch2" vcs="Git" />
     <mapping directory="$PROJECT_DIR$/lib/etl" vcs="Git" />
   </component>
diff --git a/ci/lcovrc b/ci/lcovrc
new file mode 100644
index 0000000000000000000000000000000000000000..1f8f4e3909219a3c55d2dc20e9b48ebb70c15409
--- /dev/null
+++ b/ci/lcovrc
@@ -0,0 +1,96 @@
+# genhtml configuration
+
+# External style sheet file
+#genhtml_css_file = gcov.css
+
+# Width of line coverage field in source code view
+genhtml_line_field_width = 12
+
+# Width of branch coverage field in source code view
+genhtml_branch_field_width = 16
+
+# Width of overview image
+genhtml_overview_width = 80
+
+# Resolution of overview navigation
+genhtml_nav_resolution = 4
+
+# Offset for source code navigation
+genhtml_nav_offset = 10
+
+# Do not remove unused test descriptions if non-zero
+genhtml_keep_descriptions = 1
+
+# Do not remove prefix from directory names if non-zero
+genhtml_no_prefix = 0
+
+# Specify size of tabs
+# genhtml_num_spaces = 8
+
+# Highlight lines with converted-only data if non-zero
+genhtml_highlight = 1
+
+# Include color legend in HTML output if non-zero
+genhtml_legend = 1
+
+# Compress all generated html files with gzip.
+genhtml_html_gzip = 0
+
+# Include sorted overview pages
+genhtml_sort = 1
+
+# Include function coverage data display
+genhtml_function_coverage = 1
+
+# Include branch coverage data display
+genhtml_branch_coverage = 1
+
+# Specify the character set of all generated HTML pages
+genhtml_charset=UTF-8
+
+
+
+
+
+# geninfo configuration
+
+# Calculate a checksum for each line if non-zero
+geninfo_checksum = 0
+
+# Enable libtool compatibility mode if non-zero
+geninfo_compat_libtool = 0
+
+# Specify whether to capture coverage data for external source
+# files
+geninfo_external = 0
+
+# Use gcov's --all-blocks option if non-zero
+geninfo_gcov_all_blocks = 1
+
+# Specify if geninfo should try to automatically determine
+# the base-directory when collecting coverage data.
+geninfo_auto_base = 1
+
+
+
+
+
+# lcov configuration
+
+# Show full paths during list operation if non-zero
+lcov_list_full_path = 1
+
+# Specify the maximum width for list output. This value is
+# ignored when lcov_list_full_path is non-zero.
+#lcov_list_width = 80
+
+# Specify the maximum percentage of file names which may be
+# truncated when choosing a directory prefix in list output.
+# This value is ignored when lcov_list_full_path is non-zero.
+#lcov_list_truncate_max = 20
+
+# Specify if function coverage data should be collected and processed.
+lcov_function_coverage = 1
+
+# Specify if branch coverage data should be collected andprocessed.
+lcov_branch_coverage = 1
diff --git a/ci/page_style/custom_format.css b/ci/page_style/custom_format.css
new file mode 100644
index 0000000000000000000000000000000000000000..5f5d8bdb1de416ded936f8093047ba9cbf6cf04d
--- /dev/null
+++ b/ci/page_style/custom_format.css
@@ -0,0 +1,15 @@
+.coverLegendCovLo, .coverLegendCovMed, .coverLegendCovHi {
+    border-radius: 15px;
+}
+
+#td.headerCovTableEntry {
+    border-radius: 15px;
+}
+
+.headerCovTableEntryLo, .headerCovTableEntryMed, .headerCovTableEntryHi {
+    border-radius: 15px;
+}
+
+.headerCovTableEntry {
+    border-radius: 15px;
+}
diff --git a/ci/page_style/doxygen_dark_theme b/ci/page_style/doxygen_dark_theme
new file mode 160000
index 0000000000000000000000000000000000000000..460dbe28dbe807ee1b2d03dc712fe637a1b1ab6b
--- /dev/null
+++ b/ci/page_style/doxygen_dark_theme
@@ -0,0 +1 @@
+Subproject commit 460dbe28dbe807ee1b2d03dc712fe637a1b1ab6b
diff --git a/ci/page_style/epilog.html b/ci/page_style/epilog.html
new file mode 100644
index 0000000000000000000000000000000000000000..b139036d7a0608fd210344ce151f1a1ccac68444
--- /dev/null
+++ b/ci/page_style/epilog.html
@@ -0,0 +1,3 @@
+    <button onclick="window.location.href = 'gcovr/gcovr.html';">Gcovr Reports</button>
+    <link rel="stylesheet" href="custom_format.css">
+</body>
diff --git a/ci/pages_deploy.sh b/ci/pages_deploy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e957963ae95db41be2cf641519eefae3b68a2e70
--- /dev/null
+++ b/ci/pages_deploy.sh
@@ -0,0 +1,109 @@
+#!/usr/bin/env bash
+
+# Bash color ouput
+# https://misc.flogisoft.com/bash/tip_colors_and_formatting
+
+# Variables used in gcovr
+EXCLUDED_FILES="^.*(test|lib|main.cpp|CMakeFiles)"
+HTML_TITLE="Code coverage ${CI_PROJECT_NAME}/${CI_COMMIT_REF_NAME}"
+
+# Variables to use with lcov
+cp ci/lcovrc ~/.lcovrc # The coverage configuration file resides in ~/ as ~/.lcovrc
+PAGE_TITLE="${CI_COMMIT_REF_NAME}"
+
+# Assign the correct folder names
+if [[ ${CI_COMMIT_REF_NAME} == "master" ]];
+then
+    ROOT_PATH=""
+    COVERAGE_PATH="coverage/"
+    DOCUMENT_PATH="docs/"
+else
+    ROOT_PATH="${CI_COMMIT_REF_NAME}"
+    COVERAGE_PATH="coverage/${CI_COMMIT_REF_NAME}"
+    DOCUMENT_PATH="docs/${CI_COMMIT_REF_NAME}"
+fi
+
+# Empty the contents from the stored cache, if any
+rm -rf public/${DOCUMENT_PATH}/*
+
+# Create the necessary directories
+mkdir -p public/${DOCUMENT_PATH}
+
+
+# Try to make and build the application
+cmake . -DCMAKE_CXX_FLAGS="-g -O0 --coverage" && make all -j4
+
+# If the command above returned something different than zero, generate only the docs
+if [[ $? -ne 0 ]];
+then
+    echo -e "\e[1;5;91mProgram build failed, only the documentation will be generated.\e[0m"
+else
+    # Coverage generation using lcov
+    # Generate coverage baseline
+    lcov -q --capture --initial --directory . -o coverage_base
+    ./tests --use-colour yes # Run the tests to generate coverage notes
+
+    # In the event of test failure, generate only the documentation
+    if [[ $? -ne 0 ]];
+    then
+        echo -e "\e[1;5;91mTests failed, only documentation will be generated.\e[0m"
+    else
+        # Empty the contents from the stored cache, if any, and create the necessary directories
+        rm -rf public/${COVERAGE_PATH}/*
+        mkdir -p public/${COVERAGE_PATH} "public/${COVERAGE_PATH}/gcovr"
+
+        # Generate the tracefile for the coverage reports
+        lcov -q --capture --directory . -o coverage_tests
+        lcov -q -a coverage_base -a coverage_tests -o coverage_total_unfiltered
+
+        # Remove any unwanted files from coverage report, like external libraries
+        lcov -q --remove coverage_total_unfiltered "${PWD}/lib/*" "${PWD}/CMakeFiles/*" "${PWD}/test/*" "${PWD}/src/main.cpp" -o coverage_total_filtered
+
+
+        # Coverage generation using gcovr. Also generates the html page with the results
+        # Output a summary (-s), sort by ascending percentage (-p), exclude files (-e)
+        gcovr -s -p -e "${EXCLUDED_FILES}" --html --html-details --html-title "${HTML_TITLE}" -o public/${COVERAGE_PATH}/gcovr/gcovr.html
+        gcovr -e "^.*(test|lib|main.cpp|CMakeFiles)"  # Generate coverage report for the CI
+
+        # Render the html page for the lcov results
+        genhtml --demangle-cpp -t "${PAGE_TITLE}" --html-epilog ci/page_style/epilog.html -o public/${COVERAGE_PATH} coverage_total_filtered
+        cp ci/page_style/custom_format.css ci/page_style/epilog.html public/${COVERAGE_PATH}
+
+        echo \
+        "
+        .title:after {
+            content: \" for the ${CI_COMMIT_REF_NAME} branch\";
+        }
+        " >> public/${COVERAGE_PATH}/custom_format.css
+    fi  # Test failure check
+fi  # Build failure check
+
+
+# Documentation generation
+doxygen doxygen.conf
+mv docs/html/* public/${DOCUMENT_PATH}
+
+# Expired branch deletion
+git branch -a | grep "remote" | xargs -n 1 -i sh -c "path=\"{}\"; basename \"\$path\"" > branches_list
+ls -d public/coverage/*/ | xargs -n 1 -i sh -c "name=\"{}\"; basename \"\$name\"" > directory_list
+
+# Output for debugging purposes
+echo -e "\e[1;36mBranch names list\e[0m"
+cat branches_list
+
+echo -e "\n\e[1;36mPublic directory contents list\e[0m"
+cat directory_list
+
+
+# Remove any expired branch folders
+while read directory;
+do
+	if ! grep -q "^${directory}$" branches_list
+	then
+	    echo -e "\e[1;33m${directory} will be removed from the pages.\e[0m"
+		rm -rf "public/${directory}" "public/coverage/${directory}" "public/docs/${directory}"
+	fi
+done < directory_list
+
+echo -e "\e[1;92mDocumentation page for this branch:\e[0m \e[0;36m${CI_PAGES_URL}/${DOCUMENT_PATH}\e[0m"
+echo -e "\e[1;92mCoverage Reports page for this branch:\e[0m \e[0;36m${CI_PAGES_URL}/${COVERAGE_PATH}\e[0m\n"
diff --git a/doxygen.conf b/doxygen.conf
index 4dc3effc360ce57b2ee5b120aa7a03c25188f54e..894adb9c7745763b5dd4233bfeb7c9ba51fc1377 100644
--- a/doxygen.conf
+++ b/doxygen.conf
@@ -441,19 +441,19 @@ EXTRACT_ALL            = YES
 # be included in the documentation.
 # The default value is: NO.
 
-EXTRACT_PRIVATE        = NO
+EXTRACT_PRIVATE        = YES
 
 # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
 # scope will be included in the documentation.
 # The default value is: NO.
 
-EXTRACT_PACKAGE        = NO
+EXTRACT_PACKAGE        = YES
 
 # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
 # included in the documentation.
 # The default value is: NO.
 
-EXTRACT_STATIC         = NO
+EXTRACT_STATIC         = YES
 
 # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
 # locally in source files will be included in the documentation. If set to NO,
@@ -1157,7 +1157,7 @@ HTML_FILE_EXTENSION    = .html
 # of the possible markers and block names see the documentation.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_HEADER            =
+HTML_HEADER            = "ci/page_style/doxygen_dark_theme/html_header.html"
 
 # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
 # generated HTML page. If the tag is left blank doxygen will generate a standard
@@ -1167,7 +1167,7 @@ HTML_HEADER            =
 # that doxygen normally uses.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_FOOTER            =
+HTML_FOOTER            = "ci/page_style/doxygen_dark_theme/html_footer.html"
 
 # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
 # sheet that is used by each HTML page. It can be used to fine-tune the look of
@@ -1192,7 +1192,8 @@ HTML_STYLESHEET        =
 # list). For an example see the documentation.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_EXTRA_STYLESHEET  =
+HTML_EXTRA_STYLESHEET  = "ci/page_style/doxygen_dark_theme/custom.css" \
+                         "ci/page_style/doxygen_dark_theme/custom_dark_theme.css"
 
 # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
 # other source files which should be copied to the HTML output directory. Note
@@ -1249,7 +1250,7 @@ HTML_TIMESTAMP         = NO
 # The default value is: NO.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_DYNAMIC_SECTIONS  = NO
+HTML_DYNAMIC_SECTIONS  = YES
 
 # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
 # shown in the various tree structured indices initially; the user can expand