Validation
Provide meaningful feedback to users with VelinStyle's form validation styles. Use the native
:valid / :invalid pseudo-classes triggered by the
.velin-form--validated parent class.
How it works
Validation styles are scoped under .velin-form--validated. Add this class to the
<form> element (typically on submit via JavaScript) to reveal valid/invalid states
on all controls. Feedback messages use .velin-field__feedback--valid and
.velin-field__feedback--invalid.
Optional: :user-invalid and ARIA
The core bundle covers the patterns above. For stricter “after interaction” styling, the repository ships
src/components/form-validation.css: it targets :user-invalid and
[aria-invalid="true"], with helpers such as .velin-field-error and
.velin-field-hint. Import that layer in a custom build or copy the rules you need.
aria-invalid and aria-describedby from your script when server-side or async validation fails, even if the field is not yet :user-invalid.Live example
<form class="velin-form--validated" novalidate>
<div class="velin-field">
<label class="velin-label" for="name">Name</label>
<input type="text" class="velin-input" id="name" value="Jane Doe" required>
<div class="velin-field__feedback--valid">Looks good!</div>
</div>
<div class="velin-field">
<label class="velin-label" for="email">Email</label>
<input type="email" class="velin-input" id="email" required>
<div class="velin-field__feedback--invalid">Please enter a valid email address.</div>
</div>
<button type="submit" class="btn btn-primary">Submit form</button>
</form>
JavaScript trigger
Add .velin-form--validated on submit to activate styles only after user interaction:
document.querySelectorAll('form[novalidate]').forEach(form => {
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('velin-form--validated');
});
});
Server-side validation
For server-rendered errors, apply .is-valid or .is-invalid directly to
the control — no parent .velin-form--validated needed.
<input type="text" class="velin-input is-valid" value="velinuser">
<div class="velin-field__feedback--valid">Username is available.</div>
<input type="password" class="velin-input is-invalid">
<div class="velin-field__feedback--invalid">Password must be at least 8 characters.</div>