Form Validation

The useValidate composable provides declarative, attribute-driven form validation. Chain rules with pipe syntax (required|email|min:3), choose when validation triggers, and customise every error message — all without writing JavaScript.

Required & Email
<form data-vd-validate novalidate>
  <div class="vd-form-group">
    <label for="name">Full Name</label>
    <input type="text" id="name" class="vd-input"
      data-vd-rules="required"
      data-vd-msg-required="Please enter your name">
  </div>

  <div class="vd-form-group">
    <label for="email">Email</label>
    <input type="email" id="email" class="vd-input"
      data-vd-rules="required|email">
  </div>

  <button type="submit" class="vd-btn vd-btn-primary">Submit</button>
</form>
Min / Max Length & Number Range
<input class="vd-input"
  data-vd-rules="required|min:3|max:20">

<input type="number" class="vd-input"
  data-vd-rules="required|number|minVal:18|maxVal:120">
Password Match
<input type="password" id="pass" class="vd-input"
  data-vd-rules="required|min:8">

<input type="password" class="vd-input"
  data-vd-rules="required|match:pass"
  data-vd-msg-match="Passwords do not match">
Validation Modes & Custom Messages
<!-- Form-level mode: blur -->
<form data-vd-validate data-vd-validate-mode="blur">

  <!-- URL with custom message -->
  <input class="vd-input"
    data-vd-rules="required|url"
    data-vd-msg-url="Enter a valid URL">

  <!-- Per-field mode override: input -->
  <input class="vd-input"
    data-vd-rules="required|pattern:^#[0-9a-fA-F]{6}$"
    data-vd-validate-mode="input"
    data-vd-msg-pattern="Must be a hex color">

</form>
Built-in Validation Rules
RuleSyntaxDescription
requiredrequiredField must not be empty
emailemailMust be a valid email address
urlurlMust be a valid URL
numbernumberMust be a numeric value
minmin:NMinimum string length of N characters
maxmax:NMaximum string length of N characters
minValminVal:NNumeric value must be ≥ N
maxValmaxVal:NNumeric value must be ≤ N
patternpattern:REGEXValue must match the given regular expression
matchmatch:IDValue must equal the value of the element with the given id
API Reference

Wiring

import { ref } from 'vue';
import { useValidate } from "@vanduo-oss/vd3";

const root = ref<HTMLElement | null>(null);
useValidate(root);   // wires [data-vd-validate] forms inside root; cleanup on unmount

CSS Classes

ClassDescription
.vd-validateOptional base class applied to forms for scoped styling
.is-validAdded to an input that passes all rules (green border)
.is-invalidAdded to an input that fails a rule (red border)
.vd-validate-errorAuto-generated error message element shown below the invalid input

Data Attributes

AttributeDescription
data-vd-validatePlace on a <form> to opt-in to validation
data-vd-rulesPipe-separated rule string on each input (e.g. "required|email|min:3")
data-vd-validate-modeWhen to validate: "blur" (default), "input", or "submit". Can be set per-form or per-field
data-vd-msg-*Custom error message per rule, e.g. data-vd-msg-required="Name is required"

Composable API

SymbolDescription
useValidate(root)Composable — wires every [data-vd-validate] form inside the root ref (blur/input/submit modes, error messages). Call once in setup().
(automatic cleanup)Field listeners and error nodes are removed on component unmount.

Events

EventDescription
validate:submit Fired on the form on submit. event.detail is { valid } — a boolean indicating whether every field passed validation.

Custom Rule Example

import { ref } from 'vue';
import { useValidate } from "@vanduo-oss/vd3";

const root = ref<HTMLElement | null>(null);
const { addRule } = useValidate(root);

addRule(
  'noSpaces',
  (value) => !/\s/.test(value),
  'Value must not contain spaces'
);

// Usage: data-vd-rules="required|noSpaces"
Accessibility
  • Invalid fields receive aria-invalid="true"; valid fields get aria-invalid="false"
  • Error messages are linked via aria-describedby so screen readers announce them
  • Error elements use role="alert" for immediate announcement on live changes
  • Native <form> semantics preserved — novalidate disables browser UI, not semantics
  • Focus moves to the first invalid field on submit for keyboard users