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

TokenDefault 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

TokenDefault 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

TokenDefault value
--np-picker-indicator-displaynone

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

AttributeTypeDefaultDescription
type'text' | 'password' | 'email' | 'number' | 'search' | 'tel' | 'url' | 'date' | 'time' | 'datetime-local' | 'datetime' | 'textarea''text'Specifies the type of the field.
minLinesnumber1Minimum number of visible lines. Only applies to type="textarea".
maxLinesnumber4Number of lines the field grows to before it scrolls. Only applies to type="textarea".
variant'outlined' | 'filled''outlined'Visual appearance
labelstring | undefinedundefinedLabel of the text field.
supportingTextstring''Provides additional information below the text field, such as usage guidelines.
startSnippet | undefinedundefinedIcon displayed at the beginning of the text field.
endSnippet | undefinedundefinedIcon displayed at the end of the text field.
disabledbooleanfalseDisables the text field.
noAsteriskbooleanfalseDisables the asterisk on the floating label when the text field is required.
issues{ message: string }[]undefinedOptimized to use with remote form field issues.
prefixTextstring''An optional prefix to display before the input value.
suffixTextstring''An optional suffix to display after the input value.
defaultValuestring | number | null | undefinedundefinedValue the field starts with and that a form reset returns it to, without taking control of value.
populatedbooleanfalseKeeps 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

AttributeTypeDescription
valuestring | number | null | undefinedValue of the input or textarea.
focusedbooleanWhether the input currently has focus. Defaults to false.
elementHTMLSpanElementA 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.
inputElementHTMLInputElement | HTMLTextAreaElement | undefinedAllows access to the input element
clientWidth, clientHeightnumber | undefinedMeasurements of the field, for laying something out against it. AutoComplete uses them to size its menu.