HOWTO · CSS
How to Style Select Dropdown in CSS
This tutorial introduces how to style dropdown menu in CSS.
On this page
You can style a native HTML select dropdown by changing its box and replacing its default arrow with a CSS-drawn arrow. The control remains a real select, so users keep its built-in selection behavior and keyboard interaction. Two common approaches are to remove the native appearance directly or to clip the native arrow with a wrapper.
Style a native select dropdown while keeping its behavior
A select contains option elements, and the browser renders the control using its platform and browser conventions. CSS can change the visible box, but the popup list and some details still vary between browsers and operating systems. Keep the native element instead of recreating a dropdown from generic elements when you want built-in form and keyboard behavior.
The examples use the same four choices. Their CSS gives the control room on the right for an arrow and provides a visible focus outline for keyboard users. Adjust the colors and dimensions to match your interface.
Hide the native arrow with appearance: none and add a custom arrow
Set appearance: none on the select to remove the browser’s native arrow where supported, then draw a chevron with two CSS gradients. The background layers place the two strokes near the right edge; right padding keeps option text away from them. The -webkit-appearance and -moz-appearance declarations are vendor-prefixed forms for engines that use those prefixes; they do not correspond to separate WebKit/Blink browser families. The standard property is the main declaration, so retain prefixes only when your browser support requirements call for them.
This complete page is a static browser example. Save it as an HTML file and open it in a browser to see the control; the packet provides no browser-rendering evidence for a claimed visual result.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Styled select</title>
<style>
.vehicle-select {
box-sizing: border-box;
width: 220px;
height: 40px;
padding: 0 32px 0 10px;
border: 1px solid #777;
border-radius: 4px;
color: #222;
background-color: #fff;
background-image:
linear-gradient(45deg, transparent 50%, #333 50%),
linear-gradient(135deg, #333 50%, transparent 50%);
background-position:
calc(100% - 14px) 50%,
calc(100% - 9px) 50%;
background-size: 5px 5px, 5px 5px;
background-repeat: no-repeat;
appearance: none;
}
.vehicle-select:focus-visible {
outline: 3px solid #1769aa;
outline-offset: 2px;
}
</style>
</head>
<body>
<label for="vehicle">Choose a vehicle</label>
<select class="vehicle-select" id="vehicle" name="vehicle">
<option>RE Himalayan</option>
<option selected>CRF Rally</option>
<option>GS 310</option>
<option>KTM ADV</option>
</select>
</body>
</html>
Hide the native arrow with a clipped wrapper
A wrapper offers another way to hide the native arrow. Give the select a width slightly larger than the wrapper and set the wrapper’s overflow to hidden; the native arrow falls outside the visible wrapper. Draw a replacement arrow on the wrapper with a pseudo-element. Because the select remains inside the wrapper, its choices and native interaction are still available.
Keep the wrapper wide enough for the control’s text and make the select only a little wider. If the difference is too large, the control’s right border or other content may be clipped as well. The pseudo-element uses pointer-events: none so it does not intercept clicks on the select.
For the wrapper example, the select extends 20 pixels beyond the wrapper. This small JavaScript check keeps that sizing relationship explicit:
const wrapperWidth = 200;
const selectWidth = 220;
if (selectWidth <= wrapperWidth) {
process.exitCode = 1;
}
The check completes successfully when the select is wider than the wrapper; otherwise it sets a failure exit code.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Select with clipped arrow</title>
<style>
.select-wrap {
position: relative;
box-sizing: border-box;
width: 200px;
height: 42px;
overflow: hidden;
border: 1px solid #777;
border-radius: 4px;
background: #fff;
}
.select-wrap select {
box-sizing: border-box;
width: 220px;
height: 40px;
padding: 0 10px;
border: 0;
color: #222;
background: transparent;
font: inherit;
}
.select-wrap::after {
position: absolute;
top: 18px;
right: 12px;
width: 7px;
height: 7px;
border-right: 2px solid #333;
border-bottom: 2px solid #333;
content: "";
pointer-events: none;
transform: rotate(45deg);
}
.select-wrap:focus-within {
outline: 3px solid #1769aa;
outline-offset: 2px;
}
</style>
</head>
<body>
<label for="vehicle">Choose a vehicle</label>
<div class="select-wrap">
<select id="vehicle" name="vehicle">
<option>RE Himalayan</option>
<option selected>CRF Rally</option>
<option>GS 310</option>
<option>KTM ADV</option>
</select>
</div>
</body>
</html>
Choose an approach and check its limits
Use appearance: none when you want the select itself to define its width and border and your target browsers support the styling you need. Use a clipped wrapper when you need a wrapper-level arrow or when direct appearance styling is unsuitable. In either case, test the page in the browsers and devices you support: native select rendering and popup menus are not fully uniform.
Check that the entire selected value remains visible, the custom arrow is aligned, and keyboard focus is easy to see. In the wrapper version, compare the wrapper and select widths if the border or text looks cut off. Do not remove the focus indicator just to make the control look cleaner; replace it with an equally clear style if you customize it. These techniques change presentation while leaving the native select in charge of selection and form behavior.