ce7e93df53
The design depends on three specific families, and loading them from fonts.googleapis.com means the UI silently loses its character exactly where it is used most: Bromite is a de-googled browser whose users routinely block Google's font domains, and the backend is reachable over a LAN with no internet route. In both cases the page fell back to Georgia and a system sans. Five latin-subset woff2 files, ~120 KB total, in a binary already 27 MB — the cost is noise, and //go:embed static picks them up with no build change (backend/Dockerfile already copies static/). Latin only because the UI is English; woff2 only because every browser that can run this app supports it, so there is no second format to carry. DM Sans ships as one variable file, which covers the whole 400-700 range the design uses for the price of a single request. staticHandler registers the .woff2 MIME type: Go's built-in table has no entry for it and the scratch image has no /etc/mime.types, so the fonts would otherwise be served as application/octet-stream. The two faces above the fold are preloaded, since they are now discovered a stylesheet late rather than from a preconnect. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.5 KiB
HTML
82 lines
3.5 KiB
HTML
{{define "app"}}
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
<meta name="color-scheme" content="dark light">
|
|
<title>mangaBookmark</title>
|
|
<link rel="stylesheet" href="/static/style.css">
|
|
<link rel="preload" href="/static/fonts/instrument-serif-400-latin.woff2" as="font" type="font/woff2" crossorigin>
|
|
<link rel="preload" href="/static/fonts/ibm-plex-mono-500-latin.woff2" as="font" type="font/woff2" crossorigin>
|
|
<script src="/static/htmx.min.js" defer></script>
|
|
<script src="/static/filter.js" defer></script>
|
|
</head>
|
|
<body>
|
|
{{template "icons" .}}
|
|
<div class="sheet">
|
|
<header class="topbar">
|
|
<h1 class="brand">manga<em>Bookmark</em></h1>
|
|
<form method="post" action="/logout">
|
|
<button type="submit" class="ghost">Log out</button>
|
|
</form>
|
|
</header>
|
|
|
|
{{/* Search sits above the tabs on a phone and folds into the tab row on a
|
|
wider screen — one flex container, order swapped in CSS. */}}
|
|
<div class="chrome">
|
|
<div class="searchbar">
|
|
<svg viewBox="0 0 24 24" aria-hidden="true"><use href="#i-search"/></svg>
|
|
<input id="search" class="search" type="search" placeholder="Find a title"
|
|
autocomplete="off" aria-label="Search titles">
|
|
</div>
|
|
|
|
<nav class="tabs" role="tablist">
|
|
<a role="tab" href="/?tab=all" class="{{if eq .Tab "all"}}active{{end}}"
|
|
hx-get="/ui/list?tab=all" hx-target="#list" hx-swap="innerHTML"
|
|
hx-push-url="/?tab=all" hx-on::after-request="setActiveTab(this)">All</a>
|
|
<a role="tab" href="/?tab=new" class="tab-new {{if eq .Tab "new"}}active{{end}}"
|
|
hx-get="/ui/list?tab=new" hx-target="#list" hx-swap="innerHTML"
|
|
hx-push-url="/?tab=new" hx-on::after-request="setActiveTab(this)">Updated{{if .NewCount}}
|
|
<span class="count">{{.NewCount}}</span>{{end}}</a>
|
|
<a role="tab" href="/?tab=fav" class="{{if eq .Tab "fav"}}active{{end}}"
|
|
hx-get="/ui/list?tab=fav" hx-target="#list" hx-swap="innerHTML"
|
|
hx-push-url="/?tab=fav" hx-on::after-request="setActiveTab(this)">Favourites</a>
|
|
<a role="tab" href="/?tab=archived" class="{{if eq .Tab "archived"}}active{{end}}"
|
|
hx-get="/ui/list?tab=archived" hx-target="#list" hx-swap="innerHTML"
|
|
hx-push-url="/?tab=archived" hx-on::after-request="setActiveTab(this)">Archived</a>
|
|
<a role="tab" href="/?tab=finished" class="{{if eq .Tab "finished"}}active{{end}}"
|
|
hx-get="/ui/list?tab=finished" hx-target="#list" hx-swap="innerHTML"
|
|
hx-push-url="/?tab=finished" hx-on::after-request="setActiveTab(this)">Finished</a>
|
|
</nav>
|
|
</div>
|
|
|
|
{{if .Recent}}
|
|
<section class="recent">
|
|
<h2>Continue reading</h2>
|
|
<div class="recent-strip">
|
|
{{range .Recent}}
|
|
<a class="recent-card {{if .HasNewChapter}}is-new{{end}}" href="{{.ContinueURL}}"
|
|
target="_blank" rel="noopener noreferrer">
|
|
<span class="recent-cover">
|
|
{{if .Cover}}<img src="{{.Cover}}" alt="" loading="lazy">
|
|
{{else}}<span class="monogram" aria-hidden="true">{{.Initial}}</span>{{end}}
|
|
{{if .HasNewChapter}}<span class="foot-rule"></span>
|
|
{{else if .Favorite}}<span class="foot-rule brass"></span>{{end}}
|
|
</span>
|
|
<span class="recent-title">{{.Title}}</span>
|
|
<span class="recent-chapter">Ch {{.LastChapter}}{{if .HasNewChapter}} · New{{end}}</span>
|
|
</a>
|
|
{{end}}
|
|
</div>
|
|
</section>
|
|
{{end}}
|
|
|
|
<main id="list" class="list">
|
|
{{template "list" .}}
|
|
</main>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
{{end}}
|