Text fields
Text fields let people enter and edit text. They come in two styles, filled and outlined. Both support a label, supporting text and validation.
Usage
Text fields work like <input> elements with a label.
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField label="Label" value="Value" />
<TextField label="Label" value="Value" variant="filled" />
Input type
The type attribute changes how the field works, for example which keyboard shows or which
validation applies by default.
type="text"(default)type="email"type="password"type="url"type="number"type="search"type="tel"type="date"type="time"type="datetime-local"type="datetime"type="textarea"
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField label="Email" type="email" />
<TextField label="Password" type="password" />
Disabled
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField label="Label" disabled value="Value" />
<TextField label="Label" disabled value="Value" variant="filled" />
Labels
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField label="Country" />
<TextField placeholder="email@domain.com" />
<label id="city-label">
City
<TextField aria-labelledby="city-label" />
</label>
Nesting text fields in labels without aria-labelledby is not supported. To avoid an id, use aria-label instead.
Textarea
A type="textarea" field grows with its content, from minLines up to maxLines, then scrolls.
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField label="Message" type="textarea" />
<TextField label="Message" type="textarea" variant="filled" />
Use minLines and maxLines to change that range.
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField label="Bio" type="textarea" minLines={3} maxLines={6} />
Chat prompt
A common use of an auto-growing textarea is a chat prompt, like the input of an AI tool. The "+" button at the start opens a menu.
<script lang="ts">
import { IconButton, Menu, MenuItem, TextField } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
let chatPromptPlusBtn: HTMLElement | undefined = $state()
</script>
<TextField
aria-label="Prompt"
placeholder="Ask anything"
type="textarea"
maxLines={8}
style="width:22rem"
>
{#snippet start()}
<IconButton
title="Add"
style="anchor-name:--chat-prompt-plus"
command="toggle-popover"
commandfor="chat-prompt-menu"
bind:element={chatPromptPlusBtn}
>
<Icon>add</Icon>
</IconButton>
{/snippet}
{#snippet end()}
<IconButton title="Send"><Icon>send</Icon></IconButton>
{/snippet}
</TextField>
<Menu anchor={chatPromptPlusBtn} id="chat-prompt-menu" style="position-anchor:--chat-prompt-plus">
<MenuItem>
Web search
{#snippet start()}<Icon>language</Icon>{/snippet}
</MenuItem>
<MenuItem>
Connect apps
{#snippet start()}<Icon>webhook</Icon>{/snippet}
</MenuItem>
</Menu>
Icons
<script lang="ts">
import { IconButton, TextField } from 'noph-ui'
import { Icon } from 'noph-ui/icons'
</script>
<TextField placeholder="Search" type="search" inputmode="search">
{#snippet start()}<Icon>search</Icon>{/snippet}
</TextField>
<TextField label="Password" type="password">
{#snippet end()}
<IconButton toggle title="Toggle visibility">
{#snippet selectedIcon()}
<Icon>visibility_off</Icon>
{/snippet}
<Icon>visibility</Icon>
</IconButton>
{/snippet}
</TextField>
<TextField label="Username" aria-invalid issues={[{ message: 'Username not available' }]}>
{#snippet end()}
<Icon>error</Icon>
{/snippet}
</TextField>
Prefix and suffix
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField
label="Amount"
value="0"
inputmode="numeric"
prefixText="$"
prefixLabel="US dollars"
suffixText=".00"
type="number"
/>
Supporting text
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField label="Username" supportingText="Your username is your unique identifier." />
<TextField
label="Email"
type="email"
inputmode="email"
required
supportingText="Email is required"
/>
Character counter
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField label="Name" maxlength={10} />
Validation
Constraint validation
<script lang="ts">
import { Button, TextField } from 'noph-ui'
</script>
<form>
<div style="display:flex;gap:1rem;flex-wrap: wrap;align-items: baseline;">
<TextField label="First name" required />
<TextField label="Last name" required pattern="[a-zA-Z]+" supportingText="Characters only" />
</div>
<div class="button-footer">
<Button variant="filled">Submit</Button>
</div>
</form>
<style>
.button-footer {
display: flex;
justify-content: flex-end;
margin-top: 1rem;
}
</style>
Manual validation
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField
label="Username"
aria-invalid
issues={[{ message: 'Username not available' }]}
value="eric20"
/>
Theming
Filled text field tokens
| Token | Default value |
|---|---|
--np-filled-text-field-container-shape | --np-shape-corner-extra-small |
--np-filled-text-field-container-color | --np-color-surface-container-highest |
--np-filled-text-field-label-text-color | --np-color-primary |
--np-filled-text-field-focus-active-indicator-color | --np-color-primary |
Filled text field example
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField
variant="filled"
label="Filled"
--np-filled-text-field-container-shape="0"
--np-filled-text-field-container-color="var(--np-color-surface-container)"
--np-filled-text-field-label-text-color="var(--np-color-tertiary)"
--np-filled-text-field-focus-active-indicator-color="var(--np-color-tertiary)"
/>
Outlined text field tokens
| Token | Default value |
|---|---|
--np-outlined-text-field-container-shape | --np-shape-corner-extra-small |
--np-outlined-text-field-label-text-color | --np-color-primary |
--np-outlined-text-field-focus-outline-color | --np-color-primary |
Shared tokens
| Token | Default value |
|---|---|
--np-picker-indicator-display | none |
A date, time or datetime-local field hides the browser's
picker button. Set --np-picker-indicator-display to block to show it.
Outlined text field example
<script lang="ts">
import { TextField } from 'noph-ui'
</script>
<TextField
label="Outlined"
--np-outlined-text-field-container-shape="0"
--np-outlined-text-field-label-text-color="var(--np-color-tertiary)"
--np-outlined-text-field-focus-outline-color="var(--np-color-tertiary)"
/>
Accessibility
The field is wrapped in a <label> that points at the input with for, so a click anywhere on the field puts the caret in it. Buttons in the start and end slots keep their own names. The input is named only by the label text, through aria-labelledby. Underneath is a native <input> or <textarea>, which handles type, required, the
keyboard and autofill.
Supporting text is linked to the field with aria-describedby. An error, from errorText or issues, switches that to aria-errormessage and
announces the message with role="alert". A required field shows an asterisk. Use noAsterisk to remove it when your form marks required fields another way.
Prefix, suffix and the character counter are also linked with aria-describedby. Use prefixLabel and suffixLabel to give symbols a spoken name, for example
"Euro" for "€". The counter is read as "Character count, 5/20". counterLabel translates
the first part.
API
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
type | 'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url' | 'date' | 'time' |
'datetime-local' | 'datetime' | 'textarea' | 'text' | Specifies the type of the field. |
minLines | number | 1 | Minimum number of visible lines. Only applies to type="textarea". |
maxLines | number | 4 | Number of lines the field grows to before it scrolls. Only applies to type="textarea". |
variant | 'outlined' | 'filled' | 'outlined' | Visual appearance |
label | string | undefined | undefined | Label of the text field. |
supportingText | string | '' | Provides additional information below the text field, such as usage guidelines. |
start | Snippet | undefined | undefined | Icon displayed at the beginning of the text field. |
end | Snippet | undefined | undefined | Icon displayed at the end of the text field. |
disabled | boolean | false | Disables the text field. |
noAsterisk | boolean | false | Hides the asterisk on the floating label of a required text field. |
issues | { message: string }[] | undefined | Error messages shown instead of the supporting text. Pass a remote form field's issues(). |
prefixText | string | '' | An optional prefix to display before the input value. |
suffixText | string | '' | An optional suffix to display after the input value. |
prefixLabel | string | undefined | Spoken in place of prefixText, for example "Euro" for "€". |
suffixLabel | string | undefined | Spoken in place of suffixText, for example "at gmail dot com". |
counterLabel | string | 'Character count' | Spoken before the character counter that maxlength shows. |
defaultValue | string | number | null | undefined | undefined | Value the field starts with and that a form reset returns it to, without taking control of value. |
populated | boolean | false | Keeps the label in its floating position even while the input is empty. Set it when something other than the value fills the field, such as chips. |
Bindables
| Attribute | Type | Description |
|---|---|---|
value | string | number | null | undefined | Value of the input or textarea. |
focused | boolean | Whether the input currently has focus. Defaults to false. |
element | HTMLSpanElement | The component's root DOM element, bound with bind:this. |
inputElement | HTMLInputElement | HTMLTextAreaElement | undefined | The input or textarea element. |
clientWidth, clientHeight | number | undefined | Size of the field, for positioning other elements next to it. AutoComplete uses them
to size its menu. |