ux: pulse error border on repeated validation failures

This commit is contained in:
Ulas Kalayci
2026-04-30 09:13:06 +02:00
parent d720772939
commit e10516d32f
3 changed files with 38 additions and 0 deletions
+14
View File
@@ -608,6 +608,20 @@ function _validateField(input) {
group?.classList.toggle('form-field--error', !hasValue); group?.classList.toggle('form-field--error', !hasValue);
group?.classList.toggle('form-field--valid', hasValue); group?.classList.toggle('form-field--valid', hasValue);
input.setAttribute('aria-invalid', String(!hasValue)); input.setAttribute('aria-invalid', String(!hasValue));
if (!hasValue && group) {
const count = parseInt(group.dataset.errorCount ?? '0', 10) + 1;
group.dataset.errorCount = String(count);
if (count >= 2) {
group.classList.remove('form-field--error-repeat');
void group.offsetWidth;
group.classList.add('form-field--error-repeat');
group.addEventListener('animationend', () => group.classList.remove('form-field--error-repeat'), { once: true });
}
} else if (hasValue && group) {
group.dataset.errorCount = '0';
}
return hasValue; return hasValue;
} }
+16
View File
@@ -1388,6 +1388,22 @@ html.fab-anim-done .page-fab {
display: flex; display: flex;
} }
/* Pulse-Animation bei wiederholtem Fehler */
@keyframes field-error-pulse {
0%, 100% { box-shadow: none; }
40% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-danger) 35%, transparent); }
}
.form-field--error-repeat .input,
.form-field--error-repeat .form-input {
animation: field-error-pulse 500ms var(--ease-out);
}
@media (prefers-reduced-motion: reduce) {
.form-field--error-repeat .input,
.form-field--error-repeat .form-input { animation: none; }
}
/* -------------------------------------------------------- /* --------------------------------------------------------
* Toggle-Switch * Toggle-Switch
* Custom iOS-style toggle, ersetzt native Checkboxen. * Custom iOS-style toggle, ersetzt native Checkboxen.
+8
View File
@@ -39,12 +39,20 @@ const _origSetTimeout = setTimeout;
function makeField() { function makeField() {
const classes = new Set(); const classes = new Set();
const listeners = {};
const dataset = {};
return { return {
dataset,
offsetWidth: 0,
classList: { classList: {
toggle(cls, force) { force ? classes.add(cls) : classes.delete(cls); }, toggle(cls, force) { force ? classes.add(cls) : classes.delete(cls); },
add(cls) { classes.add(cls); },
remove(cls) { classes.delete(cls); },
contains(cls) { return classes.has(cls); }, contains(cls) { return classes.has(cls); },
}, },
addEventListener(event, fn) { listeners[event] = fn; },
_classes: classes, _classes: classes,
_listeners: listeners,
}; };
} }