Files
onepager/shared/impressum.js

246 lines
8.6 KiB
JavaScript

/**
* Modulares Impressum — Single Source of Truth für alle Onepager-Sites.
*
* Einbinden:
* <script src="/shared/impressum.js"></script>
* <script src="/shared/impressum.js" data-owner="flexsiebels"></script>
* <script src="/shared/impressum.js" data-owner="flexsiebels" data-variant="full"></script>
*
* Konfiguration via data-Attribute am Script-Tag:
* data-owner="flexsiebels" (default) — Kurzverweis auf flexsiebels.de (Rechtsträger)
* data-owner="martinsiebels" — volles Impressum Martin Siebels (separate Person, Osnabrück)
* data-owner="msbls" — legacy Alias auf flexsiebels (msbls ist Marke unter flexsiebels-Umbrella)
* data-variant="minimal" (default) — Einzeiler im Footer (inline)
* data-variant="full" — kleiner Text-Trigger im Footer, voller § 5 TMG-Block im Overlay
*
* Legacy-Alias: data-style (gleiche Werte wie data-variant).
*
* Render-Ziel: Element mit id="impressum" falls vorhanden, sonst <footer>, sonst body.
*
* Theme: das Overlay liest --bg-card / --text / --accent / --border vom Host
* (siehe shared/css/variables.css), mit neutralen Dark-Fallbacks für Sites
* ohne diese Variablen.
*/
(function () {
const script = document.currentScript;
const owner = script?.getAttribute('data-owner') || 'flexsiebels';
const variant = script?.getAttribute('data-variant')
|| script?.getAttribute('data-style')
|| 'minimal';
// Gemeinsamer Block für Matthias Siebels (m) — beide Domains, gleiche Anschrift.
const matthiasAddress = 'Matthias Siebels<br>'
+ 'c/o Online-Impressum.de #5892<br>'
+ 'Europaring 90<br>'
+ '53757 Sankt Augustin';
const owners = {
flexsiebels: {
minimal: 'Ein Projekt von <a href="https://flexsiebels.de" target="_blank" rel="noopener">flexsiebels.de</a>',
full: '<strong>Angaben gemäß § 5 TMG:</strong><br>'
+ matthiasAddress + '<br>'
+ 'E-Mail: <a href="mailto:mail@flexsiebels.de">mail@flexsiebels.de</a>',
},
martinsiebels: {
minimal: 'Ein Projekt von <a href="https://martinsiebels.de" target="_blank" rel="noopener">Martin Siebels</a>',
full: '<strong>Angaben gemäß § 5 TMG:</strong><br>'
+ 'Martin Siebels<br>'
+ 'Leyer Str. 38<br>'
+ '49076 Osnabrück<br>'
+ 'E-Mail: <a href="mailto:Martin_Siebels@web.de">Martin_Siebels@web.de</a>',
},
};
// Legacy alias: msbls ist Marke unter flexsiebels-Umbrella, Rechtsträger ist flexsiebels.
owners.msbls = owners.flexsiebels;
const config = owners[owner] || owners.flexsiebels;
const target = document.getElementById('impressum')
|| document.querySelector('footer .container')
|| document.querySelector('footer')
|| document.body;
if (variant === 'full') {
renderOverlay(config.full, target);
} else {
renderInline(config.minimal, target);
}
function renderInline(html, target) {
const el = document.createElement('div');
el.className = 'onepager-impressum';
el.innerHTML = html;
el.style.cssText = 'text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;line-height:1.7;';
el.querySelectorAll('a').forEach(a => {
a.style.color = 'inherit';
a.style.textDecoration = 'underline';
a.style.textDecorationThickness = '1px';
a.style.textUnderlineOffset = '2px';
});
target.appendChild(el);
}
function renderOverlay(html, target) {
// Trigger: kleiner Text-Link, layout-äquivalent zur inline-minimal-Variante,
// damit Footer-Höhe sich nicht ändert.
const wrap = document.createElement('div');
wrap.className = 'onepager-impressum';
wrap.style.cssText = 'text-align:center;font-size:0.75rem;opacity:0.6;padding:12px 0;margin-top:4px;line-height:1.7;';
const trigger = document.createElement('a');
trigger.href = '#impressum';
trigger.className = 'onepager-impressum-trigger';
trigger.textContent = 'Impressum';
trigger.style.cssText = [
'color:inherit',
'text-decoration:underline',
'text-decoration-thickness:1px',
'text-underline-offset:2px',
'cursor:pointer',
].join(';');
wrap.appendChild(trigger);
target.appendChild(wrap);
let backdrop = null;
let lastFocus = null;
let prevBodyOverflow = '';
trigger.addEventListener('click', (e) => {
e.preventDefault();
open();
});
function open() {
if (backdrop) return;
lastFocus = document.activeElement;
backdrop = document.createElement('div');
backdrop.className = 'onepager-impressum-backdrop';
backdrop.style.cssText = [
'position:fixed',
'inset:0',
'background:rgba(0,0,0,0.6)',
'display:flex',
'align-items:center',
'justify-content:center',
'padding:16px',
'box-sizing:border-box',
'overflow:auto',
'z-index:99999',
'opacity:0',
'transition:opacity 0.18s ease',
].join(';');
const card = document.createElement('div');
card.className = 'onepager-impressum-card';
card.setAttribute('role', 'dialog');
card.setAttribute('aria-modal', 'true');
card.setAttribute('aria-label', 'Impressum');
// Theme-aware: nutzt CSS-Variablen vom Host, mit neutralen Dark-Fallbacks.
// max-width via min(...) verhindert Frame-Sprengen auf schmalen Viewports.
card.style.cssText = [
'position:relative',
'box-sizing:border-box',
'max-width:min(420px, calc(100vw - 32px))',
'width:100%',
'background:var(--bg-card, #16161b)',
'color:var(--text, #e8e8ed)',
'padding:32px 24px 24px',
'border:1px solid var(--border, rgba(255,255,255,0.08))',
'border-radius:var(--radius, 8px)',
'box-shadow:0 20px 60px rgba(0,0,0,0.5)',
'font-family:var(--font-primary, Inter, -apple-system, BlinkMacSystemFont, sans-serif)',
'font-size:0.9rem',
'line-height:1.6',
'overflow-wrap:break-word',
'word-wrap:break-word',
'transform:translateY(8px)',
'transition:transform 0.18s ease',
].join(';');
const close = document.createElement('button');
close.type = 'button';
close.setAttribute('aria-label', 'Schließen');
close.innerHTML = '&times;';
close.style.cssText = [
'position:absolute',
'top:4px',
'right:8px',
'background:none',
'border:none',
'font:inherit',
'font-size:1.5rem',
'line-height:1',
'color:inherit',
'opacity:0.5',
'cursor:pointer',
'padding:6px 10px',
'border-radius:4px',
'transition:opacity 0.15s ease',
].join(';');
close.addEventListener('mouseenter', () => { close.style.opacity = '1'; });
close.addEventListener('mouseleave', () => { close.style.opacity = '0.5'; });
close.addEventListener('focus', () => { close.style.opacity = '1'; });
close.addEventListener('blur', () => { close.style.opacity = '0.5'; });
close.addEventListener('click', dismiss);
const content = document.createElement('div');
content.innerHTML = html;
content.style.cssText = 'margin-top:4px;';
content.querySelectorAll('a').forEach(a => {
a.style.color = 'var(--accent, #c9a84c)';
a.style.textDecoration = 'underline';
});
content.querySelectorAll('strong').forEach(s => {
s.style.display = 'block';
s.style.marginBottom = '10px';
s.style.fontWeight = '600';
});
card.appendChild(close);
card.appendChild(content);
backdrop.appendChild(card);
document.body.appendChild(backdrop);
// Scroll-Lock auf body, damit Hintergrund nicht mitscrollt.
prevBodyOverflow = document.body.style.overflow;
document.body.style.overflow = 'hidden';
requestAnimationFrame(() => {
backdrop.style.opacity = '1';
card.style.transform = 'translateY(0)';
});
backdrop.addEventListener('click', (e) => {
if (e.target === backdrop) dismiss();
});
document.addEventListener('keydown', onKey);
close.focus();
}
function dismiss() {
if (!backdrop) return;
document.removeEventListener('keydown', onKey);
const node = backdrop;
backdrop = null;
node.style.opacity = '0';
document.body.style.overflow = prevBodyOverflow;
setTimeout(() => {
if (node.parentNode) node.parentNode.removeChild(node);
if (lastFocus && typeof lastFocus.focus === 'function') {
lastFocus.focus();
}
}, 180);
}
function onKey(e) {
if (e.key === 'Escape') {
e.preventDefault();
dismiss();
}
}
}
})();