commit 6e83768b8ce5f9f9483e4dbd75bf6db207dafa5c Author: JUSN Date: Fri Aug 14 22:43:01 2026 +0200 Landingpage-Template v1: config-gesteuert, zwei Themes (papier/clean) - config.js: alle Inhalte an einer Stelle (Marke, SEO, Farben, Hero, Features, Zahlen, Schritte, Warteliste, FAQ, Footer) - build.js: rendert statisches HTML aus der Config (gut fuer SEO), Abschnitte optional, Theme papier/clean, Assets-Kopie - README mit Anleitung, .gitignore - BehördenKlar als fertiges Beispiel; node build.js getestet -> dist/index.html Co-Authored-By: Claude Opus 4.8 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6acdec3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.DS_Store +~$* diff --git a/README.md b/README.md new file mode 100644 index 0000000..ec5c5d8 --- /dev/null +++ b/README.md @@ -0,0 +1,57 @@ +# Landingpage-Template + +Ein wiederverwendbares Landingpage-Template für neue (SaaS-)Projekte. +Du füllst **eine** Datei aus (`config.js`) und bekommst eine fertige, schnelle +Landingpage als statisches HTML — gut für Google, deploybar überall. + +## In 3 Schritten zu einer neuen Landingpage + +1. **Dieses Repo kopieren** (oder als Vorlage klonen). +2. **`config.js` ausfüllen** — Name, Farben, Texte, Features, Warteliste, FAQ … + (Das ist die einzige Datei, die du normalerweise anfasst.) +3. **Bauen:** + ```bash + node build.js + ``` + → erzeugt `dist/index.html`. + +Fertig. Den Ordner `dist/` dann auf einen Static-Host hochladen — z. B. +**Cloudflare Pages** (kostenlos): +```bash +npx wrangler pages deploy dist --project-name DEIN-PROJEKT +``` + +## Was ist wo? + +| Datei | Zweck | +|-------|-------| +| `config.js` | **Alle Inhalte** — hier änderst du alles pro Projekt. | +| `build.js` | Das Design/Layout. Nur anfassen, wenn du das Aussehen ändern willst. | +| `dist/` | Das fertige Ergebnis (wird von `build.js` erzeugt). | +| `assets/` | Favicon, Vorschaubild usw. — wird beim Bauen nach `dist/` kopiert. | + +## Zwei Looks (Theme) + +In `config.js` unter `theme`: + +- `"papier"` — warmer Amtsbrief-/Papier-Stil (Serifenschrift, Textur, §-Nummern, Stempel). Der BehördenKlar-Look. +- `"clean"` — schlicht/modern (serifenlos, glatte Fläche, abgerundete Karten). + +Die vier Farben unter `colors` bestimmen in beiden Themes den Look. + +## Abschnitte + +Alle Abschnitte sind **optional** — leer lassen (`[]` oder `""`), und sie +verschwinden automatisch: + +- **Hero** — Überschrift, Text, bis zu zwei Knöpfe, Stempel +- **Features** — Vorteils-Karten +- **Zahlen** — Vertrauens-/Statistik-Karten +- **So funktioniert es** — nummerierte Schritte +- **Warteliste** — Anmeldeformular (z. B. Brevo) oder Mailto-Knopf +- **FAQ** — aufklappbare Fragen +- **Footer** — Links + Hinweis + +## Voraussetzung + +Nur **Node.js** (für `node build.js`). Kein weiteres Setup, keine Abhängigkeiten. diff --git a/build.js b/build.js new file mode 100644 index 0000000..3d65bef --- /dev/null +++ b/build.js @@ -0,0 +1,207 @@ +/** + * BUILD — macht aus config.js die fertige dist/index.html. + * Aufruf: node build.js + * + * Hier steckt das DESIGN. Normalerweise musst du diese Datei nicht anfassen — + * nur config.js ausfüllen. Wer das Layout ändern will, ist hier richtig. + */ +const fs = require('fs'); +const path = require('path'); +const c = require('./config.js'); + +// kleine Helfer ------------------------------------------------------- +const esc = (s = '') => String(s).replace(/&/g, '&').replace(//g, '>'); +const has = (x) => Array.isArray(x) ? x.length > 0 : !!x; +const papier = c.theme !== 'clean'; // Standard = Papier-Look + +// CSS ---------------------------------------------------------------- +const css = ` + :root{ + --bg:${c.colors.bg}; --text:${c.colors.text}; --accent:${c.colors.accent}; + --marker:${c.colors.marker}; + --grau:color-mix(in srgb, var(--text) 55%, transparent); + --linie:color-mix(in srgb, var(--text) 18%, transparent); + color-scheme:light; + } + *{margin:0;padding:0;box-sizing:border-box} + html{scroll-behavior:smooth} + body{ + font-family:${papier ? "Georgia,'Times New Roman',serif" : "-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif"}; + color:var(--text); line-height:1.7; font-size:17px; + background:${papier + ? "repeating-linear-gradient(0deg,transparent 0 34px,rgba(0,0,0,.025) 34px 35px),var(--bg)" + : "var(--bg)"}; + } + .wrap{max-width:720px;margin:0 auto;padding:0 20px 72px} + a{color:var(--accent)} + .kopf{display:flex;justify-content:space-between;align-items:baseline;padding:20px 0 12px;border-bottom:2px solid var(--text);font-size:15px} + .marke{font-weight:700;font-size:17px;text-decoration:none;color:var(--text)} + header{padding:44px 0 8px} + .kicker{font-size:13px;color:var(--grau);text-transform:uppercase;letter-spacing:.14em;margin-bottom:10px} + h1{font-size:clamp(34px,7vw,52px);line-height:1.12;font-weight:700;letter-spacing:-.01em} + .mark{background:linear-gradient(104deg,transparent 2%,var(--marker) 4%,var(--marker) 96%,transparent 98%);padding:0 6px;box-decoration-break:clone;-webkit-box-decoration-break:clone} + .anrede{margin-top:24px;font-size:19px;max-width:560px} + .hero-fuss{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-top:30px} + .cta{display:inline-block;padding:16px 28px;background:var(--accent);color:#fff;text-decoration:none;font-family:inherit;font-size:17px;font-weight:700;border:2px solid var(--text);box-shadow:5px 5px 0 var(--text);transition:transform .12s,box-shadow .12s} + .cta:hover{transform:translate(2px,2px);box-shadow:3px 3px 0 var(--text)} + .cta-sek{background:#fff;color:var(--accent)} + .stempel{display:inline-block;padding:7px 14px;border:2.5px solid var(--accent);color:var(--accent);font-family:${papier ? "'Courier New',monospace" : 'inherit'};font-weight:700;font-size:14px;letter-spacing:.1em;text-transform:uppercase;${papier ? 'transform:rotate(-4deg);' : 'border-radius:6px;'}opacity:.9} + section{margin-top:60px} + h2{font-size:15px;text-transform:uppercase;letter-spacing:.14em;color:var(--grau);font-weight:400;margin-bottom:22px;display:flex;align-items:center;gap:12px} + h2::after{content:'';flex:1;border-top:1px solid var(--linie)} + h2 .nr{font-family:'Courier New',monospace} + .grid{display:grid;grid-template-columns:1fr 1fr;gap:14px} + @media(max-width:560px){.grid{grid-template-columns:1fr}} + .karte{background:#fff;border:1px solid var(--linie);padding:20px 22px;${papier ? 'box-shadow:4px 5px 0 rgba(0,0,0,.06);' : 'border-radius:12px;'}} + .karte .ico{font-size:26px} + .karte h3{font-size:19px;margin:8px 0 4px} + .karte p{font-size:15px;color:var(--grau)} + .zahl{font-size:38px;font-weight:700;color:var(--accent);line-height:1.1} + .zahl-erkl{font-size:15px;color:var(--grau);margin-top:6px} + .zahl-q{font-size:12px;color:var(--grau);margin-top:8px;font-family:'Courier New',monospace} + .schritt{display:flex;gap:20px;padding:18px 4px;border-bottom:1px solid var(--linie);align-items:baseline} + .schritt:last-child{border-bottom:none} + .schritt .n{font-family:'Courier New',monospace;font-size:28px;font-weight:700;color:var(--accent);width:48px;flex-shrink:0} + .schritt strong{font-size:19px;display:block;margin-bottom:2px} + .schritt span{color:var(--grau);font-size:16px} + .antrag{background:#fff;border:2px solid var(--text);${papier ? 'box-shadow:7px 8px 0 rgba(0,0,0,.85);' : 'border-radius:14px;box-shadow:0 4px 16px rgba(0,0,0,.08);'}padding:28px 26px} + .antrag h3{font-size:26px;line-height:1.25} + .antrag p{margin-top:12px;max-width:520px} + .bonus{margin:16px 0;padding:12px 16px;background:linear-gradient(104deg,transparent 1%,var(--marker) 3%,var(--marker) 97%,transparent 99%);font-size:16px} + .wl-form{display:flex;flex-direction:column;gap:12px;margin-top:6px} + .wl-input{width:100%;padding:15px 16px;font-size:17px;font-family:inherit;border:2px solid var(--text);background:#fff;color:var(--text)} + .wl-btn{width:100%;cursor:pointer;text-align:center;font-family:inherit} + .klein{font-size:13.5px;color:var(--grau);margin-top:12px} + details{border-bottom:1px solid var(--linie);padding:14px 4px} + summary{font-weight:700;cursor:pointer;font-size:17px;list-style:none;display:flex;justify-content:space-between;gap:12px} + summary::-webkit-details-marker{display:none} + summary::after{content:'+';font-family:'Courier New',monospace;font-size:22px;color:var(--accent)} + details[open] summary::after{content:'–'} + details p{margin-top:10px;color:var(--grau);font-size:16px;max-width:580px} + footer{margin-top:56px;padding-top:16px;border-top:2px solid var(--text);font-size:14.5px;color:var(--grau)} + footer a{color:var(--grau)} +`; + +// Abschnitte --------------------------------------------------------- +const nr = (n) => papier ? `§ ${n} ` : ''; +let s = 0; // Abschnittszähler + +function features() { + if (!has(c.features)) return ''; + s++; + return `

