Input
A text field.
Native <input>
That username is taken.
<script lang="ts">
import { Input } from '#lib/components/ui/input/index.js';
import { Label } from '#lib/components/ui/label/index.js';
</script>
<div class="field">
<Label for="email">Email</Label>
<Input id="email" type="email" placeholder="you@example.com" />
</div>
<div class="field">
<Label for="invalid">Username</Label>
<Input id="invalid" value="taken" aria-invalid="true" aria-describedby="invalid-hint" />
<p id="invalid-hint" class="hint">That username is taken.</p>
</div>
<div class="field">
<Label for="picture">Picture</Label>
<Input id="picture" type="file" />
</div>
<div class="field">
<Label for="disabled-input">Disabled</Label>
<Input id="disabled-input" disabled placeholder="Can't type here" />
</div>
<style>
.field {
display: grid;
gap: var(--dui-space-2);
width: min(100%, 20rem);
}
.field + .field {
margin-top: var(--dui-space-4);
}
.hint {
margin: 0;
color: var(--dui-color-muted-foreground);
font-size: var(--dui-text-sm);
}
</style>
Customizing from a parent
<script lang="ts">
import { Input } from '#lib/components/ui/input/index.js';
import { Label } from '#lib/components/ui/label/index.js';
</script>
<div class="field" style="--dui-input-radius: 9999px; --dui-input-height: 2.75rem">
<Label for="pill">Pill input</Label>
<Input id="pill" placeholder="Rounded from a parent" />
</div>
<style>
.field {
display: grid;
gap: var(--dui-space-2);
width: min(100%, 20rem);
}
</style>
Installation
npx distill-ui add input- Set up the tokens once, if you haven't: see manual installation.
- Copy these files into your project:
import Root from './input.svelte';
export { Root, Root as Input };
<script lang="ts">
import type { HTMLInputAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
value = $bindable(),
files = $bindable(),
type,
class: className,
...restProps
}: HTMLInputAttributes & { ref?: HTMLInputElement | null; files?: FileList } = $props();
</script>
{#if type === 'file'}
<input {...restProps} type="file" bind:this={ref} bind:files class={['input', className]} />
{:else}
<input {...restProps} {type} bind:this={ref} bind:value class={['input', className]} />
{/if}
<!--
Options you can set from a parent or on the input itself:
--dui-input-height, --dui-input-padding-x, --dui-input-border, --dui-input-radius, --dui-input-bg
-->
<style>
.input {
width: 100%;
min-width: 0;
height: var(--dui-input-height, 2.25rem);
padding: var(--dui-space-1) var(--dui-input-padding-x, var(--dui-space-2-5));
border: 1px solid var(--dui-input-border, var(--dui-color-input));
border-radius: var(--dui-input-radius, var(--dui-radius-md));
background: var(--dui-input-bg, var(--dui-color-control));
color: var(--dui-color-foreground);
font: inherit;
font-size: var(--dui-text-sm);
line-height: var(--dui-text-sm-line-height);
box-shadow: var(--dui-shadow-xs);
outline: none;
transition:
border-color var(--dui-duration-fast) var(--dui-ease-out),
box-shadow var(--dui-duration-fast) var(--dui-ease-out);
&::placeholder {
color: var(--dui-color-muted-foreground);
}
&::file-selector-button {
height: 1.75rem;
margin-inline-end: var(--dui-space-2);
padding: 0;
border: 0;
background: transparent;
color: var(--dui-color-foreground);
font: inherit;
font-size: var(--dui-text-sm);
font-weight: var(--dui-font-weight-medium);
}
&:focus-visible {
border-color: var(--dui-color-ring);
box-shadow: 0 0 0 var(--dui-ring-width)
color-mix(in oklch, var(--dui-color-ring) 50%, transparent);
}
&[aria-invalid='true'] {
border-color: var(--dui-color-destructive);
box-shadow: 0 0 0 var(--dui-ring-width)
color-mix(in oklch, var(--dui-color-destructive) 20%, transparent);
}
&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
}
</style>
Customizing
Set any of these on the component or on a parent element. See Customizing for how they work.
| Custom property | Default |
|---|---|
--dui-input-height | 2.25rem |
--dui-input-padding-x | var(--dui-space-2-5) |
--dui-input-border | var(--dui-color-input) |
--dui-input-radius | var(--dui-radius-md) |
--dui-input-bg | var(--dui-color-control) |