diff --git a/app/scodoc/gen_tables.py b/app/scodoc/gen_tables.py
index 2f13fdd8e0294d8e6c867125af28bf985e0d1a74..9c2c2880c48956d3097247d9ee22ca1d40c98e42 100644
--- a/app/scodoc/gen_tables.py
+++ b/app/scodoc/gen_tables.py
@@ -55,6 +55,8 @@ from reportlab.lib.colors import Color
 from reportlab.lib import styles
 from reportlab.lib.units import cm
 
+from flask import render_template
+
 from app.scodoc import html_sco_header
 from app.scodoc import sco_utils as scu
 from app.scodoc import sco_excel
@@ -681,16 +683,15 @@ class GenTable:
         fmt="html",
         page_title="",
         filename=None,
-        cssstyles=[],
-        javascripts=[],
+        javascripts=(),
         with_html_headers=True,
         publish=True,
-        init_qtip=False,
     ):
         """
         Build page at given format
         This is a simple page with only a title and the table.
-        If not publish, does not set response header
+        If not publish, do not set response header for non HTML formats.
+        If with_html_headers, render a full page using ScoDoc template.
         """
         if not filename:
             filename = self.filename
@@ -698,21 +699,16 @@ class GenTable:
         html_title = self.html_title or title
         if fmt == "html":
             H = []
-            if with_html_headers:
-                H.append(
-                    self.html_header
-                    or html_sco_header.sco_header(
-                        cssstyles=cssstyles,
-                        page_title=page_title,
-                        javascripts=javascripts,
-                        init_qtip=init_qtip,
-                    )
-                )
             if html_title:
                 H.append(html_title)
             H.append(self.html())
             if with_html_headers:
-                H.append(html_sco_header.sco_footer())
+                return render_template(
+                    "sco_page.j2",
+                    content="\n".join(H),
+                    title=page_title,
+                    javascripts=javascripts,
+                )
             return "\n".join(H)
         elif fmt == "pdf":
             pdf_objs = self.pdf()
diff --git a/app/scodoc/sco_apogee_compare.py b/app/scodoc/sco_apogee_compare.py
index 308403c114e126dd72d36010638ac5e022cf78f2..c7b34d9067df0f7f9b8250b223f0c9a608222b43 100644
--- a/app/scodoc/sco_apogee_compare.py
+++ b/app/scodoc/sco_apogee_compare.py
@@ -43,52 +43,15 @@ Pour chaque étudiant commun:
     comparer les résultats
 
 """
-from flask import g, url_for
+from flask import g, render_template, url_for
 
 from app import log
 from app.scodoc import sco_apogee_csv, sco_apogee_reader
 from app.scodoc.sco_apogee_csv import ApoData
 from app.scodoc.gen_tables import GenTable
 from app.scodoc.sco_exceptions import ScoValueError
-from app.scodoc import html_sco_header
 from app.scodoc import sco_preferences
 
-_HELP_TXT = """
-<div class="help">
-<p>Outil de comparaison de fichiers (maquettes CSV) Apogée.
-</p>
-<p>Cet outil compare deux fichiers fournis. Aucune donnée stockée dans ScoDoc n'est utilisée.
-</p>
-</div>
-"""
-
-
-def apo_compare_csv_form():
-    """Form: submit 2 CSV files to compare them."""
-    H = [
-        html_sco_header.sco_header(page_title="Comparaison de fichiers Apogée"),
-        """<h2>Comparaison de fichiers Apogée</h2>
-        <form id="apo_csv_add" action="apo_compare_csv" method="post" enctype="multipart/form-data">
-        """,
-        _HELP_TXT,
-        """
-        <div class="apo_compare_csv_form_but">
-        Fichier Apogée A:
-        <input type="file" size="30" name="file_a"/>
-        </div>
-        <div class="apo_compare_csv_form_but">
-        Fichier Apogée B:
-        <input type="file" size="30" name="file_b"/>
-        </div>
-        <input type="checkbox" name="autodetect" checked/>autodétecter encodage</input>
-        <div class="apo_compare_csv_form_submit">
-        <input type="submit" value="Comparer ces fichiers"/>
-        </div>
-        </form>""",
-        html_sco_header.sco_footer(),
-    ]
-    return "\n".join(H)
-
 
 def apo_compare_csv(file_a, file_b, autodetect=True):
     """Page comparing 2 Apogee CSV files"""
@@ -114,17 +77,12 @@ def apo_compare_csv(file_a, file_b, autodetect=True):
             """,
                 dest_url=dest_url,
             ) from exc
-    H = [
-        html_sco_header.sco_header(page_title="Comparaison de fichiers Apogée"),
-        "<h2>Comparaison de fichiers Apogée</h2>",
-        _HELP_TXT,
-        '<div class="apo_compare_csv">',
-        _apo_compare_csv(apo_data_a, apo_data_b),
-        "</div>",
-        """<p><a href="apo_compare_csv_form" class="stdlink">Autre comparaison</a></p>""",
-        html_sco_header.sco_footer(),
-    ]
-    return "\n".join(H)
+
+    return render_template(
+        "apogee/apo_compare_csv.j2",
+        title="Comparaison de fichiers Apogée",
+        content=_apo_compare_csv(apo_data_a, apo_data_b),
+    )
 
 
 def _load_apo_data(csvfile, autodetect=True):
