Code

js/forms.js — live validation

In the code column — js/forms.js, and in it attachLiveValidation, which implements the progression. Read the blur and input listeners side by side:

el.addEventListener('blur', () => {           // (2) leave a field → validate it
  touched.add(el.name);
  validateField(el.name);
});

el.addEventListener('input', () => {          // (3) typing → validate ONLY if already touched
  if (touched.has(el.name)) validateField(el.name);
});

The whole trick is the touched Set. A field becomes "touched" only when you leave it (or submit the form). And the input listener validates only touched fields. So:

  • Until you leave a field — silence (no nagging while you type it the first time).
  • Leave it with an error → the field is "touched" → from now every keystroke validates, and the error clears the moment you fix it.

validateAll() (the returned function) is called on submit: it validates everything and marks all fields "touched", so from then on feedback is live.

Settings that work. settings.js ties this to state: fillForm fills from getSettings(), and on submit saveSettings(...) persists to localStorage AND applyTheme(...) changes the theme immediately. Settings aren't decoration; they change the app and persist. (settings-store.js holds DEFAULT_SETTINGS; theme.js holds applyTheme.)

Verify. In the settings form:

1. Leave "Display name" empty → the error appears (blur).
2. Start typing → the error clears immediately (touched → live).
3. Save → the theme changes, and the values survive a reload.

Gotcha (CSS — a real bug we hit). We styled inputs like this:

input[type="text"] { ... }        /* ← only type="text" */

input[type="text"] is an exact-match selector — it does not style type="email", type="url", type="password". Half the fields (email, URL, password) looked different. The fix is to style by "everything except the toggles":

input:not([type="checkbox"]):not([type="radio"]) { ... }

That catches every text-like type at once, and leaves checkbox/radio (which have their own look) alone.