Skip to content

Input

Flexible input component with complete HTML type support, prefixes/suffixes, text alignment, RTL/LTR direction and accessibility according to W3C standards.

Description

SuInput is an enhanced input field component that extends the capabilities of the native HTML <input> element. It offers advanced support for text/icon prefixes/suffixes, text alignment, and complete accessibility management.

Usage examples

Basic usage

Simple text input

vue
<template>
  <SuInput type="text" placeholder="Enter text" />
</template>

with FormField

Types d'input supportés

vue
<template>
  <SuFormField label="User name">
    <SuInput placeholder="Enter username" />
  </SuFormField>
</template>

Find out more about how to use SuFormField.

Input types

Types d'input supportés

vue
<template>
  <SuFormField label="Texte" >
    <SuInput type="text" placeholder="Enter text" />
  </SuFormField>
  <SuFormField label="Email" >
    <SuInput type="email" placeholder="nom@exemple.com" />
  </SuFormField>
  <SuFormField label="Password" >
    <SuInput type="password" placeholder="••••••••" />
  </SuFormField>
  <SuFormField label="number" >
    <SuInput type="number" placeholder="123" />
  </SuFormField>
  <SuFormField label="Phone" >
    <SuInput type="tel" placeholder="+33 1 23 45 67 89" />
  </SuFormField>
  <SuFormField label="Url" >
    <SuInput type="url" placeholder="https://exemple.com" />
  </SuFormField>
  <SuFormField label="Search" >
    <SuInput type="search" placeholder="Search..." />
  </SuFormField>
  <SuFormField label="Date" >
    <SuInput type="date" />
  </SuFormField>
</template>

Prefixes and suffixes

Text prefixes and suffixes

vue
<template>
  <SuFormField label="Price" >
    <SuInput placeholder="0.00" suffix="€" text-align="right" type="number" />
  </SuFormField>
  <SuFormField label="Email" >
    <SuInput placeholder="nom" suffix="@entreprise.com" />
  </SuFormField>
  <SuFormField label="Website" >
    <SuInput placeholder="monsite" prefix="https://" suffix=".com" />
  </SuFormField>
  <SuFormField label="Phone" >
    <SuInput placeholder="123456789" prefix="+33" />
  </SuFormField>
</template>

Prefixes and suffixes with icons

Prefixes and suffixes with icons

vue
<script setup>
import { 
  MagnifyingGlassIcon, 
  AtSymbolIcon, 
  LockClosedIcon, 
  UserIcon 
} from '@heroicons/vue/24/outline'
</script>

<template>
  <SuFormField label="Search" >
    <SuInput placeholder="Search..." :prefixIcon="MagnifyingGlassIcon" />
  </SuFormField>
  <SuFormField label="Email" >
    <SuInput placeholder="votre@email.com" :prefixIcon="AtSymbolIcon" />
  </SuFormField>
  <SuFormField label="Password" >
    <SuInput placeholder="••••••••" type="password" :prefixIcon="LockClosedIcon" />
  </SuFormField>
  <SuFormField label="User" >
    <SuInput placeholder="Username" :prefixIcon="UserIcon" />
  </SuFormField>
</template>

Sizes

Available sizes

vue
<template>
  <SuFormField label="Small" >
    <SuInput size="sm" placeholder="Small input" />
  </SuFormField>
  <SuFormField label="Medium" >
    <SuInput size="md" placeholder="Medium input" />
  </SuFormField>
  <SuFormField label="Large" >
    <SuInput size="lg" placeholder="Large input" />
  </SuFormField>
</template>

States and validation

Input states

vue
<template>
  <SuFormField 
    label="Default state" 
    message="Help text to guide the user"
  >
    <SuInput placeholder="Enter text" />
  </SuFormField>
  <SuFormField 
    state="error"
    label="Error state" 
    message="This field contains an error"
  >
    <SuInput value="incorrect value" />
  </SuFormField>
  <SuFormField 
    state="success"
    label="Success state" 
    message="Valid value!"
  >
    <SuInput value="correct value" />
  </SuFormField>
  <SuFormField 
    state="warning"
    label="Warning state" 
    message="Be careful with this value"
  >
    <SuInput value="warning" />
  </SuFormField>
</template>

Text alignment

Text alignment

