Those layout designers who create signup or feedback forms will surely face the problem of displaying multiple items of a single selection, i.e. multiselect. Unfortunately, not all <select> tag elements can be styled out of the box as designers would like to. In this small tutorial, I would like to share my experience in solving this problem based on my knowledge of CSS and vanilla JS. What’s Wrong With Native Select? If a designer creates a user-friendly <select> with 10-15 possible choices, you will most likely decide to take the first available design-like library from Google (which I was hoping for). My pain in this matter was that the project could not use third-party libraries, for example, jQuery, which greatly reduced the number of proposed options. Small to Great So, let’s get down to writing a simple multiselect form: Categories Select category HTML CSS JavaScript < = = > form name "" action "" < = > div class "form" < = > span class "form__label" </ > span < = > div class "form__multiselect" < = = > label for "select" class "form__multiselect__label" </ > label < = = = > select id "select" class "form__multiselect__select" name "category" multiple < = > option value "HTML" </ > option < = > option value "CSS" </ > option < = > option value "JavaScript" </ > option </ > select </ > div < = > span class "form__error" </ > span </ > div </ > form There is a section title, a choice of options, <select> with the multiple attributes that make it possible to select several elements at the same time. And of course, there is an error_text field where we will insert our error text if this multiselect form is required. It seems to be working, but it doesn’t look very good. And the multiselection is not evident… So, let’s improve. Categories Select category HTML CSS JavaScript You can select several items by pressing Ctrl (or Command) + Element < = = > form name "" action "" < = > div class "form" < = > span class "form__label" </ > span < = > div class "form__multiselect" < = = > label for "select" class "form__multiselect__label" </ > label < = = = > input id "checkbox" class "form__multiselect__checkbox" type "checkbox" < = = > label for "checkbox" class "form__multiselect__checkbox__label" </ > label < = = = > select id "select" class "form__multiselect__select" name "category" multiple < = > option value "HTML" </ > option < = > option value "CSS" </ > option < = > option value "JavaScript" </ > option </ > select < = > span class "form__multiselect__help" < > b </ > b </ > span </ > div < = > span class "form__error" </ > span </ > div </ > form Let’s add a few styles to make it less scary. { : relative; : ; } { : top; : block; : ; : ; : ; : ; : . ; : ; } { : ; : relative; : ; : ; : red; } , { : relative; : ; : block; : ; : solid ; : border-box; : ; : ; : ; : ; : ; } , { : ; } , { : (0, 0, 0, .16); } , { : (0, 0, 0, .16); } { : absolute; : ; : ; : - ; : ; : ; : none; } { : ; } { : ; } { : ; } { : ; : absolute; : ; : ; : ; : ; : ( ) no-repeat; } { : relative; : } { : absolute; : (100% - 2px); : ; : ; : solid ; : ; : ; : border-box; : ; : ; } { : auto; } { : block; : ; : (370px - 32px); : pointer; } { : ; } { : ; } { : relative; : ; : ; : ; : ; : ; } , { : ; } { : ; : absolute; : ; : ; : ; : ; : ( ) no-repeat; : contain; } { : absolute; : ; : ; : ; : ; : pointer; : ; } { : none; } { : absolute; : ; : ; : ; : ; : none; : ; } ~ { : block; } ~ { : ; : initial; : ; : ( ) no-repeat; } ~ { : ; } .form position min-height 88px .form__label vertical-align display margin-bottom 6px font-weight 500 font-size 12px line-height 16px letter-spacing 04em color #686ea1 .form__label :after content "*" position top 0 font-size 13px color .form__multiselect__label .form input position width 100% display min-height 46px border 1px #cdd6f3 box-sizing border-radius 8px padding 12px 40px 10px 16px font-size 14px color #a8acc9 outline-color #cdd6f3 .form__multiselect__label ::placeholder .form input ::placeholder color #a8acc9 .form__multiselect__label :hover .form input :hover box-shadow 0 0 2px rgba .form__multiselect__label :focus .form input :focus box-shadow 0 0 2px rgba .form__multiselect__help position max-width 100% background-color #fff top 48px left 0 opacity 0 display .form input .error border-color #eb5757 .form__error color #eb5757 .form__multiselect__label padding-right 60px .form__multiselect__label :after content "" position right 14px top 15px width 6px height 16px background url "data:image/svg+xml, %3Csvg width='6' height='16' viewBox='0 0 6 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M3 0L6 5H0L3 0Z' fill='%23A8ACC9'/%3E%3Cpath d='M3 16L6 11H0L3 16Z' fill='%23A8ACC9'/%3E%3C/svg%3E" 50% 50% .form__multiselect position width 100% .form__multiselect__select position top calc left 0 width 100% border 2px #cdd6f3 border-bottom-right-radius 2px border-bottom-left-radius 2px box-sizing outline-color #cdd6f3 z-index 6 .form__multiselect__select [multiple] overflow-y .form__multiselect__select option display padding 8px 16px width calc cursor .form__multiselect__select option :checked background-color #eceff3 .form__multiselect__select option :hover background-color #d5e8fb .form__multiselect__label button position padding 7px 34px 7px 8px background #ebe4fb border-radius 4px margin-right 9px margin-bottom 10px .form__multiselect__label button :focus .form__multiselect__label button :hover background-color #dbd1ee .form__multiselect__label button :after content "" position top 7px right 10px width 16px height 16px background url "data:image/svg+xml, %3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M19.4958 6.49499C19.7691 6.22162 19.7691 5.7784 19.4958 5.50504C19.2224 5.23167 18.7792 5.23167 18.5058 5.50504L12.5008 11.5101L6.49576 5.50504C6.22239 5.23167 5.77917 5.23167 5.50581 5.50504C5.23244 5.7784 5.23244 6.22162 5.50581 6.49499L11.5108 12.5L5.50581 18.505C5.23244 18.7784 5.23244 19.2216 5.50581 19.495C5.77917 19.7684 6.22239 19.7684 6.49576 19.495L12.5008 13.49L18.5058 19.495C18.7792 19.7684 19.2224 19.7684 19.4958 19.495C19.7691 19.2216 19.7691 18.7784 19.4958 18.505L13.4907 12.5L19.4958 6.49499Z' fill='%234F5588'/%3E%3C/svg%3E" 50% 50% background-size .form__multiselect__checkbox__label position top 1px left 2px width 100% height 44px cursor z-index 3 .form__multiselect__select display input .form__multiselect__checkbox position right 0 top 0 width 40px height 40px border opacity 0 .form__multiselect__checkbox :checked .form__multiselect__select display .form__multiselect__checkbox :checked .form__multiselect__checkbox__label width 40px left right 4px background #fff url "data:image/svg+xml, %3Csvg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M19.4958 6.49499C19.7691 6.22162 19.7691 5.7784 19.4958 5.50504C19.2224 5.23167 18.7792 5.23167 18.5058 5.50504L12.5008 11.5101L6.49576 5.50504C6.22239 5.23167 5.77917 5.23167 5.50581 5.50504C5.23244 5.7784 5.23244 6.22162 5.50581 6.49499L11.5108 12.5L5.50581 18.505C5.23244 18.7784 5.23244 19.2216 5.50581 19.495C5.77917 19.7684 6.22239 19.7684 6.49576 19.495L12.5008 13.49L18.5058 19.495C18.7792 19.7684 19.2224 19.7684 19.4958 19.495C19.7691 19.2216 19.7691 18.7784 19.4958 18.505L13.4907 12.5L19.4958 6.49499Z' fill='%234F5588'/%3E%3C/svg%3E" 50% 50% .form__multiselect__checkbox :checked .form__multiselect__help opacity 1 Now it is at least pleasant to click on it. I used CSS for SVG icons, but this wasn’t quite effective and it’s better to keep them as header files. I added <label class="form__multiselect__checkbox__label"> and <input type="checkbox" class="form__multiselect__checkbox"> to interact with <select>. This is some kind of magic that implies overlapping layers with z-index and using the:checked CSS pseudo-class to interact with elements. These new elements make it possible to style the tooltips and make the choice easier for users, which will facilitate better interaction with the element. Let’s extend the functionality even more by adding buttons for displaying selected items. multiselectBlock = .querySelectorAll( ); multiselectBlock.forEach( { label = parent.querySelector( ); text = label.innerHTML; select = parent.querySelector( ); select.addEventListener( , { selectedOptions = .selectedOptions; label.innerHTML = ; ( option selectedOptions) { button = .createElement( ); button.type = ; button.className = ; button.textContent = option.value; button.onclick = { option.selected = ; button.remove(); (!select.selectedOptions.length) label.innerHTML = text; }; label.append(button); } }); }); let document ".form__multiselect" ( ) => parent let ".form__multiselect__label" let let ".form__multiselect__select" "change" ( ) function e let this "" for let of let document "button" "button" "btn_multiselect" => () false if Now, when selecting <option> inside the <select> element, we will create a button that will show what elements are selected by the user with an option to delete them. Also, since I’m sure I am not the only one who did not know that <select> uses the pressed Ctrl or Shift, let’s add this hint separately. We could have put it into a tooltip (and use the data attribute to display it), but I thought about the availability and the possibility of translating this hint into other languages. Ready Multiselect for Fast Work Here’s what I did. To get the entire picture, I added several elements at once so that it would be clear that you could scale them by simply adding the necessary id and associated <label>. Also, I separated the necessary elements with comments so that you could apply this code in your projects. It’s not perfect in terms of accessibility, but it is much better than the native select or jQuery library. The main thing about the <select> tag is that in smartphones, the engines show the multiselection when you focus on the tag and you don’t need to think about how to insert 15-20 items without breaking the layout. I hope this may help someone.