feat(web): afegir OG image 1200x630 amb xifres d'impacte

This commit is contained in:
2026-07-14 13:37:02 +02:00
parent 322704a945
commit 6b09a711fc
4 changed files with 90 additions and 3 deletions
+1
View File
@@ -21,6 +21,7 @@
"@astrojs/check": "^0.9.9",
"@tailwindcss/vite": "^4.3.2",
"@types/node": "^26.1.1",
"sharp": "^0.35.3",
"tailwindcss": "^4.3.2",
"typescript": "^5.9.3"
}
+4 -3
View File
@@ -30,6 +30,9 @@ importers:
'@types/node':
specifier: ^26.1.1
version: 26.1.1
sharp:
specifier: ^0.35.3
version: 0.35.3(@types/node@26.1.1)
tailwindcss:
specifier: ^4.3.2
version: 4.3.2
@@ -2309,8 +2312,7 @@ snapshots:
'@esbuild/win32-x64@0.28.1':
optional: true
'@img/colour@1.1.0':
optional: true
'@img/colour@1.1.0': {}
'@img/sharp-darwin-arm64@0.35.3':
optionalDependencies:
@@ -3513,7 +3515,6 @@ snapshots:
'@img/sharp-win32-ia32': 0.35.3
'@img/sharp-win32-x64': 0.35.3
'@types/node': 26.1.1
optional: true
shiki@4.3.1:
dependencies:
Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

+85
View File
@@ -0,0 +1,85 @@
// Genera l'Open Graph image (1200x630 PNG) per a previsualitzacions en xarxes.
// Output: web/public/og-image.png
//
// Re-executar si canvien les xifres d'impacto o el missatge principal.
// Dependència: sharp (devDep de web/). El text s'incrusta al SVG i es
// rasteritza amb el motor de sharp. Si volem una font corporativa pròpia
// (Inter, etc.) caldrà incrustar el fitxer .ttf i passar-lo a sharp.
//
// Ús:
// cd web && node scripts/generate-og-image.mjs
// export PATH="/Users/ichi/.nvm/versions/node/v22.13.1/bin:$PATH" # Node 22 + pnpm 11
import sharp from "sharp";
import { writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = dirname(fileURLToPath(import.meta.url));
const outPath = resolve(__dirname, "../public/og-image.png");
// Paleta extreta de web/src/styles/global.css (@theme brand)
const COLOR_BRAND = "#c2410c";
const COLOR_BRAND_DARK = "#9a3412";
const COLOR_INK = "#1c1917";
const COLOR_PAPER = "#fafaf9";
const COLOR_LINE = "#e7e5e4";
const COLOR_MUTED = "#78716c";
const FONT = "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif";
const svg = `<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" width="1200" height="630">
<!-- Background -->
<rect width="1200" height="630" fill="${COLOR_PAPER}"/>
<!-- Top accent bar -->
<rect width="1200" height="8" fill="${COLOR_BRAND}"/>
<!-- Eyebrow -->
<text x="60" y="78" font-family="${FONT}" font-size="20" font-weight="700"
fill="${COLOR_BRAND_DARK}" letter-spacing="2">CASES TANCADES · ILLES BALEARS</text>
<!-- Main title (2 lines) -->
<text x="60" y="190" font-family="${FONT}" font-size="76" font-weight="800" fill="${COLOR_INK}">Quants pisos tancats</text>
<text x="60" y="278" font-family="${FONT}" font-size="76" font-weight="800" fill="${COLOR_INK}">hi ha a les Balears?</text>
<!-- Subtitle -->
<text x="60" y="338" font-family="${FONT}" font-size="26" font-weight="400" fill="#44403c">Creuem dades oficials d'habitatge semi-buit amb lloguer turístic.</text>
<text x="60" y="372" font-family="${FONT}" font-size="26" font-weight="400" fill="#44403c">La diferència entre el legal i el real és la denúncia.</text>
<!-- Stat card 1: Airbnb -->
<g transform="translate(60, 430)">
<rect width="340" height="150" rx="10" fill="#ffffff" stroke="${COLOR_LINE}" stroke-width="2"/>
<text x="22" y="58" font-family="${FONT}" font-size="46" font-weight="800" fill="${COLOR_BRAND}">18.175</text>
<text x="22" y="98" font-family="${FONT}" font-size="20" font-weight="600" fill="${COLOR_INK}">Listings Airbnb actius</text>
<text x="22" y="124" font-family="${FONT}" font-size="16" font-weight="400" fill="${COLOR_MUTED}">Mallorca + Menorca (geo-localitzats)</text>
</g>
<!-- Stat card 2: HUT -->
<g transform="translate(430, 430)">
<rect width="340" height="150" rx="10" fill="#ffffff" stroke="${COLOR_LINE}" stroke-width="2"/>
<text x="22" y="58" font-family="${FONT}" font-size="46" font-weight="800" fill="${COLOR_BRAND}">18.184</text>
<text x="22" y="98" font-family="${FONT}" font-size="20" font-weight="600" fill="${COLOR_INK}">Places HUT legals</text>
<text x="22" y="124" font-family="${FONT}" font-size="16" font-weight="400" fill="${COLOR_MUTED}">Eivissa · 40% a Sant Josep</text>
</g>
<!-- Stat card 3: Vivenda buida -->
<g transform="translate(800, 430)">
<rect width="340" height="150" rx="10" fill="#ffffff" stroke="${COLOR_LINE}" stroke-width="2"/>
<text x="22" y="58" font-family="${FONT}" font-size="46" font-weight="800" fill="${COLOR_BRAND}">16,2%</text>
<text x="22" y="98" font-family="${FONT}" font-size="20" font-weight="600" fill="${COLOR_INK}">Habitatges buits</text>
<text x="22" y="124" font-family="${FONT}" font-size="16" font-weight="400" fill="${COLOR_MUTED}">INE Balears (CCAA, 2024)</text>
</g>
<!-- Footer URL -->
<text x="60" y="615" font-family="${FONT}" font-size="20" font-weight="600" fill="${COLOR_BRAND_DARK}">casestancades.vercel.app</text>
<text x="1140" y="615" text-anchor="end" font-family="${FONT}" font-size="16" font-weight="400" fill="${COLOR_MUTED}">Dades públiques · CC BY-SA 4.0</text>
</svg>`;
const png = await sharp(Buffer.from(svg), { density: 150 })
.png({ compressionLevel: 9 })
.toBuffer();
await writeFile(outPath, png);
console.log(`OG image written: ${outPath} (${png.length} bytes, 1200x630)`);