Forms
Phone input
A phone number field for any country. Pick or search a country, and the number formats as you type. Paste a full international number and the country follows. It says plainly when a number doesn't look right.
Installation
npx sinaui add phone-inputCopy src/lib/ui/PhoneInput.svelte, phone.ts, floating.ts, glide.ts, scroll-edges.ts, announce.ts, Icon.svelte and tokens.css, then install its
phone number library:
pnpm add libphonenumber-jsExamples
Countries you choose first
preferred puts a few countries at the top of the list. Search by name, code (BD) or dialling code (+880).
We'll text you when your manuscript is ready to collect.
Nothing saved yet
<script lang="ts">
import PhoneInput from '#lib/ui/PhoneInput.svelte';
let value = $state('');
</script>
<PhoneInput
label="Phone"
country="BD"
preferred={['BD', 'MY', 'SA', 'TR']}
hint="We'll text you when your manuscript is ready to collect."
bind:value
/>
<p class="out">{value ? `Saved as ${value}` : 'Nothing saved yet'}</p>
<style>
.out {
margin: 0.75rem 0 0;
color: var(--ui-muted);
font-size: 0.875rem;
font-variant-numeric: tabular-nums;
}
</style>In a form
With name, the form sends the full international number. Submitting an empty or wrong number stops the form and shows why.
<script lang="ts">
import Button from '#lib/ui/Button.svelte';
import PhoneInput from '#lib/ui/PhoneInput.svelte';
let sent = $state('');
</script>
<form
onsubmit={(e) => {
e.preventDefault();
sent = String(new FormData(e.currentTarget).get('phone'));
}}
>
<PhoneInput label="Phone for the reading room" name="phone" country="TR" required />
<Button type="submit">Book a desk</Button>
<p class="out" role="status">{sent ? `Would send phone=${sent}` : ''}</p>
</form>
<style>
form {
display: grid;
justify-items: start;
gap: 0.75rem;
}
form > :global(.field) {
justify-self: stretch;
}
.out {
margin: 0;
color: var(--ui-muted);
font-size: 0.875rem;
font-variant-numeric: tabular-nums;
}
</style>Your own error
error replaces the built-in messages, for problems only your server knows about.
This number is already on another library card.
<script lang="ts">
import PhoneInput from '#lib/ui/PhoneInput.svelte';
</script>
<PhoneInput
label="Phone"
value="+60312345678"
error="This number is already on another library card."
/>Props
| Prop | Type | Default |
|---|---|---|
label | string (required) | |
value | string (bindable): the number as +8801712345678, empty until
valid | '' |
valid | boolean (bindable) | false |
country | string (bindable): a country code like 'BD' | from the browser's language |
preferred | string[]: listed first | [] |
name | string: sent with forms | |
required | boolean | false |
disabled | boolean | false |
placeholder | string | |
hint | string | |
error | string: replaces the built-in messages |
Keyboard
| Key | Does |
|---|---|
| โ โ on the country | Open the country list |
| Typing in the list | Search by name, code or dialling code |
| โ โ Page Up Page Down | Move through countries |
| Enter | Choose the country and go back to the number |
| Escape | Close the list |
Accessibility
- The country button is named with the country and its dialling code.
- The number field has its label; hints and errors are read out with it.
- Messages appear when you leave the field or submit, not while you're still typing.
- When a pasted number changes the country, the new country is announced.
- The number always reads left to right, including in right-to-left pages.