[object Object]

← 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 => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[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 →