← back to Domain Landings
Add per-brand SVG favicon (link + /favicon.ico route) so domain bookmarks render in Chrome
1e7e85f88a14401e2d796e8874cca6ae954c7186 · 2026-08-19 12:34:33 -0700 · Steve Abrams
Files touched
Diff
commit 1e7e85f88a14401e2d796e8874cca6ae954c7186
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Aug 19 12:34:33 2026 -0700
Add per-brand SVG favicon (link + /favicon.ico route) so domain bookmarks render in Chrome
---
server.js | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/server.js b/server.js
index 4959106..6480e29 100644
--- a/server.js
+++ b/server.js
@@ -31,6 +31,16 @@ const INQ = path.join(__dirname, 'data/inquiries.jsonl');
function readPort() { try { return Number(fs.readFileSync(path.join(__dirname, '.port'), 'utf8').trim()); } catch { return 0; } }
const esc = s => String(s == null ? '' : s).replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
+// Per-brand SVG favicon: an accent-colored tile with the brand's initial.
+// Without this, Chrome (and every browser) draws a blank/globe icon for a
+// bookmark of the domain — the "my bookmark won't show" symptom.
+function faviconSvg(cfg) {
+ const bg = (cfg && cfg.palette && cfg.palette.bg) || '#0d1117';
+ const accent = (cfg && cfg.palette && cfg.palette.accent) || '#6ca8ff';
+ const letter = esc(String((cfg && cfg.title) || 'A').trim().charAt(0).toUpperCase() || 'A');
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="${bg}"/><text x="32" y="45" font-family="Helvetica,Arial,sans-serif" font-size="38" font-weight="700" text-anchor="middle" fill="${accent}">${letter}</text></svg>`;
+}
+
function hostOf(req) {
let h = (req.headers['x-forwarded-host'] || req.headers.host || '').split(',')[0].trim().toLowerCase();
h = h.replace(/:\d+$/, '').replace(/^www\./, '');
@@ -49,6 +59,7 @@ function page(cfg) {
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>${esc(cfg.title)}</title>
<meta name="description" content="${esc(cfg.tagline)}">
+<link rel="icon" href="data:image/svg+xml,${encodeURIComponent(faviconSvg(cfg))}">
${ga}
${pixel}
<style>
@@ -335,6 +346,11 @@ const server = http.createServer((req, res) => {
});
return;
}
+ if (req.method === 'GET' && req.url.split('?')[0] === '/favicon.ico') {
+ // real icon so Chrome's automatic /favicon.ico probe stops getting HTML
+ res.writeHead(200, { 'content-type': 'image/svg+xml', 'cache-control': 'public, max-age=86400' });
+ return res.end(faviconSvg(CFG[host]));
+ }
const cfg = CFG[host];
res.writeHead(cfg ? 200 : 404, { 'content-type': 'text/html; charset=utf-8' });
res.end(cfg ? page(cfg) : notFound(host));
← 0579bb1 chore: v0.3.1 (session close — lander configs for un-parked
·
back to Domain Landings
·
chore: v0.3.2 (session close — favicon fix lint/review) 677a4bc →