Self-host the Cinder webfonts
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>
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,3 +1,45 @@
|
||||
/* Fonts are served from this binary, not from Google: this UI is read in
|
||||
Bromite, where fonts.googleapis.com is routinely blocked, and over a LAN
|
||||
with no internet route. Latin subsets only, woff2 only — every browser that
|
||||
runs the app supports it, so there is no second format to ship. Roughly
|
||||
120 KB for all five faces, embedded via //go:embed static. */
|
||||
@font-face {
|
||||
font-family: "Instrument Serif";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/fonts/instrument-serif-400-latin.woff2) format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Instrument Serif";
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url(/static/fonts/instrument-serif-400-italic-latin.woff2) format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "IBM Plex Mono";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url(/static/fonts/ibm-plex-mono-500-latin.woff2) format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "IBM Plex Mono";
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url(/static/fonts/ibm-plex-mono-600-latin.woff2) format("woff2");
|
||||
}
|
||||
/* One variable file covers the 400–700 range the design uses. */
|
||||
@font-face {
|
||||
font-family: "DM Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url(/static/fonts/dm-sans-var-latin.woff2) format("woff2");
|
||||
}
|
||||
|
||||
/* Cinder — editorial serif, containerless sheets, ash hairlines.
|
||||
Heat is purely typographic: a series with an unread chapter turns its title
|
||||
crimson and gains an ember underline. Everything else stays cool. Dark is
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
<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="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Instrument+Serif:ital@0;1&family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<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>
|
||||
|
||||
@@ -6,10 +6,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="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500;600&family=Instrument+Serif:ital@0;1&family=DM+Sans:wght@400;500;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
<link rel="preload" href="/static/fonts/instrument-serif-400-latin.woff2" as="font" type="font/woff2" crossorigin>
|
||||
</head>
|
||||
<body>
|
||||
<main class="login-card">
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io/fs"
|
||||
"log"
|
||||
"math"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -98,6 +99,12 @@ func staticHandler() http.Handler {
|
||||
if err != nil {
|
||||
panic("embed static: " + err.Error())
|
||||
}
|
||||
// Go's built-in table has no .woff2 and the scratch image has no
|
||||
// /etc/mime.types, so without this the fonts go out as
|
||||
// application/octet-stream.
|
||||
if err := mime.AddExtensionType(".woff2", "font/woff2"); err != nil {
|
||||
panic("woff2 mime: " + err.Error())
|
||||
}
|
||||
files := http.FileServer(http.FS(sub))
|
||||
return http.StripPrefix("/static/", http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user