Input
A native input with Looma’s shared control theme. Optional segments support block selection, character filtering, automatic or typed separator advancement, and continuous authentication-code entry.
Default
With no value or state properties supplied, Input exposes an empty, editable native control.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<ui-input
type="text"
aria-label="Office search"
placeholder="Search Fellowship office"
> </ui-input><script setup>
import { UiInput } from "@threadlabs/looma";
</script>
<template>
<UiInput
type="text"
aria-label="Office search"
placeholder="Search Fellowship office"
> </UiInput>
</template>import { UiInput } from "@threadlabs/looma";
export default function Example() {
return <UiInput
type="text"
aria-label="Office search"
placeholder="Search Fellowship office"
> </UiInput>;
}/** @jsxImportSource preact */
import { UiInput } from "@threadlabs/looma";
export default function Example() {
return <UiInput
type="text"
aria-label="Office search"
placeholder="Search Fellowship office"
> </UiInput>;
}<script>
import { UiInput } from "@threadlabs/looma";
</script>
<UiInput
type="text"
aria-label="Office search"
placeholder="Search Fellowship office"
> </UiInput>import { Component } from "@angular/core";
import { UiInput } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiInput],
template: `
<input
ui-input
[type]="'text'"
aria-label="Office search"
placeholder="Search Fellowship office"
>
`,
})
export default class Example {}invalid, readonly, and disabled
These properties configure validation and availability on the native input Looma renders.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<ui-stack
gap="s"
>
<ui-input
invalid
value="Invalid"
aria-label="Invalid"
> </ui-input>
<ui-input
readonly
value="Read only"
aria-label="Read only"
> </ui-input>
<ui-input
disabled
value="Disabled"
aria-label="Disabled"
> </ui-input>
</ui-stack><script setup>
import { UiInput, UiStack } from "@threadlabs/looma";
</script>
<template>
<UiStack
gap="s"
>
<UiInput
:invalid="true"
value="Invalid"
aria-label="Invalid"
> </UiInput>
<UiInput
:readonly="true"
value="Read only"
aria-label="Read only"
> </UiInput>
<UiInput
:disabled="true"
value="Disabled"
aria-label="Disabled"
> </UiInput>
</UiStack>
</template>import { UiInput, UiStack } from "@threadlabs/looma";
export default function Example() {
return <UiStack
gap="s"
>
<UiInput
invalid
value="Invalid"
aria-label="Invalid"
> </UiInput>
<UiInput
readOnly
value="Read only"
aria-label="Read only"
> </UiInput>
<UiInput
disabled
value="Disabled"
aria-label="Disabled"
> </UiInput>
</UiStack>;
}/** @jsxImportSource preact */
import { UiInput, UiStack } from "@threadlabs/looma";
export default function Example() {
return <UiStack
gap="s"
>
<UiInput
invalid
value="Invalid"
aria-label="Invalid"
> </UiInput>
<UiInput
readOnly
value="Read only"
aria-label="Read only"
> </UiInput>
<UiInput
disabled
value="Disabled"
aria-label="Disabled"
> </UiInput>
</UiStack>;
}<script>
import { UiInput, UiStack } from "@threadlabs/looma";
</script>
<UiStack
gap="s"
>
<UiInput
invalid
value="Invalid"
aria-label="Invalid"
> </UiInput>
<UiInput
readonly
value="Read only"
aria-label="Read only"
> </UiInput>
<UiInput
disabled
value="Disabled"
aria-label="Disabled"
> </UiInput>
</UiStack>import { Component } from "@angular/core";
import { UiInput, UiStack } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiStack, UiInput],
template: `
<div
ui-stack
[gap]="'s'"
>
<input
ui-input
[invalid]="true"
[value]="'Invalid'"
aria-label="Invalid"
>
<input
ui-input
[readonly]="true"
[value]="'Read only'"
aria-label="Read only"
>
<input
ui-input
[disabled]="true"
[value]="'Disabled'"
aria-label="Disabled"
>
</div>
`,
})
export default class Example {}required
Required marks the field for native form validation and exposes the requirement to assistive technology.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<ui-input
aria-label="Email"
placeholder="you@example.com"
required
> </ui-input><script setup>
import { UiInput } from "@threadlabs/looma";
</script>
<template>
<UiInput
aria-label="Email"
placeholder="you@example.com"
:required="true"
> </UiInput>
</template>import { UiInput } from "@threadlabs/looma";
export default function Example() {
return <UiInput
aria-label="Email"
placeholder="you@example.com"
required
> </UiInput>;
}/** @jsxImportSource preact */
import { UiInput } from "@threadlabs/looma";
export default function Example() {
return <UiInput
aria-label="Email"
placeholder="you@example.com"
required
> </UiInput>;
}<script>
import { UiInput } from "@threadlabs/looma";
</script>
<UiInput
aria-label="Email"
placeholder="you@example.com"
required
> </UiInput>import { Component } from "@angular/core";
import { UiInput } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiInput],
template: `
<input
ui-input
aria-label="Email"
placeholder="you@example.com"
[required]="true"
>
`,
})
export default class Example {}size
Each size matches Button's (sm 32px, md 40px, lg 48px), so a field and a button of the same size share a row.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-stack/ui-stack.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<ui-stack
gap="s"
>
<ui-cluster
gap="s"
align="center"
>
<ui-input
size="sm"
aria-label="Small"
placeholder="Small"
style="inline-size: 14rem"
></ui-input>
<ui-button
size="sm"
>Small</ui-button>
</ui-cluster>
<ui-cluster
gap="s"
align="center"
>
<ui-input
aria-label="Medium"
placeholder="Medium"
style="inline-size: 14rem"
></ui-input>
<ui-button>Medium</ui-button>
</ui-cluster>
<ui-cluster
gap="s"
align="center"
>
<ui-input
size="lg"
aria-label="Large"
placeholder="Large"
style="inline-size: 14rem"
></ui-input>
<ui-button
size="lg"
>Large</ui-button>
</ui-cluster>
</ui-stack><script setup>
import { UiButton, UiCluster, UiInput, UiStack } from "@threadlabs/looma";
</script>
<template>
<UiStack
gap="s"
>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="sm"
aria-label="Small"
placeholder="Small"
style="inline-size: 14rem"
/>
<UiButton
size="sm"
>Small</UiButton>
</UiCluster>
<UiCluster
gap="s"
align="center"
>
<UiInput
aria-label="Medium"
placeholder="Medium"
style="inline-size: 14rem"
/>
<UiButton>Medium</UiButton>
</UiCluster>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="lg"
aria-label="Large"
placeholder="Large"
style="inline-size: 14rem"
/>
<UiButton
size="lg"
>Large</UiButton>
</UiCluster>
</UiStack>
</template>import { UiButton, UiCluster, UiInput, UiStack } from "@threadlabs/looma";
export default function Example() {
return <UiStack
gap="s"
>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="sm"
aria-label="Small"
placeholder="Small"
style={{"inlineSize":"14rem"}}
/>
<UiButton
size="sm"
>Small</UiButton>
</UiCluster>
<UiCluster
gap="s"
align="center"
>
<UiInput
aria-label="Medium"
placeholder="Medium"
style={{"inlineSize":"14rem"}}
/>
<UiButton>Medium</UiButton>
</UiCluster>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="lg"
aria-label="Large"
placeholder="Large"
style={{"inlineSize":"14rem"}}
/>
<UiButton
size="lg"
>Large</UiButton>
</UiCluster>
</UiStack>;
}/** @jsxImportSource preact */
import { UiButton, UiCluster, UiInput, UiStack } from "@threadlabs/looma";
export default function Example() {
return <UiStack
gap="s"
>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="sm"
aria-label="Small"
placeholder="Small"
style={{"inlineSize":"14rem"}}
/>
<UiButton
size="sm"
>Small</UiButton>
</UiCluster>
<UiCluster
gap="s"
align="center"
>
<UiInput
aria-label="Medium"
placeholder="Medium"
style={{"inlineSize":"14rem"}}
/>
<UiButton>Medium</UiButton>
</UiCluster>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="lg"
aria-label="Large"
placeholder="Large"
style={{"inlineSize":"14rem"}}
/>
<UiButton
size="lg"
>Large</UiButton>
</UiCluster>
</UiStack>;
}<script>
import { UiButton, UiCluster, UiInput, UiStack } from "@threadlabs/looma";
</script>
<UiStack
gap="s"
>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="sm"
aria-label="Small"
placeholder="Small"
style="inline-size: 14rem"
/>
<UiButton
size="sm"
>Small</UiButton>
</UiCluster>
<UiCluster
gap="s"
align="center"
>
<UiInput
aria-label="Medium"
placeholder="Medium"
style="inline-size: 14rem"
/>
<UiButton>Medium</UiButton>
</UiCluster>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="lg"
aria-label="Large"
placeholder="Large"
style="inline-size: 14rem"
/>
<UiButton
size="lg"
>Large</UiButton>
</UiCluster>
</UiStack>import { Component } from "@angular/core";
import { UiButton, UiCluster, UiInput, UiStack } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiStack, UiCluster, UiInput, UiButton],
template: `
<div
ui-stack
[gap]="'s'"
>
<div
ui-cluster
[gap]="'s'"
[align]="'center'"
>
<input
ui-input
[size]="'sm'"
aria-label="Small"
placeholder="Small"
style="inline-size: 14rem"
>
<ng-template
ui-button
[size]="'sm'"
>Small</ng-template>
</div>
<div
ui-cluster
[gap]="'s'"
[align]="'center'"
>
<input
ui-input
aria-label="Medium"
placeholder="Medium"
style="inline-size: 14rem"
>
<ng-template
ui-button
>Medium</ng-template>
</div>
<div
ui-cluster
[gap]="'s'"
[align]="'center'"
>
<input
ui-input
[size]="'lg'"
aria-label="Large"
placeholder="Large"
style="inline-size: 14rem"
>
<ng-template
ui-button
[size]="'lg'"
>Large</ng-template>
</div>
</div>
`,
})
export default class Example {}Compact row
size="sm" across a row. Inputs, selects, comboboxes, and buttons shrink to the small control height; a checkbox, radio, or switch keeps its box and centres its label in that height, so every control and label lines up.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-cluster/ui-cluster.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-select/ui-select.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-combobox/ui-combobox.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-checkbox/ui-checkbox.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-radio/ui-radio.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-switch/ui-switch.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-button/ui-button.html"
>
<ui-cluster
gap="s"
align="center"
>
<ui-input
size="sm"
type="search"
aria-label="Search orders"
placeholder="Search orders"
style="inline-size: 12rem"
></ui-input>
<ui-select
size="sm"
aria-label="Status"
style="inline-size: 9rem"
>
<option
value
>Any status</option>
<option
value="open"
>Open</option>
<option
value="closed"
>Closed</option>
</ui-select>
<ui-combobox
size="sm"
label="Owner"
label-visibility="sr-only"
placeholder="Owner"
disclosure
style="inline-size: 10rem"
>
<option
value="ada"
>Ada</option>
<option
value="grace"
>Grace</option>
</ui-combobox>
<ui-checkbox
size="sm"
>Mine only</ui-checkbox>
<ui-radio
size="sm"
name="compact-row-range"
value="week"
checked
>This week</ui-radio>
<ui-radio
size="sm"
name="compact-row-range"
value="month"
>This month</ui-radio>
<ui-switch
size="sm"
>Archived</ui-switch>
<ui-button
size="sm"
variant="solid"
>Apply</ui-button>
</ui-cluster><script setup>
import {
UiButton,
UiCheckbox,
UiCluster,
UiCombobox,
UiInput,
UiRadio,
UiSelect,
UiSwitch,
} from "@threadlabs/looma";
</script>
<template>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="sm"
type="search"
aria-label="Search orders"
placeholder="Search orders"
style="inline-size: 12rem"
/>
<UiSelect
size="sm"
aria-label="Status"
style="inline-size: 9rem"
>
<option
value=""
>Any status</option>
<option
value="open"
>Open</option>
<option
value="closed"
>Closed</option>
</UiSelect>
<UiCombobox
size="sm"
label="Owner"
label-visibility="sr-only"
placeholder="Owner"
:disclosure="true"
style="inline-size: 10rem"
>
<option
value="ada"
>Ada</option>
<option
value="grace"
>Grace</option>
</UiCombobox>
<UiCheckbox
size="sm"
>Mine only</UiCheckbox>
<UiRadio
size="sm"
name="compact-row-range"
value="week"
:checked="true"
>This week</UiRadio>
<UiRadio
size="sm"
name="compact-row-range"
value="month"
>This month</UiRadio>
<UiSwitch
size="sm"
>Archived</UiSwitch>
<UiButton
size="sm"
variant="solid"
>Apply</UiButton>
</UiCluster>
</template>import {
UiButton,
UiCheckbox,
UiCluster,
UiCombobox,
UiInput,
UiRadio,
UiSelect,
UiSwitch,
} from "@threadlabs/looma";
export default function Example() {
return <UiCluster
gap="s"
align="center"
>
<UiInput
size="sm"
type="search"
aria-label="Search orders"
placeholder="Search orders"
style={{"inlineSize":"12rem"}}
/>
<UiSelect
size="sm"
aria-label="Status"
style={{"inlineSize":"9rem"}}
>
<option
value=""
>Any status</option>
<option
value="open"
>Open</option>
<option
value="closed"
>Closed</option>
</UiSelect>
<UiCombobox
size="sm"
label="Owner"
labelVisibility="sr-only"
placeholder="Owner"
disclosure
style={{"inlineSize":"10rem"}}
>
<option
value="ada"
>Ada</option>
<option
value="grace"
>Grace</option>
</UiCombobox>
<UiCheckbox
size="sm"
>Mine only</UiCheckbox>
<UiRadio
size="sm"
name="compact-row-range"
value="week"
checked
>This week</UiRadio>
<UiRadio
size="sm"
name="compact-row-range"
value="month"
>This month</UiRadio>
<UiSwitch
size="sm"
>Archived</UiSwitch>
<UiButton
size="sm"
variant="solid"
>Apply</UiButton>
</UiCluster>;
}/** @jsxImportSource preact */
import {
UiButton,
UiCheckbox,
UiCluster,
UiCombobox,
UiInput,
UiRadio,
UiSelect,
UiSwitch,
} from "@threadlabs/looma";
export default function Example() {
return <UiCluster
gap="s"
align="center"
>
<UiInput
size="sm"
type="search"
aria-label="Search orders"
placeholder="Search orders"
style={{"inlineSize":"12rem"}}
/>
<UiSelect
size="sm"
aria-label="Status"
style={{"inlineSize":"9rem"}}
>
<option
value=""
>Any status</option>
<option
value="open"
>Open</option>
<option
value="closed"
>Closed</option>
</UiSelect>
<UiCombobox
size="sm"
label="Owner"
labelVisibility="sr-only"
placeholder="Owner"
disclosure
style={{"inlineSize":"10rem"}}
>
<option
value="ada"
>Ada</option>
<option
value="grace"
>Grace</option>
</UiCombobox>
<UiCheckbox
size="sm"
>Mine only</UiCheckbox>
<UiRadio
size="sm"
name="compact-row-range"
value="week"
checked
>This week</UiRadio>
<UiRadio
size="sm"
name="compact-row-range"
value="month"
>This month</UiRadio>
<UiSwitch
size="sm"
>Archived</UiSwitch>
<UiButton
size="sm"
variant="solid"
>Apply</UiButton>
</UiCluster>;
}<script>
import {
UiButton,
UiCheckbox,
UiCluster,
UiCombobox,
UiInput,
UiRadio,
UiSelect,
UiSwitch,
} from "@threadlabs/looma";
</script>
<UiCluster
gap="s"
align="center"
>
<UiInput
size="sm"
type="search"
aria-label="Search orders"
placeholder="Search orders"
style="inline-size: 12rem"
/>
<UiSelect
size="sm"
aria-label="Status"
style="inline-size: 9rem"
>
<option
value=""
>Any status</option>
<option
value="open"
>Open</option>
<option
value="closed"
>Closed</option>
</UiSelect>
<UiCombobox
size="sm"
label="Owner"
labelVisibility="sr-only"
placeholder="Owner"
disclosure
style="inline-size: 10rem"
>
<option
value="ada"
>Ada</option>
<option
value="grace"
>Grace</option>
</UiCombobox>
<UiCheckbox
size="sm"
>Mine only</UiCheckbox>
<UiRadio
size="sm"
name="compact-row-range"
value="week"
checked
>This week</UiRadio>
<UiRadio
size="sm"
name="compact-row-range"
value="month"
>This month</UiRadio>
<UiSwitch
size="sm"
>Archived</UiSwitch>
<UiButton
size="sm"
variant="solid"
>Apply</UiButton>
</UiCluster>import { Component } from "@angular/core";
import {
UiButton,
UiCheckbox,
UiCluster,
UiCombobox,
UiInput,
UiRadio,
UiSelect,
UiSwitch,
} from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiCluster, UiInput, UiSelect, UiCombobox, UiCheckbox, UiRadio, UiSwitch, UiButton],
template: `
<div
ui-cluster
[gap]="'s'"
[align]="'center'"
>
<input
ui-input
[size]="'sm'"
[type]="'search'"
aria-label="Search orders"
placeholder="Search orders"
style="inline-size: 12rem"
>
<select
ui-select
[size]="'sm'"
aria-label="Status"
style="inline-size: 9rem"
>
<option
value=""
>Any status</option>
<option
value="open"
>Open</option>
<option
value="closed"
>Closed</option>
</select>
<div
ui-combobox
[size]="'sm'"
[label]="'Owner'"
[labelVisibility]="'sr-only'"
[placeholder]="'Owner'"
[disclosure]="true"
style="inline-size: 10rem"
>
<option
value="ada"
>Ada</option>
<option
value="grace"
>Grace</option>
</div>
<label
ui-checkbox
[size]="'sm'"
>Mine only</label>
<label
ui-radio
[size]="'sm'"
[name]="'compact-row-range'"
[value]="'week'"
[checked]="true"
>This week</label>
<label
ui-radio
[size]="'sm'"
[name]="'compact-row-range'"
[value]="'month'"
>This month</label>
<label
ui-switch
[size]="'sm'"
>Archived</label>
<ng-template
ui-button
[size]="'sm'"
[variant]="'solid'"
>Apply</ng-template>
</div>
`,
})
export default class Example {}Edit blocks within one field
Focus selects the first block. Type digits to advance automatically, or type the colon to move to minutes. Arrow keys select neighbouring blocks; Backspace clears or moves back. Date Field and Time Field supply their localized blocks automatically.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-form-field/ui-form-field.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<ui-form-field>
<label
slot="label"
for="segmented-time"
>Time (24-hour)</label>
<ui-input
id="segmented-time"
name="time"
value="09:30"
inputmode="numeric"
segments="[{ placeholder: "hh", pattern: "[0-9]" }, { literal: ":" }, { placeholder: "mm", pattern: "[0-9]" }]"
></ui-input>
<small
slot="help"
>Type hours, then minutes. You can also type the colon to advance.</small>
</ui-form-field><script setup>
import { UiFormField, UiInput } from "@threadlabs/looma";
</script>
<template>
<UiFormField>
<template #label>
<label
for="segmented-time"
>Time (24-hour)</label>
</template>
<UiInput
id="segmented-time"
name="time"
value="09:30"
inputmode="numeric"
:segments="[{ placeholder: 'hh', pattern: '[0-9]' }, { literal: ':' }, { placeholder: 'mm', pattern: '[0-9]' }]"
/>
<template #help>
<small>Type hours, then minutes. You can also type the colon to advance.</small>
</template>
</UiFormField>
</template>import { UiFormField, UiInput } from "@threadlabs/looma";
export default function Example() {
return <UiFormField
slots={{
label: <label
htmlFor="segmented-time"
>Time (24-hour)</label>,
help: <small>Type hours, then minutes. You can also type the colon to advance.</small>,
}}
>
<UiInput
id="segmented-time"
name="time"
value="09:30"
inputmode="numeric"
segments={[{"placeholder":"hh","pattern":"[0-9]"},{"literal":":"},{"placeholder":"mm","pattern":"[0-9]"}]}
/>
</UiFormField>;
}/** @jsxImportSource preact */
import { UiFormField, UiInput } from "@threadlabs/looma";
export default function Example() {
return <UiFormField
slots={{
label: <label
htmlFor="segmented-time"
>Time (24-hour)</label>,
help: <small>Type hours, then minutes. You can also type the colon to advance.</small>,
}}
>
<UiInput
id="segmented-time"
name="time"
value="09:30"
inputmode="numeric"
segments={[{"placeholder":"hh","pattern":"[0-9]"},{"literal":":"},{"placeholder":"mm","pattern":"[0-9]"}]}
/>
</UiFormField>;
}<script>
import { UiFormField, UiInput } from "@threadlabs/looma";
</script>
{#snippet label()}
<label
for="segmented-time"
>Time (24-hour)</label>
{/snippet}
{#snippet help()}
<small>Type hours, then minutes. You can also type the colon to advance.</small>
{/snippet}
<UiFormField
slots={{ label, help }}
>
<UiInput
id="segmented-time"
name="time"
value="09:30"
inputmode="numeric"
segments={[{"placeholder":"hh","pattern":"[0-9]"},{"literal":":"},{"placeholder":"mm","pattern":"[0-9]"}]}
/>
</UiFormField>import { Component } from "@angular/core";
import { UiFormField, UiInput } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiFormField, UiInput],
template: `
<div
ui-form-field
>
<ng-container ngProjectAs="[slot='label']">
<label
for="segmented-time"
>Time (24-hour)</label>
</ng-container>
<input
ui-input
id="segmented-time"
name="time"
[value]="'09:30'"
inputmode="numeric"
[segments]="[{ placeholder: 'hh', pattern: '[0-9]' }, { literal: ':' }, { placeholder: 'mm', pattern: '[0-9]' }]"
>
<ng-container ngProjectAs="[slot='help']">
<small>Type hours, then minutes. You can also type the colon to advance.</small>
</ng-container>
</div>
`,
})
export default class Example {}Enter an authentication code
Each digit advances to the next block. Backspace moves backwards, and a pasted code fills the remaining blocks. One native input retains browser autofill, clipboard and form behavior.
Code
<link
rel="component"
href="@threadlabs/looma/components/ui-form-field/ui-form-field.html"
>
<link
rel="component"
href="@threadlabs/looma/components/ui-input/ui-input.html"
>
<ui-form-field
required
>
<label
slot="label"
for="authentication-code"
>Authentication code</label>
<ui-input
id="authentication-code"
name="code"
inputmode="numeric"
autocomplete="one-time-code"
placeholder="······"
pattern="[0-9]{6}"
segments="[{ placeholder: "·", pattern: "[0-9]" }, { placeholder: "·", pattern: "[0-9]" }, { placeholder: "·", pattern: "[0-9]" }, { placeholder: "·", pattern: "[0-9]" }, { placeholder: "·", pattern: "[0-9]" }, { placeholder: "·", pattern: "[0-9]" }]"
></ui-input>
<small
slot="help"
>Enter the six digits from your authentication message.</small>
</ui-form-field><script setup>
import { UiFormField, UiInput } from "@threadlabs/looma";
</script>
<template>
<UiFormField
:required="true"
>
<template #label>
<label
for="authentication-code"
>Authentication code</label>
</template>
<UiInput
id="authentication-code"
name="code"
inputmode="numeric"
autocomplete="one-time-code"
placeholder="······"
pattern="[0-9]{6}"
:segments="[{ placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }]"
/>
<template #help>
<small>Enter the six digits from your authentication message.</small>
</template>
</UiFormField>
</template>import { UiFormField, UiInput } from "@threadlabs/looma";
export default function Example() {
return <UiFormField
required
slots={{
label: <label
htmlFor="authentication-code"
>Authentication code</label>,
help: <small>Enter the six digits from your authentication message.</small>,
}}
>
<UiInput
id="authentication-code"
name="code"
inputmode="numeric"
autocomplete="one-time-code"
placeholder="······"
pattern="[0-9]{6}"
segments={[{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"}]}
/>
</UiFormField>;
}/** @jsxImportSource preact */
import { UiFormField, UiInput } from "@threadlabs/looma";
export default function Example() {
return <UiFormField
required
slots={{
label: <label
htmlFor="authentication-code"
>Authentication code</label>,
help: <small>Enter the six digits from your authentication message.</small>,
}}
>
<UiInput
id="authentication-code"
name="code"
inputmode="numeric"
autocomplete="one-time-code"
placeholder="······"
pattern="[0-9]{6}"
segments={[{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"}]}
/>
</UiFormField>;
}<script>
import { UiFormField, UiInput } from "@threadlabs/looma";
</script>
{#snippet label()}
<label
for="authentication-code"
>Authentication code</label>
{/snippet}
{#snippet help()}
<small>Enter the six digits from your authentication message.</small>
{/snippet}
<UiFormField
required
slots={{ label, help }}
>
<UiInput
id="authentication-code"
name="code"
inputmode="numeric"
autocomplete="one-time-code"
placeholder="······"
pattern="[0-9]{6}"
segments={[{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"},{"placeholder":"·","pattern":"[0-9]"}]}
/>
</UiFormField>import { Component } from "@angular/core";
import { UiFormField, UiInput } from "@threadlabs/looma";
@Component({
selector: "example-view",
standalone: true,
imports: [UiFormField, UiInput],
template: `
<div
ui-form-field
[required]="true"
>
<ng-container ngProjectAs="[slot='label']">
<label
for="authentication-code"
>Authentication code</label>
</ng-container>
<input
ui-input
id="authentication-code"
name="code"
inputmode="numeric"
autocomplete="one-time-code"
placeholder="······"
pattern="[0-9]{6}"
[segments]="[{ placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }, { placeholder: '·', pattern: '[0-9]' }]"
>
<ng-container ngProjectAs="[slot='help']">
<small>Enter the six digits from your authentication message.</small>
</ng-container>
</div>
`,
})
export default class Example {}API
Package: @threadlabs/looma · Native root: input
Attributes
| Name | Description | Property | Type | Default | Allowed values |
|---|---|---|---|---|---|
disabled | Prevents editing and form submission. | disabled | boolean | false | — |
invalid | Exposes invalid state to assistive technology and styling. | invalid | boolean | false | — |
readonly | Allows selection without allowing edits. | readonly | boolean | false | — |
required | Marks the control as required for form validation. | required | boolean | false | — |
segments | Optional text-entry blocks. A placeholder defines an editable block’s maximum length; pattern matches one allowed Unicode character. A literal is skipped automatically or by typing it. Focus selects the first block; arrows and deletion move between blocks. Native paste, composition and autofill remain available. Placeholder text is never inserted into the value; empty fields use the native placeholder attribute. Applies to text, tel and search inputs. | segments | list(object)
| — | — |
size | Height, padding, and type size, matching Button's sizes so a field and a button of one size share a row. sm is the small control height (2rem by default) with smaller text, for compact rows such as a table row or a filter bar; md is the standard control height; lg is taller with roomier padding. Touch input keeps body-size field text without inflating the visible size ladder. Change it after render through this option; the element's size property is the native one. | size | keyword | md | sm, md, lg |
type | The native input mode. Number and range modes accept numeric values; the other supported modes accept strings. File inputs expose selected files through the native change event's target.files. Their value can only be set to an empty string to clear the selection. | type | keyword | text | text, search, tel, url, email, password, date, month, week, time, datetime-local, number, range, color, file |
value | The input's value: text, or a number for type="number" and type="range" fields. Setting or changing it replaces the live text; between changes the user's typing stands, so follow input events to keep it in step. A number field keeps text that means the same number, such as 1.0 for 1. It is also the input's default, so a form reset returns to it. Native edits made before the first browser connection stay live when that external value is unchanged. | value | type=number, range → number; otherwise → string | — | — |
Framework props
| Name | Type | Default | Allowed values |
|---|---|---|---|
disabled | boolean | false | — |
invalid | boolean | false | — |
readonly | boolean | false | — |
required | boolean | false | — |
segments | readonly ({ readonly placeholder?: string | null; readonly pattern?: string | null; readonly literal?: string | null })[]
| — | — |
size | "sm" | "md" | "lg" | md | sm, md, lg |
type | "text" | "search" | "tel" | "url" | "email" | "password" | "date" | "month" | "week" | "time" | "datetime-local" | "number" | "range" | "color" | "file" | text | text, search, tel, url, email, password, date, month, week, time, datetime-local, number, range, color, file |
value | string | number | — | — |
Events
No events.
Slots
No slots.
Design tokens
Set shared theme properties on a theme boundary. Component hooks apply only to their owning component.
Component tokens
| Token | Declared values | Fallback |
|---|---|---|
--ui-input-border | — | var(--ui-control-border, var(--ui-border-strong)) |
--ui-input-border-hover | — | var(--ui-control-border-hover, var(--ui-text-secondary)) |
--ui-input-border-width | — | var(--ui-border-width) |
--ui-input-focus-border | — | var(--ui-control-focus, var(--ui-accent-solid)) |
--ui-input-focus-outline | — | var(--ui-focus-width) solid Highlight |
--ui-input-focus-shadow | — | var(--ui-control-inset), var(--ui-control-focus-halo-shadow) |
--ui-input-inline-size | — | 100% |
--ui-input-invalid-border | — | var(--ui-field-danger, var(--ui-danger-solid)) |
--ui-input-invalid-focus-shadow | — | var(
--_ui-default-input-invalid-focus-shadow,
var(--ui-control-inset),
0 0 0 calc(var(--ui-focus-width) * 1.5) color-mix(
in srgb,
var(--ui-input-invalid-border, var(--ui-field-danger, var(--ui-danger-solid))) 22%,
transparent
)
) |
--ui-input-min-block-size | — | var(--ui-control-size-md) | var(--ui-control-size-sm) | var(--ui-control-size-lg) |
--ui-input-radius | — | var(--ui-field-radius, var(--ui-radius-md)) |
--ui-input-shadow | — | var(--ui-control-inset) |
--ui-input-surface | — | var(--ui-control-surface, var(--ui-surface-elevated)) |
Shared tokens
| Token | Fallback |
|---|---|
--ui-accent-solid | — |
--ui-border-strong | — |
--ui-border-width | — |
--ui-control-border | var(--ui-border-strong) |
--ui-control-border-hover | var(--ui-text-secondary) |
--ui-control-focus | var(--ui-accent-solid) |
--ui-control-focus-halo-shadow | — |
--ui-control-inset | — |
--ui-control-inset-block-lg | var(--ui-space-3) |
--ui-control-inset-block-md | var(--ui-space-2) |
--ui-control-inset-block-sm | var(--ui-space-1) |
--ui-control-placeholder | var(--ui-text-secondary) |
--ui-control-size-lg | — |
--ui-control-size-md | — |
--ui-control-size-sm | — |
--ui-control-surface | var(--ui-surface-elevated) |
--ui-control-surface-disabled | var(--ui-surface-muted) |
--ui-danger-solid | — |
--ui-disabled-text | — |
--ui-field-danger | var(--ui-danger-solid) |
--ui-field-radius | var(--ui-radius-md) |
--ui-focus-width | — |
--ui-font-size-md | — |
--ui-font-size-sm | — |
--ui-line-height-tight | — |
--ui-motion-ease | — |
--ui-motion-fast | — |
--ui-radius-md | — |
--ui-space-1 | — |
--ui-space-2 | — |
--ui-space-3 | — |
--ui-space-4 | — |
--ui-surface-elevated | — |
--ui-surface-highlight | none |
--ui-surface-muted | — |
--ui-text-primary | — |
--ui-text-secondary | — |