Skip to main content

Style Presets

Dropdown Options

Option 1
Option 2
Option 3

Size & Dimensions

Colors

Dropdown Menu

Border & Shape

Arrow

Effects

Live Preview

Option 1
Option 2
Option 3

Click dropdown to test interaction

States Preview

Default
Focused
Selected
Disabled

Generated CSS

.custom-select-wrapper {
  position: relative;
  width: 280px;
  font-family: system-ui, -apple-system, sans-serif;
}

.custom-select {
  width: 100%;
  height: 44px;
  padding: 0 44px 0 16px;
  font-size: 16px;
  color: #374151;
  background-color: #ffffff;
  
  border: 1px solid #d1d5db;
  border-radius: 8px;
  
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%236b7280'%3E%3Cpath fill-rule='evenodd' d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z' clip-rule='evenodd'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 18px;
  
  transition: all 0.2s ease;
  outline: none;
}

.custom-select:hover {
  background-color: #f3f4f6;
  border-color: #3b82f6;
}

.custom-select:focus {
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
}

.custom-select:disabled {
  background-color: #f9fafb;
  color: #9ca3af;
  cursor: not-allowed;
  opacity: 0.7;
}

/* For Firefox */
.custom-select:-moz-focusring {
  color: transparent;
  text-shadow: 0 0 0 #374151;
}

/* Hide default arrow in IE */
.custom-select::-ms-expand {
  display: none;
}

/* Dropdown options (native) */
.custom-select option {
  padding: 12px;
  background-color: #ffffff;
  color: #374151;
}

.custom-select option:checked {
  background-color: #dbeafe;
}

/* Placeholder styling */
.custom-select option[value=""] {
  color: #9ca3af;
}

/* === Custom Dropdown (JS-based) === */

.custom-dropdown {
  position: relative;
  width: 280px;
  font-family: system-ui, -apple-system, sans-serif;
}

.custom-dropdown-trigger {
  width: 100%;
  height: 44px;
  padding: 0 44px 0 16px;
  font-size: 16px;
  color: #374151;
  background-color: #ffffff;
  
  border: 1px solid #d1d5db;
  border-radius: 8px;
  
  cursor: pointer;
  text-align: left;
  
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%236b7280'%3E%3Cpath fill-rule='evenodd' d='M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z' clip-rule='evenodd'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 13px center;
  background-size: 18px;
  
  transition: all 0.2s ease;
  outline: none;
}

.custom-dropdown-trigger:hover {
  background-color: #f3f4f6;
}

.custom-dropdown-trigger:focus,
.custom-dropdown.open .custom-dropdown-trigger {
  border-color: #3b82f6;
  box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.25);
}

.custom-dropdown.open .custom-dropdown-trigger {
  
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%236b7280'%3E%3Cpath fill-rule='evenodd' d='M5.293 12.707a1 1 0 011.414 0L10 10.586l3.293 3.293a1 1 0 111.414 1.414l4-4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z' clip-rule='evenodd'/%3E%3C/svg%3E");
  
}

.custom-dropdown-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  width: 100%;
  max-height: 200px;
  overflow-y: auto;
  background-color: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 8px;
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
  z-index: 1000;
  
  opacity: 0;
  transform: translateY(-8px);
  visibility: hidden;
  transition: all 0.2s ease;
  
}

.custom-dropdown.open .custom-dropdown-menu {
  
  opacity: 1;
  transform: translateY(0);
  visibility: visible;
  
}

.custom-dropdown-option {
  padding: 12px 16px;
  font-size: 16px;
  color: #374151;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.custom-dropdown-option:first-child {
  border-radius: 7px 7px 0 0;
}

.custom-dropdown-option:last-child {
  border-radius: 0 0 7px 7px;
}

.custom-dropdown-option:hover {
  background-color: #eff6ff;
}

.custom-dropdown-option.selected {
  background-color: #dbeafe;
  font-weight: 500;
}

.custom-dropdown-option:focus {
  outline: none;
  background-color: #eff6ff;
}

HTML

