I experimented the Aida validation approach with its AddressBook tutorial, and modified it as follow:

DemoAddressApp>>viewEdit
   | e field |
   e := WebElement new.
   e addTextH1: 'Add/Edit the address'.
   self inError ifTrue: [e cell add: self errorReport. e newRow].
[...]
   e cell
   addText: 'Email:';
   addBreak.
   field := e cell addInputFieldAspect: #email for: self observee.
   field
      onChangePost;
      validIfTrue: [ :value | value includes: $@];
      errorText: 'Cela ne ressemble pas à un email'.
   e newCell add: field errorElement.
[...]

Then, the default action of the editView need to be modified accordingly:

ADemoAddressApp>>actionEdit
   self context form isValid ifFalse:
      [self showError: self context form collectErrorTexts.
      ^ self redirectToView: #edit].
   self observee changeToPreferredUrl.
   self redirectToView: #main

That's it!

Aida validation

It is definitely not a Saturn V class rocket approach, but more something like a manageable Ariane V class rocket.