vue-a11y-phone

Accessible international phone input for Vue 3 with country selector, keyboard navigation, and WCAG 2.2 AA compliance
958
Install
npm install vue-a11y-phone
Latest Version:1.1.1
Vue:3
License:MIT
Last Updated:May 18, 2026
Links: GitHub  ·  npm
Maintainer: rickardlind

vue-a11y-phone

npm CI License

Accessible international phone input for Vue 3. WCAG 2.2 AA, keyboard-first, screen-reader friendly.

  • 9 KB JS + 1 KB CSS gzipped
  • Zero runtime dependencies
  • Vue 3.4+, peer-only

Install

npm install vue-a11y-phone

Usage

<template>
    <Vue3PhoneInput v-model="phone" default-country="se" :preferred-countries="['se', 'no', 'dk']" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { Vue3PhoneInput } from 'vue-a11y-phone';
import 'vue-a11y-phone/vue-a11y-phone.css';

const phone = ref('');
</script>

Global registration

import { createApp } from 'vue';
import VueA11yPhone from 'vue-a11y-phone';
import 'vue-a11y-phone/vue-a11y-phone.css';

const app = createApp(App);
app.use(VueA11yPhone);

Then use <vue3-phone-input> in any template.

Props

Prop Type Default Description
modelValue string '' Phone number (full international format with dial code)
defaultCountry string 'us' Default country ISO2 code (lowercase)
preferredCountries string[] [] Countries shown at the top of the dropdown
onlyCountries string[] [] Only show these countries
ignoredCountries string[] [] Hide these countries from the dropdown
disabled boolean false Disable the input and dropdown
inputId string — Native input id attribute
name string 'phone' Native input name attribute
required boolean false Native input required attribute
placeholder string — Native input placeholder
autocomplete string 'tel' Native input autocomplete attribute
invalid boolean false Marks the input as invalid (styling + aria-invalid)
ariaDescribedby string — Additional aria-describedby ID(s) for the input
inputLabel string 'Phone number' aria-label for the phone input
countryLabel string 'Select country code' aria-label for the country selector button
searchPlaceholder string 'Search country...' Placeholder and aria-label for the country search
searchResultsLabel (count: number) => string — Custom function for screen reader search result announcements
formatDisplay (digits, iso2) => string — Custom display formatter. Overrides built-in national formatting. Model value stays unformatted
useNationalFormat boolean true Apply built-in country-aware grouping (e.g. 070 123 45 67 for SE) when no formatDisplay is provided
stripLeadingZero boolean true Strip a single leading 0 from typed input (common in countries with a trunk prefix)
showNativeNames boolean false Show each country's native name next to the English name in the dropdown
maxLength number per-country Max digits in the national number. Defaults to the per-country max (or 15)
styleClasses string | string[] | Record<string, boolean> — Custom CSS classes for the wrapper element

Events

Event Payload Description
update:modelValue string Phone number changed (full international format)
update:valid boolean Validity changed (length within country's min/max). Use with v-model:valid
country-changed Country Selected country changed
open — Dropdown opened
close — Dropdown closed
focus FocusEvent Phone input received focus
blur FocusEvent Phone input lost focus

Exposed methods

const phoneRef = ref<InstanceType<typeof Vue3PhoneInput>>();

phoneRef.value?.focus();
phoneRef.value?.blur();
phoneRef.value?.validate(); // boolean — current validity

Helpers

The package also exports utilities for working with phone numbers outside the component:

import {
    allCountries,
    formatNational,
    formatSwedishPhone,
    getCountryMeta,
    getNativeName,
    isValidNationalNumber
} from 'vue-a11y-phone';

formatNational('se', '701234567');     // → "70 123 45 67"
formatNational('us', '5551234567');    // → "555 123 4567"
isValidNationalNumber('se', '7012345'); // → true (7+ digits)
getCountryMeta('gb');                   // → { minLength, maxLength, groups }
getNativeName('de');                    // → "Deutschland"

Theming

All styles use CSS custom properties. Override any --v3pi-* variable to customize:

.v3pi {
    --v3pi-border-color: #ced4da;
    --v3pi-border-radius: 0.375rem;
    --v3pi-bg: #ffffff;
    --v3pi-font-family: inherit;
    --v3pi-font-size: 1rem;
    --v3pi-padding-y: 0.375rem;
    --v3pi-padding-x: 0.75rem;
    --v3pi-focus-color: #0d6efd;
    --v3pi-focus-width: 0.25rem;
    --v3pi-error-color: #dc3545;
    --v3pi-hover-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
    --v3pi-dropdown-shadow: 0 0.125rem 0.5rem rgba(0, 0, 0, 0.15);
    --v3pi-item-hover-bg: #f8f9fa;
    --v3pi-item-selected-bg: #e9ecef;
    --v3pi-divider-color: #dee2e6;
    --v3pi-disabled-bg: #e9ecef;
    --v3pi-disabled-opacity: 0.65;
}

Accessibility

This component is built for WCAG 2.2 AA compliance:

  • Native <input type="tel"> — no fake inputs or masking
  • Real <button> for the country selector with aria-expanded, aria-haspopup, and aria-controls
  • WAI-ARIA combobox pattern for country search (role="combobox", aria-autocomplete, aria-activedescendant)
  • role="listbox" and role="option" with aria-selected for country list
  • aria-live="polite" region announcing search result count to screen readers
  • aria-label on the phone input, country button, and search input
  • aria-describedby linking the phone input to a country hint for screen readers
  • aria-invalid support for validation states
  • Full keyboard navigation: Arrow keys, Enter, Escape, Tab
  • :focus-visible outlines on all interactive elements
  • Unique IDs per instance — safe to use multiple components on one page

Keyboard navigation

DOM focus stays on the search input while the dropdown is open; only aria-activedescendant moves through the options. This follows the WAI-ARIA APG combobox pattern.

Key Context Action
Arrow Down / Up Country button Opens dropdown
Enter / Space Country button Opens dropdown
Arrow Down / Up Search input Highlights next/previous country
Home / End Search input Highlights first/last country
Enter Search input Selects highlighted country
Escape Dropdown open Closes dropdown, returns focus to button
Tab Search input Closes dropdown and moves focus to next focusable element

TypeScript

Full type definitions are included. Import types directly:

import type { Country, CountryMeta, Vue3PhoneInputProps, Vue3PhoneInputEmits } from 'vue-a11y-phone';

Browser support

The component targets evergreen browsers (Chrome, Firefox, Safari, Edge). It uses Intl.DisplayNames for native country names — older browsers fall back to the English name.

Contributing

Issues and pull requests welcome. See CONTRIBUTING.md.

License

MIT © Rickard Lindbom

Related Packages

@teselia/phone

Accessible international phone input with country calling code, shipped as a fra...

247 0
maskdial

Modern phone number formatting library with React/Vue components, TypeScript sup...

1,047 5
vue-a11y-directives

A comprehensive set of Vue 3 directives for building accessible web applications...

20,510
tel-input-reka-ui

International Telephone Input for Vue 3 with Reka UI primitives

1,463 0

Version History

Version Released Vue License
1.1.1 ^3.4.0 MIT
1.1.0 ^3.4.0 MIT
1.0.1 ^3.4.0 MIT
1.0.0 ^3.4.0 MIT