feat(brand): актуальный фавикон по брендовому знаку
All checks were successful
deploy / deploy (push) Successful in 1m33s
security / security (push) Successful in 2m41s

Старый favicon.svg (стилизованная «A» от скаффолдинга) заменён на
оптимизированный для 16-32px вариант logo-mark: кривые-отражения +
точка на тёмном фоне, тот же градиент #b794ff→#6c4ed4.

- public/favicon.svg — новый 64×64 viewBox, утолщённые штрихи
- public/favicon.ico — 32×32 (sharp → PNG-in-ICO)
- public/apple-touch-icon.png — 180×180 для iOS
- scripts/build-favicon.mjs + npm run build:favicon
- BaseLayout: добавлены <link> на ico (fallback) и apple-touch-icon
This commit is contained in:
Dmitry Gusev
2026-05-25 13:33:22 +03:00
parent 23c8deebd2
commit 048cb673e0
7 changed files with 52 additions and 11 deletions

View File

@@ -75,7 +75,9 @@ src/
└── consts.ts (SITE_TITLE, WORLDS, ANALYTICS, CATEGORY_COLORS, plural())
public/
├── favicon.svg
├── favicon.svg (брендовый знак на тёмном фоне, оптимизирован под 16-32px)
├── favicon.ico (32×32 PNG-in-ICO, генерируется из favicon.svg)
├── apple-touch-icon.png (180×180 для iOS home screen)
├── logo.svg, logo-mark.svg
├── og-image.png (1200×630 для расшаривания в TG/VK/Twitter)
├── og-image.svg (исходник)
@@ -86,7 +88,8 @@ public/
scripts/
├── migrate-wp.mjs (одноразовый: _wp-export.json → src/content/*.md)
── build-og-image.mjs (sharp → public/og-image.png из SVG)
── build-og-image.mjs (sharp → public/og-image.png из SVG)
└── build-favicon.mjs (sharp → public/favicon.ico + apple-touch-icon.png из favicon.svg; `npm run build:favicon`)
Dockerfile (multi-stage: node:22-alpine builds → nginx:1.29-alpine serves)
nginx.conf (gzip, кэш _astro/ 1y, MIME application/rss+xml для feed)

View File

@@ -12,7 +12,8 @@
"preview": "astro preview",
"astro": "astro",
"migrate": "node scripts/migrate-wp.mjs",
"indexnow": "node scripts/indexnow.js"
"indexnow": "node scripts/indexnow.js",
"build:favicon": "node scripts/build-favicon.mjs"
},
"dependencies": {
"@astrojs/rss": "^4.0.12",

BIN
public/apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,9 +1,16 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<defs>
<linearGradient id="fg" x1="0" y1="0" x2="64" y2="64" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#b794ff"/>
<stop offset="1" stop-color="#6c4ed4"/>
</linearGradient>
<radialGradient id="bg" cx="50%" cy="38%" r="65%">
<stop offset="0" stop-color="#1a1430"/>
<stop offset="1" stop-color="#07090f"/>
</radialGradient>
</defs>
<rect width="64" height="64" rx="12" fill="url(#bg)"/>
<path d="M14 40 Q32 16 50 40" stroke="url(#fg)" stroke-width="3.5" stroke-linecap="round" fill="none"/>
<path d="M14 28 Q32 52 50 28" stroke="url(#fg)" stroke-width="3" stroke-linecap="round" fill="none" opacity="0.7"/>
<circle cx="32" cy="34" r="3.2" fill="#d4b9ff"/>
</svg>

Before

Width:  |  Height:  |  Size: 749 B

After

Width:  |  Height:  |  Size: 792 B

28
scripts/build-favicon.mjs Normal file
View File

@@ -0,0 +1,28 @@
#!/usr/bin/env node
// Генерирует public/favicon.ico (32x32 PNG-in-ICO, как у Astro по умолчанию)
// и public/apple-touch-icon.png (180x180) из public/favicon.svg.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import sharp from 'sharp';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, '..');
const svgPath = path.join(ROOT, 'public/favicon.svg');
const svg = fs.readFileSync(svgPath);
const targets = [
{ out: 'public/favicon.ico', size: 32 },
{ out: 'public/apple-touch-icon.png', size: 180 },
];
for (const { out, size } of targets) {
const dst = path.join(ROOT, out);
await sharp(svg, { density: 384 })
.resize(size, size)
.png({ compressionLevel: 9 })
.toFile(dst);
const kb = (fs.statSync(dst).size / 1024).toFixed(1);
console.log(`wrote ${out} (${size}x${size}) — ${kb} KB`);
}

View File

@@ -69,6 +69,8 @@ const jsonLd = [
<meta name="theme-color" content="#07090f" />
<link rel="canonical" href={canonical} />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" sizes="32x32" />
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
<meta property="og:type" content={ogType} />
<meta property="og:title" content={pageTitle} />