diff --git a/app/scodoc/sco_debouche.py b/app/scodoc/sco_debouche.py
index 18b62e899900b7c847990557963d99e0007de42a..a0c9d7a5d39bcd3b6408b07667902fbe1a9814e5 100644
--- a/app/scodoc/sco_debouche.py
+++ b/app/scodoc/sco_debouche.py
@@ -76,7 +76,6 @@ def report_debouche_date(start_year=None, fmt="html"):
     tab.base_url = f"{request.base_url}?start_year={start_year}"
     return tab.make_page(
         title="""<h2 class="formsemestre">Débouchés étudiants </h2>""",
-        init_qtip=True,
         javascripts=["js/etud_info.js"],
         fmt=fmt,
         with_html_headers=True,
diff --git a/app/scodoc/sco_inscr_passage.py b/app/scodoc/sco_inscr_passage.py
index 2e1268ed75ea9cc116129b8ef08e809de922abdd..968c85d6c601b1082c3400d3d71acf3c797906f0 100644
--- a/app/scodoc/sco_inscr_passage.py
+++ b/app/scodoc/sco_inscr_passage.py
@@ -754,4 +754,4 @@ def etuds_select_box_xls(src_cat):
         table_id="etuds_select_box_xls",
         titles=titles,
     )
-    return tab.excel()  # tab.make_page(filename=src_cat["infos"]["filename"])
+    return tab.excel()
diff --git a/app/scodoc/sco_moduleimpl_status.py b/app/scodoc/sco_moduleimpl_status.py
index 8fe8eb3f4cac12eda6c4256c9a6dbedcb86cd8e4..565aa515b10b2abc2c3e3145c8c2a7478bcb5991 100644
--- a/app/scodoc/sco_moduleimpl_status.py
+++ b/app/scodoc/sco_moduleimpl_status.py
@@ -30,7 +30,7 @@
 import math
 import datetime
 
-from flask import g, url_for
+from flask import g, render_template, url_for
 from flask_login import current_user
 
 from app import db
@@ -45,7 +45,6 @@ from app.scodoc.codes_cursus import UE_SPORT
 from app.scodoc.sco_cursus_dut import formsemestre_has_decisions
 from app.scodoc.sco_permissions import Permission
 
-from app.scodoc import html_sco_header
 from app.scodoc import htmlutils
 from app.scodoc import sco_evaluations
 from app.scodoc import sco_groups
@@ -253,11 +252,6 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
     module_resp = db.session.get(User, modimpl.responsable_id)
     mod_type_name = scu.MODULE_TYPE_NAMES[module.module_type]
     H = [
-        html_sco_header.sco_header(
-            page_title=f"{mod_type_name} {module.code} {module.titre}",
-            javascripts=["js/etud_info.js"],
-            init_qtip=True,
-        ),
         f"""<h2 class="formsemestre">{mod_type_name}
         <tt>{module.code}</tt> {module.titre}
         {"dans l'UE " + modimpl.module.ue.acronyme
@@ -564,8 +558,11 @@ def moduleimpl_status(moduleimpl_id=None, partition_id=None):
 </p>
     """
     )
