Welcome to a tutorial on how to create a custom dropdown arrow using pure CSS and HTML. Yes, we can pretty much do any customization with modern HTML and CSS. But strangely, there is no straightforward way to customize a dropdown arrow in pure CSS.
There are no direct CSS properties to change the dropdown arrow of <select>
. To customize the dropdown arrow:
- Wrap the select box –
<div class="cselect"><select><option>Demo</option></select></div>
- Hide the default arrow –
.cselect select { apperance: none }
- Create a custom arrow using HTML symbol –
.cselect::after { content: "\25b6" }
- Position the custom arrow –
.cselect { position: relative } .cselect::after { position: absolute; top: 0; right: 0 }
That covers the basic mechanics, but let us walk through a detailed example in this guide – Read on!
ⓘ I have included a zip file with all the source code at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.
QUICK SLIDES
TABLE OF CONTENTS
DOWNLOAD & DEMO
Firstly, here is the download link to the example code as promised.
QUICK NOTES
If you spot a bug, feel free to comment below. I try to answer short questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.
EXAMPLE CODE DOWNLOAD
Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
THE DEMO
PURE CSS CUSTOM DROPDOWN
All right, let us now dive into the example of how to customize a dropdown using pure CSS only.
PART 1) WRAP THE SELECT BOX
<div class="cselect"><select>
<option>Good Boy Doge</option>
<option>Evil Cate</option>
<option>Sneezing Panda</option>
<option>Confession Bear</option>
<option>Fabulous Llama</option>
</select></div>
No sweat. All we need in the HTML is to wrap a normal <select>
within a <div>
wrapper.
PART 2) CUSTOM HTML SYMBOL DROPDOWN ARROW
/* (A) BASIC CUSTOM DROPDOWN MECHANICS */
/* (A1) HIDE DEFAULT ARROW */
.cselect select { appearance: none; }
/* (A2) DEFINE OUR OWN CUSTOM ARROW */
.cselect::after { content: "\25b6"; }
/* (A3) POSITION CUSTOM ARROW */
.cselect { position: relative; }
.cselect::after {
position: absolute;
top: 0; right: 0;
}
Now, here comes the irritating part – CSS does not have a direct property to set the dropdown arrow, we have to do it through “alternative means”.
- (A1) Hide the default dropdown arrow using
appearance: none
. - (A2) Attach an HTML symbol to
.cselect::after
to build our own custom arrow. - (A3) Position the custom dropdown arrow.
PART 3) CUSTOM DROPDOWN BOX DIMENSIONS
/* (B) DIMENSTIONS */
/* (B1) MAKE <SELECT> FILL THE WRAPPER */
.cselect select { width: 100%; }
.cselect { max-width: 400px; } /* OPTIONAL */
/* (B2) ALL SAME HEIGHT */
.cselect, .cselect::after, .cselect select {
box-sizing: border-box;
height: 40px;
}
/* (B3) A SQUARE CUSTOM ARROW BOX */
/* ADD BORDER-RADIUS: 50% TO TURN IT CIRCLE */
.cselect::after { width: 40px; }
Next, we fix a funky-looking custom dropdown box by setting the dimensions.
- (B1) Make the
<select>
fill up the<div>
wrapper. - (B2) “Standardize” the height of all the
<div> <select> <div>::after
. - (B3) Finally, turn the dropdown arrow into a square (same width and height).
PART 4) COSMETICS
/* (C) COSMETICS */
/* (C1) OVERRIDE DEFAULT SELECT BOX STYLES */
.cselect select {
border: 1px solid #ccc;
background: #fafafa;
padding: 5px 10px;
}
/* (C2) DROPDOWN ARROW BOX */
.cselect::after {
/* COLORS */
color: #fff;
background: #cfdcff;
/* CENTER ARROW IN BOX */
display: flex;
align-items: center;
justify-content: center;
}
/* (C3) FUNKY ANIMATIONS */
.cselect::after { transition: all 0.3s; }
.cselect:hover::after {
transform: rotate(90deg);
background: #274396;
color: #fff;
}
/* (C4) REMOVE DEFAULT HIGHLIGHTS */
.cselect select:active,
.cselect select:focus,
.cselect select:hover {
outline: none;
}
No need to panic, this final part is just the cosmetics – Font, color, borders, etc… Feel free to customize this to your own liking.
EXTRA) ARROW ON THE LEFT SIDE
/* (D) ARROW ON LEFT SIDE */
.left.cselect select { padding-left: 50px; }
.left.cselect::after {
right: auto;
left: 0;
border-radius: 50%;
}
Easy. Just set .cselect::after
to left: 0
.
USEFUL BITS & LINKS
That’s all for the tutorial, and here is a small section on some extras and links that may be useful to you.
A SMALL LIMITATION
Just a small note – The custom dropdown arrow is purely “cosmetic”. Clicking on it will not trigger focus on the select list.
CAN WE CUSTOMIZE THE DROPDOWN ITEMS!?
No, not directly. Every browser renders the <select>
differently – On a desktop, the selectable list will be below the box. But on mobile, it will probably become a popup selection list instead. The only way to do it is to make your own “entirely customized selectable list made from scratch”.
LINKS & REFERENCES
- HTML Symbols – Toptal
- Example on CodePen – Custom Dropdown Arrow With Pure CSS
TUTORIAL VIDEO
INFOGRAPHIC CHEAT SHEET

THE END
Thank you for reading, and we have come to the end of this guide. I hope that it has helped you with your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!
Hi
I want to add a three dot loading indicator for select icon while options are being loaded. Can you guide me?
1) Study AJAX
2) While loading – Disable dropdown and show “now loading”
3) On loaded – Enable dropdown and show “dropdown arrow”
Otherwise, I will take this as a request for a new tutorial. Good luck.
https://code-boxx.com/faq/#help “Requests for new features and tutorials may be considered, but will not be immediately answered”
Hi
Thank you for this quick and easy method. It helped me and one thing that I added was the z-index so clicking the arrow “seem” to activate the dropdown:
simply added :
.cselect select { z-index: 2; }
.cselect::after { z-index: 1; }
Thanks again!
Hello,
Would it be possible to add support for a scrollbar if there are many items in the list?
No, there’s no CSS to directly control it. Also on mobile browsers, this will probably be rendered differently as a scrollable popup list.