Input

A basic text input component for user data entry

Basic Input

Standard input without addons


Code Svelte
1
2<script lang="ts">
3	import { Input, Label } from "@kareyes/aether";
4</script>
5
6<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
7	<div class="space-y-2">
8		<Label for="basic-text">Text Input</Label>
9		<Input id="basic-text" placeholder="Enter text..." />
10	</div>
11	<div class="space-y-2">
12		<Label for="basic-email">Email Input</Label>
13		<Input id="basic-email" type="email" placeholder="Enter email..." />
14	</div>
15	<div class="space-y-2">
16		<Label for="basic-password">Password Input</Label>
17		<Input id="basic-password" type="password" placeholder="Enter password..." />
18	</div>
19	<div class="space-y-2">
20		<Label for="basic-number">Number Input</Label>
21		<Input id="basic-number" type="number" placeholder="Enter number..." />
22	</div>
23</div>

Icon Addons

Add icons to enhance visual clarity

USD

Code Svelte
1
2<script lang="ts">
3	import { Input, Label, InputGroupPrimitives } from "@kareyes/aether";
4	import { Search, Mail, DollarSign, Lock, Eye, EyeOff } from "@kareyes/aether/icons";
5
6	const { InputGroupButton } = InputGroupPrimitives;
7
8	let searchValue = $state("");
9	let emailValue = $state("");
10	let priceValue = $state("");
11	let passwordValue = $state("");
12	let showPassword = $state(false);
13</script>
14
15<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
16	<!-- Start Icon -->
17	<Input bind:value={searchValue} placeholder="Search...">
18		{#snippet startIcon()}<Search class="size-4" />{/snippet}
19	</Input>
20
21	<!-- End Icon -->
22	<Input bind:value={emailValue} type="email" placeholder="Enter your email">
23		{#snippet endIcon()}<Mail class="size-4" />{/snippet}
24	</Input>
25
26	<!-- Both Icons -->
27	<Input bind:value={priceValue} type="number" placeholder="0.00">
28		{#snippet startIcon()}<DollarSign class="size-4" />{/snippet}
29		{#snippet endIcon()}<span class="text-xs text-muted-foreground">USD</span>{/snippet}
30	</Input>
31
32	<!-- Password with Toggle -->
33	<Input bind:value={passwordValue} type={showPassword ? "text" : "password"} placeholder="Enter password">
34		{#snippet startIcon()}<Lock class="size-4" />{/snippet}
35		{#snippet endButton()}
36			<InputGroupButton size="icon-xs" variant="ghost" onclick={() => (showPassword = !showPassword)}>
37				{#if showPassword}<EyeOff class="size-4" />{:else}<Eye class="size-4" />{/if}
38			</InputGroupButton>
39		{/snippet}
40	</Input>
41</div>

Text Addons

Display additional text context

https://
.com
$
USD
@company.com
kg

Code Svelte
1
2<script lang="ts">
3	import { Input, Label } from "@kareyes/aether";
4	import { User } from "@kareyes/aether/icons";
5
6	let usernameValue = $state("");
7</script>
8
9<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
10	<!-- URL Prefix -->
11	<Input placeholder="example.com" startText="https://" endText=".com" />
12
13	<!-- Currency -->
14	<Input type="number" placeholder="0.00" startText="$" endText="USD" />
15
16	<!-- Email Domain -->
17	<Input bind:value={usernameValue} placeholder="username" endText="@company.com">
18		{#snippet startIcon()}<User class="size-4" />{/snippet}
19	</Input>
20
21	<!-- Unit Suffix -->
22	<Input type="number" placeholder="0" endText="kg" />
23</div>

Button Addons

Add interactive buttons for actions


Code Svelte
1
2<script lang="ts">
3	import { Input, Label, InputGroupPrimitives } from "@kareyes/aether";
4	import { Search, Copy, Check, Send } from "@kareyes/aether/icons";
5
6	const { InputGroupButton } = InputGroupPrimitives;
7
8	let urlValue = $state("https://example.com");
9	let copiedUrl = $state(false);
10	let messageValue = $state("");
11	let searchValue = $state("");
12
13	function handleCopy() {
14		navigator.clipboard.writeText(urlValue);
15		copiedUrl = true;
16		setTimeout(() => (copiedUrl = false), 2000);
17	}
18</script>
19
20<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
21	<!-- Copy Button -->
22	<Input bind:value={urlValue} readonly>
23		{#snippet endButton()}
24			<InputGroupButton size="icon-xs" onclick={handleCopy}>
25				{#if copiedUrl}<Check class="size-4 text-green-500" />{:else}<Copy class="size-4" />{/if}
26			</InputGroupButton>
27		{/snippet}
28	</Input>
29
30	<!-- Search Button -->
31	<Input placeholder="Type to search...">
32		{#snippet startIcon()}<Search class="size-4" />{/snippet}
33		{#snippet endButton()}
34			<InputGroupButton size="xs" variant="default">Search</InputGroupButton>
35		{/snippet}
36	</Input>
37
38	<!-- Send Message -->
39	<Input bind:value={messageValue} placeholder="Type your message...">
40		{#snippet endButton()}
41			<InputGroupButton size="icon-xs" variant="default" disabled={!messageValue}>
42				<Send class="size-4" />
43			</InputGroupButton>
44		{/snippet}
45	</Input>
46
47	<!-- Clear Button -->
48	<Input bind:value={searchValue} placeholder="Type something...">
49		{#if searchValue}
50			{#snippet endButton()}
51				<InputGroupButton size="icon-xs" variant="ghost" onclick={() => (searchValue = "")}>
52					<span class="text-lg">x</span>
53				</InputGroupButton>
54			{/snippet}
55		{/if}
56	</Input>
57</div>

Input Masks

Automatic formatting with input masks


Code Svelte
1
2<script lang="ts">
3	import { Input, Label } from "@kareyes/aether";
4	import { Phone, CreditCard } from "@kareyes/aether/icons";
5</script>
6
7<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
8	<!-- Phone -->
9	<Input mask="phone" placeholder="(555) 555-5555">
10		{#snippet startIcon()}<Phone class="size-4" />{/snippet}
11	</Input>
12
13	<!-- Credit Card -->
14	<Input mask="creditCard" placeholder="1234 5678 9012 3456">
15		{#snippet startIcon()}<CreditCard class="size-4" />{/snippet}
16	</Input>
17
18	<!-- SSN -->
19	<Input mask="ssn" placeholder="123-45-6789" />
20
21	<!-- Date -->
22	<Input mask="date" placeholder="MM/DD/YYYY" />
23</div>

Combined Features

Mix addons with masks and other features

$
Secure

Code Svelte
1
2<script lang="ts">
3	import { Input, Label, InputGroupPrimitives } from "@kareyes/aether";
4	import { Phone, Lock, Copy } from "@kareyes/aether/icons";
5
6	const { InputGroupButton } = InputGroupPrimitives;
7</script>
8
9<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
10	<!-- Phone with Icon and Mask -->
11	<Input mask="phone" placeholder="(555) 555-5555">
12		{#snippet startIcon()}<Phone class="size-4" />{/snippet}
13		{#snippet endButton()}
14			<InputGroupButton size="icon-xs" variant="ghost">
15				<Copy class="size-4" />
16			</InputGroupButton>
17		{/snippet}
18	</Input>
19
20	<!-- Price with Icons and Text -->
21	<Input type="number" placeholder="0.00" startText="$">
22		{#snippet endIcon()}<Lock class="size-4 text-green-600" />{/snippet}
23		{#snippet endText()}<span class="text-xs text-muted-foreground">Secure</span>{/snippet}
24	</Input>
25</div>

Input States

Different input states with addons

Please enter a valid email address


Code Svelte
1
2<script lang="ts">
3	import { Input, Label } from "@kareyes/aether";
4	import { Mail, Lock, Eye } from "@kareyes/aether/icons";
5</script>
6
7<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
8	<!-- Error State -->
9	<Input value="invalid@" error={true} placeholder="Enter email">
10		{#snippet startIcon()}<Mail class="size-4" />{/snippet}
11	</Input>
12
13	<!-- Disabled State -->
14	<Input value="Disabled input" disabled>
15		{#snippet startIcon()}<Lock class="size-4" />{/snippet}
16	</Input>
17
18	<!-- Readonly State -->
19	<Input value="Read-only value" readonly>
20		{#snippet endIcon()}<Eye class="size-4" />{/snippet}
21	</Input>
22
23	<!-- Loading State -->
24	<Input value="Loading..." loading />
25
26</div>

Custom Addons

Use custom snippets for complete control

Live

Code Svelte
1
2<script lang="ts">
3	import { Input, InputGroupPrimitives } from "@kareyes/aether";
4	import { Send } from "@kareyes/aether/icons";
5
6	const { InputGroupButton } = InputGroupPrimitives;
7</script>
8
9<div class="grid grid-cols-1 gap-6 max-w-2xl">
10	<!-- Custom Start Addon -->
11	<Input placeholder="Enter value">
12		{#snippet startAddon()}
13			<div class="flex items-center gap-2">
14				<div class="size-2 rounded-full bg-green-500 animate-pulse"></div>
15				<span class="text-xs font-medium">Live</span>
16			</div>
17		{/snippet}
18	</Input>
19
20	<!-- Custom End Addon -->
21	<Input placeholder="Type your message...">
22		{#snippet endAddon()}
23			<div class="flex items-center gap-1">
24				<InputGroupButton size="icon-xs" variant="ghost"><span>😀</span></InputGroupButton>
25				<InputGroupButton size="icon-xs" variant="default"><Send class="size-4" /></InputGroupButton>
26			</div>
27		{/snippet}
28	</Input>
29</div>

Using Input with Field Component

Recommended pattern for forms with labels, descriptions, and error handling

We'll never share your email

Choose a unique username

Must be at least 8 characters

Enter your phone number

Enter the product price

$
USD

Your secret API key (read-only)


Code Svelte
1
2<script lang="ts">
3	import { Input, Field, Button, InputGroupPrimitives } from "@kareyes/aether";
4	import { Mail, User, Lock, Eye, EyeOff, Phone, Copy } from "@kareyes/aether/icons";
5
6	const { InputGroupButton } = InputGroupPrimitives;
7
8	let formData = $state({
9		email: "", password: "", username: "",
10		phone: "", price: "", apiKey: "sk_live_1234567890abcdef",
11	});
12	let errors = $state<Record<string, string>>({});
13	let showFormPassword = $state(false);
14
15	function validateForm() {
16		errors = {};
17		if (!formData.email) errors.email = "Email is required";
18		if (!formData.password) errors.password = "Password is required";
19		if (!formData.username) errors.username = "Username is required";
20	}
21</script>
22
23<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
24	<Field label="Email" description="We'll never share your email" required error={errors.email}>
25		<Input type="email" bind:value={formData.email} placeholder="you@example.com" error={!!errors.email}>
26			{#snippet startIcon()}<Mail class="size-4" />{/snippet}
27		</Input>
28	</Field>
29
30	<Field label="Username" description="Choose a unique username" required error={errors.username}>
31		<Input bind:value={formData.username} placeholder="johndoe" error={!!errors.username}>
32			{#snippet startIcon()}<User class="size-4" />{/snippet}
33		</Input>
34	</Field>
35
36	<Field label="Password" description="Must be at least 8 characters" required error={errors.password}>
37		<Input type={showFormPassword ? "text" : "password"} bind:value={formData.password} placeholder="••••••••" error={!!errors.password}>
38			{#snippet startIcon()}<Lock class="size-4" />{/snippet}
39			{#snippet endButton()}
40				<InputGroupButton size="icon-xs" variant="ghost" onclick={() => (showFormPassword = !showFormPassword)}>
41					{#if showFormPassword}<EyeOff class="size-4" />{:else}<Eye class="size-4" />{/if}
42				</InputGroupButton>
43			{/snippet}
44		</Input>
45	</Field>
46
47	<Field label="Phone Number" description="Enter your phone number">
48		<Input bind:value={formData.phone} mask="phone" placeholder="(555) 555-5555">
49			{#snippet startIcon()}<Phone class="size-4" />{/snippet}
50		</Input>
51	</Field>
52</div>
53<Button onclick={validateForm}>Validate Form</Button>

Complete Form with Field Component

Full form using Field.Set and Field.Group for proper structure

Create Account

Enter your details to create a new account

We'll send a verification email

Choose a unique username (3-20 characters)

Must be at least 8 characters with a mix of letters and numbers

For account verification

Payment information


Code Svelte
1
2<script lang="ts">
3	import { Input, Field, FieldPrimitives, Button, InputGroupPrimitives } from "@kareyes/aether";
4	import { Mail, User, Lock, Eye, EyeOff, Phone, CreditCard } from "@kareyes/aether/icons";
5
6	const { InputGroupButton } = InputGroupPrimitives;
7
8	let formData = $state({ email: "", password: "", username: "", phone: "" });
9	let errors = $state<Record<string, string>>({});
10	let showFormPassword = $state(false);
11</script>
12
13<form onsubmit={(e) => { e.preventDefault(); }}>
14	<FieldPrimitives.Set>
15		<FieldPrimitives.Legend>Create Account</FieldPrimitives.Legend>
16		<FieldPrimitives.Description>Enter your details to create a new account</FieldPrimitives.Description>
17		<FieldPrimitives.Separator />
18		<FieldPrimitives.Group class="gap-6">
19			<Field label="Email Address" required error={errors.email}>
20				<Input type="email" bind:value={formData.email} placeholder="you@example.com" error={!!errors.email}>
21					{#snippet startIcon()}<Mail class="size-4" />{/snippet}
22				</Input>
23			</Field>
24
25			<Field label="Username" required error={errors.username}>
26				<Input bind:value={formData.username} placeholder="johndoe" error={!!errors.username}>
27					{#snippet startIcon()}<User class="size-4" />{/snippet}
28				</Input>
29			</Field>
30
31			<Field label="Password" required error={errors.password}>
32				<Input type={showFormPassword ? "text" : "password"} bind:value={formData.password} placeholder="••••••••">
33					{#snippet startIcon()}<Lock class="size-4" />{/snippet}
34					{#snippet endButton()}
35						<InputGroupButton size="icon-xs" variant="ghost" onclick={() => (showFormPassword = !showFormPassword)}>
36							{#if showFormPassword}<EyeOff class="size-4" />{:else}<Eye class="size-4" />{/if}
37						</InputGroupButton>
38					{/snippet}
39				</Input>
40			</Field>
41
42			<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
43				<Field label="Phone Number">
44					<Input bind:value={formData.phone} mask="phone" placeholder="(555) 555-5555">
45						{#snippet startIcon()}<Phone class="size-4" />{/snippet}
46					</Input>
47				</Field>
48				<Field label="Credit Card">
49					<Input mask="creditCard" placeholder="1234 5678 9012 3456">
50						{#snippet startIcon()}<CreditCard class="size-4" />{/snippet}
51					</Input>
52				</Field>
53			</div>
54		</FieldPrimitives.Group>
55
56		<div class="flex gap-4 pt-4">
57			<Button type="submit">Create Account</Button>
58			<Button type="button" variant="outline">Cancel</Button>
59		</div>
60	</FieldPrimitives.Set>
61</form>