Veto
Components

Text box

A single-line input with a label, helper text and validation, generic over the value it binds to.

Basic

A label, a placeholder and helper text. The label is wired to the input, so clicking it focuses the field.

Shown until the field becomes invalid.

null
razor
<VetoTextBox TValue="string"
             Label="Project name"
             Placeholder="e.g. Veto"
             HelperText="Shown until the field becomes invalid."
             @bind-Value="_name" />

Binding on every keystroke

By default the value updates when the field loses focus, matching a plain two-way binding. Immediate updates as you type - what you want for a search box.

null
razor
<VetoTextBox TValue="string"
             Label="Search"
             Immediate="true"
             @bind-Value="_query" />

Any bindable type

The component is generic. Bind it to an int?, a DateOnly, a Guid or an enum and input is converted and rejected for you.

razor
<VetoTextBox TValue="int?" Label="Quantity" Type="number" @bind-Value="_quantity" />
<VetoTextBox TValue="DateOnly?" Label="Due" Type="date" @bind-Value="_due" />

Clear button, counter, prefix and suffix

Clearable adds a button that empties the field. MaxLength caps the input and ShowCounter shows how much is left. Prefix and Suffix put a unit or a scheme inside the field.

16 / 40

razor
<VetoTextBox TValue="string" Label="Title" Clearable="true"
             MaxLength="40" ShowCounter="true" @bind-Value="_title" />
<VetoTextBox TValue="decimal?" Label="Price" Prefix="$" Suffix="USD" @bind-Value="_price" />
<VetoTextBox TValue="string" Label="Website" Prefix="https://"
             Style="--veto-textbox-prefix-width: 7ch" @bind-Value="_site" />

Sizes and variants

Five sizes and seven visual treatments, all driven by modifier classes rather than inline styles.

razor
<VetoTextBox TValue="string" Label="Small" Size="VetoSize.Small" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="Filled" Variant="VetoVariant.Filled" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="Text" Variant="VetoVariant.Text" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="Soft" Variant="VetoVariant.Soft" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="Underlined" Variant="VetoVariant.Underlined" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="Ghost" Variant="VetoVariant.Ghost" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="Elevated" Variant="VetoVariant.Elevated" @bind-Value="_v" />

Corners

Radius rounds all four corners. RadiusTopLeft, RadiusTopRight, RadiusBottomRight and RadiusBottomLeft each override it for one corner.

razor
<VetoTextBox TValue="string" Label="Square" Radius="0" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="Pill" Radius="999px" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="One corner" Radius="0" RadiusTopLeft="1rem" @bind-Value="_v" />

Disabled and read-only

Disabled cannot be focused. Read-only can be focused and copied from, but not edited - the right choice for a value the user needs to read.

razor
<VetoTextBox TValue="string" Label="Disabled" Disabled="true" @bind-Value="_v" />
<VetoTextBox TValue="string" Label="Read-only" ReadOnly="true" @bind-Value="_v" />

Validation

Inside an EditForm the field picks up the model's validation attributes. An invalid field sets aria-invalid, replaces its helper text with the message, and announces it.

Between 3 and 20 characters.

razor
<EditForm Model="_form" OnValidSubmit="Save">
    <DataAnnotationsValidator />

    <VetoTextBox Label="Username"
                 Required="true"
                 HelperText="Between 3 and 20 characters."
                 @bind-Value="_form.Username" />

    <button type="submit">Save</button>
</EditForm>

@code {
    private sealed class FormModel
    {
        [Required(ErrorMessage = "Username is required.")]
        [StringLength(20, MinimumLength = 3,
            ErrorMessage = "Username must be 3-20 characters.")]
        public string? Username { get; set; }
    }
}

Parameters

Parameter Type Default Description
Value TValue? default The bound value. Use @bind-Value.
Label string? null Text for the field's label. No label element is rendered without it.
HelperText string? null Guidance under the field, replaced by the validation message while invalid.
Placeholder string? null Shown while the field is empty.
Type string "text" The input's type: text, email, password, search, tel, url, number, date.
Immediate bool false Update the bound value on every keystroke instead of on blur.
Format string? null Format used to convert the value to and from its string form, e.g. dd/MM/yyyy. Dates and times are parsed back with the same format.
Clearable bool false Shows a button that empties the field while it has a value and can be edited.
ClearLabel string "Clear" The clear button's accessible name.
MaxLength int? null The most characters the browser lets the user type.
ShowCounter bool false Shows a length / MaxLength counter under the field.
Prefix string? null Text inside the field before the value. Widen its slot with --veto-textbox-prefix-width.
Suffix string? null Text inside the field after the value. Widen its slot with --veto-textbox-suffix-width.
Size VetoSize Medium Small, Medium or Large.
Variant VetoVariant Outlined Outlined, Filled or Text.
Radius string? null Rounds all four corners, as any CSS length. Null keeps the theme's radius.
RadiusTopLeft / TopRight / BottomRight / BottomLeft string? null One corner's radius, overriding Radius for that corner.
Disabled bool false Cannot be focused or edited.
ReadOnly bool false Can be focused and copied from, but not edited.
Required bool false Renders the required marker and sets aria-required. Enforcement comes from the model.
OnValueChanged EventCallback<TValue?> — Raised after the bound value changes.
Something went wrong. Reload 🗙