Files
ai-resume/internal/brand/brand.go
Sam Rolfe a76882de67 visuals: technology marks, area icons, client wall, two diagrams
Phase 7 (AUD-149), cheapest first.

- internal/brand: vendored Simple Icons path data (CC0) for 50 marks, looked up
  by the technology label the content already uses. Inline SVG, 16 px,
  currentColor, no request and no font. tools/gen_brand_icons.sh regenerates it.
- Six hand-drawn area icons replace the two-letter badges in the nav and on the
  landing cards; the evidence index gets a seventh.
- TechChips renders a mark where one exists and plain text where one does not,
  so a missing icon reads as a decision rather than a broken image.
- Client wall: 45 names as one typeface, one size, one grid of equal tiles, and
  the reason for no logo collage written on the page. No mark invented.
- Two diagrams, authored in-repo: homelab topology (platform-network.md) and
  the RAG pipeline (ai-rag-corpus.md), each referenced from the page it explains.
- Content images now carry intrinsic width/height, loading="lazy" and
  decoding="async" at render time; a site test fails the build if an image
  ships without them.
- Topbar is two rows at desktop: the area names plus marks no longer overlap the
  search form (Lighthouse target-size was failing at 1440 px).
- Favicon, so the browser stops requesting a missing /favicon.ico.

Satisfies: logos on every area page, area icons in nav and cards, two diagrams
referenced from their pages, images sized and lazy, Lighthouse >= 95 held.

(cherry picked from commit a473e4738a3751418c2097df2bcf0568fb6d0208)
2026-10-28 15:56:11 +11:00

91 lines
2.9 KiB
Go

// Package brand holds the small monochrome technology marks the site inlines
// beside a technology name.
//
// A mark is path data for a 24x24 viewBox, drawn with fill="currentColor" so it
// inherits the chip's colour and adds no request, no font and no layout shift.
// Marks are opt-in: a technology with no published mark renders as text alone,
// which is the honest default and keeps the chips from looking broken.
//
// The path data is vendored (see icons_gen.go and tools/gen_brand_icons.sh),
// not fetched at build or request time. The marks are trademarks of their
// owners and are used only to identify the technology named beside them.
package brand
import "strings"
// slugs maps a technology label, lowercased, to its Simple Icons slug. Labels
// are the ones the content uses; aliases point at the same mark.
var slugs = map[string]string{
"javascript": "javascript",
"php": "php",
"python": "python",
"home assistant": "homeassistant",
"raspberry pi": "raspberrypi",
"pi": "raspberrypi",
"nixos": "nixos",
"nix": "nixos",
"qdrant": "qdrant",
"prefect": "prefect",
"n8n": "n8n",
"mqtt": "mqtt",
"langgraph": "langgraph",
"langchain": "langchain",
"ffmpeg": "ffmpeg",
"zigbee": "zigbee",
"three.js": "threedotjs",
"react": "react",
"proxmox": "proxmox",
"mysql": "mysql",
"postgres": "postgresql",
"postgresql": "postgresql",
"caddy": "caddy",
"tailscale": "tailscale",
"neovim": "neovim",
"htmx": "htmx",
"go": "go",
"docker": "docker",
"compose": "docker",
"borg": "borgbackup",
"sqlite": "sqlite",
"spotify": "spotify",
"rabbitmq": "rabbitmq",
"openrouter": "openrouter",
"obsidian": "obsidian",
"niri": "niri",
"gitea": "gitea",
"ghostty": "ghostty",
"gemini": "googlegemini",
"deepseek": "deepseek",
"c++": "cplusplus",
"typescript": "typescript",
"zsh": "zsh",
"minimax": "minimax",
"esp32": "espressif",
"esp32-cam": "espressif",
"grafana": "grafana",
"wireguard": "wireguard",
"nginx": "nginx",
"linux": "linux",
"zig": "zig",
"huggingface": "huggingface",
"ollama": "ollama",
}
// Mark returns the path data for a technology label and whether one exists.
// The lookup is case-insensitive and tolerates the surrounding whitespace that
// hand-written front-matter tends to carry.
func Mark(label string) ([]string, bool) {
slug, ok := slugs[strings.ToLower(strings.TrimSpace(label))]
if !ok {
return nil, false
}
paths, ok := marks[slug]
return paths, ok
}
// Has reports whether a technology label has a published mark.
func Has(label string) bool {
_, ok := Mark(label)
return ok
}