Foundations

Forms and validation architecture.

Form wraps EditForm + Card, FormField wraps inputs with label/helper/error, and SilliumInputBase provides the shared parameter surface for all 19 input components.

Overview

The Form component combines Blazor's EditForm with a Card shell, providing title, description, validation summary, and a footer slot in one composable unit. It accepts either a Model or an EditContext.

FormField wraps any input with a label, required indicator, helper text, and error text. Every input component inherits SilliumInputBase, which defines the shared parameter contract: value binding, labels, icons, validation, and update behavior.

Form / FormField hierarchy

The recommended pattern nests inputs inside FormField inside Form. Form owns the EditForm and Card chrome; FormField owns label, helper, and error display; the input component owns its own value binding and rendering.

Form > FormField > Input pattern

A registration form using the three-layer hierarchy.

Show code
<Form Model="@Model" Title="Registration" Description="Create your account.">
    <ChildContent>
        <FormField Label="Full name" Required="true">
            <TextInput @bind-Value="Model.FullName"
                       Placeholder="Enter your full name"
                       HelperText="As it appears on your ID" />
        </FormField>

        <FormField Label="Email" Required="true">
            <EmailInput @bind-Value="Model.Email"
                        Placeholder="you@example.com" />
        </FormField>
    </ChildContent>
    <FooterContent>
        <Button Type="submit" Text="Register" />
    </FooterContent>
</Form>

Input states

Every input supports five visual states. The focused state is applied by the browser when the user clicks or tabs into the field and cannot be forced programmatically in these demos.

Normal

Default idle state, ready for input.

Focused

Click or tab into this field to see the focus ring. Cannot be forced programmatically.

Error

Please enter a valid email address.

Disabled

Disabled inputs cannot be edited or focused.

Read-only

Read-only inputs can be focused and copied, but not edited.

All input types

The library ships 19 input components, all inheriting SilliumInputBase. They are grouped into five categories.

Category Component Purpose
Text TextInput Single-line plain text entry
TextArea Multi-line text entry with optional auto-resize
PasswordInput Masked text entry with show/hide toggle
EmailInput Email entry with built-in format validation
Selection SelectInput Single-value dropdown selection
MultiSelect Multi-value selection with chip display
Autocomplete Searchable dropdown with async data loading
RadioGroup Exclusive choice from a small set of options
Checkbox Boolean toggle or tri-state selection
ToggleSwitch On/off toggle with immediate effect
Numeric NumberInput Numeric entry with increment/decrement controls
Slider Numeric value selection via drag handle
Date / Time DateInput Date-only picker with calendar popup
DateTimeInput Combined date and time picker
TimeInput Time-only picker
DateRangePicker Start/end date range selection
TimeSpanPicker Duration picker (hours, minutes, seconds)
Special FileUpload File selection with drag-and-drop support
IconPicker Material Symbols icon browser and selector

Shared input features (SilliumInputBase)

Every input component inherits these parameters from SilliumInputBase. They provide a consistent API surface across all 19 input types. See the Component Base foundations page for the full SilliumComponentBase contract.

Parameter Type Default Description
Value TValue? The current value of the input, two-way bound via @bind-Value.
ValueChanged EventCallback<TValue?> Callback invoked when the value changes. Used by @bind-Value.
Label string? null Text label rendered above the input field.
Placeholder string? null Placeholder text shown when value is empty.
HelperText string? null Assistive text shown below the input (hidden when error is active).
ErrorText string? null Explicit error message. Takes priority over EditContext validation messages.
Required bool false Shows a required indicator (*) next to the label.
ReadOnly bool false Input is focusable and copyable but not editable.
FullWidth bool true When true, the input stretches to fill its container width.
Clearable bool true Shows a clear button when the input has a value.
UpdateMode InputUpdateMode Instant Controls when ValueChanged fires (Instant, Standard, Enter, FocusLost, Debounced).
UpdateDelay int 400 Debounce delay in milliseconds when UpdateMode is Debounced. Default: 400ms.
StartIcon string? null Material Symbol icon name rendered at the start (left) of the input.
EndIcon string? null Material Symbol icon name rendered at the end (right) of the input.

ValidationHelper

ValidationHelper is an internal utility shared by all input components. It encapsulates error display logic: resolving which error text to show, computing ARIA attributes, and determining which description ID to reference.

Member Purpose
AriaInvalid Returns "true" when the input has an error, null otherwise. Bound to aria-invalid.
DescribedBy Returns the error element ID when in error state, otherwise the helper text ID. Bound to aria-describedby.
ResolvedErrorText Explicit ErrorText takes priority; falls back to the first EditContext validation message.
HasError True when ResolvedErrorText is not empty. Used for conditional styling.

Error resolution chain

How ValidationHelper decides which error to display.

Show code
// Error resolution chain inside ValidationHelper:
//
// 1. Explicit ErrorText parameter (highest priority)
//    <TextInput ErrorText="Email is required" />
//
// 2. EditContext validation messages (automatic)
//    Picked up from DataAnnotations or FluentValidation
//    when the input has a ValueExpression bound to a model property.
//
// ResolvedErrorText =
//     !string.IsNullOrWhiteSpace(ErrorText)
//         ? ErrorText
//         : ValidationErrorText;   // from EditContext
//
// AriaInvalid  = HasError ? "true" : null
// DescribedBy  = HasError ? errorId : helperId

InputUpdateMode & debounce

InputUpdateMode controls when an input commits its value via ValueChanged. The Debounced mode uses DebounceHelper internally, which cancels and restarts a timer on every keystroke. The default delay is 400ms.

Mode Behavior
Instant Fires ValueChanged on every keystroke (oninput). Default mode.
Standard Fires ValueChanged on the browser's onchange event (typically on blur or Enter).
Enter Fires ValueChanged only when the user presses Enter.
FocusLost Fires ValueChanged when the input loses focus (onblur).
Debounced Fires ValueChanged after a configurable delay (UpdateDelay, default 400ms) since the last keystroke.

Update mode examples

Configure when the input commits its value.

Show code
// Default: updates on every keystroke
<TextInput @bind-Value="Search"
           UpdateMode="InputUpdateMode.Instant" />

// Debounced: waits 400ms (default) after last keystroke
<TextInput @bind-Value="Search"
           UpdateMode="InputUpdateMode.Debounced" />

// Custom delay: 800ms debounce
<TextInput @bind-Value="Search"
           UpdateMode="InputUpdateMode.Debounced"
           UpdateDelay="800" />

// Only commit on Enter key
<TextInput @bind-Value="Search"
           UpdateMode="InputUpdateMode.Enter" />

// Only commit on focus lost (blur)
<TextInput @bind-Value="Search"
           UpdateMode="InputUpdateMode.FocusLost" />

File reference

Key source files in the Sillium.Core.Blazor library related to forms and validation.

File Responsibility
Components/Forms/Form.razor Wraps EditForm + Card with title, description, validation summary, and footer slot.
Components/Forms/FormField.razor Wraps any input with label, required indicator, helper text, and error text.
SilliumInputBase.cs Abstract base class defining shared parameters for all 19 input components.
Utilities/ValidationHelper.cs Internal utility for error resolution, ARIA attributes, and describedby logic.
Utilities/DebounceHelper.cs Cancellable debounce timer using CancellationTokenSource + Task.Delay.
An unhandled error has occurred. Reload Dismiss