refactor(frontend): standardize HTML formatting and update font families
CI / test (push) Successful in 1m42s
CI / docker (push) Successful in 32s

This commit is contained in:
zwitschi committed 2026-08-13 18:40:03 +02:00
1 parent 76397d52d7
commit a15a9f2df0
4 files changed
+119 -177

No files matched your search

+55 -122
View File
@@ -1,130 +1,63 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Linear Void Admin</title>
<link rel="stylesheet" href="../src/style.css" />
</head>
<body class="admin-body"> <head>
<div class="admin-shell"> <meta charset="UTF-8" />
<aside class="admin-sidebar" aria-label="Primary"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<div class="brand-block"> <title>Linear Void Admin</title>
<div class="brand-badge" aria-hidden="true">LV</div> <link rel="preconnect" href="https://fonts.googleapis.com">
<div class="brand-text">Admin</div> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
</div> <link href="https://fonts.googleapis.com/css2?family=Doto:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Space+Grotesk:wght@300..700&family=Tektur:wght@400..900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../src/style.css" />
</head>
<button id="logout-btn" type="button" class="btn btn-secondary btn-block">Sign out</button> <body class="admin-body">
<div class="admin-shell">
<aside class="admin-sidebar" aria-label="Primary">
<div class="brand-block">
<div class="brand-badge" aria-hidden="true">LV</div>
<div class="brand-text">Admin</div>
</div>
<nav class="admin-nav" aria-label="Admin sections" role="tablist" aria-orientation="vertical"> <button id="logout-btn" type="button" class="btn btn-secondary btn-block">Sign out</button>
<button
type="button"
id="tab-trigger-dashboard"
role="tab"
class="tab-trigger is-active"
data-tab="dashboard"
aria-controls="tab-dashboard"
aria-selected="true"
>
Dashboard
</button>
<button
type="button"
id="tab-trigger-upload"
role="tab"
class="tab-trigger"
data-tab="upload"
aria-controls="tab-upload"
aria-selected="false"
>
Upload Video
</button>
<button
type="button"
id="tab-trigger-playlist"
role="tab"
class="tab-trigger"
data-tab="playlist"
aria-controls="tab-playlist"
aria-selected="false"
>
Playlist
</button>
<button
type="button"
id="tab-trigger-schedule"
role="tab"
class="tab-trigger"
data-tab="schedule"
aria-controls="tab-schedule"
aria-selected="false"
>
Schedule
</button>
<button
type="button"
id="tab-trigger-moderation"
role="tab"
class="tab-trigger"
data-tab="moderation"
aria-controls="tab-moderation"
aria-selected="false"
>
Moderation
</button>
<button
type="button"
id="tab-trigger-chat-log"
role="tab"
class="tab-trigger"
data-tab="chat-log"
aria-controls="tab-chat-log"
aria-selected="false"
>
Chat Log
</button>
<button
type="button"
id="tab-trigger-users"
role="tab"
class="tab-trigger"
data-tab="users"
aria-controls="tab-users"
aria-selected="false"
>
Users
</button>
<button
type="button"
id="tab-trigger-analytics"
role="tab"
class="tab-trigger"
data-tab="analytics"
aria-controls="tab-analytics"
aria-selected="false"
>
Analytics
</button>
<button
type="button"
id="tab-trigger-system"
role="tab"
class="tab-trigger"
data-tab="system"
aria-controls="tab-system"
aria-selected="false"
>
System
</button>
</nav>
</aside>
<main class="admin-main" id="admin-main" tabindex="-1"> <nav class="admin-nav" aria-label="Admin sections" role="tablist" aria-orientation="vertical">
<div id="tab-content-container" aria-live="polite">Loading...</div> <button type="button" id="tab-trigger-dashboard" role="tab" class="tab-trigger is-active" data-tab="dashboard" aria-controls="tab-dashboard" aria-selected="true">
</main> Dashboard
</div> </button>
<button type="button" id="tab-trigger-upload" role="tab" class="tab-trigger" data-tab="upload" aria-controls="tab-upload" aria-selected="false">
Upload Video
</button>
<button type="button" id="tab-trigger-playlist" role="tab" class="tab-trigger" data-tab="playlist" aria-controls="tab-playlist" aria-selected="false">
Playlist
</button>
<button type="button" id="tab-trigger-schedule" role="tab" class="tab-trigger" data-tab="schedule" aria-controls="tab-schedule" aria-selected="false">
Schedule
</button>
<button type="button" id="tab-trigger-moderation" role="tab" class="tab-trigger" data-tab="moderation" aria-controls="tab-moderation" aria-selected="false">
Moderation
</button>
<button type="button" id="tab-trigger-chat-log" role="tab" class="tab-trigger" data-tab="chat-log" aria-controls="tab-chat-log" aria-selected="false">
Chat Log
</button>
<button type="button" id="tab-trigger-users" role="tab" class="tab-trigger" data-tab="users" aria-controls="tab-users" aria-selected="false">
Users
</button>
<button type="button" id="tab-trigger-analytics" role="tab" class="tab-trigger" data-tab="analytics" aria-controls="tab-analytics" aria-selected="false">
Analytics
</button>
<button type="button" id="tab-trigger-system" role="tab" class="tab-trigger" data-tab="system" aria-controls="tab-system" aria-selected="false">
System
</button>
</nav>
</aside>
<main class="admin-main" id="admin-main" tabindex="-1">
<div id="tab-content-container" aria-live="polite">Loading...</div>
</main>
</div>
<script type="module" src="/src/admin.ts"></script>
</body>
<script type="module" src="/src/admin.ts"></script>
</body>
</html> </html>
+57 -52
View File
@@ -1,65 +1,70 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Linear Void Admin Login</title>
<link rel="stylesheet" href="../../src/style.css" />
</head>
<body class="auth-body"> <head>
<main class="auth-shell" aria-label="Admin authentication"> <meta charset="UTF-8" />
<section class="auth-card" aria-labelledby="auth-title"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<p class="auth-kicker">Linear Void</p> <title>Linear Void Admin Login</title>
<h1 id="auth-title">Admin Login</h1> <link rel="preconnect" href="https://fonts.googleapis.com">
<p class="auth-subtitle">Authenticate to access protected admin controls.</p> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Doto:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Space+Grotesk:wght@300..700&family=Tektur:wght@400..900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../../src/style.css" />
</head>
<form id="login-form" class="form-stack" novalidate> <body class="auth-body">
<label for="username">Username</label> <main class="auth-shell" aria-label="Admin authentication">
<input id="username" type="text" class="text-input" autocomplete="username" required /> <section class="auth-card" aria-labelledby="auth-title">
<p class="auth-kicker">Linear Void</p>
<h1 id="auth-title">Admin Login</h1>
<p class="auth-subtitle">Authenticate to access protected admin controls.</p>
<label for="password">Password</label> <form id="login-form" class="form-stack" novalidate>
<input id="password" type="password" class="text-input" autocomplete="current-password" required /> <label for="username">Username</label>
<input id="username" type="text" class="text-input" autocomplete="username" required />
<button id="submit" type="submit" class="btn btn-primary btn-block">Sign in</button> <label for="password">Password</label>
<p id="status" class="status-line" aria-live="polite"></p> <input id="password" type="password" class="text-input" autocomplete="current-password" required />
</form>
</section>
</main>
<script> <button id="submit" type="submit" class="btn btn-primary btn-block">Sign in</button>
const form = document.getElementById("login-form"); <p id="status" class="status-line" aria-live="polite"></p>
const username = document.getElementById("username"); </form>
const password = document.getElementById("password"); </section>
const submit = document.getElementById("submit"); </main>
const status = document.getElementById("status");
form.addEventListener("submit", async (event) => { <script>
event.preventDefault(); const form = document.getElementById("login-form");
submit.disabled = true; const username = document.getElementById("username");
status.textContent = "Signing in..."; const password = document.getElementById("password");
status.className = "status-line"; const submit = document.getElementById("submit");
const status = document.getElementById("status");
try { form.addEventListener("submit", async (event) => {
const response = await fetch("/api/admin/login", { event.preventDefault();
method: "POST", submit.disabled = true;
headers: { "Content-Type": "application/json" }, status.textContent = "Signing in...";
body: JSON.stringify({ username: username.value, password: password.value }), status.className = "status-line";
});
if (!response.ok) { try {
throw new Error(`HTTP ${response.status}`); const response = await fetch("/api/admin/login", {
} method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: username.value, password: password.value }),
});
window.location.href = "/admin/"; if (!response.ok) {
} catch (error) { throw new Error(`HTTP ${response.status}`);
console.error("Failed to sign in", error);
status.textContent = "Login failed. Check your credentials.";
status.className = "status-line status-error";
} finally {
submit.disabled = false;
} }
});
</script> window.location.href = "/admin/";
</body> } catch (error) {
console.error("Failed to sign in", error);
status.textContent = "Login failed. Check your credentials.";
status.className = "status-line status-error";
} finally {
submit.disabled = false;
}
});
</script>
</body>
</html> </html>
+3
View File
@@ -5,6 +5,9 @@
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Linear Void - 24/7 Synchronized Channel</title> <title>Linear Void - 24/7 Synchronized Channel</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Doto:wght@100..900&family=IBM+Plex+Mono:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;1,100;1,200;1,300;1,400;1,500;1,600;1,700&family=Space+Grotesk:wght@300..700&family=Tektur:wght@400..900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/src/style.css" /> <link rel="stylesheet" href="/src/style.css" />
</head> </head>
+4 -3
View File
@@ -1,7 +1,8 @@
:root { :root {
color-scheme: dark; color-scheme: dark;
--font-body: "Atkinson Hyperlegible", "Segoe UI", "Helvetica Neue", sans-serif; --font-body: "Tektur", "Segoe UI", "Helvetica Neue", sans-serif;
--font-heading: "Doto", "Segoe UI", "Helvetica Neue", sans-serif;
--font-display: "Space Grotesk", "Segoe UI", sans-serif; --font-display: "Space Grotesk", "Segoe UI", sans-serif;
--font-mono: "IBM Plex Mono", Consolas, monospace; --font-mono: "IBM Plex Mono", Consolas, monospace;
@@ -125,7 +126,7 @@ h1,
h2, h2,
h3 { h3 {
margin: 0; margin: 0;
font-family: var(--font-display); font-family: var(--font-heading);
color: var(--ink-0); color: var(--ink-0);
letter-spacing: -0.02em; letter-spacing: -0.02em;
} }
@@ -539,7 +540,7 @@ select {
.brand-text, .brand-text,
.viewer-title { .viewer-title {
font-family: var(--font-display); font-family: var(--font-heading);
} }
.viewer-title { .viewer-title {