Brand colours on the marks; round the app's corners; move the lights in

Simple Icons publishes each brand's own hex next to its glyph, and the
build was throwing it away. Tiles now carry it - Gmail red, Drive blue,
Chat green, Gemini violet - and the glyph is drawn black or white by the
tile's perceived brightness, since brand colours are picked to look right
rather than to carry a white mark. GitHub and Notion are near-black,
Snapchat is pure yellow, and a fixed white glyph loses one end of that.

An app's right corners are now rounded on its own layer. The container's
rounded-xl could never have clipped them: an app is a native view sitting
on top, not something the shell lays out, so it kept square corners and
overhung the curve. Only the right pair - the left edge butts against the
nav, and rounding it would notch the middle of the window.

The traffic lights move to (26, 24) and the drag strip deepens to match,
so they sit inside the window's margin instead of against its corner.

Adds Gemini, Claude, ChatGPT, Linear, ClickUp, HubSpot, Shopify and
Cloudflare to the host table.
This commit is contained in:
2026-09-01 14:51:20 +02:00
parent ada18d54ae
commit ffedb72b83
10 changed files with 175 additions and 43 deletions
+17 -12
View File
@@ -1,29 +1,34 @@
/**
* Turns Simple Icons' 3,400 SVG files into one map of slug → path data.
* Turns Simple Icons into one map of slug → [path, brand colour].
*
* The package ships a file per icon and 15MB of SVG wrapper around what is
* really a single `d` attribute each. This keeps the attribute and throws the
* rest away, so the app carries about a megabyte instead of fifteen.
* really a single `d` attribute each, plus a separate metadata file carrying
* the brand's own hex. This joins the two and throws the rest away.
*
* The output goes to public/ rather than src/: it is fetched once at runtime
* instead of being parsed into the JS bundle at every startup.
*
* Run `npm run icons` after upgrading simple-icons. The output is committed,
* so a build never depends on this having been run.
* `npm run build` runs this, so the result is never committed and never stale.
*/
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
import { readFileSync, readdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
const SRC = "node_modules/simple-icons/icons";
const ICONS = "node_modules/simple-icons/icons";
const META = "node_modules/simple-icons/data/simple-icons.json";
const OUT = "public/brand-icons.json";
const hexes = new Map();
for (const icon of JSON.parse(readFileSync(META, "utf8"))) {
hexes.set(icon.slug, icon.hex);
}
const icons = {};
for (const file of readdirSync(SRC)) {
for (const file of readdirSync(ICONS)) {
if (!file.endsWith(".svg")) continue;
const svg = readFileSync(join(SRC, file), "utf8");
const match = /\sd="([^"]+)"/.exec(svg);
if (!match) continue;
icons[file.slice(0, -4)] = match[1];
const slug = file.slice(0, -4);
const path = /\sd="([^"]+)"/.exec(readFileSync(join(ICONS, file), "utf8"))?.[1];
if (!path) continue;
icons[slug] = [path, hexes.get(slug) ?? "64748B"];
}
writeFileSync(OUT, JSON.stringify(icons));