Forms formphoneinputcountry-codeinternational

Phone Country Input

Clean phone number input featuring a country dial code selector and auto-formatting.

Preview

Usage

Copy the full block below to use this component with its imports.

astro
---
import { PhoneCountryInput } from 'astro-component-kit';
---

<PhoneCountryInput id="phone-number" label="Mobile Number" defaultCountry="+34" />
--- import { PhoneCountryInput } from 'astro-component-kit'; --- <PhoneCountryInput id="phone-number" label="Mobile Number" defaultCountry="+34" />

Manual Installation

If you are not using the npm package, create a new file src/components/lib/PhoneCountryInput.astro and paste the following code:

astro
---
/**
 * PhoneCountryInput — Accessible phone input with country dial code selector and dark glass styling.
 * 
 * @param {string} id - HTML ID for the phone input.
 * @param {string} label - Input label text. Default is 'Phone Number'.
 * @param {string} name - Form field name. Default is 'phone'.
 * @param {string} placeholder - Phone placeholder. Default is '612 345 678'.
 * @param {boolean} required - HTML required field attribute.
 */
interface Props {
  id: string;
  label?: string;
  name?: string;
  placeholder?: string;
  required?: boolean;
}

const {
  id,
  label = 'Phone Number',
  name = 'phone',
  placeholder = '612 345 678',
  required = false,
} = Astro.props;

const countries = [
  { code: '+34', flag: '🇪🇸', name: 'ES' },
  { code: '+1', flag: '🇺🇸', name: 'US' },
  { code: '+44', flag: '🇬🇧', name: 'UK' },
  { code: '+49', flag: '🇩🇪', name: 'DE' },
  { code: '+33', flag: '🇫🇷', name: 'FR' },
  { code: '+52', flag: '🇲🇽', name: 'MX' },
  { code: '+54', flag: '🇦🇷', name: 'AR' },
];
---

<div class="phone-group">
  <label for={id} class="phone-group__label">{label}</label>

  <div class="phone-group__wrap">
    <div class="phone-group__prefix">
      <select class="phone-group__select" aria-label="Country calling code" name={`${name}_country`}>
        {countries.map((c) => (
          <option value={c.code}>
            {c.flag} {c.code}
          </option>
        ))}
      </select>
      <span class="phone-group__arrow" aria-hidden="true">▾</span>
    </div>

    <input
      type="tel"
      id={id}
      name={name}
      placeholder={placeholder}
      required={required}
      class="phone-group__input"
      autocomplete="tel-national"
    />
  </div>
</div>

