Unity 6 ships no form validation for UI Toolkit. UGUI's `CharacterValidation.EmailAddress` only filters which characters can be typed — it does not validate the final string is a valid email. UITK's `regex` attribute on TextField evaluates per-character: try a full-email or full-IP pattern and *all input is blocked* because no single character satisfies the full-string pattern. This asset is the runtime validation layer that fills the gap.
Forms are plain C# — no GameObject pollution.
`Form.For(VisualElement)` and `Form.For(UIDocument)` give you a plain C# form object — no MonoBehaviour wrapping, no required components, no hidden inspector toggles. Register fields fluently from a controller's OnEnable and drop multiple forms into a single UI sub-tree without GameObject contortions.
Fluent schema with two equivalent styles.
Wrap any UI Toolkit control via its value-change surface — `TextField`, `Toggle`, `Slider`, `IntegerField`, `DropdownField`, `EnumField`, custom controls. Register fields atomically or as chained specs:
```
form.RegisterText("email")
.WithRule(Rules.Required())
.WithRule(Rules.Email());
form.RegisterText("username")
.WithRule(Rules.Required().MinLength(3).MaxLength(20))
.WithRule(Rules.Regex("[a-zA-Z][a-zA-Z0-9_]*"))
.WithMessage("3–20 characters, must start with a letter.");
```
Both styles produce the same result. `WithMessage` overrides per-rule error text.
14 built-in rules.
`Required`, `MinLength`, `MaxLength`, `Regex` (auto-anchored with `^...$` so full-string patterns actually work), `Email`, `Url`, `Range<T>`, `MinValue<T>`, `MaxValue<T>`, `OneOf<T>`, `NotEqual<T>`, `Custom`, `MatchesField`, `RequiredIf`. Extend with your own rules via the standard rule interfaces.
Async validation with stale-result protection.
`Rules.CustomAsync<T>(handler, debounceMs)` wraps any awaitable into a validation rule with built-in cancellation. Field-level debounce defaults to 300ms when any async rule is present. The framework defends against late-arriving results from two directions at once: cancellation tokens stop in-flight work on every new value change, and a generation counter discards stale-but-completed results even when a rule ignores its cancellation token. Bind a spinner to the form's `IsValidating` flag for "Checking availability..." UI with one line.
Cross-field rules with cycle detection.
`MatchesField("password")` and `RequiredIf("over18", v => v == true)` ship built-in. Declare custom cross-field rules with the standard rule interface. The framework runs cycle detection at form-build time — any circular dependency is logged with the full cycle path before validation runs, so the framework never hangs on a cyclic graph. Dependents re-validate when their source field changes without resetting touched/dirty state and without applying their own debounce.
Form-level state aggregation.
Each field cycles through clean, dirty, touched, validating, and valid/invalid states with predictable transitions. Roll the field state up into `form.IsValid`, `form.IsDirty`, and `form.IsValidating` for one-line button-enabling, "save in progress" indicators, and dirty-form prompts. Errors display after first blur by default so empty forms aren't pre-shouted at on first paint — configurable per field.
Three error display strategies.
Inline error labels (default) auto-create a sibling label per field, or honor a buyer-supplied label. A summary-panel strategy appends one label per error into a top-of-form container. The interface is open for custom strategies (banner, accessibility aria, toast). Swap globally via a single static assignment.
Async submit pipeline.
`form.OnSubmit(async ctx => ...)` registers your handler. `await form.SubmitAsync()` validates every field (sync rules + async rules immediately, bypassing debounce), then dispatches to the handler with a submit context that carries the values dictionary, a cancellation token, and a server-error sink. Return Success, Failure(serverErrors), or let it throw — exceptions become an Errored result and the form moves on. Failed submits trigger focus-first-invalid automatically.
Focus-first-invalid.
After a failed submit, the framework focuses the first invalid field and (when a ScrollView ancestor exists) scrolls the field into view. Works standalone via the built-in focus + scroll fallback; upgraded when the UI Toolkit: Focus & Navigation asset is installed and its Setup panel checkbox is enabled (adds gamepad-compatible navigation and indicator-aware focus).
Plays well with the rest of the UI Toolkit Components suite.
If you own other assets from the suite, Form Validation lights up additional polish through a per-pair opt-in. Each sibling asset ships a Setup panel under `Tools > KrookedLilly > <Asset> Setup` with a checkbox for every integration that touches it. Open the panel, flip the checkbox, and the integration assembly compiles in. Integrations ship disabled by default so nothing slips into your build until you ask for it.
**With UI Toolkit: Focus & Navigation:** failed submits jump focus to the first broken field and scroll it into view smoothly, with gamepad-compatible navigation. Without it, focus still moves to the first invalid field via a built-in fallback — the integration upgrades scroll behavior and adds controller support.
**With UI Toolkit: Screen Manager:** add a one-line guard to your screens to prompt the user before they navigate away from a form with unsaved changes. No more silent data loss when someone hits Back mid-edit.
**With UI Toolkit: Tween Engine:** error messages fade and slide into place instead of popping in, and the form briefly shakes when an invalid submit is rejected. Visible, tactile feedback without any animation code on your side.
**With UI Toolkit: Modal & Notifications:** validation errors can surface as toast notifications instead of inline labels — handy on compact layouts where there's no room beneath the field. The confirm-discard dialog used by the UI Toolkit: Screen Manager guard above also flows through here, so the prompt looks like the rest of your app's modals.
**With UI Toolkit: Responsive Layout:** the framework switches from inline error labels to toast notifications when your layout enters its mobile breakpoint, so small-screen users see errors that don't crowd the field. Requires UI Toolkit: Modal & Notifications enabled for the toast renderer.
**With UI Toolkit: Theme Switcher:** the default styling reads theme tokens (`--color-error`, `--color-success`, `--color-warning`, `--field-invalid-border`) so error/success colors follow your theme automatically. With the integration enabled, the asset logs the token names it expects on first load so theme authors know exactly which variables to define.
Three demo scenes included.
**Login** — email + password + remember-me. Required + Email + MinLength. The smallest demo; mirrors the quickstart in the documentation.
**Registration** — username + email + password + confirm-password + terms checkbox. Cross-field `MatchesField("password")`, async username availability rule (mocks 800ms latency with a visible "Checking availability..." spinner), strong-password regex with character-class requirements. The killer-feature demo.
**Settings** — `DropdownField` + `Slider` + `IntegerField` + `Toggle`. Proves the framework isn't text-only and demonstrates code-set choices for dropdowns. All three ship with controller scripts, UXML, USS, and configured panel settings.
Full C# source, no DLLs.
XML documentation on every public API. Form is plain C# — no MonoBehaviour wrapping, no Unity-Editor coupling. Zero external dependencies.