${nr(s)}${esc(c.featuresTitle || 'Vorteile')}

+
${c.features.map(f => ` +
${f.icon ? `
${esc(f.icon)}
` : ''} +

${esc(f.title)}

${esc(f.text)}

`).join('')} +
`; +} + +function stats() { + if (!has(c.stats)) return ''; + s++; + return `

${nr(s)}In Zahlen

+
${c.stats.map(z => ` +
${esc(z.value)}
+
${esc(z.label)}
+ ${z.source ? `
Quelle: ${esc(z.source)}
` : ''}
`).join('')} +
`; +} + +function steps() { + if (!has(c.steps)) return ''; + s++; + return `

${nr(s)}${esc(c.stepsTitle || 'So funktioniert es')}

+ ${c.steps.map((st, i) => ` +
${String(i + 1).padStart(2, '0')}
+
${esc(st.title)}${esc(st.text)}
`).join('')} +
`; +} + +function waitlist() { + const w = c.waitlist; if (!w || !w.title) return ''; + s++; + const form = w.formAction + ? `
+ + + + +
` + : `${esc(w.buttonLabel || 'Per E-Mail eintragen')}`; + return `

${nr(s)}Warteliste

+

${esc(w.title)}

+ ${w.text ? `

${esc(w.text)}

` : ''} + ${w.bonus ? `

${esc(w.bonus)}

` : ''} + ${form} + ${w.note ? `

${esc(w.note)}

` : ''} +
`; +} + +function faq() { + if (!has(c.faq)) return ''; + s++; + return `

