Skip to main content

Select

A dropdown selection component with support for options, validation, and consistent typography integration

Select

The Select component provides a dropdown interface for choosing from predefined options. It includes support for disabled options, validation states, and size variants while maintaining consistency with other form components through typography tokens.

Basic Select

Choose from predefined options

Select the most relevant category

Choose your country of residence

Selected Values:

Category: None
Country: None

API Reference

Props

PropTypeDefaultDescription
idstringrequiredUnique identifier for the select
namestringidForm field name
labelstringrequiredField label text
valuestring''Selected value (bindable)
optionsOption[]requiredArray of selectable options
placeholderstring'Select option'Placeholder text
requiredbooleanfalseWhether field is required
disabledbooleanfalseDisables the select
errorstring \| nullnullError message to display
helperstring-Helper text below select
size'sm' \| 'md' \| 'lg''md'Select size variant

Option Interface

interface Option {
  value: string;        // The value when selected
  label: string;        // Display text
  disabled?: boolean;   // Whether option is selectable
}

Usage Examples

Basic Select

<script>
  import Select from '$lib/shared/components/forms/Select.svelte';
  
  let category = $state('');
  
  const categories = [
    { value: 'tech', label: 'Technology' },
    { value: 'design', label: 'Design' },
    { value: 'marketing', label: 'Marketing' }
  ];
</script>

<Select
  id="category"
  label="Category"
  bind:value={category}
  {options}={categories}
  placeholder="Choose category..."
/>

With Disabled Options

<script>
  import Select from '$lib/shared/components/forms/Select.svelte';
  
  let priority = $state('');
  
  const priorities = [
    { value: 'low', label: 'Low Priority' },
    { value: 'medium', label: 'Medium Priority' },
    { value: 'high', label: 'High Priority' },
    { value: 'critical', label: 'Critical (Admin Only)', disabled: true }
  ];
</script>

<Select
  id="priority"
  label="Task Priority"
  bind:value={priority}
  options={priorities}
  helper="Critical priority requires admin access"
/>

Pre-selected Value

<script>
  import Select from '$lib/shared/components/forms/Select.svelte';
  
  let country = $state('us'); // Pre-selected
  
  const countries = [
    { value: 'us', label: 'United States' },
    { value: 'uk', label: 'United Kingdom' },
    { value: 'ca', label: 'Canada' }
  ];
</script>

<Select
  id="country"
  label="Country"
  bind:value={country}
  options={countries}
  helper="Default selection applied"
/>

With Validation

<script>
  import Select from '$lib/shared/components/forms/Select.svelte';
  
  let selection = $state('');
  let error = $state(null);
  
  const options = [
    { value: 'option1', label: 'Option 1' },
    { value: 'option2', label: 'Option 2' }
  ];
  
  function validate() {
    if (!selection) {
      error = 'Please make a selection';
    } else {
      error = null;
    }
  }
</script>

<Select
  id="required-select"
  label="Required Field"
  bind:value={selection}
  {options}
  {error}
  required
  onblur={validate}
/>

Typography System

The Select component uses design system typography tokens:

Applied Tokens

/* Label Text */
--typography-caption-size
--typography-caption-color
--typography-caption-weight

/* Option Text */
--typography-body-size    /* md size */
--typography-body-color
--typography-body-weight

/* Helper/Error Text */
--typography-caption-size
--typography-muted-color  /* helper */
--error                   /* error state */

Size Variants

SizeTypography TokensUse Case
smcaptionCompact forms, filters
mdbody (default)Standard forms
lgheadingProminent selections

Styling

Visual Design

  • Consistent appearance - Matches TextInput styling
  • Dropdown arrow - Custom chevron icon
  • Hover states - Subtle background changes
  • Focus indicators - Clear focus ring
  • Dark mode support - Automatic color adjustments

CSS Classes

  • .field-select - Base select element
  • .select-wrapper - Container with icon positioning
  • .select-icon - Dropdown arrow styling
  • .field-error - Error state modifier
  • .field-error-message - Error message styling
  • .field-hint - Helper text styling

Custom Styling

The native select element is styled to match the design system:

.field-select {
  appearance: none; /* Remove native styling */
  background-image: none; /* Custom arrow via icon */
  padding-right: 2.5rem; /* Space for arrow icon */
}

Accessibility

ARIA Support

  • aria-required - Indicates required fields
  • aria-invalid - Communicates error states
  • aria-describedby - Links helper text and errors
  • role="alert" - Error messages announced

Keyboard Interaction

  • Tab - Navigate to select
  • Space/Enter - Open dropdown
  • Arrow keys - Navigate options
  • Escape - Close dropdown
  • Type ahead - Jump to options by typing

Label Association

  • Proper for/id linking between label and select
  • Clicking label focuses the select element
  • Screen readers announce label when focused

Option Management

Dynamic Options

Options can be updated reactively:

<script>
  let category = $state('');
  let subcategory = $state('');
  
  const categoryOptions = [/*...*/];
  
  // Reactive subcategory options based on category
  const subcategoryOptions = $derived(() => {
    if (category === 'tech') {
      return [
        { value: 'frontend', label: 'Frontend' },
        { value: 'backend', label: 'Backend' }
      ];
    }
    return [];
  });
  
  // Reset subcategory when category changes
  $effect(() => {
    if (category) {
      subcategory = '';
    }
  });
</script>

<Select
  id="category"
  label="Category"
  bind:value={category}
  options={categoryOptions}
/>

{#if subcategoryOptions.length > 0}
  <Select
    id="subcategory"
    label="Subcategory"
    bind:value={subcategory}
    options={subcategoryOptions}
  />
{/if}

Option Formatting

For complex option data:

<script>
  const users = [
    { id: '1', name: 'John Doe', email: 'john@example.com' },
    { id: '2', name: 'Jane Smith', email: 'jane@example.com' }
  ];
  
  const userOptions = users.map(user => ({
    value: user.id,
    label: `${user.name} (${user.email})`
  }));
</script>

Validation Patterns

Required Selection

<script>
  let value = $state('');
  let error = $state(null);
  
  function handleSubmit() {
    if (!value) {
      error = 'Please make a selection';
      return;
    }
    // Submit logic
  }
</script>

Conditional Validation

<script>
  let paymentMethod = $state('');
  let cardType = $state('');
  let cardError = $state(null);
  
  $effect(() => {
    if (paymentMethod === 'card' && !cardType) {
      cardError = 'Please select card type';
    } else {
      cardError = null;
    }
  });
</script>

Best Practices

Option Organization

  1. Logical ordering - Alphabetical or by frequency of use
  2. Group related options - Use separators or grouping
  3. Limit options - Consider search/filter for >10 options
  4. Clear labels - Descriptive and unambiguous

User Experience

  1. Sensible defaults - Pre-select common choices
  2. Progressive disclosure - Show relevant options based on context
  3. Error recovery - Clear guidance on fixing invalid selections
  4. Loading states - Show feedback when options are loading

Performance

  1. Large option sets - Consider virtual scrolling
  2. Search functionality - For 20+ options
  3. Lazy loading - Load options when needed
  4. Memoization - Cache computed option arrays

Common Patterns

Cascading Selects

Country → State → City selection chains

Multi-level Categories

Product categories with subcategories

User/Role Selection

Users grouped by role or department

Date/Time Pickers

Separate selects for day, month, year

Alternatives

Consider these alternatives for specific use cases:

  • Radio buttons - For 2-5 visible options
  • Checkbox groups - For multiple selections
  • Autocomplete - For searchable options
  • Custom dropdowns - For complex option rendering