-    H.append(html_sco_header.sco_footer())
-    return "".join(H)
+    return render_template(
+        "sco_page.j2",
+        content="".join(H),
+        title=f"{mod_type_name} {module.code} {module.titre}",
+    )
 
 
 def _ligne_evaluation(
diff --git a/app/scodoc/sco_poursuite_dut.py b/app/scodoc/sco_poursuite_dut.py
index e312076354391ca872e46c702b59e99949ba5393..628608a59b0148e2142f1f7f3a938fa575564e50 100644
--- a/app/scodoc/sco_poursuite_dut.py
+++ b/app/scodoc/sco_poursuite_dut.py
@@ -233,7 +233,6 @@ def formsemestre_poursuite_report(formsemestre_id, fmt="html"):
     tab.base_url = "%s?formsemestre_id=%s" % (request.base_url, formsemestre_id)
     return tab.make_page(
         title="""<h2 class="formsemestre">Poursuite d'études</h2>""",
-        init_qtip=True,
         javascripts=["js/etud_info.js"],
         fmt=fmt,
         with_html_headers=True,
diff --git a/app/templates/apogee/apo_compare.j2 b/app/templates/apogee/apo_compare.j2
new file mode 100644
index 0000000000000000000000000000000000000000..1ab0a7b2d0e15958bf443d3bb47c60081b3afaf4
--- /dev/null
+++ b/app/templates/apogee/apo_compare.j2
@@ -0,0 +1,15 @@
+{% extends "sco_page.j2" %}
+{% import 'wtf.j2' as wtf %}
+
+{% block app_content %}
+<h1>Comparaison de fichiers Apogée</h1>
+
+<div class="help">
+    <p>Outil de comparaison de fichiers (maquettes CSV) Apogée.
+    </p>
+    <p>Cet outil compare deux fichiers fournis.
+    Aucune donnée stockée dans ScoDoc n'est utilisée ni modifiée.
+    </p>
+</div>
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/templates/apogee/apo_compare_csv.j2 b/app/templates/apogee/apo_compare_csv.j2
new file mode 100644
index 0000000000000000000000000000000000000000..48c2af348e3500af0de73b8ddc983fbdaff11786
--- /dev/null
+++ b/app/templates/apogee/apo_compare_csv.j2
@@ -0,0 +1,14 @@
+{% extends "apogee/apo_compare.j2" %}
+
+{% block app_content %}
+{{super()}}
+
+<div class="apo_compare_csv">
+    {{content|safe}}
+</div>
+
+<p>
+    <a href="apo_compare_csv_form" class="stdlink">Autre comparaison</a>
+</p>
+
+{% endblock %}
\ No newline at end of file
diff --git a/app/templates/apogee/apo_compare_form.j2 b/app/templates/apogee/apo_compare_form.j2
new file mode 100644
index 0000000000000000000000000000000000000000..713853a97c483b2c048c8bfadc95a7bfeaa81982
--- /dev/null
+++ b/app/templates/apogee/apo_compare_form.j2
@@ -0,0 +1,23 @@
+{% extends "apogee/apo_compare.j2" %}
+
+{% block app_content %}
+{{super()}}
+
+<form id="apo_csv_add" action="apo_compare_csv" method="post" enctype="multipart/form-data">
+    <div class="apo_compare_csv_form_but">
+        Fichier Apogée A:
+        <input type="file" size="30" name="file_a"/>
+    </div>
+    <div class="apo_compare_csv_form_but">
+        Fichier Apogée B:
+        <input type="file" size="30" name="file_b"/>
+    </div>
+    <label>
+      <input type="checkbox" name="autodetect" checked/> autodétecter encodage
+    </label>
+
+    <div class="apo_compare_csv_form_submit">
+        <input type="submit" value="Comparer ces fichiers"/>
+    </div>
+</form>
+{% endblock %}
\ No newline at end of file
diff --git a/app/templates/babase.j2 b/app/templates/babase.j2
index ef58a1a770aeb30cf6c42c833cb5f6e5d77857cf..690a460a8952027679f737b0a08381b5dfa86579 100644
--- a/app/templates/babase.j2
+++ b/app/templates/babase.j2
@@ -36,6 +36,9 @@
     <script>
     const SCO_TIMEZONE = "{{ scu.TIME_ZONE }}";
     </script>
+    {% for js in javascripts %}
+      <script src="{{scu.STATIC_DIR}}/{{js}}"></script>
+    {% endfor %}
     {%- endblock scripts %}
     {%- endblock body %}
   </body>
diff --git a/app/templates/sco_page.j2 b/app/templates/sco_page.j2
index 26e035ecb0d62feb57b770ae7a714547d2558931..20229d90c56ed4962bcf65b412596d006db89050 100644
--- a/app/templates/sco_page.j2
+++ b/app/templates/sco_page.j2
@@ -36,7 +36,7 @@
 
         <div class="sco-app-content">
             {% block app_content %}
-            page vide
+                {{ content | safe }}
             {% endblock %}
         </div>
     </div>
diff --git a/app/views/notes.py b/app/views/notes.py
index 476535c37ee6388308954957ebe66f8edd5a3a5d..025df9307b64f54090aeb2b67a5117a73fb51fec 100644
--- a/app/views/notes.py
+++ b/app/views/notes.py
@@ -2439,18 +2439,30 @@ sco_publish(
     Permission.EditApogee,
 )
 
-sco_publish(
-    "/apo_compare_csv_form",
-    sco_apogee_compare.apo_compare_csv_form,
-    Permission.ScoView,
-    methods=["GET", "POST"],
-)
-sco_publish(
-    "/apo_compare_csv",
-    sco_apogee_compare.apo_compare_csv,
-    Permission.ScoView,
-    methods=["GET", "POST"],
-)
+
+@bp.route("/apo_compare_csv_form")
+@scodoc
+@permission_required(Permission.ScoView)
+def apo_compare_csv_form():
+    "Choix de fichiers Apogée à comparer"
+    return render_template(
+        "apogee/apo_compare_form.j2", title="Comparaison de fichiers Apogée"
+    )
+
+
+@bp.route("/apo_compare_csv", methods=["POST"])
+@scodoc
+@permission_required(Permission.ScoView)
+def apo_compare_csv():
+    "Page comparaison 2 fichiers CSV"
+    try:
+        file_a = request.files["file_a"]
+        file_b = request.files["file_b"]
+        autodetect = request.form.get("autodetect", False)
+    except KeyError as exc:
+        raise ScoValueError("invalid arguments") from exc
+    return sco_apogee_compare.apo_compare_csv(file_a, file_b, autodetect=autodetect)
+
 
 # ------------- INSCRIPTIONS: PASSAGE D'UN SEMESTRE A UN AUTRE
 sco_publish(