vue
<template>
  <SuFormField label="Left alignment" >
    <SuInput textAlign="left" value="Left aligned" />
  </SuFormField>
  <SuFormField label="Centered alignment" >
    <SuInput textAlign="center" value="Centered" />
  </SuFormField>
  <SuFormField label="Right alignment" >
    <SuInput textAlign="right" value="Right aligned" />
  </SuFormField>
</template>

RTL support (right-to-left)

RTL language support

vue
<template>
  <div dir="rtl">
    <SuInput 
      placeholder="أدخل النص هنا"
      value="النص العربي"
    />
    <SuInput 
      placeholder="הכנס טקסט כאן"
      value="טקסט עברי"
    />
    <SuInput 
      placeholder="0.00"
      suffix="$"
      type="number"
    />
  </div>
</template>

Disabled and readonly states

Special states

vue
<template>
  <SuInput 
    :disabled="true"
    value="Disabled value"
  />
  <SuInput 
    :readonly="true"
    value="Read-only value"
  />
  <SuInput 
    :required="true"
    placeholder="This field is required"
  />
</template>

API

Props

PropTypeDefaultDescription
typeInputType'text'HTML input type
size'sm' | 'md' | 'lg''md'Input size
state'default' | 'error' | 'success' | 'warning''default'Input visual state
disabledbooleanfalseDisable the input
readonlybooleanfalseRead-only input
requiredbooleanfalseRequired field
placeholderstringundefinedPlaceholder text
valuestring | numberundefinedInput value
modelValuestring | numberundefinedv-model value
prefixstringundefinedText prefix
suffixstringundefinedText suffix
prefixIconComponentundefinedPrefix icon (Heroicons component)
suffixIconComponentundefinedSuffix icon (Heroicons component)
textAlign'left' | 'center' | 'right''left'Text alignment

Accessibility attributes

PropTypeDefaultDescription
ariaLabelstringundefinedAccessible label for screen readers
ariaDescribedBystringundefinedID of description element
ariaInvalidbooleanundefinedIndicates if value is invalid
ariaRequiredbooleanundefinedIndicates if field is required

HTML validation attributes

PropTypeDefaultDescription
autocompletestringundefinedHTML autocomplete attribute
minnumber | stringundefinedMinimum value (number, date types)
maxnumber | stringundefinedMaximum value (number, date types)
stepnumber | stringundefinedStep for numbers
minLengthnumberundefinedMinimum text length
maxLengthnumberundefinedMaximum text length
patternstringundefinedValidation regular expression

Events

EventTypeDescription
@update:modelValue(value: string | number) => voidEmitted when value changes (v-model)
@input(event: Event) => voidEmitted on input
@change(event: Event) => voidEmitted on change
@focus(event: FocusEvent) => voidEmitted on focus
@blur(event: FocusEvent) => voidEmitted on blur
@keydown(event: KeyboardEvent) => voidEmitted on key press
@keyup(event: KeyboardEvent) => voidEmitted on key release

Exposed methods

MethodTypeDescription
focus()() => voidFocuses the input
select()() => voidSelects input text
blur()() => voidRemoves focus from input
inputRefRef<HTMLInputElement>Reference to native input element

Accessibility

The Input component follows WCAG 2.1 AA standards and W3C best practices:

✅ Accessibility features

  • ARIA attributes: Complete support for ARIA attributes (aria-label, aria-describedby, aria-invalid, etc.)
  • Color contrast: WCAG AA compliant contrast ratios (4.5:1 minimum)
  • Visible focus: Clear and contrasted focus indicator with outline
  • Minimum sizes: Respects minimum touch target sizes (44x44px minimum)
  • RTL support: Automatic handling of right-to-left languages
  • Dark mode: Adapted contrast in dark mode
  • High contrast: Support for prefers-contrast: high
  • Reduced motion: Respects prefers-reduced-motion
  • Clear states: Distinct visual states for error, success, warning

🎯 Best practices

vue
<!-- Input with validation and accessibility -->
<SuInput 
  type="email"
  placeholder="name@example.com"
  :required="true"
  autocomplete="email"
  ariaLabel="Email address"
  ariaDescribedBy="email-help"
  v-model="email"
/>

