iPhone-Symbole: quadratisch und deckend statt abgerundet

Gemeldet: "auf dem pc und auf dem handy soll alles funktionieren, auf
dem einen klappt es und auf dem anderen nicht."

Der Grund ist, dass PC und Handy ihr Symbol an drei verschiedenen
Stellen holen -- und jede stellt andere Anforderungen:

  Browser-Reiter   das normale Symbol, runde Ecken erlaubt
  Android          die maskable-Fassung, aussen wird beschnitten
  iPhone           <link rel=apple-touch-icon> -- und iOS rundet SELBST
                   ab und fuellt alles Durchsichtige mit SCHWARZ

Unser 180er brachte seine eigene Rundung mit, also durchsichtige Ecken.
Auf dem iPhone wurden daraus schwarze Zipfel, die dann ein zweites Mal
beschnitten wurden. Auf dem PC sieht dieselbe Datei tadellos aus --
genau daher der Unterschied zwischen den Geraeten.

Jetzt wird die 180er-Fassung randvoll und deckend gebaut. Nachgemessen
an allen zehn: 0,00 Prozent durchsichtig, Ecken voll deckend. Die
normale Fassung bleibt abgerundet (4,75 Prozent) -- auch das gemessen,
damit der Fix nicht die andere Seite kaputtmacht.

Dazu eine Pruefung in pruef-struktur.mjs: alle sechs Groessen je App
vorhanden, und jede Seite nennt beide Symbolarten. Fehlt das
iPhone-Symbol, nimmt iOS einen Bildschirmausschnitt der Seite.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-05 12:46:32 +02:00
co-authored by Claude Opus 5
parent e9709a5aa8
commit 8759a46e5b
12 changed files with 80 additions and 5 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 30 KiB

