refactor(ui): implement base template and shared CSS

Introduce a `base.html` template to establish a common structure for all pages using Jinja2 template inheritance. This eliminates redundant HTML boilerplate across multiple files.

Additionally, common CSS rules have been extracted from individual templates and consolidated into a single `static/css/main.css` file. All pages now link to this shared stylesheet.

This refactoring significantly reduces code duplication, improves maintainability, and ensures a consistent user interface.
This commit is contained in:
Karl 2025-07-18 13:56:38 +01:00
parent 31b373b34a
commit aa8e1d1701
8 changed files with 296 additions and 403 deletions

51
static/css/main.css Normal file
View File

@ -0,0 +1,51 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: black;
color: white;
font-family: sans-serif;
padding: 2rem;
}
.button-link {
background: #333;
color: white;
text-decoration: none;
padding: 10px 20px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
display: inline-block;
text-align: center;
border: none;
}
.button-link:hover {
background: #555;
}
.version {
position: fixed;
bottom: 8px;
right: 12px;
color: #666;
font-size: 12px;
font-family: monospace;
user-select: none;
opacity: 0.6;
}
.version a {
color: inherit;
text-decoration: none;
cursor: pointer;
}
.version a:hover {
text-decoration: underline;
}

20
templates/base.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% block title %}AI Image Frame{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='css/main.css') }}">
{% block head %}{% endblock %}
</head>
<body>
{% block content %}{% endblock %}
<!-- Version number at bottom right -->
<div class="version">
<a href="{{ url_for('settings_route.config_editor') }}">v{{ version }}</a>
</div>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@ -1,219 +1,199 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en">
<head> {% block title %}Create An Image{% endblock %}
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> {% block head %}
<title>Create An Image</title> <style>
<style> body {
/* ---------- reset ---------- */ display: flex;
* { flex-direction: column;
margin: 0; align-items: center;
padding: 0; justify-content: center;
box-sizing: border-box; height: 100vh;
padding: 20px;
}
textarea {
width: 80vw;
height: 200px;
border-radius: 10px;
padding: 15px;
font-size: 16px;
font-family: monospace;
resize: none;
margin-bottom: 20px;
background: #111;
color: #eee;
border: 1px solid #333;
}
.button-group {
display: flex;
gap: 20px;
align-items: center;
}
button,
select {
background: #333;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
}
button:hover,
select:hover {
background: #555;
}
#spinner-overlay {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
visibility: hidden;
z-index: 1000;
}
.spinner {
width: 50px;
height: 50px;
border: 6px solid #555;
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
} }
}
/* ---------- layout ---------- */ @media (max-width: 600px) {
body { body {
display: flex; min-height: 100dvh;
flex-direction: column; height: auto;
align-items: center; justify-content: flex-start;
justify-content: center; padding-top: 40px;
height: 100vh;
background: black;
color: white;
font-family: Arial, sans-serif;
padding: 20px;
}
textarea {
width: 80vw;
height: 200px;
border-radius: 10px;
padding: 15px;
font-size: 16px;
font-family: monospace;
resize: none;
margin-bottom: 20px;
background: #111;
color: #eee;
border: 1px solid #333;
} }
.button-group { .button-group {
display: flex; flex-direction: column;
gap: 20px; align-items: stretch;
align-items: center; width: 100%;
} }
button, button,
select { select {
background: #333; width: 100%;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
} }
button:hover, textarea {
select:hover { height: 150px;
background: #555;
} }
}
</style>
{% endblock %}
/* ---------- spinner ---------- */ {% block content %}
#spinner-overlay { <h1 style="margin-bottom: 20px;">Create An Image</h1>
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.6);
visibility: hidden;
z-index: 1000;
}
.spinner { <textarea id="prompt-box" placeholder="Enter your custom prompt here..."></textarea>
width: 50px;
height: 50px;
border: 6px solid #555;
border-top-color: white;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin { <div class="button-group">
to { <button onclick="showSpinner(); location.href='/'">Back</button>
transform: rotate(360deg);
}
}
@media (max-width: 600px) { <button onclick="sendPrompt()">Send Prompt</button>
body {
min-height: 100dvh;
height: auto;
justify-content: flex-start;
padding-top: 40px;
}
.button-group { <button onclick="randomPrompt()">Random Prompt</button>
flex-direction: column;
align-items: stretch;
width: 100%;
}
button, <select id="model-select">
select { <option value="" selected>Random</option>
width: 100%; <optgroup label="FLUX">
} {% for m in models if 'flux' in m|lower %}
<option value="{{ m }}">{{ m.rsplit('.', 1)[0] }}</option>
{% endfor %}
</optgroup>
<optgroup label="SDXL">
{% for m in models if 'flux' not in m|lower %}
<option value="{{ m }}">{{ m.rsplit('.', 1)[0] }}</option>
{% endfor %}
</optgroup>
</select>
textarea { <select id="topic-select">
height: 150px; <option value="">No Topic</option>
} <option value="random">Random</option>
} <optgroup label="Topics">
</style> {% for t in topics %}
</head> <option value="{{ t }}">{{ t }}</option>
{% endfor %}
</optgroup>
</select>
</div>
<body> <div id="spinner-overlay">
<h1 style="margin-bottom: 20px;">Create An Image</h1> <div class="spinner"></div>
</div>
{% endblock %}
<textarea id="prompt-box" placeholder="Enter your custom prompt here..."></textarea> {% block scripts %}
<script>
const overlay = document.getElementById('spinner-overlay');
<div class="button-group"> function showSpinner() { overlay.style.visibility = 'visible'; }
<button onclick="showSpinner(); location.href='/'">Back</button>
<button onclick="sendPrompt()">Send Prompt</button> function sendPrompt() {
showSpinner();
const prompt = document.getElementById('prompt-box').value;
const model = document.getElementById('model-select').value;
<button onclick="randomPrompt()">Random Prompt</button> const formData = new URLSearchParams();
formData.append('prompt', prompt);
formData.append('model', model);
<select id="model-select"> fetch('/create', {
<option value="" selected>Random</option> method: 'POST',
<!-- Group: FLUX --> headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
<optgroup label="FLUX"> body: formData.toString()
{% for m in models if 'flux' in m|lower %} })
<option value="{{ m }}">{{ m.rsplit('.', 1)[0] }}</option> .then(response => {
{% endfor %} window.location.href = response.redirected ? response.url : '/create';
</optgroup>
<!-- Group: SDXL -->
<optgroup label="SDXL">
{% for m in models if 'flux' not in m|lower %}
<option value="{{ m }}">{{ m.rsplit('.', 1)[0] }}</option>
{% endfor %}
</optgroup>
</select>
<select id="topic-select">
<option value="">No Topic</option>
<option value="random">Random</option>
<optgroup label="Topics">
{% for t in topics %}
<option value="{{ t }}">{{ t }}</option>
{% endfor %}
</optgroup>
</select>
</div>
<!-- waiting overlay -->
<div id="spinner-overlay">
<div class="spinner"></div>
</div>
<script>
const overlay = document.getElementById('spinner-overlay');
function showSpinner() { overlay.style.visibility = 'visible'; }
function sendPrompt() {
showSpinner();
const prompt = document.getElementById('prompt-box').value;
const model = document.getElementById('model-select').value;
const formData = new URLSearchParams();
formData.append('prompt', prompt);
formData.append('model', model);
fetch('/create', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString()
}) })
.then(response => { .catch(error => {
window.location.href = response.redirected ? response.url : '/create'; overlay.style.visibility = 'hidden';
}) alert("Error sending prompt: " + error);
.catch(error => { });
overlay.style.visibility = 'hidden'; }
alert("Error sending prompt: " + error);
});
}
// wrapper for Random Prompt button so it also sends the model function randomPrompt() {
function randomPrompt() { showSpinner();
showSpinner(); const model = document.getElementById('model-select').value;
const model = document.getElementById('model-select').value; const topic = document.getElementById('topic-select').value;
const topic = document.getElementById('topic-select').value; // this line was missing
const formData = new URLSearchParams(); const formData = new URLSearchParams();
formData.append('model', model); formData.append('model', model);
formData.append('topic', topic); // include topic in request formData.append('topic', topic);
fetch('/create', { fetch('/create', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: formData.toString() body: formData.toString()
})
.then(response => {
window.location.href = response.redirected ? response.url : '/create';
}) })
.then(response => { .catch(error => {
window.location.href = response.redirected ? response.url : '/create'; overlay.style.visibility = 'hidden';
}) alert("Error requesting random prompt: " + error);
.catch(error => { });
overlay.style.visibility = 'hidden'; }
alert("Error requesting random prompt: " + error); </script>
}); {% endblock %}
}
</script>
</body>
</html>

