Skip to content

SelectBoxField

Custom SelectBox component with support for multiple selection, integrated search, option groups and complete accessibility according to W3C standards.

Usage examples

Basic SelectBoxField

Simple SelectBox

vue
<script setup>
const selectedValue = ref('')
const options = [
  { value: 'option1', label: 'Option 1' },
  { value: 'option2', label: 'Option 2' },
  { value: 'option3', label: 'Option 3' },
  { value: 'option4', label: 'Option 4', disabled: true }
]
</script>

<template>
  <SuSelectBoxField 
    :options="options"
    label="Simple selection"
    placeholder="Choose an option..."
    v-model="selectedValue"
  />
</template>

Multiple selection

Multiple selection with tags

vue
<script setup>
const selectedTechnologies = ref([])
const technologies = [
  { value: 'js', label: 'JavaScript' },
  { value: 'ts', label: 'TypeScript' },
  { value: 'vue', label: 'Vue.js' },
  { value: 'react', label: 'React' },
  { value: 'angular', label: 'Angular' }
]
</script>

<template>
  <SuSelectBoxField 
    :options="technologies"
    :multiple="true"
    :clearable="true"
    label="Technologies"
    placeholder="Select your technologies..."
    message="You can select multiple options"
    v-model="selectedTechnologies"
  />
</template>

SelectBox with search

vue
<script setup>
const selectedCountry = ref('')
const countries = [
  { value: 'fr', label: 'France' },
  { value: 'us', label: 'United States' },
  { value: 'de', label: 'Germany' },
  { value: 'es', label: 'Spain' },
  { value: 'it', label: 'Italy' },
  { value: 'uk', label: 'United Kingdom' },
  { value: 'ca', label: 'Canada' },
  { value: 'jp', label: 'Japan' }
]
</script>

<template>
  <SuSelectBoxField 
    :options="countries"
    :searchable="true"
    :clearable="true"
    label="Country"
    placeholder="Search for a country..."
    searchPlaceholder="Type to search..."
    v-model="selectedCountry"
  />
</template>

Options with icons and descriptions

Rich options

vue
<script setup>
import { ref } from 'vue'
import { StarIcon, BuildingOfficeIcon, GlobeAltIcon } from '@heroicons/vue/24/outline'

const selectedPlan = ref('')
const plans = [
  { 
    value: 'basic', 
    label: 'Basic Plan', 
    description: 'Basic features to get started',
    icon: StarIcon 
  },
  { 
    value: 'pro', 
    label: 'Pro Plan', 
    description: 'Advanced features for professionals',
    icon: BuildingOfficeIcon 
  },
  { 
    value: 'enterprise', 
    label: 'Enterprise Plan', 
    description: 'Complete solution for large companies',
    icon: GlobeAltIcon 
  }
]
</script>

<template>
  <SuSelectBoxField 
    :options="plans"
    :searchable="true"
    label="Subscription plan"
    placeholder="Choose your plan..."
    v-model="selectedPlan"
  />
</template>

Grouped options

Options organized in groups

vue
<template>
  <SuSelectBoxField 
    :groups="[
      {
        label: 'Fruits',
        options: [
          { value: 'apple', label: 'Apple' },
          { value: 'banana', label: 'Banana' },
          { value: 'orange', label: 'Orange' }
        ]
      },
      {
        label: 'Vegetables',
        options: [
          { value: 'carrot', label: 'Carrot' },
          { value: 'broccoli', label: 'Broccoli' },
          { value: 'spinach', label: 'Spinach' }
        ]
      }
    ]"
    :searchable="true"
    :multiple="true"
    label="Food products"
    placeholder="Select products..."
    v-model="selectedProducts"
  />
</template>

API

Props

PropTypeDefaultDescription
optionsSelectOption[][]List of available options
groupsSelectGroup[][]Options organized in groups
valuestring | number | (string | number)[]undefinedSelected value
multiplebooleanfalseMultiple selection
searchablebooleanfalseIntegrated search
clearablebooleanfalseClear button
size'sm' | 'md' | 'lg''md'SelectBox size
state'default' | 'error' | 'success' | 'warning''default'Visual state
disabledbooleanfalseDisable SelectBox
readonlybooleanfalseRead-only SelectBox
requiredbooleanfalseRequired field
placeholderstring'Select an option...'Placeholder text
searchPlaceholderstring'Search...'Search placeholder
noOptionsTextstring'No options available'Text when no options
noResultsTextstring'No results found'Text when no search results
maxHeightstring'200px'Maximum dropdown height
loadingbooleanfalseLoading state
closeOnSelectbooleantrueClose after selection
maxSelectedItemsnumberundefinedMultiple selection limit
labelstringundefinedSelectBox label
messagestringundefinedDisplayed message

Option types

SelectOption

typescript
interface SelectOption {
  value: string | number
  label: string
  disabled?: boolean
  group?: string
  icon?: Component
  description?: string
}

SelectGroup

typescript
interface SelectGroup {
  label: string
  options: SelectOption[]
}

Events

