Fonts: every Google font the editor offers (13 families, latin and latin-ext, 1.2 MB) now lives in public/fonts; the layout inlines @font-face rules only for the fonts in use, built from src/lib/font-faces.json. scripts/fonts-holen.py re-fetches them. The admin font preview loads the same files via /admin/fonts and /admin/api/font-faces. Checked: no request leaves larpaso.de on the start page, Impressum or Datenschutz, nor in the admin editor. nginx: access_log off, so visitor IPs are not stored. Caddy has no access log configured either. Privacy policy replaces the placeholder and describes only what the site actually does: controller (from the Impressum), hosting at netcup in Nuremberg (from the server IP), no access logs, HTTPS, mailto contact form and e-mail, self-hosted fonts and media, no cookies, data subject rights and the Lower Saxony supervisory authority. The closing note is no longer styled as a warning. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
65 lines
3.9 KiB
TypeScript
65 lines
3.9 KiB
TypeScript
import fontFaces from './font-faces.json';
|
|
|
|
export interface FontOption {
|
|
name: string;
|
|
category: 'serif' | 'sans';
|
|
google: boolean; // true = a Google Font we host ourselves in public/fonts; false = system font
|
|
fallback: string;
|
|
}
|
|
|
|
/**
|
|
* Curated font list for the admin "Design" editor. Kept in sync by hand with
|
|
* admin/lib/fonts.js (duplicated rather than shared because the Astro site
|
|
* is ESM/TS and the admin backend is CommonJS).
|
|
*/
|
|
export const FONT_OPTIONS: FontOption[] = [
|
|
{ name: 'Playfair Display', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
|
|
{ name: 'Lora', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
|
|
{ name: 'Merriweather', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
|
|
{ name: 'Libre Baskerville', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
|
|
{ name: 'DM Serif Display', category: 'serif', google: true, fallback: "Georgia, 'Times New Roman', serif" },
|
|
{ name: 'Georgia (Systemschrift)', category: 'serif', google: false, fallback: "Georgia, 'Times New Roman', serif" },
|
|
{ name: 'Inter', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
{ name: 'Roboto', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
{ name: 'Open Sans', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
{ name: 'Poppins', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
{ name: 'Montserrat', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
{ name: 'Nunito', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
{ name: 'Work Sans', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
{ name: 'Source Sans 3', category: 'sans', google: true, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
{ name: 'System UI (Systemschrift)', category: 'sans', google: false, fallback: "system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif" },
|
|
];
|
|
|
|
export function findFont(name: string): FontOption {
|
|
return FONT_OPTIONS.find((f) => f.name === name) || FONT_OPTIONS[0];
|
|
}
|
|
|
|
export function fontFamilyCss(name: string): string {
|
|
const font = findFont(name);
|
|
return font.google ? `'${font.name}', ${font.fallback}` : font.fallback;
|
|
}
|
|
|
|
/** Builds the @font-face rules for the given font names from the files in
|
|
* public/fonts (see font-faces.json). The fonts are served from our own
|
|
* server so a page view never contacts Google. System fonts are skipped. */
|
|
export function localFontFacesCss(fontNames: string[]): string {
|
|
const faces = fontFaces as Record<string, { file: string; weight: string; unicodeRange: string }[]>;
|
|
return fontNames
|
|
.map(findFont)
|
|
.filter((f) => f.google)
|
|
.filter((f, i, arr) => arr.findIndex((g) => g.name === f.name) === i)
|
|
.flatMap((f) =>
|
|
(faces[f.name] || []).map(
|
|
(face) =>
|
|
`@font-face { font-family: '${f.name}'; font-style: normal; font-weight: ${face.weight}; ` +
|
|
`font-display: swap; src: url('${face.file}') format('woff2'); unicode-range: ${face.unicodeRange}; }`,
|
|
),
|
|
)
|
|
.join('\n');
|
|
}
|
|
|
|
/** Builds `[data-font="Name"] { font-family: ...; }` rules for fonts used
|
|
* inline via the rich-text editor's per-selection font picker. */
|
|
export function inlineFontFacesCss(fontNames: string[]): string {
|
|
return fontNames.map((name) => `[data-font="${name}"] { font-family: ${fontFamilyCss(name)}; }`).join('\n');
|
|
}
|