${nr(s)}${esc(c.faqTitle || 'Häufige Fragen')}

+ ${c.faq.map(f => `
${esc(f.q)}

${esc(f.a)}

`).join('')} +
`; +} + +// Seite zusammenbauen ------------------------------------------------ +const h = c.hero || {}; +const secondary = h.secondaryCta && h.secondaryCta.href !== h.primaryCta?.href + ? `${esc(h.secondaryCta.label)}` : ''; + +const html = ` + + + + +${esc(c.seo.title || c.brand.name)} + +${c.brand.url ? `` : ''} + + + +${c.seo.ogImage ? `` : ''} + + + + +
+ + +
+ ${h.kicker ? `

${esc(h.kicker)}

` : ''} +

${esc(h.title || c.brand.name)}${h.titleHighlight ? `
${esc(h.titleHighlight)}` : ''}

+ ${h.text ? `

${esc(h.text)}

` : ''} +
+ ${h.primaryCta ? `${esc(h.primaryCta.label)}` : ''} + ${secondary} + ${h.stamp ? `${esc(h.stamp)}` : ''} +
+
+ + ${features()} + ${stats()} + ${steps()} + ${waitlist()} + ${faq()} + +
+ ${(c.footer?.links || []).map(l => `${esc(l.label)}`).join(' · ')} + ${c.footer?.note ? `

${esc(c.footer.note)}

` : ''} +
+
+${c.analyticsToken ? `` : ''} + +`; + +// schreiben ---------------------------------------------------------- +const out = path.join(__dirname, 'dist'); +fs.mkdirSync(out, { recursive: true }); +fs.writeFileSync(path.join(out, 'index.html'), html); +// assets/ (Favicon etc.) mitkopieren, falls vorhanden +const assets = path.join(__dirname, 'assets'); +if (fs.existsSync(assets)) { + for (const f of fs.readdirSync(assets)) fs.copyFileSync(path.join(assets, f), path.join(out, f)); +} +console.log(`✓ Fertig: dist/index.html (Theme: ${c.theme}, ${s} Abschnitte)`); diff --git a/config.js b/config.js new file mode 100644 index 0000000..77825fb --- /dev/null +++ b/config.js @@ -0,0 +1,102 @@ +/** + * ═══════════════════════════════════════════════════════════════════ + * LANDINGPAGE-CONFIG — Das ist die EINZIGE Datei, die du pro Projekt + * ausfüllst. Danach: node build.js → erzeugt dist/index.html + * ═══════════════════════════════════════════════════════════════════ + * + * Tipp: Abschnitte, die du NICHT brauchst, einfach leer lassen ([] oder "") + * oder ganz weglassen — dann werden sie nicht angezeigt. + */ + +module.exports = { + // ── Grund-Look ────────────────────────────────────────────────── + // "papier" = warmer Amtsbrief-/Papier-Stil (Serifenschrift, Textur) + // "clean" = schlicht/modern (serifenlos, glatte Fläche) + theme: 'papier', + + // Farben (gelten in beiden Themes). Nur diese vier bestimmen den Look. + colors: { + bg: '#faf7f0', // Hintergrund + text: '#21211d', // Textfarbe + accent: '#1d4e89', // Akzent (Knöpfe, Links) + marker: '#ffe66b', // Textmarker-Highlight + }, + + // ── Marke & SEO ───────────────────────────────────────────────── + brand: { + name: 'BehördenKlar', + emoji: '📬', // kleines Logo-Emoji (optional) + url: 'https://behoerdenklar.de', // deine Domain (für Canonical/OG) + lang: 'de', + }, + seo: { + title: 'Behördenbriefe einfach erklärt – per Foto | BehördenKlar', + description: 'BehördenKlar erklärt Amtsbriefe in einfacher Sprache: Foto machen, verstehen, richtig reagieren – in 9 Sprachen.', + ogImage: 'https://behoerdenklar.de/vorschau.png', // 1200×630 Vorschaubild + }, + // Cloudflare Web Analytics Token (optional, "" = aus) + analyticsToken: '', + + // ── Hero (Kopfbereich) ────────────────────────────────────────── + hero: { + kicker: 'Betreff', + title: 'Brief vom Amt.', + titleHighlight: 'Endlich verstanden.', // wird gelb markiert + text: 'Ein Brief vom Amt muss nicht schwer sein. Machen Sie ein Foto — die App erklärt ihn in einfachen Worten.', + primaryCta: { label: '📷 Kostenlos testen', href: '#anmelden' }, + secondaryCta: { label: '✉️ Auf die Warteliste', href: '#anmelden' }, // optional + stamp: 'Bald verfügbar', // kleiner Stempel (optional, "" = aus) + }, + + // ── Vorteile / Features ───────────────────────────────────────── + featuresTitle: 'Was die App kann', + features: [ + { icon: '🔎', title: 'Klartext statt Amtsdeutsch', text: 'Jeder Brief in einfacher Sprache erklärt.' }, + { icon: '⏰', title: 'Fristen im Blick', text: 'Die App zeigt, bis wann Sie handeln müssen.' }, + { icon: '🌍', title: '9 Sprachen', text: 'Übersetzung u. a. auf Arabisch, Türkisch, Ukrainisch.' }, + { icon: '🔒', title: 'Sicher & privat', text: 'Alles verschlüsselt auf Ihrem Gerät. Kein Konto.' }, + ], + + // ── Zahlen / Vertrauen ────────────────────────────────────────── + stats: [ + { value: '6,2 Mio.', label: 'Erwachsene in Deutschland haben Mühe mit schweren Texten.', source: 'LEO-Studie 2018' }, + { value: '~ 60 Sek.', label: 'braucht die App, um einen Brief zu erklären.', source: '' }, + ], + + // ── So funktioniert es ────────────────────────────────────────── + stepsTitle: 'So funktioniert es', + steps: [ + { title: 'Brief fotografieren', text: 'Mit dem Handy, auch als PDF.' }, + { title: 'Klartext lesen', text: 'Kurz, einfach, ohne schwere Wörter.' }, + { title: 'Richtig reagieren', text: 'Frist merken, Antwort erstellen, fertig.' }, + ], + + // ── Anmeldung / Warteliste ────────────────────────────────────── + waitlist: { + id: 'anmelden', + title: 'Seien Sie früh dabei', + text: 'Tragen Sie sich ein — wir schreiben Ihnen, sobald es losgeht.', + bonus: '🎁 Als Dankeschön: 5 Gratis-Analysen statt 3.', + // Formular-Adresse (z. B. Brevo). Leer = nur ein Mailto-Knopf. + formAction: '', + emailPlaceholder: 'Ihre E-Mail-Adresse', + buttonLabel: 'Eintragen →', + note: 'Keine Werbung. Jederzeit abmeldbar.', + }, + + // ── Häufige Fragen ────────────────────────────────────────────── + faqTitle: 'Häufige Fragen', + faq: [ + { q: 'Was kostet die App?', a: 'Die ersten Analysen sind gratis. Danach ein günstiges Abo.' }, + { q: 'Was passiert mit meinen Daten?', a: 'Ihre Briefe bleiben verschlüsselt auf Ihrem Gerät.' }, + ], + + // ── Fußzeile ──────────────────────────────────────────────────── + footer: { + links: [ + { label: 'Impressum', href: '/impressum.html' }, + { label: 'Datenschutz', href: '/datenschutz.html' }, + ], + note: 'Diese Seite ist kein echter Amtsbrief.', + }, +}; diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..c2185dc --- /dev/null +++ b/dist/index.html @@ -0,0 +1,141 @@ + + + + + +Behördenbriefe einfach erklärt – per Foto | BehördenKlar + + + + + + + + + + +
+ + +
+

Betreff

+

Brief vom Amt.
Endlich verstanden.

+

Ein Brief vom Amt muss nicht schwer sein. Machen Sie ein Foto — die App erklärt ihn in einfachen Worten.

+
+ 📷 Kostenlos testen + + Bald verfügbar +
+
+ +

§ 1 Was die App kann

+
+
🔎
+

Klartext statt Amtsdeutsch

Jeder Brief in einfacher Sprache erklärt.

+
+

Fristen im Blick

Die App zeigt, bis wann Sie handeln müssen.

+
🌍
+

9 Sprachen

Übersetzung u. a. auf Arabisch, Türkisch, Ukrainisch.

+
🔒
+

Sicher & privat

Alles verschlüsselt auf Ihrem Gerät. Kein Konto.

+
+

§ 2 In Zahlen

+
+
6,2 Mio.
+
Erwachsene in Deutschland haben Mühe mit schweren Texten.
+
Quelle: LEO-Studie 2018
+
~ 60 Sek.
+
braucht die App, um einen Brief zu erklären.
+
+
+

§ 3 So funktioniert es

+ +
01
+
Brief fotografierenMit dem Handy, auch als PDF.
+
02
+
Klartext lesenKurz, einfach, ohne schwere Wörter.
+
03
+
Richtig reagierenFrist merken, Antwort erstellen, fertig.
+
+

§ 4 Warteliste

+

Seien Sie früh dabei

+

Tragen Sie sich ein — wir schreiben Ihnen, sobald es losgeht.

+

🎁 Als Dankeschön: 5 Gratis-Analysen statt 3.

+ Eintragen → +

Keine Werbung. Jederzeit abmeldbar.

+
+

§ 5 Häufige Fragen

+
Was kostet die App?

Die ersten Analysen sind gratis. Danach ein günstiges Abo.

Was passiert mit meinen Daten?

Ihre Briefe bleiben verschlüsselt auf Ihrem Gerät.

+
+ + +
+ + + \ No newline at end of file