Home / Snippets / UI Components /
Native select
The built-in <select> element fully restyled — custom arrow, theme border, hover and focus states.
Quick implementation
.select-native {
appearance: none;
-webkit-appearance: none;
background: oklch(0.19 0.02 260); /* --card */
border: 1px solid oklch(0.35 0.03 260);
border-radius: 0.5rem;
padding: 0.5rem 2.25rem 0.5rem 0.75rem;
font-family: inherit;
font-size: 0.9rem;
color: oklch(0.93 0.01 260); /* --text */
cursor: pointer;
/* Inline SVG chevron arrow */
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23a0a8c0' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 0.625rem center;
transition: border-color 0.15s ease-out, box-shadow 0.15s ease-out;
}
.select-native:hover {
border-color: oklch(0.52 0.22 265);
}
.select-native:focus-visible {
outline: 2px solid oklch(0.72 0.19 265);
outline-offset: 2px;
border-color: oklch(0.52 0.22 265);
}
.select-native:disabled {
opacity: 0.45;
cursor: not-allowed;
}
.select-native option {
background: oklch(0.19 0.02 260);
color: oklch(0.93 0.01 260);
}
Prompt this to your LLM
Includes role, constraints, arrow technique, all interactive states, and accessibility notes.
You are a senior frontend engineer building a form component library.
Goal: A fully styled native <select> element that matches a dark design system.
Technical constraints:
- Use appearance: none and -webkit-appearance: none to remove browser default styling.
- Use oklch() for all colors — no hex, no rgba().
- Background: oklch(0.19 0.02 260) (dark card color).
- Border: 1px solid oklch(0.35 0.03 260) — subtle, visible.
- Border-radius: 0.5rem.
- Padding: 0.5rem on top/bottom, 0.75rem left, 2.25rem right (space for arrow icon).
- Custom arrow: inline SVG data URI as background-image — a simple chevron in a light gray stroke.
- Arrow position: right 0.625rem center.
- Hover: border-color transitions to oklch(0.52 0.22 265) — accent.
- Focus-visible: 2px solid oklch(0.72 0.19 265) outline with 2px offset. NO outline on mouse click.
- Disabled: opacity 0.45, cursor not-allowed.
- Option elements: dark background to match — oklch(0.19 0.02 260).
Framework variants (pick one):
A) Plain CSS + HTML — <select class="select-native">.
B) React component — accept options array prop, render native <select> with the CSS applied.
Edge cases to handle:
- The custom arrow must not overlap long option text — use right-padding to create clearance.
- Ensure disabled state is visually distinct but still readable (opacity, not display:none).
- option styling is limited cross-browser — note that background/color on options works in Chrome/Firefox but not Safari.
Return CSS only.
Why this matters in 2026
Custom select dropdowns built entirely in JavaScript — with ARIA roles, keyboard handlers, and focus traps — are one of the most over-engineered patterns in frontend development. The native <select> gives you all of that for free: keyboard navigation, screen reader announcements, mobile-native pickers, and form submission. Styling it with appearance: none and a custom arrow gets you 90% of the visual customisation you need without a single line of JavaScript.
The logic
appearance: none (with the -webkit- prefix for Safari) strips the browser's default dropdown arrow and styling. The replacement arrow is a lightweight inline SVG chevron injected via background-image as a data URI. Right padding of 2.25rem prevents long option labels from underlapping the arrow. The border-color transition on hover and :focus-visible give clear interactive feedback while keeping the keyboard outline distinct from the mouse hover state.
Accessibility & performance
The native <select> is natively accessible — it maps to the listbox ARIA role, all options are announced by screen readers, and keyboard navigation (arrow keys, Home/End, typing to jump) works without any JavaScript. The :focus-visible ring is the only focus indicator needed; it appears only for keyboard users. Always pair the select with a visible <label> — never use placeholder as a substitute for a label.