Text fields
Text fields let people enter and edit text. Material 3 gives two styles, filled and outlined, and both carry the label, supporting text and validation that a real form needs.
Usage
Text fields function similarly to <input> elements, serving as containers with labels
to facilitate user input.
<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 of a text field changes how the text field works, such as displaying
a different keyboard or providing default validation.
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 currently supported. If you want to avoid using an id, you can 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 field, like the input of an AI tool. The leading "+" button below opens a menu, similar to ChatGPT's tools 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 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="$"
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
own picker button, since it clashes with the field's own trailing content. Set --np-picker-indicator-display to block to bring it back.
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>, so the label text names it
without any for and id wiring, and clicking the label puts the caret in
the field. Underneath sits a native <input> or <textarea>,
which is what carries type, required, the keyboard and autofill.
Supporting text is tied to the field with aria-describedby. An error, from errorText or from issues, switches that to aria-errormessage and announces the message through role="alert". A
required field is marked with an asterisk, which noAsterisk removes when your form says
so elsewhere.
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 | Disables the asterisk on the floating label when the text field is required. |
issues | { message: string }[] | undefined | Optimized to use with remote form field issues. |
prefixText | string | '' | An optional prefix to display before the input value. |
suffixText | string | '' | An optional suffix to display after the input value. |
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 | A reference to the root DOM element of the component. This variable is bound using bind:this, allowing direct access to the underlying HTML element for manipulation or querying within
the component's logic. |
inputElement | HTMLInputElement | HTMLTextAreaElement | undefined | Allows access to the input element |
clientWidth, clientHeight | number | undefined | Measurements of the field, for laying something out against it. AutoComplete uses
them to size its menu. |