Phone Input
International phone number input with country code selector, searchable dropdown, sizes, and validation states.
Basic Phone Input
Preview
<div class="phone-input">
<button class="phone-input-country" type="button">
<span class="phone-input-flag">🇺🇸</span>
<span class="phone-input-code">+1</span>
<svg class="phone-input-arrow" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"/></svg>
</button>
<input type="tel" class="input" placeholder="(555) 000-0000" />
</div> Requires: ux.min.css
<div class="relative flex items-stretch w-full">
<button class="flex items-center gap-1.5 shrink-0 h-11 px-3 bg-base-200 border border-base-300 border-r-0 rounded-l-field cursor-pointer hover:bg-base-300 transition-colors" type="button">
<span class="text-xl leading-none">🇺🇸</span>
<span class="text-sm font-medium text-base-content min-w-10">+1</span>
<svg class="size-4 text-base-content/60" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"/></svg>
</button>
<input type="tel" class="input flex-1 min-w-0 rounded-l-none" placeholder="(555) 000-0000" />
</div> Requires: tw.min.css
// Toggle dropdown:
// countryBtn.addEventListener('click', () => {
// wrapper.classList.toggle('phone-input-open');
// });
// Format input:
// input.addEventListener('input', () => {
// let digits = input.value.replace(/\D/g, '');
// if (digits.length > 3) digits = '(' + digits.slice(0,3) + ') ' + digits.slice(3);
// if (digits.length > 9) digits = digits.slice(0,9) + '-' + digits.slice(9,13);
// input.value = digits;
// }); With Dropdown Open
Preview
🇺🇸
United States
+1
🇬🇧
United Kingdom
+44
🇩🇪
Germany
+49
🇫🇷
France
+33
<div class="phone-input phone-input-open" style="margin-bottom: 220px;">
<button class="phone-input-country" type="button">
<span class="phone-input-flag">🇺🇸</span>
<span class="phone-input-code">+1</span>
<svg class="phone-input-arrow" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"/></svg>
</button>
<input type="tel" class="input" placeholder="(555) 000-0000" />
<div class="phone-input-dropdown">
<div class="phone-input-search">
<input class="phone-input-search-input" type="text" placeholder="Search countries..." />
</div>
<div class="phone-input-list">
<div class="phone-input-option phone-input-option-selected">
<span class="phone-input-option-flag">🇺🇸</span>
<span class="phone-input-option-name">United States</span>
<span class="phone-input-option-code">+1</span>
</div>
<div class="phone-input-option">
<span class="phone-input-option-flag">🇬🇧</span>
<span class="phone-input-option-name">United Kingdom</span>
<span class="phone-input-option-code">+44</span>
</div>
<div class="phone-input-option">
<span class="phone-input-option-flag">🇩🇪</span>
<span class="phone-input-option-name">Germany</span>
<span class="phone-input-option-code">+49</span>
</div>
<div class="phone-input-option">
<span class="phone-input-option-flag">🇫🇷</span>
<span class="phone-input-option-name">France</span>
<span class="phone-input-option-code">+33</span>
</div>
</div>
</div>
</div> Requires: ux.min.css
<div class="relative flex items-stretch w-full" style="margin-bottom: 220px;">
<button class="flex items-center gap-1.5 shrink-0 h-11 px-3 bg-base-200 border border-primary border-r-0 rounded-l-field cursor-pointer" type="button">
<span class="text-xl leading-none">🇺🇸</span>
<span class="text-sm font-medium text-base-content min-w-10">+1</span>
<svg class="size-4 text-base-content/60 rotate-180" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z" clip-rule="evenodd"/></svg>
</button>
<input type="tel" class="input flex-1 min-w-0 rounded-l-none" placeholder="(555) 000-0000" />
<div class="absolute top-full left-0 right-0 mt-1 max-h-[300px] bg-base-100 border border-base-300 rounded-box shadow-lg z-50 overflow-hidden">
<div class="p-3 border-b border-base-300">
<input class="w-full py-2 px-3 text-sm bg-base-200 border border-base-300 rounded-field outline-none focus:border-primary" type="text" placeholder="Search countries..." />
</div>
<div class="max-h-60 overflow-y-auto py-2">
<div class="flex items-center gap-3 px-4 py-2.5 bg-primary/10 cursor-pointer">
<span class="text-xl">🇺🇸</span>
<span class="flex-1 text-sm text-base-content">United States</span>
<span class="text-sm font-medium text-base-content/60">+1</span>
</div>
<div class="flex items-center gap-3 px-4 py-2.5 hover:bg-base-200 cursor-pointer">
<span class="text-xl">🇬🇧</span>
<span class="flex-1 text-sm text-base-content">United Kingdom</span>
<span class="text-sm font-medium text-base-content/60">+44</span>
</div>
</div>
</div>
</div> Requires: tw.min.css
// Select country from dropdown:
// options.forEach(opt => opt.addEventListener('click', () => {
// flag.textContent = opt.dataset.flag;
// code.textContent = opt.dataset.code;
// wrapper.classList.remove('phone-input-open');
// })); Sizes
Preview
<!-- Small -->
<div class="phone-input phone-input-sm mb-3">
<button class="phone-input-country" type="button">
<span class="phone-input-flag">🇬🇧</span>
<span class="phone-input-code">+44</span>
</button>
<input type="tel" class="input" placeholder="7911 123456" />
</div>
<!-- Default -->
<div class="phone-input mb-3">
<button class="phone-input-country" type="button">
<span class="phone-input-flag">🇬🇧</span>
<span class="phone-input-code">+44</span>
</button>
<input type="tel" class="input" placeholder="7911 123456" />
</div>
<!-- Large -->
<div class="phone-input phone-input-lg">
<button class="phone-input-country" type="button">
<span class="phone-input-flag">🇬🇧</span>
<span class="phone-input-code">+44</span>
</button>
<input type="tel" class="input" placeholder="7911 123456" />
</div> Requires: ux.min.css
<!-- Small -->
<div class="relative flex items-stretch w-full mb-3">
<button class="flex items-center gap-1 shrink-0 h-9 min-h-9 px-2 bg-base-200 border border-base-300 border-r-0 rounded-l-field cursor-pointer text-base" type="button">
<span class="leading-none">🇬🇧</span>
<span class="text-xs font-medium text-base-content min-w-10">+44</span>
</button>
<input type="tel" class="input flex-1 min-w-0 h-9 min-h-9 text-sm rounded-l-none" placeholder="7911 123456" />
</div>
<!-- Default -->
<div class="relative flex items-stretch w-full mb-3">
<button class="flex items-center gap-1.5 shrink-0 h-11 px-3 bg-base-200 border border-base-300 border-r-0 rounded-l-field cursor-pointer" type="button">
<span class="text-xl leading-none">🇬🇧</span>
<span class="text-sm font-medium text-base-content min-w-10">+44</span>
</button>
<input type="tel" class="input flex-1 min-w-0 rounded-l-none" placeholder="7911 123456" />
</div>
<!-- Large -->
<div class="relative flex items-stretch w-full">
<button class="flex items-center gap-1.5 shrink-0 h-[3.25rem] min-h-[3.25rem] px-4 bg-base-200 border border-base-300 border-r-0 rounded-l-field cursor-pointer" type="button">
<span class="text-2xl leading-none">🇬🇧</span>
<span class="font-medium text-base-content min-w-10">+44</span>
</button>
<input type="tel" class="input flex-1 min-w-0 h-[3.25rem] min-h-[3.25rem] text-lg rounded-l-none" placeholder="7911 123456" />
</div> Requires: tw.min.css
// No JavaScript required for sizing States
Preview
<!-- Error -->
<div class="phone-input-field mb-4">
<label class="phone-input-label">Phone Number</label>
<div class="phone-input phone-input-error">
<button class="phone-input-country" type="button">
<span class="phone-input-flag">🇺🇸</span>
<span class="phone-input-code">+1</span>
</button>
<input type="tel" class="input" value="123" />
</div>
<span class="phone-input-helper phone-input-helper-error">Invalid phone number</span>
</div>
<!-- Disabled -->
<div class="phone-input phone-input-disabled">
<button class="phone-input-country" type="button" disabled>
<span class="phone-input-flag">🇺🇸</span>
<span class="phone-input-code">+1</span>
</button>
<input type="tel" class="input" placeholder="(555) 000-0000" disabled />
</div> Requires: ux.min.css
<!-- Error -->
<div class="flex flex-col gap-1 mb-4">
<label class="text-sm font-medium text-base-content">Phone Number</label>
<div class="relative flex items-stretch w-full">
<button class="flex items-center gap-1.5 shrink-0 h-11 px-3 bg-base-200 border border-error border-r-0 rounded-l-field cursor-pointer" type="button">
<span class="text-xl leading-none">🇺🇸</span>
<span class="text-sm font-medium text-base-content min-w-10">+1</span>
</button>
<input type="tel" class="input flex-1 min-w-0 rounded-l-none border-error focus:ring-3 focus:ring-error/15" value="123" />
</div>
<span class="text-xs text-error">Invalid phone number</span>
</div>
<!-- Disabled -->
<div class="relative flex items-stretch w-full opacity-60">
<button class="flex items-center gap-1.5 shrink-0 h-11 px-3 bg-base-200 border border-base-300 border-r-0 rounded-l-field cursor-not-allowed pointer-events-none" type="button" disabled>
<span class="text-xl leading-none">🇺🇸</span>
<span class="text-sm font-medium text-base-content min-w-10">+1</span>
</button>
<input type="tel" class="input flex-1 min-w-0 rounded-l-none cursor-not-allowed pointer-events-none" placeholder="(555) 000-0000" disabled />
</div> Requires: tw.min.css
// Toggle error: wrapper.classList.toggle('phone-input-error'); | Class | Description |
|---|---|
.phone-input | Container (flex) |
.phone-input-country | Country selector button |
.phone-input-flag | Flag emoji |
.phone-input-code | Dial code text |
.phone-input-arrow | Chevron arrow icon |
.phone-input-open | Open state (shows dropdown, rotates arrow) |
.phone-input-dropdown | Country dropdown panel |
.phone-input-search | Search section in dropdown |
.phone-input-search-input | Search text field |
.phone-input-list | Scrollable country list |
.phone-input-option | Country option row |
.phone-input-option-selected | Selected option highlight |
.phone-input-sm | Small size |
.phone-input-lg | Large size |
.phone-input-error | Error state |
.phone-input-success | Success state |
.phone-input-disabled | Disabled state |
.phone-input-field | Field wrapper (label + helper) |
.phone-input.glass | Glass morphism variant |