View File

@ -1,24 +1,9 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en">
<head> {% block title %}Image Archive{% endblock %}
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> {% block head %}
<title>Image Archive</title>
<style> <style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: black;
color: white;
font-family: sans-serif;
padding: 2rem;
}
h1 { h1 {
text-align: center; text-align: center;
margin-bottom: 2rem; margin-bottom: 2rem;
@ -102,18 +87,14 @@
text-align: left; text-align: left;
margin-top: 20px; margin-top: 20px;
max-height: 25vh; max-height: 25vh;
/* NEW: restrict height */
overflow-y: auto; overflow-y: auto;
/* NEW: allow vertical scroll */
} }
/* Back button fixed top right */
.home-button { .home-button {
position: fixed; position: fixed;
top: 20px; top: 20px;
right: 20px; right: 20px;
z-index: 500; z-index: 500;
/* lower than lightbox (999) */
} }
.favourites-button { .favourites-button {
@ -132,24 +113,6 @@
cursor: pointer; cursor: pointer;
} }
.button-link {
background: #333;
color: white;
text-decoration: none;
padding: 10px 20px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
display: inline-block;
text-align: center;
border: none;
}
.button-link:hover {
background: #555;
}
@media (max-width: 600px) { @media (max-width: 600px) {
body { body {
padding: 1rem; padding: 1rem;
@ -181,9 +144,7 @@
max-width: 90%; max-width: 90%;
padding: 8px 16px; padding: 8px 16px;
max-height: 20vh; max-height: 20vh;
/* smaller height for mobile */
overflow-y: auto; overflow-y: auto;
/* keep scroll on mobile too */
} }
.button-link { .button-link {
@ -192,18 +153,16 @@
} }
} }
</style> </style>
</head> {% endblock %}
<body> {% block content %}
<a href="/" class="button-link home-button">Home</a> <a href="/" class="button-link home-button">Home</a>
<button class="button-link favourites-button" id="favourites-button" onclick="toggleFavouritesView()">Show Favourites</button> <button class="button-link favourites-button" id="favourites-button" onclick="toggleFavouritesView()">Show Favourites</button>
<h1 id="page-title">Image Archive</h1> <h1 id="page-title">Image Archive</h1>
<!-- Empty gallery container; images will be loaded incrementally -->
<div class="gallery" id="gallery"></div> <div class="gallery" id="gallery"></div>
<!-- Lightbox -->
<div class="lightbox" id="lightbox" tabindex="-1" onkeyup="handleLightboxKeys(event)"> <div class="lightbox" id="lightbox" tabindex="-1" onkeyup="handleLightboxKeys(event)">
<span class="close" onclick="closeLightbox()">&times;</span> <span class="close" onclick="closeLightbox()">&times;</span>
<span class="favourite-heart" id="favourite-heart" onclick="toggleFavourite()">&#9825;</span> <span class="favourite-heart" id="favourite-heart" onclick="toggleFavourite()">&#9825;</span>
@ -212,8 +171,9 @@
<p id="lightbox-prompt"></p> <p id="lightbox-prompt"></p>
<span class="arrow right" onclick="nextImage()">&#10095;</span> <span class="arrow right" onclick="nextImage()">&#10095;</span>
</div> </div>
{% endblock %}
<!-- Pass image filenames from Flask to JS --> {% block scripts %}
<script> <script>
let allImages = JSON.parse(`[ let allImages = JSON.parse(`[
{% for image in images %} {% for image in images %}
@ -224,10 +184,10 @@
<script> <script>
const gallery = document.getElementById('gallery'); const gallery = document.getElementById('gallery');
const batchSize = 9; // images to load per batch const batchSize = 9;
let loadedCount = 0; let loadedCount = 0;
let currentIndex = 0; let currentIndex = 0;
const detailsCache = {}; // Cache for image details const detailsCache = {};
let showingFavourites = false; let showingFavourites = false;
let filteredImages = allImages; let filteredImages = allImages;
@ -276,19 +236,16 @@
renderGallery(); renderGallery();
} }
// Load initial batch
renderGallery(); renderGallery();
// Load more images when scrolling near bottom
window.addEventListener('scroll', () => { window.addEventListener('scroll', () => {
const imagesToLoad = showingFavourites ? filteredImages : allImages; const imagesToLoad = showingFavourites ? filteredImages : allImages;
if (loadedCount >= imagesToLoad.length) return; // all loaded if (loadedCount >= imagesToLoad.length) return;
if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 100)) { if ((window.innerHeight + window.scrollY) >= (document.body.offsetHeight - 100)) {
loadNextBatch(); loadNextBatch();
} }
}); });
// Get current images in gallery for lightbox navigation
function getGalleryImages() { function getGalleryImages() {
return Array.from(gallery.querySelectorAll('img')); return Array.from(gallery.querySelectorAll('img'));
} }
@ -304,7 +261,7 @@
function updateFavouriteHeart(isFavourited) { function updateFavouriteHeart(isFavourited) {
const heart = document.getElementById('favourite-heart'); const heart = document.getElementById('favourite-heart');
heart.innerHTML = isFavourited ? '&#9829;' : '&#9825;'; // solid vs outline heart heart.innerHTML = isFavourited ? '&#9829;' : '&#9825;';
heart.style.color = isFavourited ? 'red' : 'white'; heart.style.color = isFavourited ? 'red' : 'white';
} }
@ -361,7 +318,7 @@
return response.json(); return response.json();
}) })
.then(data => { .then(data => {
detailsCache[filename] = data; // Cache the data detailsCache[filename] = data;
document.getElementById("lightbox-prompt").textContent = document.getElementById("lightbox-prompt").textContent =
`Model:${data.model} - Created:${data.date}\n\n${data.prompt}`; `Model:${data.model} - Created:${data.date}\n\n${data.prompt}`;
}) })
@ -376,7 +333,6 @@
const imagesToLoad = showingFavourites ? filteredImages : allImages; const imagesToLoad = showingFavourites ? filteredImages : allImages;
if (currentIndex + 1 >= images.length && loadedCount < imagesToLoad.length) { if (currentIndex + 1 >= images.length && loadedCount < imagesToLoad.length) {
loadNextBatch(); loadNextBatch();
// Wait briefly to ensure DOM updates
setTimeout(() => { setTimeout(() => {
const updatedImages = getGalleryImages(); const updatedImages = getGalleryImages();
if (currentIndex + 1 < updatedImages.length) { if (currentIndex + 1 < updatedImages.length) {
@ -396,11 +352,9 @@
showImageAndLoadDetails(currentIndex); showImageAndLoadDetails(currentIndex);
} }
function closeLightbox() { function closeLightbox() {
document.getElementById("lightbox").style.display = "none"; document.getElementById("lightbox").style.display = "none";
if (showingFavourites) { if (showingFavourites) {
// Refresh the gallery if a favourite was removed
const currentImage = getGalleryImages()[currentIndex]; const currentImage = getGalleryImages()[currentIndex];
const wasFavourited = currentImage.dataset.favourited === 'true'; const wasFavourited = currentImage.dataset.favourited === 'true';
const originalImage = allImages.find(img => img.filename === currentImage.dataset.filename); const originalImage = allImages.find(img => img.filename === currentImage.dataset.filename);
@ -422,6 +376,4 @@
} }
} }
</script> </script>
</body> {% endblock %}
</html>

View File

@ -1,25 +1,15 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en">
<head> {% block title %}Image Created{% endblock %}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> {% block head %}
<title>Image Created</title>
<style> <style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body { body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;
background: black;
color: white;
font-family: Arial, sans-serif;
padding: 20px;
text-align: center; text-align: center;
} }
.message { .message {
@ -50,13 +40,13 @@
background: #555; background: #555;
} }
</style> </style>
</head> {% endblock %}
<body>
{% block content %}
<div class="message">Image will be made with <i>{{ model }}</i> using prompt:</div> <div class="message">Image will be made with <i>{{ model }}</i> using prompt:</div>
<div class="prompt-text"> <div class="prompt-text">
{{ prompt }} {{ prompt }}
</div> </div>
<button onclick="location.href='/'">Home</button> <button onclick="location.href='/'">Home</button>
</body> {% endblock %}
</html>

View File

@ -1,32 +1,20 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en">
<head> {% block title %}AI Image of the Day{% endblock %}
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> {% block head %}
<title>AI Image of the Day</title>
<style> <style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body { body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;
background: black;
color: white;
font-family: Arial, sans-serif;
position: relative; position: relative;
padding-top: 20px; padding-top: 20px;
padding-bottom: 20px; padding-bottom: 20px;
} }
.image-container { .image-container {
max-width: 90vw; max-width: 90vw;
max-height: 80vh; max-height: 80vh;
@ -54,9 +42,7 @@
max-width: 80vw; max-width: 80vw;
text-align: left; text-align: left;
max-height: 30vh; max-height: 30vh;
/* NEW: limit height */
overflow-y: auto; overflow-y: auto;
/* NEW: allow scrolling */
} }
.button-group { .button-group {
@ -66,46 +52,6 @@
justify-content: center; justify-content: center;
} }
.button-link {
background: #333;
color: white;
text-decoration: none;
padding: 10px 20px;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: background 0.3s;
display: inline-block;
text-align: center;
}
.button-link:hover {
background: #555;
}
/* New style for version number */
.version {
position: fixed;
bottom: 8px;
right: 12px;
color: #666;
font-size: 12px;
font-family: monospace;
user-select: none;
opacity: 0.6;
}
.version a {
color: inherit;
text-decoration: none;
cursor: pointer;
}
.version a:hover {
text-decoration: underline;
}
@media (max-width: 768px) { @media (max-width: 768px) {
.image-container { .image-container {
max-width: 100vw; max-width: 100vw;
@ -134,14 +80,9 @@
} }
} }
</style> </style>
<script> {% endblock %}
setInterval(() => {
location.reload();
}, {{ reload_interval }}); // Refresh every X ms
</script>
</head>
<body> {% block content %}
{% if image %} {% if image %}
<div class="image-container"> <div class="image-container">
<img src="{{ url_for('image_routes.serve_image', filename=image) }}" alt="Latest Image" /> <img src="{{ url_for('image_routes.serve_image', filename=image) }}" alt="Latest Image" />
@ -156,11 +97,12 @@
{% else %} {% else %}
<p>No images found</p> <p>No images found</p>
{% endif %} {% endif %}
{% endblock %}
<!-- Version number at bottom right --> {% block scripts %}
<div class="version"> <script>
<a href="{{ url_for('settings_route.config_editor') }}">v{{ version }}</a> setInterval(() => {
</div> location.reload();
</body> }, {{ reload_interval }}); // Refresh every X ms
</script>
</html> {% endblock %}

View File

@ -1,25 +1,15 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en">
<head> {% block title %}Login{% endblock %}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> {% block head %}
<title>Login</title>
<style> <style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body { body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh; height: 100vh;
background: black;
color: white;
font-family: Arial, sans-serif;
padding: 20px;
text-align: center; text-align: center;
} }
.message { .message {
@ -59,8 +49,9 @@
background: #555; background: #555;
} }
</style> </style>
</head> {% endblock %}
<body>
{% block content %}
<div class="message">Please enter the password to continue:</div> <div class="message">Please enter the password to continue:</div>
<form method="post"> <form method="post">
<div class="prompt-text"> <div class="prompt-text">
@ -69,5 +60,4 @@
</div> </div>
<button type="submit">Login</button> <button type="submit">Login</button>
</form> </form>
</body> {% endblock %}
</html>

View File

@ -1,21 +1,10 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en">
<head> {% block title %}Config Editor{% endblock %}
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> {% block head %}
<title>Config Editor</title>
<style> <style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body { body {
background: black;
color: white;
font-family: Arial, sans-serif;
min-height: 100vh; min-height: 100vh;
padding: 40px 20px; padding: 40px 20px;
display: flex; display: flex;
@ -74,21 +63,11 @@
margin-top: 20px; margin-top: 20px;
} }
.button-link { .back-button-wrapper {
background: #333; width: 100%;
height: 40px; display: flex;
color: white; justify-content: center;
text-decoration: none; margin-top: 20px;
padding: 10px 20px;
border-radius: 8px;
font-size: 16px;
transition: background 0.3s;
display: inline-block;
text-align: center;
}
.button-link:hover {
background: #555;
} }
@media (max-width: 768px) { @media (max-width: 768px) {
@ -102,18 +81,10 @@
margin-bottom: 30px; margin-bottom: 30px;
} }
} }
.back-button-wrapper {
width: 100%;
display: flex;
justify-content: center;
margin-top: 20px;
}
</style> </style>
</head> {% endblock %}
<body>
{% block content %}
<div class="box"> <div class="box">
<h2>Topics</h2> <h2>Topics</h2>
<form method="post"> <form method="post">
@ -186,7 +157,4 @@
<div class="back-button-wrapper"> <div class="back-button-wrapper">
<a href="/" class="button-link">Back to Home</a> <a href="/" class="button-link">Back to Home</a>
</div> </div>
{% endblock %}
</body>
</html>