+60
View File
@@ -341,6 +341,66 @@ console.log(`
: `alle ${hinterWand.length} stehen in der Ausnahmeliste`);
}
/* ---------- 10. Sind die Symbole fuer JEDES Geraet richtig gebaut? -------
PC und Handy holen ihr Symbol an drei verschiedenen Stellen, und
jede stellt andere Anforderungen:
Browser-Reiter das normale Symbol, darf runde Ecken haben
Android die maskable-Fassung, davon wird aussen bis zu
ein Fuenftel weggeschnitten
iPhone <link rel="apple-touch-icon"> -- und iOS rundet
SELBST ab und fuellt alles Durchsichtige mit
SCHWARZ
Der letzte Punkt ist der, der auffiel ("auf dem einen klappt es und
auf dem anderen nicht"): Ein apple-touch-icon mit eigener Rundung
bekommt auf dem iPhone schwarze Zipfel. Auf dem PC sieht dieselbe
Datei tadellos aus -- deshalb faellt es nur auf einem der beiden
Geraete auf, und deshalb muss es gemessen werden statt angesehen.
Geprueft werden die Dateinamen, nicht die Bilder selbst -- die
Bildpruefung braucht einen Browser und laeuft in pruef-symbole.mjs.
Hier geht es darum, dass ueberhaupt fuer jede App und jedes Geraet
eine Datei da ist. */
console.log(`
=== Symbole fuer alle Geraete ===`);
{
const symOrdner = join(REPO, "assets", "img", "app-symbole");
if (!existsSync(symOrdner)) {
ok(false, "der Ordner assets/img/app-symbole fehlt");
} else {
const da = readdirSync(symOrdner);
const apps = [...new Set(da.map((n) => n.replace(/-(\d+)(-maskable)?\.png$/, "")))]
.filter((n) => n.endsWith(".png") === false);
ok(apps.length >= 8, `${apps.length} Apps mit eigenen Symbolen`);
const noetig = ["192", "512", "192-maskable", "512-maskable", "180", "32"];
const fehlend = [];
for (const a of apps) {
for (const g of noetig) {
if (!da.includes(`${a}-${g}.png`)) fehlend.push(`${a}-${g}.png`);
}
}
ok(fehlend.length === 0,
fehlend.length ? `fehlende Symbole: ${fehlend.slice(0, 6).join(", ")}`
: `alle ${apps.length * noetig.length} Symbole vorhanden `
+ `(Reiter, Android-maskable, iPhone)`);
/* Jede Seite muss BEIDES nennen: das Reiter-Symbol und das fuers
iPhone. Fehlt das zweite, nimmt iOS einen Bildschirmausschnitt
der Seite -- unbrauchbar und unerkennbar. */
let ohneApple = [];
for (const seite of seiten) {
const text = readFileSync(join(WS, seite), "utf8");
if (!/rel="apple-touch-icon"/i.test(text)) ohneApple.push(seite);
}
ok(ohneApple.length === 0,
ohneApple.length ? `ohne apple-touch-icon (iPhone nimmt sonst einen Screenshot): ${ohneApple.join(", ")}`
: `alle ${seiten.length} Workspace-Seiten nennen ein iPhone-Symbol`);
}
}
/* ---------- Gegenprobe --------------------------------------------------- */
console.log(`\n=== Gegenprobe (muss anschlagen) ===`);
+20 -5
View File
@@ -317,7 +317,7 @@ export const STIL_JE_APP = {
* Jede Kachel bekommt deshalb einen eigenen Namen aus Stil und App.
* Damit lässt sich beliebig viel davon in ein Dokument legen, ohne
* dass sich zwei ins Gehege kommen. */
export function symbolTeil(f, stilName, n, { maskable = false, logoDaten } = {}) {
export function symbolTeil(f, stilName, n, { maskable = false, apple = false, logoDaten } = {}) {
const stil = STILE[stilName];
const logoFilter = typeof stil.logo === "function" ? stil.logo(f) : stil.logo;
@@ -345,8 +345,23 @@ export function symbolTeil(f, stilName, n, { maskable = false, logoDaten } = {})
78 % ist der Anteil, der bei jeder gaengigen Maske heil bleibt
(Kreis, Quadrat mit runden Ecken, Tropfen). */
const radius = maskable ? 0 : 22.4; // 22,4 % ist die Apple-Rundung
const k = `s-${stilName}-${f.schluessel}${maskable ? "-m" : ""}`;
/* APPLE-TOUCH-ICON: quadratisch, undurchsichtig, OHNE eigene Rundung.
BEFUND 05.09.2026 ("auf dem einen klappt es und auf dem anderen
nicht"). iOS holt sein Symbol NICHT aus dem Manifest, sondern aus
<link rel="apple-touch-icon">. Und es behandelt diese Datei anders
als jeder andere Browser: Es rundet die Ecken SELBST ab und fuellt
alles Durchsichtige mit SCHWARZ.
Unser Symbol brachte seine eigene Rundung mit -- also durchsichtige
Ecken. Auf dem iPhone wurden daraus schwarze Zipfel, die dann ein
zweites Mal beschnitten wurden. Auf dem PC faellt das nicht auf,
weil Windows und Chrome mit Transparenz umgehen koennen. Genau
daher der Unterschied zwischen den beiden Geraeten.
Deshalb hier: randvoll, eckig, deckend. Die Rundung macht iOS. */
const radius = (maskable || apple) ? 0 : 22.4; // 22,4 % ist die Apple-Rundung
const k = `s-${stilName}-${f.schluessel}${maskable ? "-m" : ""}${apple ? "-a" : ""}`;
/* Das eigentliche Symbol ist bei maskable kleiner als die Datei. */
const innen = maskable ? Math.round(n * 0.78) : n;
@@ -527,7 +542,7 @@ const GROESSEN = [
{ n: 512, maskable: false, endung: "512" },
{ n: 192, maskable: true, endung: "192-maskable" },
{ n: 512, maskable: true, endung: "512-maskable" },
{ n: 180, maskable: false, endung: "180" },
{ n: 180, maskable: false, apple: true, endung: "180" },
{ n: 32, maskable: false, endung: "32" },
];
@@ -551,7 +566,7 @@ async function bauen() {
}
for (const g of GROESSEN) {
await seite.setViewportSize({ width: g.n, height: g.n });
await seite.setContent(symbolHtml(f, stilName, g.n, { maskable: g.maskable, logoDaten }),
await seite.setContent(symbolHtml(f, stilName, g.n, { maskable: g.maskable, apple: g.apple, logoDaten }),
{ waitUntil: "networkidle" });
await seite.waitForTimeout(60);
const datei = join(ziel, `${f.schluessel}-${g.endung}.png`);