@nanggo/social-preview
Version:
Generate beautiful social media preview images from any URL
242 lines (229 loc) • 8.07 kB
JavaScript
"use strict";
/**
* Modern Template
* A contemporary design with gradient overlays and clean typography
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.modernTemplate = void 0;
exports.generateModernOverlay = generateModernOverlay;
const utils_1 = require("../utils");
const validators_1 = require("../utils/validators");
const fonts_1 = require("../constants/fonts");
const shared_1 = require("./shared");
/**
* Modern template configuration
*/
exports.modernTemplate = {
name: 'modern',
layout: {
padding: 80,
titlePosition: 'center',
descriptionPosition: 'below-title',
imagePosition: 'background',
logoPosition: 'bottom-left',
},
typography: {
title: {
fontSize: 56,
fontWeight: '700',
lineHeight: 1.2,
maxLines: 2,
},
description: {
fontSize: 28,
fontWeight: '400',
lineHeight: 1.4,
maxLines: 2,
},
siteName: {
fontSize: 22,
fontWeight: '600',
},
},
effects: {
gradient: {
type: 'linear',
colors: ['rgba(0,0,0,0.7)', 'rgba(0,0,0,0.3)'],
direction: '180deg',
opacity: 1,
},
blur: {
radius: 3,
areas: 'background',
},
shadow: {
text: true,
box: false,
},
borderRadius: 0,
},
imageProcessing: {
brightness: 0.7,
requiresTransparentCanvas: false,
},
overlayGenerator: generateModernOverlay,
};
/**
* Generate modern template SVG overlay
*/
function generateModernOverlay(metadata, width, height, options = {}, template = exports.modernTemplate) {
const padding = template.layout.padding;
const textColor = (0, validators_1.validateColor)(options.colors?.text || '#ffffff');
const accentColor = (0, validators_1.validateColor)(options.colors?.accent || '#4a9eff');
const overlayColor = (0, validators_1.validateColor)(options.colors?.overlay || 'rgba(0,0,0,0.5)');
// Typography settings
const titleFontSize = template.typography.title.fontSize;
const titleLineHeight = template.typography.title.lineHeight || 1.2;
const descFontSize = template.typography.description?.fontSize || 28;
const descLineHeight = template.typography.description?.lineHeight || 1.4;
const siteNameFontSize = template.typography.siteName?.fontSize || 22;
// Calculate text wrapping
const maxTitleWidth = width - padding * 2;
const titleLines = (0, utils_1.wrapText)(metadata.title, maxTitleWidth, titleFontSize, template.typography.title.maxLines || 2, 'inter');
const descLines = metadata.description
? (0, utils_1.wrapText)(metadata.description, maxTitleWidth, descFontSize, template.typography.description?.maxLines || 2, 'inter')
: [];
const TITLE_DESCRIPTION_GAP = 30;
const layout = (0, shared_1.layoutCenteredTitleDescription)({
height,
titleLineCount: titleLines.length,
titleFontSize,
titleLineHeight,
descLineCount: descLines.length,
descFontSize,
descLineHeight,
gap: TITLE_DESCRIPTION_GAP,
});
const contentStartY = layout.contentStartY;
const titleY = layout.titleStartY;
const descY = layout.descStartY;
const totalContentHeight = layout.totalContentHeight;
return `
<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
<defs>
${(0, shared_1.createSvgStyleCdata)(`
.title {
font-family: ${fonts_1.SYSTEM_FONT_STACK};
font-size: ${titleFontSize}px;
font-weight: 800;
fill: ${textColor};
filter: drop-shadow(0 4px 6px rgba(0,0,0,0.4));
letter-spacing: -0.02em;
}
.description {
font-family: ${fonts_1.SYSTEM_FONT_STACK};
font-size: ${descFontSize}px;
font-weight: 400;
fill: ${textColor};
opacity: 0.95;
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
letter-spacing: -0.01em;
}
.siteName {
font-family: ${fonts_1.SYSTEM_FONT_STACK};
font-size: ${siteNameFontSize}px;
font-weight: 600;
fill: ${textColor};
opacity: 0.8;
letter-spacing: 0.05em;
text-transform: uppercase;
}
.domain {
font-family: ${fonts_1.SYSTEM_FONT_STACK};
font-size: 18px;
font-weight: 500;
fill: ${textColor};
opacity: 0.6;
}
`)}
<!-- Gradient overlays -->
<linearGradient id="bgOverlay" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:${overlayColor};stop-opacity:1" />
<stop offset="50%" style="stop-color:${overlayColor};stop-opacity:0.7" />
<stop offset="100%" style="stop-color:${overlayColor};stop-opacity:0.3" />
</linearGradient>
<linearGradient id="accentGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:${accentColor};stop-opacity:1" />
<stop offset="100%" style="stop-color:${(0, utils_1.adjustBrightness)(accentColor, 20)};stop-opacity:1" />
</linearGradient>
<!-- Blur filter for background -->
<filter id="blur" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" />
</filter>
</defs>
<!-- Background overlay -->
${metadata.image
? `
<rect width="${width}" height="${height}" fill="url(#bgOverlay)"/>
`
: ''}
<!-- Top accent bar -->
<rect x="0" y="0" width="${width}" height="4" fill="url(#accentGradient)"/>
<!-- Content container with subtle background -->
<rect
x="${padding - 20}"
y="${contentStartY - 20}"
width="${width - padding * 2 + 40}"
height="${totalContentHeight + 40}"
fill="rgba(0,0,0,0.2)"
rx="8"
filter="url(#blur)"
/>
<!-- Title -->
${titleLines
.map((line, index) => `
<text
x="${width / 2}"
y="${titleY + index * titleFontSize * titleLineHeight}"
class="title"
text-anchor="middle"
>
${(0, utils_1.escapeXml)(line)}
</text>
`)
.join('')}
<!-- Description -->
${descLines.length > 0
? descLines
.map((line, index) => `
<text
x="${width / 2}"
y="${descY + index * descFontSize * descLineHeight}"
class="description"
text-anchor="middle"
>
${(0, utils_1.escapeXml)(line)}
</text>
`)
.join('')
: ''}
<!-- Bottom section -->
<g transform="translate(${padding}, ${height - padding})">
<!-- Site name -->
${metadata.siteName
? `
<text x="0" y="-30" class="siteName">
${(0, utils_1.escapeXml)(metadata.siteName.toUpperCase())}
</text>
`
: ''}
<!-- Domain -->
${metadata.domain
? `
<text x="0" y="-8" class="domain">
${(0, utils_1.escapeXml)(metadata.domain)}
</text>
`
: ''}
<!-- Decorative accent line -->
<rect x="0" y="-50" width="60" height="3" fill="${accentColor}" rx="1.5"/>
</g>
<!-- Right corner accent -->
<g transform="translate(${width - padding}, ${padding})">
<circle cx="0" cy="0" r="3" fill="${accentColor}" opacity="0.8"/>
<circle cx="12" cy="0" r="3" fill="${accentColor}" opacity="0.6"/>
<circle cx="24" cy="0" r="3" fill="${accentColor}" opacity="0.4"/>
</g>
</svg>
`;
}