<!-- Input with error handling -->
<SuInput 
  type="password"
  placeholder="••••••••"
  :required="true"
  :state="hasError ? 'error' : 'default'"
  :ariaInvalid="hasError"
  :minLength="8"
  autocomplete="new-password"
  v-model="password"
/>

<!-- Input with prefix and numeric validation -->
<SuInput 
  type="number"
  :required="true"
  :min="0"
  :max="120"
  placeholder="Age"
  ariaLabel="Your age in years"
  v-model="age"
/>

Supported types

The Input component supports all HTML5 input types:

  • text - Free text
  • email - Email address with validation
  • password - Masked password
  • number - Number with controls
  • tel - Phone number
  • url - URL with validation
  • search - Search field
  • date - Date picker
  • time - Time picker
  • datetime-local - Local date and time
  • month - Month picker
  • week - Week picker
  • color - Color picker
  • file - File selection

Advanced usage examples

Search input with icon

vue
<script setup>
import { ref } from 'vue'
import { MagnifyingGlassIcon } from '@heroicons/vue/24/outline'

const searchQuery = ref('')

const handleSearch = () => {
  console.log('Search:', searchQuery.value)
}
</script>

<template>
  <SuInput 
    type="search"
    placeholder="Search..."
    :prefixIcon="MagnifyingGlassIcon"
    v-model="searchQuery"
    @keyup.enter="handleSearch"
  />
</template>

Price field with formatting

vue
<script setup>
import { ref, computed } from 'vue'

const price = ref('')

const formattedPrice = computed({
  get: () => price.value,
  set: (value) => {
    const numericValue = value.replace(/[^\d.,]/g, '')
    price.value = numericValue
  }
})
</script>

<template>
  <SuInput 
    type="number"
    suffix="$"
    placeholder="0.00"
    :step="0.01"
    :min="0"
    textAlign="right"
    v-model="formattedPrice"
  />
</template>

Input with real-time validation

vue
<script setup>
import { ref, computed } from 'vue'
import { AtSymbolIcon } from '@heroicons/vue/24/outline'

const email = ref('')

const emailState = computed(() => {
  if (!email.value) return 'default'
  const isValid = /\S+@\S+\.\S+/.test(email.value)
  return isValid ? 'success' : 'error'
})
</script>

<template>
  <SuInput 
    type="email"
    :prefixIcon="AtSymbolIcon"
    placeholder="name@example.com"
    :state="emailState"
    autocomplete="email"
    v-model="email"
  />
</template>

International phone input

vue
<script setup>
import { ref } from 'vue'

const phoneNumber = ref('')
const countryCode = ref('+1')
</script>

<template>
  <SuInput 
    type="tel"
    :prefix="countryCode"
    placeholder="234 567 8900"
    autocomplete="tel"
    v-model="phoneNumber"
  />
</template>

Input with suffix actions

vue
<script setup>
import { ref } from 'vue'
import { EyeIcon, EyeSlashIcon } from '@heroicons/vue/24/outline'

const password = ref('')
const showPassword = ref(false)

const togglePasswordVisibility = () => {
  showPassword.value = !showPassword.value
}
</script>

<template>
  <div class="relative">
    <SuInput 
      :type="showPassword ? 'text' : 'password'"
      placeholder="••••••••"
      autocomplete="current-password"
      v-model="password"
    />
    <button 
      type="button"
      class="absolute right-3 top-1/2 -translate-y-1/2"
      @click="togglePasswordVisibility"
      aria-label="Show/hide password"
    >
      <component :is="showPassword ? EyeSlashIcon : EyeIcon" class="w-5 h-5" />
    </button>
  </div>
</template>

Usage with SuFormField

For a complete experience with label, message and validation, use SuInput with SuFormField:

vue
<template>
  <SuFormField 
    label="Email"
    :required="true"
    :state="emailError ? 'error' : 'default'"
    :message="emailError || 'Used for login'"
  >
    <SuInput 
      type="email"
      placeholder="name@example.com"
      :prefixIcon="AtSymbolIcon"
      autocomplete="email"
      v-model="email"
    />
  </SuFormField>
</template>

Implementation notes

  • The component automatically handles text direction (LTR/RTL) based on context
  • Prefixes and suffixes are positioned responsively
  • Icons are automatically sized according to input size
  • Focus ring is always visible for keyboard accessibility
  • Validation states are communicated visually and via ARIA

Publié sous licence MIT.