refactor: Replace Google Fonts with system fonts for improved performance and consistency

This commit is contained in:
2026-05-27 13:31:30 +02:00
parent 296c25192d
commit f68906f3cb
5 changed files with 157 additions and 48 deletions
+2 -1
View File
@@ -82,7 +82,8 @@ def set_security_headers(response):
response.headers["X-XSS-Protection"] = "1; mode=block"
response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
response.headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com https://fonts.googleapis.com; img-src 'self' data:; connect-src 'self'; frame-src 'self' https://www.youtube.com https://youtube.com https://www.youtube-nocookie.com https://youtube-nocookie.com; media-src 'self' https:;"
# Fonts no longer loaded from Google Fonts; remove font hosts
response.headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' data:; connect-src 'self'; frame-src 'self' https://www.youtube.com https://youtube.com https://www.youtube-nocookie.com https://youtube-nocookie.com; media-src 'self' https:;"
return response
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+2 -2
View File
@@ -232,7 +232,7 @@ td,
button,
.action-link,
.inline-admin-form input {
font-family: "JetBrains Mono", "Fira Code", "Consolas", monospace;
font-family: Arial, Helvetica, sans-serif;
}
.head h1 {
@@ -385,7 +385,7 @@ button,
background: #0b1114;
color: var(--term-text);
padding: 0.36rem 0.5rem;
font-family: "JetBrains Mono", "Fira Code", "Consolas", monospace;
font-family: Arial, Helvetica, sans-serif;
}
.log-output {
margin: 0;
+150 -41
View File
@@ -24,9 +24,7 @@
<meta name="twitter:description" content="{{ meta_description|default('Invario — Lehrmittel-, Inventar- und Ausleihverwaltung für Schulen. Sicher, einfach, nachvollziehbar.') }}">
<meta name="twitter:image" content="{{ url_for('static', filename='images/logo-1.jpeg') }}">
{% block head %}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Merriweather:wght@700;900&family=Public+Sans:wght@400;500;600;700&display=swap" rel="stylesheet">
<!-- Using system Arial font instead of Google Fonts -->
<style>
:root {
--bg-main: #f3f1ea;
@@ -47,7 +45,7 @@
/* Verhinderung von iOS Auto-Zoom bei Inputs */
input, textarea, select, button {
font-family: inherit;
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
}
@@ -59,7 +57,7 @@
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
min-height: 100%;
font-family: "Public Sans", system-ui, sans-serif;
font-family: Arial, Helvetica, sans-serif;
color: var(--text-main);
background: radial-gradient(circle at 88% -8%, #dbe4d9 0%, transparent 34%),
radial-gradient(circle at -5% 58%, #ece4d2 0%, transparent 40%),
@@ -72,33 +70,16 @@
flex-direction: column;
}
h1 {
h1, h2, h3, h4 {
margin: 0;
font-family: "Merriweather", Georgia, serif;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.16;
font-size: clamp(1.85rem, 4vw, 3.5rem);
}
h2 {
margin: 0;
font-family: "Merriweather", Georgia, serif;
line-height: 1.2;
font-size: clamp(1.5rem, 3vw, 2.5rem);
}
h3 {
margin: 0;
font-family: "Merriweather", Georgia, serif;
line-height: 1.24;
font-size: clamp(1.2rem, 2vw, 1.8rem);
}
h4 {
margin: 0;
font-family: "Merriweather", Georgia, serif;
line-height: 1.24;
font-size: clamp(1rem, 1.8vw, 1.5rem);
}
h1 { font-size: clamp(1.85rem, 4vw, 3.5rem); }
h2 { font-size: clamp(1.5rem, 3vw, 2.5rem); }
h3 { font-size: clamp(1.2rem, 2vw, 1.8rem); }
h4 { font-size: clamp(1rem, 1.8vw, 1.5rem); }
p {
margin: 0;
@@ -702,10 +683,45 @@
</style>
</footer>
<div class="cookie-banner hidden" id="cookieBanner" role="dialog" aria-live="polite" aria-label="Cookie Hinweis">
<p>Wir verwenden Cookies, um die Website sicher und benutzerfreundlich zu betreiben. Mit "Cookies akzeptieren" wird die Nutzung bestätigt. Details in <a href="{{ url_for('datenschutz') }}">Datenschutz</a>.</p>
<p>Wir verwenden Cookies, um die Website sicher und benutzerfreundlich zu betreiben. Du kannst auswählen, welche Cookies du erlaubst. Details in <a href="{{ url_for('datenschutz') }}">Datenschutz</a>.</p>
<div class="cookie-actions">
<button type="button" class="cookie-btn" id="cookieAcceptBtn">Cookies akzeptieren</button>
<button type="button" class="cookie-btn secondary" id="cookieDeclineBtn">Ablehnen und zu Ecosia</button>
<button type="button" class="cookie-btn" id="cookieAcceptBtn">Alle Cookies akzeptieren</button>
<button type="button" class="cookie-btn secondary" id="cookiePrefsBtn">Einstellungen</button>
</div>
</div>
<div id="cookiePrefsModal" class="cookie-banner hidden" role="dialog" aria-live="polite" aria-label="Cookie Einstellungen" style="max-width:640px; left:50%; transform:translateX(-50%); bottom:2rem;">
<div style="flex-direction:column; display:flex; gap:0.6rem;">
<h3 style="margin:0 0 0.3rem 0;">Cookie-Einstellungen</h3>
<p style="margin:0 0 0.6rem 0; color:#315055;">Wähle, welche Cookies du erlauben möchtest. Technisch notwendige Cookies sind für die Anmeldung und Sicherheit erforderlich.</p>
<div style="display:flex; flex-direction:column; gap:0.6rem;">
<label style="display:flex; justify-content:space-between; align-items:center; gap:1rem;">
<div>
<strong>Notwendige Cookies</strong>
<div style="font-size:0.9rem; color:#52686a;">Erforderlich für Login, Sitzungsverwaltung und Sicherheit (nicht deaktivierbar).</div>
</div>
<input type="checkbox" id="cookie_necessary" disabled checked />
</label>
<label style="display:flex; justify-content:space-between; align-items:center; gap:1rem;">
<div>
<strong>Analytik</strong>
<div style="font-size:0.9rem; color:#52686a;">Anonyme Nutzungsauswertung zur Verbesserung der Website (optional).</div>
</div>
<input type="checkbox" id="cookie_analytics" />
</label>
<label style="display:flex; justify-content:space-between; align-items:center; gap:1rem;">
<div>
<strong>Marketing</strong>
<div style="font-size:0.9rem; color:#52686a;">Personalisierte Inhalte und externe Dienste (optional).</div>
</div>
<input type="checkbox" id="cookie_marketing" />
</label>
</div>
<div style="display:flex; gap:0.6rem; margin-top:0.6rem;">
<button type="button" class="cookie-btn" id="cookiePrefsAcceptAll">Alle akzeptieren</button>
<button type="button" class="cookie-btn secondary" id="cookiePrefsSave">Speichern</button>
<button type="button" class="cookie-btn secondary" id="cookiePrefsOnlyNecessary">Nur notwendige Cookies</button>
</div>
</div>
</div>
<!-- Mobile compatibility scripts -->
@@ -769,27 +785,120 @@
var consentKey = "cookie_consent_v1";
function hasConsent() {
return window.localStorage.getItem(consentKey) === "accepted" || document.cookie.indexOf("cookie_consent=accepted") !== -1;
function getConsent() {
try {
var raw = window.localStorage.getItem(consentKey);
if (!raw) return null;
return JSON.parse(raw);
} catch (e) {
return null;
}
}
function setConsent() {
window.localStorage.setItem(consentKey, "accepted");
document.cookie = "cookie_consent=accepted; Max-Age=" + (60 * 60 * 24 * 365) + "; Path=/; SameSite=Strict";
function saveConsent(obj) {
try {
window.localStorage.setItem(consentKey, JSON.stringify(obj));
var cookieVal = obj.status || (obj.accepted ? 'accepted' : 'custom');
document.cookie = "cookie_consent=" + encodeURIComponent(JSON.stringify(obj)) + "; Max-Age=" + (60 * 60 * 24 * 365) + "; Path=/; SameSite=Strict";
// Apply consent immediately: enable any scripts gated by consent
applyConsent();
} catch (e) {
// ignore
}
}
if (!hasConsent()) {
banner.classList.remove("hidden");
function showBannerIfNeeded() {
var c = getConsent();
if (!c) {
banner.classList.remove("hidden");
}
}
showBannerIfNeeded();
acceptBtn.addEventListener("click", function () {
setConsent();
var obj = { status: 'accepted', necessary: true, analytics: true, marketing: true, saved_at: Date.now() };
saveConsent(obj);
banner.classList.add("hidden");
});
declineBtn.addEventListener("click", function () {
window.location.href = "https://www.ecosia.org";
// Preferences modal handling
var prefsBtn = document.getElementById('cookiePrefsBtn');
var prefsModal = document.getElementById('cookiePrefsModal');
var analyticsCheckbox = document.getElementById('cookie_analytics');
var marketingCheckbox = document.getElementById('cookie_marketing');
var prefsSave = document.getElementById('cookiePrefsSave');
var prefsAcceptAll = document.getElementById('cookiePrefsAcceptAll');
var prefsOnlyNecessary = document.getElementById('cookiePrefsOnlyNecessary');
function openPrefs() {
// initialize from existing consent if present
var c = getConsent() || {};
analyticsCheckbox.checked = !!c.analytics;
marketingCheckbox.checked = !!c.marketing;
prefsModal.classList.remove('hidden');
}
function closePrefs() {
prefsModal.classList.add('hidden');
banner.classList.add('hidden');
}
if (prefsBtn) prefsBtn.addEventListener('click', openPrefs);
prefsSave.addEventListener('click', function () {
var obj = { status: 'custom', necessary: true, analytics: !!analyticsCheckbox.checked, marketing: !!marketingCheckbox.checked, saved_at: Date.now() };
saveConsent(obj);
closePrefs();
});
prefsAcceptAll.addEventListener('click', function () {
var obj = { status: 'accepted', necessary: true, analytics: true, marketing: true, saved_at: Date.now() };
saveConsent(obj);
closePrefs();
});
prefsOnlyNecessary.addEventListener('click', function () {
var obj = { status: 'necessary', necessary: true, analytics: false, marketing: false, saved_at: Date.now() };
saveConsent(obj);
closePrefs();
});
// --- Consent-based script loader -------------------------------------------------
// Usage: include scripts that require consent like this in templates:
// <script type="text/plain" data-consent="analytics" src="https://www.googletagmanager.com/gtag/js?id=XXX"></script>
// or for inline scripts:
// <script type="text/plain" data-consent="marketing"> /* fbq or other init code */ </script>
function applyConsent() {
try {
var c = getConsent() || {};
var analyticsAllowed = !!c.analytics;
var marketingAllowed = !!c.marketing;
// Find all scripts that were intentionally disabled by using type="text/plain"
document.querySelectorAll('script[type="text/plain"]').forEach(function (node) {
var required = node.getAttribute('data-consent') || 'analytics';
if ((required === 'analytics' && analyticsAllowed) || (required === 'marketing' && marketingAllowed) || required === 'necessary') {
var s = document.createElement('script');
// copy src or inline content
if (node.src) s.src = node.src;
else s.text = node.textContent || node.innerText || '';
// copy attributes that are safe/needed
var type = node.getAttribute('data-script-type');
if (type) s.type = type;
// append to same parent
node.parentNode.insertBefore(s, node);
// mark original as applied to avoid double-inserting
node.setAttribute('data-consent-applied', '1');
}
});
} catch (e) {
console.error('applyConsent error', e);
}
}
// Run once on load in case user already saved consent
applyConsent();
})();
</script>
</body>
+1 -2
View File
@@ -717,7 +717,6 @@
</script>
<header class="hero" id="top" aria-label="Seitenstart">
<div>
<span class="eyebrow">Lehrmittelverwaltungssoftware für Schulen</span>
<h1>Die Software, die iPads genauso effizient verwaltet wie das Schulbuch.</h1>
<p>
Radikale Einfachheit für Ihren Schulalltag. Starten Sie in nur 5 Minuten mit der modernen Inventarsoftware für alle Schularten. Hybrid, intuitiv, sofort einsatzbereit.
@@ -796,7 +795,7 @@
</article>
<article class="card">
<h3>Zero Maintenance</h3>
<p>Reine WebApp: automatische Updates, keine lokalen Server, nahezu kein Betriebsaufwand für Ihre IT.</p>
<p>Reine WebApp: automatische Updates, keine lokalen Server, nahezu kein Betriebsaufwand für Ihre IT. Deswegen benötigen sie keine App oder irgend etwas anderes als einen Internetzugang!</p>
</article>
<article class="card">
<h3>Datenschutz &amp; DSGVO</h3>
+2 -2
View File
@@ -72,7 +72,7 @@
font-size: 1.8rem;
color: var(--text-main);
margin-bottom: 0.5rem;
font-family: "Merriweather", Georgia, serif;
font-family: Arial, Helvetica, sans-serif;
}
.video-meta {
@@ -188,7 +188,7 @@
margin-bottom: 0.5rem;
font-weight: 600;
line-height: 1.4;
font-family: "Merriweather", Georgia, serif;
font-family: Arial, Helvetica, sans-serif;
}
.tutorial-card-meta {