diff --git a/package.json b/package.json
index 7d2874c8..95972745 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,6 @@
{
"scripts": {
+ "icons:generate": "node tools/generate_apple_touch_icons.js",
"links:refresh": "node tools/check_external_links.js",
"stats:generate": "node tools/generate_stats.js"
},
diff --git a/static/apple-touch-icon-precomposed.png b/static/apple-touch-icon-precomposed.png
new file mode 100644
index 00000000..449e77aa
Binary files /dev/null and b/static/apple-touch-icon-precomposed.png differ
diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png
new file mode 100644
index 00000000..449e77aa
Binary files /dev/null and b/static/apple-touch-icon.png differ
diff --git a/themes/2026/layouts/_partials/head.html b/themes/2026/layouts/_partials/head.html
index d62618cc..ea2d474e 100644
--- a/themes/2026/layouts/_partials/head.html
+++ b/themes/2026/layouts/_partials/head.html
@@ -11,4 +11,5 @@
+
{{ partialCached "head/css.html" . }}
diff --git a/themes/42/layouts/_partials/head.html b/themes/42/layouts/_partials/head.html
index a73074ee..5b9cef15 100644
--- a/themes/42/layouts/_partials/head.html
+++ b/themes/42/layouts/_partials/head.html
@@ -4,4 +4,5 @@
+
{{ partialCached "head/css.html" . }}
diff --git a/tools/generate_apple_touch_icons.js b/tools/generate_apple_touch_icons.js
new file mode 100644
index 00000000..001c805b
--- /dev/null
+++ b/tools/generate_apple_touch_icons.js
@@ -0,0 +1,53 @@
+#!/usr/bin/env node
+
+const fs = require("fs");
+const path = require("path");
+const sharp = require("sharp");
+
+const PROJECT_ROOT = path.resolve(__dirname, "..");
+const SOURCE_ICON_PATH = path.join(PROJECT_ROOT, "static", "favicon.png");
+const APPLE_TOUCH_ICON_SIZE = 180;
+const APPLE_TOUCH_ICON_BACKGROUND = "#060c14";
+const OUTPUT_ICON_PATHS = [
+ path.join(PROJECT_ROOT, "static", "apple-touch-icon.png"),
+ path.join(PROJECT_ROOT, "static", "apple-touch-icon-precomposed.png"),
+];
+
+/**
+ * Génère le PNG Apple Touch à partir du favicon principal du site.
+ *
+ * L'image finale est rendue opaque sur le fond sombre du thème actif pour
+ * éviter les rendus incohérents des zones transparentes sur certains appareils iOS.
+ *
+ * @returns {Promise}
+ */
+function buildAppleTouchIconBuffer() {
+ return sharp(SOURCE_ICON_PATH)
+ .resize(APPLE_TOUCH_ICON_SIZE, APPLE_TOUCH_ICON_SIZE, {
+ fit: "cover",
+ })
+ .flatten({
+ background: APPLE_TOUCH_ICON_BACKGROUND,
+ })
+ .png({
+ compressionLevel: 9,
+ adaptiveFiltering: true,
+ })
+ .toBuffer();
+}
+
+/**
+ * Écrit la même icône sous les deux noms historiques encore demandés par les navigateurs.
+ *
+ * @param {Buffer} iconBuffer
+ */
+function writeAppleTouchIcons(iconBuffer) {
+ for (const outputPath of OUTPUT_ICON_PATHS) {
+ fs.writeFileSync(outputPath, iconBuffer);
+ }
+}
+
+(async function main() {
+ const iconBuffer = await buildAppleTouchIconBuffer();
+ writeAppleTouchIcons(iconBuffer);
+})();