EventTypeDescription
@update:value(value: string | number | (string | number)[]) => voidEmitted when value changes
@change(value: string | number | (string | number)[]) => voidEmitted on change
@open() => voidEmitted when dropdown opens
@close() => voidEmitted when dropdown closes
@search(query: string) => voidEmitted during search
@focus(event: FocusEvent) => voidEmitted on focus
@blur(event: FocusEvent) => voidEmitted on blur

Accessibility

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

✅ Accessibility features

  • Keyboard navigation: Complete support for arrow keys, Enter, Space, Escape, Home/End
  • ARIA attributes: role="combobox", aria-expanded, aria-controls, aria-multiselectable
  • Focus trap: Focus management in open dropdown
  • Voice announcements: Messages for screen readers during selections
  • Associated labels: Each SelectBox has a properly associated label
  • State messages: Error/success/warning messages with aria-live
  • Color contrast: WCAG AA compliant contrast ratios (4.5:1 minimum)
  • Visible focus: Clear and contrasted focus indicators
  • RTL support: Complete support for right-to-left languages
  • Dark mode: Automatically adapted contrast
  • High contrast: Support for prefers-contrast: high
  • Reduced motion: Respects prefers-reduced-motion

🎯 Best practices

vue
<!-- SelectBox with validation and accessibility -->
<SuSelectBoxField 
  :options="options"
  :required="true"
  label="Category"
  placeholder="Select a category..."
  :state="hasError ? 'error' : 'default'"
  :message="hasError ? 'Please select a category' : 'Choose the category that best fits'"
  v-model="category"
/>

<!-- Multiple SelectBox with limit -->
<SuSelectBoxField 
  :options="skills"
  :multiple="true"
  :maxSelectedItems="5"
  :searchable="true"
  :clearable="true"
  label="Skills"
  placeholder="Select your skills..."
  message="Maximum 5 skills"
  v-model="selectedSkills"
/>

<!-- SelectBox with groups and search -->
<SuSelectBoxField 
  :groups="categorizedOptions"
  :searchable="true"
  label="Product"
  placeholder="Search for a product..."
  searchPlaceholder="Type to filter..."
  ariaLabel="Product selector by category"
  v-model="selectedProduct"
/>

Keyboard navigation

KeyAction
TabNavigate to/from SelectBox
Enter / SpaceOpen/close dropdown, select focused option
Arrow DownOpen dropdown or navigate to next option
Arrow UpNavigate to previous option
HomeGo to first option
EndGo to last option
EscapeClose dropdown
A-ZQuick search by first letter (if no integrated search)

Advanced usage examples

User profile form

vue
<script setup>
import { ref } from 'vue'
import { UserIcon, BuildingOfficeIcon, GlobeAltIcon } from '@heroicons/vue/24/outline'

const userType = ref('')
const skills = ref([])
const country = ref('')

const userTypes = [
  { value: 'individual', label: 'Individual', icon: UserIcon },
  { value: 'business', label: 'Business', icon: BuildingOfficeIcon },
  { value: 'organization', label: 'Organization', icon: GlobeAltIcon }
]

const skillOptions = [
  { value: 'js', label: 'JavaScript', group: 'Frontend' },
  { value: 'ts', label: 'TypeScript', group: 'Frontend' },
  { value: 'vue', label: 'Vue.js', group: 'Frontend' },
  { value: 'node', label: 'Node.js', group: 'Backend' },
  { value: 'python', label: 'Python', group: 'Backend' },
  { value: 'docker', label: 'Docker', group: 'DevOps' }
]

const countries = [
  { value: 'fr', label: 'France' },
  { value: 'us', label: 'United States' },
  { value: 'de', label: 'Germany' }
]
</script>

<template>
  <form class="space-y-6">
    <SuSelectBoxField 
      :options="userTypes"
      :required="true"
      label="User type"
      placeholder="Select your type..."
      v-model="userType"
    />
    
    <SuSelectBoxField 
      :options="skillOptions"
      :multiple="true"
      :searchable="true"
      :clearable="true"
      :maxSelectedItems="3"
      label="Skills"
      placeholder="Select your skills..."
      message="Maximum 3 skills"
      v-model="skills"
    />
    
    <SuSelectBoxField 
      :options="countries"
      :searchable="true"
      :clearable="true"
      label="Country"
      placeholder="Search your country..."
      v-model="country"
    />
  </form>
</template>

Dynamic loading SelectBox

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

const searchQuery = ref('')
const options = ref([])
const loading = ref(false)
const selectedValue = ref('')

const searchOptions = async (query) => {
  loading.value = true
  try {
    // API simulation
    const response = await fetch(`/api/search?q=${query}`)
    options.value = await response.json()
  } finally {
    loading.value = false
  }
}

watch(searchQuery, (newQuery) => {
  if (newQuery.length > 2) {
    searchOptions(newQuery)
  }
})
</script>

<template>
  <SuSelectBoxField 
    :options="options"
    :searchable="true"
    :loading="loading"
    label="Dynamic search"
    placeholder="Type to search..."
    searchPlaceholder="Minimum 3 characters..."
    @search="searchQuery = $event"
    v-model="selectedValue"
  />
</template>

Publié sous licence MIT.