<!-- Native Select (Simple, Accessible) -->
<div class="custom-select-wrapper">
  <select class="custom-select">
    <option value="">Select an option</option>
  <option value="option1">Option 1</option>
  <option value="option2">Option 2</option>
  <option value="option3">Option 3</option>
  </select>
</div>

<!-- Custom Dropdown (Full Styling Control) -->
<div class="custom-dropdown" id="myDropdown">
  <button
    class="custom-dropdown-trigger"
    type="button"
    aria-haspopup="listbox"
    aria-expanded="false"
  >
    Select an option
  </button>
  <div class="custom-dropdown-menu" role="listbox">
    <div class="custom-dropdown-option" role="option" data-value="option1">Option 1</div>
    <div class="custom-dropdown-option" role="option" data-value="option2">Option 2</div>
    <div class="custom-dropdown-option" role="option" data-value="option3">Option 3</div>
  </div>
</div>

JavaScript (for Custom Dropdown)

// Custom Dropdown JavaScript
document.addEventListener('DOMContentLoaded', () => {
  const dropdown = document.getElementById('myDropdown');
  const trigger = dropdown.querySelector('.custom-dropdown-trigger');
  const menu = dropdown.querySelector('.custom-dropdown-menu');
  const options = dropdown.querySelectorAll('.custom-dropdown-option');

  let selectedValue = '';
  let selectedLabel = 'Select an option';

  // Toggle dropdown
  trigger.addEventListener('click', () => {
    const isOpen = dropdown.classList.contains('open');
    dropdown.classList.toggle('open');
    trigger.setAttribute('aria-expanded', !isOpen);
  });

  // Handle option selection
  options.forEach(option => {
    option.addEventListener('click', () => {
      // Update selected state
      options.forEach(opt => opt.classList.remove('selected'));
      option.classList.add('selected');

      // Update values
      selectedValue = option.dataset.value;
      selectedLabel = option.textContent;
      trigger.textContent = selectedLabel;

      // Close dropdown
      dropdown.classList.remove('open');
      trigger.setAttribute('aria-expanded', 'false');

      // Dispatch change event
      dropdown.dispatchEvent(new CustomEvent('change', {
        detail: { value: selectedValue, label: selectedLabel }
      }));
    });

    // Keyboard navigation
    option.addEventListener('keydown', (e) => {
      if (e.key === 'Enter' || e.key === ' ') {
        e.preventDefault();
        option.click();
      }
    });
  });

  // Close on outside click
  document.addEventListener('click', (e) => {
    if (!dropdown.contains(e.target)) {
      dropdown.classList.remove('open');
      trigger.setAttribute('aria-expanded', 'false');
    }
  });

  // Keyboard navigation for trigger
  trigger.addEventListener('keydown', (e) => {
    if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
      e.preventDefault();
      if (!dropdown.classList.contains('open')) {
        dropdown.classList.add('open');
        trigger.setAttribute('aria-expanded', 'true');
      }
      const firstOption = menu.querySelector('.custom-dropdown-option');
      if (firstOption) firstOption.focus();
    }
    if (e.key === 'Escape') {
      dropdown.classList.remove('open');
      trigger.setAttribute('aria-expanded', 'false');
    }
  });
});

Usage & Accessibility

Two Approaches

  • Native Select: Best for accessibility, works without JavaScript, limited styling options
  • Custom Dropdown: Full styling control, requires JavaScript, needs ARIA attributes for accessibility

Accessibility Tips

  • Use aria-haspopup="listbox" on the trigger button
  • Toggle aria-expanded when dropdown opens/closes
  • Use role="listbox" on the menu and role="option" on items
  • Support keyboard navigation (Arrow keys, Enter, Escape)
  • Ensure sufficient color contrast (4.5:1 minimum)
  • Provide visible focus indicators

Best Practices

  • Keep the dropdown list reasonably short (consider search for 10+ items)
  • Provide a clear placeholder or default option
  • Close dropdown when clicking outside
  • Show the selected item clearly in the trigger

Need Custom Form Components?

Brix340 specializes in creating accessible, beautiful form designs that enhance user experience.

Get Professional Help