Skip to content
Sveltier
Mode
Forms

TextField

A single line of text with a label, a hint and validation that waits until the user is done, then clears as soon as the value is fixed.

Usage

Bind the value and give it a label. The input receives every other attribute you pass, such as name, autocomplete, placeholder and events, while class and style apply to the whole field.

Receipts go to this address

At least 8 characters, with a digit

Type something, then leave the field.

SignUp.svelte
<script>
  import { TextField } from 'sveltier';
  let email = $state('');
</script>

<TextField
  label="Email"
  type="email"
  name="email"
  autocomplete="email"
  hint="Receipts go to this address"
  required
  bind:value={email}
/>

Validation

A field's message comes from three places, checked in this order:

  1. The HTML constraints you already know: required, minlength, maxlength, pattern, and type="email" or "url". Their messages come from the locale, so they read the same in every browser and translate with your app.
  2. rules: functions that return true or the message to show. They run in order and stop at the first message. Like the constraints, they skip an empty field that is not required.
  3. error: a message from outside the field, usually the answer of your server. It shows at once, and it never stops the form from being submitted again.

A message appears when the user leaves a field they have changed, not while they tab through the form. Once it is shown, it follows every keystroke, so fixing the value clears it at once. Set validateOn to "input" to check from the first change, or to "submit" to wait for the form. Inside a plain <form>, the browser also refuses to submit an invalid field, with the same words.

Lowercase letters, digits and _. Try “ada”.

+page.svelte
<script>
  import { TextField } from 'sveltier';

  // The result of a SvelteKit form action, with the server's own errors.
  let { form } = $props();
  let username = $state('');

  const rules = [
    (value) => /^[a-z0-9_]+$/.test(value) || 'Use lowercase letters, digits and _',
    (value) => !value.startsWith('_') || 'Start with a letter or a digit'
  ];
</script>

<TextField
  label="Username"
  name="username"
  required
  minlength={3}
  maxlength={20}
  {rules}
  error={form?.errors?.username}
  bind:value={username}
/>

Variants

Fields take five of the shared variants: outlined (the default) draws a border, tonal a wash with a rule under it, elevated a raised surface for a field over an image, text only the rule, and plain nothing, for a field inside another component. color is the accent of the focused field; an invalid field turns to the error colour.

Size and density

A field is as tall as a Button of the same size and density, so they line up in a row. The label keeps the theme's label style at every size.

Icons and actions

prepend and append put content inside the box: an icon, a prefix, or a button. A press anywhere else in the box focuses the input.

https://
Toolbar.svelte
<script>
  import { Button, Icon, TextField } from 'sveltier';
  let visible = $state(false);
</script>

<TextField label="Search" type="search" variant="elevated">
  {#snippet prepend()}<Icon path={SEARCH} />{/snippet}
</TextField>

<TextField label="Password" type={visible ? 'text' : 'password'} autocomplete="current-password">
  {#snippet append()}
    <Button variant="text" size="sm" onclick={() => (visible = !visible)}>
      {visible ? 'Hide' : 'Show'}
    </Button>
  {/snippet}
</TextField>

Accessibility

  • The label is a <label> for the input. Without a visible label, name the input with aria-label.
  • The hint, or the error in its place, describes the input, and an invalid field is marked aria-invalid. An aria-describedby of your own is kept alongside.
  • An error that appears after the user has moved on is announced politely.
  • required adds an asterisk that screen readers skip: the input already says it is required.
  • Text is at least 16 px on touch screens, where smaller text makes iOS zoom into the page.

API

NameTypeDefaultDescription
variant'outlined' | 'tonal' | 'elevated' | 'text' | 'plain''outlined'How the box is drawn.
colorColor'primary'The accent of the focused field. An invalid field uses error.
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Height and text size, matching a Button of the same size.
density'compact' | 'default' | 'comfortable''default'Multiplies the height and padding, never the text.
roundedboolean | 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'full'trueCorner rounding. true keeps the theme's radius.
disabledbooleanfalseMutes the field and disables its control.
labelstring–The visible label, linked to the control. Without it, name the control with aria-label.
hintstring–Help under the field. An error replaces it.
errorFieldError–An error from outside the field, such as the server's answer: a message, a list of which the first is shown, or true for an invalid state described elsewhere.
prependSnippet–Content at the start of the box, usually an icon.
appendSnippet–Content at the end of the box: an icon, a unit or a button.
valuestring–The text. Bindable.
type'text' | 'email' | 'password' | 'search' | 'tel' | 'url''text'The kind of text: it picks the keyboard and autofill, and email and url are checked.
rulesreadonly Rule<string>[]–Checks run in order after the HTML constraints. The first message is shown.
validateOn'blur' | 'input' | 'submit'–When the message shows. Defaults to the form's setting, else 'blur'.
requiredbooleanfalseThe field must not be empty. Adds an asterisk to the label.
readonlybooleanfalseThe text can be read, selected and copied but not changed.
stylestring | null–Inline style of the field's root element.
classClassValue | null–Classes added after the component's own: a string, an array or an object.
refHTMLInputElement | null–The root element, bindable with bind:ref.