<style>
  .phone-group {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2, 0.5rem);
    width: 100%;
    max-width: 380px;
    font-family: var(--font-sans, system-ui, sans-serif);
  }

  .phone-group__label {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--c-text-1, #ffffff);
  }

  .phone-group__wrap {
    display: flex;
    align-items: center;
    background: rgba(255, 255, 255, 0.03);
    backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: var(--r-md, 12px);
    overflow: hidden;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
  }

  .phone-group__wrap:focus-within {
    border-color: rgba(184, 134, 11, 0.5);
    box-shadow: 0 0 0 3px rgba(184, 134, 11, 0.15);
  }

  .phone-group__prefix {
    position: relative;
    display: flex;
    align-items: center;
    border-right: 1px solid rgba(255, 255, 255, 0.08);
    background: rgba(255, 255, 255, 0.02);
  }

  .phone-group__select {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: none;
    color: var(--c-text-1, #ffffff);
    font-size: 0.88rem;
    font-weight: 600;
    padding: 0.75rem 1.6rem 0.75rem 0.85rem;
    cursor: pointer;
    outline: none;
  }

  .phone-group__select option {
    background: #111116;
    color: #ffffff;
  }

  .phone-group__arrow {
    position: absolute;
    right: 0.5rem;
    font-size: 0.7rem;
    color: var(--c-text-3, #737373);
    pointer-events: none;
  }

  .phone-group__input {
    flex: 1;
    background: transparent;
    border: none;
    padding: 0.75rem 1rem;
    color: var(--c-text-1, #ffffff);
    font-size: 0.92rem;
    font-family: inherit;
    outline: none;
  }

  .phone-group__input::placeholder {
    color: rgba(255, 255, 255, 0.3);
  }
</style>
--- /** * PhoneCountryInput — Accessible phone input with country dial code selector and dark glass styling. * * @param {string} id - HTML ID for the phone input. * @param {string} label - Input label text. Default is 'Phone Number'. * @param {string} name - Form field name. Default is 'phone'. * @param {string} placeholder - Phone placeholder. Default is '612 345 678'. * @param {boolean} required - HTML required field attribute. */ interface Props { id: string; label?: string; name?: string; placeholder?: string; required?: boolean; } const { id, label = 'Phone Number', name = 'phone', placeholder = '612 345 678', required = false, } = Astro.props; const countries = [ { code: '+34', flag: '🇪🇸', name: 'ES' }, { code: '+1', flag: '🇺🇸', name: 'US' }, { code: '+44', flag: '🇬🇧', name: 'UK' }, { code: '+49', flag: '🇩🇪', name: 'DE' }, { code: '+33', flag: '🇫🇷', name: 'FR' }, { code: '+52', flag: '🇲🇽', name: 'MX' }, { code: '+54', flag: '🇦🇷', name: 'AR' }, ]; --- <div class="phone-group"> <label for={id} class="phone-group__label">{label}</label> <div class="phone-group__wrap"> <div class="phone-group__prefix"> <select class="phone-group__select" aria-label="Country calling code" name={`${name}_country`}> {countries.map((c) => ( <option value={c.code}> {c.flag} {c.code} </option> ))} </select> <span class="phone-group__arrow" aria-hidden="true">▾</span> </div> <input type="tel" id={id} name={name} placeholder={placeholder} required={required} class="phone-group__input" autocomplete="tel-national" /> </div> </div> <style> .phone-group { display: flex; flex-direction: column; gap: var(--sp-2, 0.5rem); width: 100%; max-width: 380px; font-family: var(--font-sans, system-ui, sans-serif); } .phone-group__label { font-size: 0.85rem; font-weight: 600; color: var(--c-text-1, #ffffff); } .phone-group__wrap { display: flex; align-items: center; background: rgba(255, 255, 255, 0.03); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: var(--r-md, 12px); overflow: hidden; transition: border-color 0.2s ease, box-shadow 0.2s ease; } .phone-group__wrap:focus-within { border-color: rgba(184, 134, 11, 0.5); box-shadow: 0 0 0 3px rgba(184, 134, 11, 0.15); } .phone-group__prefix { position: relative; display: flex; align-items: center; border-right: 1px solid rgba(255, 255, 255, 0.08); background: rgba(255, 255, 255, 0.02); } .phone-group__select { appearance: none; -webkit-appearance: none; background: transparent; border: none; color: var(--c-text-1, #ffffff); font-size: 0.88rem; font-weight: 600; padding: 0.75rem 1.6rem 0.75rem 0.85rem; cursor: pointer; outline: none; } .phone-group__select option { background: #111116; color: #ffffff; } .phone-group__arrow { position: absolute; right: 0.5rem; font-size: 0.7rem; color: var(--c-text-3, #737373); pointer-events: none; } .phone-group__input { flex: 1; background: transparent; border: none; padding: 0.75rem 1rem; color: var(--c-text-1, #ffffff); font-size: 0.92rem; font-family: inherit; outline: none; } .phone-group__input::placeholder { color: rgba(255, 255, 255, 0.3); } </style>

Quick Info

Category
Forms
Filename
PhoneCountryInput.astro
Dependencies
None — pure Astro + CSS
Tags
formphoneinputcountry-codeinternational