diff --git a/assets/static/back-top-button.js b/assets/static/back-top-button.js
new file mode 100644
index 0000000000000000000000000000000000000000..733dba612a7afa24047c0e28920a97086f821ee7
--- /dev/null
+++ b/assets/static/back-top-button.js
@@ -0,0 +1,18 @@
+document.addEventListener("DOMContentLoaded", () => {
+  const btn = document.getElementById("back-top-button");
+
+  window.addEventListener("scroll", () => {
+    if (window.scrollY > 300) {
+      btn.classList.add("active");
+    } else {
+      btn.classList.remove("active");
+    }
+  });
+
+  btn.addEventListener("click", () => {
+    window.scrollTo({
+      top: 0,
+      behavior: "smooth"
+    });
+  });
+});
diff --git a/assets/static/chevrons-up.svg b/assets/static/chevrons-up.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9019c8b537e77ba1bf0913cbce7f113f0d94239d
--- /dev/null
+++ b/assets/static/chevrons-up.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevrons-up"><polyline points="17 11 12 6 7 11"></polyline><polyline points="17 18 12 13 7 18"></polyline></svg>
diff --git a/assets/style.css b/assets/style.css
index 4a19fb4155661d2b7309c6094888a817b35c6912..27b92462cf63703eff4f197b822a9dacb253a9e0 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -458,6 +458,46 @@ body .external_warning {
 	font-weight: bold;
 }
 
+/* back-to-top button */
+
+#back-top-button {
+	position: fixed;
+	bottom: 2rem;
+	right: 2rem;
+
+	cursor: pointer;
+
+	width: 3rem;
+	height: 3rem;
+
+	background-color: white;
+	border: 1.5pt solid #ddd;
+	border-radius: 50%;
+	box-shadow: 2pt 2pt 6pt #ddd;
+
+	visibility: hidden;
+	opacity: 0;
+
+	transition: opacity 250ms, border-color 250ms, box-shadow 250ms, visibility 250ms;
+
+	display: none; /* hidden on small screens */
+}
+
+#back-top-button.active {
+	visibility: visible;
+	opacity: 1;
+}
+
+#back-top-button img {
+	width: 1.44rem;
+	height: 1.44rem;
+}
+
+#back-top-button:hover {
+	border-color: #0ad;
+	box-shadow: 2pt 2pt 6pt #8ce;
+}
+
 /* search form and related stuff */
 
 .search-form-wrap {
@@ -740,6 +780,7 @@ main {
 	 * - switch header to "normal" form
 	 * - switch headers to normal size
 	 * - start growing borders
+	 * - show the back-to-top button
 	 */
 
 	header {
@@ -797,6 +838,12 @@ main {
 		font-size: 120%;
 	}
 
+	/* button gets shown, we're likely not on a phone anymore */
+
+	#back-top-button {
+		display: block;
+	}
+
 	/* frontpage cards expand here */
 
 	div.frontpage-card {
@@ -896,7 +943,8 @@ main {
 	a.header-local-anchor,
 	.flex-sidefill-l, .flex-sidefill-r,
 	.width-sidebox-placeholder, .width-sidebox-counterwieght,
-	#lap-cookies-banner {
+	#lap-cookies-banner,
+	#back-top-button {
 		  display: none;
 	}
 
diff --git a/templates/backtopbutton.html b/templates/backtopbutton.html
new file mode 100644
index 0000000000000000000000000000000000000000..794f2a8fa63a5a1870b0b178f72e470aae42662f
--- /dev/null
+++ b/templates/backtopbutton.html
@@ -0,0 +1,4 @@
+<button id="back-top-button">
+    <img src="{{#rawRootUrl}}/static/chevrons-up.svg{{/rawRootUrl}}?build_nonce={{build_nonce}}" alt="Back to top button" title="Back to top">
+</button>
+<script src="{{#rawRootUrl}}/static/back-top-button.js{{/rawRootUrl}}?build_nonce={{build_nonce}}"></script>
diff --git a/templates/footer.html b/templates/footer.html
index 610d58192ff1f47cbb29b43e9a4b1264d05102e8..9b8ecc28b99b4e0818012e899beeeb35ed42473a 100644
--- a/templates/footer.html
+++ b/templates/footer.html
@@ -24,6 +24,8 @@
   <div class="flex-sidefill-r"></div>
 </main>
 
+{{> backtopbutton.html}}
+
 <!-- cookie banner kindly provided by analytics.uni.lu -->
 <div id="lap-cookies-banner">
   <div class="banner-intro">