ux: pulse error border on repeated validation failures
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user