Files
tsv08kulmbach-website/public/assets/js/form.js
fs 8437f00e06 Feinschliff: Formulare neu, Glas global dunkler, Hero-Fade sauber
Formulare (form-field.php, 3 Form-Wrapper, components.css, form.js):
- Floating Labels (Label im Feld, wandert bei Fokus/Inhalt hoch; select/date
  dauerhaft oben) via :has() + placeholder=" "-Trick. Echtes <label> bleibt
  im DOM (kein Placeholder-als-Label).
- Globale "Pflichtfelder mit *"-Hinweiszeile entfernt; Stern bleibt am Feld,
  pro Feld als visually-hidden "(Pflichtfeld)" für Screenreader erklärt.
- Markenroter Fokus-Ring statt Weiß.
- Felder als sanfter Füll-Look: weichere Ränder (eigene --clr-field-*-Tokens),
  Erkennbarkeit über Fläche + Rand (WCAG 1.4.11 erfüllt).
- form.js: Submit-Button-Text wird je Formular korrekt wiederhergestellt.

Glas-Look global (tokens.css, components.css):
- Frosted Glass zentral: --glass-blur + --glass-shadow (Glow + Top-Sheen),
  eine Regel statt 28× wiederholt; Leaf-Karten mit Blur, Container nur Glow
  (kein Doppel-Frost). @supports-Fallback.
- Dunkleres Glas (--clr-glass-bg auf Schwarz-Tint), dezenter Card-Rand.

Hero-Fade (components.css):
- Unterer Auslauf nicht mehr mit grauem --clr-bg zugedeckt, sondern .hero__media
  per mask-image ausgeblendet → echter dunkler, texturierter Hintergrund scheint
  durch (schwärzer, durchsichtig, kein graues Band). Abdunklung auf reines
  Schwarz. Gilt für alle drei Hero-Varianten.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HGzc6GhWhmLJt1jC2q1SRZ
2026-06-20 22:18:34 +02:00

97 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Formulare: Progressive Enhancement — ohne JS normales POST + Redirect.
Mit JS: Inline-Feldfehler (aria-describedby, Fokus aufs erste ungültige Feld),
fetch-Submit, Status in aria-live-Region, Button-Sperre während des Sendens. */
(function () {
'use strict';
var form = document.querySelector('.form');
if (!form || !window.fetch) {
return;
}
var status = form.querySelector('[data-form-status]');
var button = form.querySelector('button[type="submit"]');
var MESSAGES = {
success: 'Danke für deine Anfrage! Wir melden uns so schnell wie möglich bei dir.',
validation: 'Bitte prüfe deine Eingaben Pflichtfelder fehlen oder die E-Mail-Adresse ist ungültig.',
ratelimit: 'Zu viele Anfragen in kurzer Zeit. Bitte versuche es in ein paar Minuten erneut.',
mail: 'Deine Nachricht konnte gerade nicht versendet werden. Bitte versuche es später erneut.'
};
function errorEl(field) {
return field.id ? document.getElementById(field.id + '-error') : null;
}
function showFieldError(field) {
var msg = errorEl(field);
field.setAttribute('aria-invalid', 'true');
if (msg) {
msg.textContent = field.validationMessage;
msg.hidden = false;
}
}
function clearFieldError(field) {
var msg = errorEl(field);
field.removeAttribute('aria-invalid');
if (msg) {
msg.textContent = '';
msg.hidden = true;
}
}
Array.prototype.forEach.call(form.elements, function (field) {
field.addEventListener('input', function () { clearFieldError(field); });
field.addEventListener('change', function () { clearFieldError(field); });
});
function show(type, text) {
var p = document.createElement('p');
p.className = type === 'success' ? 'form__success' : 'form__error';
p.textContent = text;
status.replaceChildren(p);
status.focus({ preventScroll: true });
status.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
}
form.addEventListener('submit', function (event) {
if (!form.checkValidity()) {
event.preventDefault();
var invalid = form.querySelectorAll(':invalid');
Array.prototype.forEach.call(invalid, showFieldError);
if (invalid.length) {
invalid[0].focus();
}
return;
}
event.preventDefault();
var buttonLabel = button.textContent;
button.disabled = true;
button.textContent = 'Wird gesendet …';
fetch(form.action, {
method: 'POST',
body: new FormData(form),
headers: { Accept: 'application/json' }
})
.then(function (response) { return response.json(); })
.then(function (data) {
if (data.ok) {
show('success', MESSAGES.success);
form.reset();
} else {
show('error', MESSAGES[data.error] || MESSAGES.mail);
}
})
.catch(function () {
show('error', MESSAGES.mail);
})
.finally(function () {
button.disabled = false;
button.textContent = buttonLabel;
});
});
})();