Go to homepage

← All articles Last edited:

How to create a country dropdown selector in HTML and CSS

In this article, we're going to build a country selector component using HTML and CSS, which will dynamically display the updated flag for each country as it's selected.

This is the final result:

Before we begin: for those in search of flag icons, check out our collection of over 200 free SVG country flag icons.

Our component consists of three key elements: a <select> element featuring a list of countries, an <svg> element for creating a custom arrow icon, and a <span> element for displaying the country flag.

<div class="select">
  <span class="select__flag" aria-hidden="true"></span>

  <select class="select__input" name="country-selector" id="country-selector">
    <option value="0">Select option</option>
    <option value="IT">Italy</option>
    <option value="UK">United Kingdom</option>
    <!-- other <option>s here -->
  </select>

  <svg class="select__arrow" aria-hidden="true" viewBox="0 0 16 16"><polyline points="3.5,6.5 8,11 12.5,6.5" stroke-width="2" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></polyline></svg>
</div>

Both the arrow and flag icons have the aria-hidden="true" attribute: they are both decorative icons (purely aesthetic) so they don't have to be announced to screen readers.

If you want to learn more about icon accessibility, take a look at our guide to creating accessible icons.

First, we need to reset the <select> element's default browser styling, then modify it further with CSS:

.select__input {
  /* Reset default dropdown style */
  appearance: none;
  border: 0;
  margin: 0;
  font-size: 1rem;
  width: 100%;
  height: 100%;
  /* Custom dropdown styling */
  padding: 0.5rem 1rem;
  box-shadow: inset 0 1px 0 hsla(0, 0%, 100%, 0.1), 0 0 0 1px hsla(230, 13%, 9%, 0.02), 0 0.3px 0.4px hsla(230, 13%, 9%, 0.025), 0 1px 3px -1px hsla(230, 13%, 9%, 0.2), 0 3.5px 6px hsla(230, 13%, 9%, 0.12);
  border-radius: 0.375em;
  transition: box-shadow 0.2s;
  user-select: none;
}

.select__input:focus  {
  outline: none;
  box-shadow: inset 0 1px 0 hsla(0, 0%, 100%, 0.1), 0 0 0 1px hsla(230, 13%, 9%, 0.02), 0 0.3px 0.4px hsla(230, 13%, 9%, 0.025), 0 1px 3px -1px hsla(230, 13%, 9%, 0.2), 0 3.5px 6px hsla(230, 13%, 9%, 0.12), 0 0 0 2px hsl(0, 0%, 100%), 0 0 0 4px hsl(230, 7%, 23%);
}

Next step is styling the arrow and flag icons. Both icons will be in absolute position, the first to the right and the second to the left of the <select> element:

:root {
  --select-icon-size: 16px; /* Icon size */
  --select-icon-margin: 1rem; /* Icon left/right positioning */
  --select-text-icon-gap: 0.5rem; /* Space between text and icon */
}

.select {
  position: relative;
}

.select__arrow,
.select__flag {
  display: inline-block;
  position: absolute;
  /* Center both icons vertically */
  top: 50%;
  transform: translateY(-50%);
  width: var(--select-icon-size);
  height: var(--select-icon-size);
  pointer-events: none;
}

.select__arrow {
  color: inherit;
  right: var(--select-icon-margin);
}

.select__flag {
  left: var(--select-icon-margin);
}

We have been using CSS variables to define the size of the icons, their horizontal position (left and right position relative to the container) and the gap between the icon and the select content.

This allows us to easily update the horizontal padding of the <select> element to avoid overlapping between the icons and the select content:

.select__input {
  padding: 0.5rem calc(var(--select-icon-size) + var(--select-icon-margin) + var(--select-text-icon-gap));
}

Now let's focus on the flag icon: a default world icon is displayed when no country is selected.

.select__flag {
  background-position: center;
  background-repeat: no-repeat;
  background-size: var(--select-icon-size);
  /* Default world icon as SVG dataURI */
  background-image: url("data:image/svg...");
}

As background image, we have used the dataURI version of the SVG world icon.

If you are using a tool like Nucleo to manage your icons, you can get the dataURI code automatically from the icon context menu (right-click icon > Copy Data URI).

Icon context menu in Nucleo application
Copy SVG code as data URI

If you want to read more about data URI, check our article on converting SVG code to data URI.

The last step is dynamically updating the flag icon to match the country selected in the dropdown. For this purpose, we'll use the :has CSS pseudo-class. Consider the following CSS selector:

.select:has(option[value="IT"]) {}

This selector targets any element with a class of .select containing an <option> child with the value 'IT' (IT stands for Italy).

To specifically address the case where an <option> is selected, we modify our selector with the :checked pseudo-class:

.select:has(option[value="IT"]:checked) {}

Now, this selector represents .select elements with a selected <option> child with value 'IT'. In this case, we want to update the flag icon to show the Italian flag:

.select:has(option[value="IT"]:checked) .select__flag {
  /* SVG Italian flag via dataURI  */	
  background-image: url("data:image/svg...");
}

That's it! You can target each country and update the background-image value with the proper flag icon.

For those in search of flag icons, check out our collection of over 200 free SVG country flag icons.


The end! Check our documentation tutorials page for more articles on working with icons.

Preview of the Nucleo icons

Cookie Compliance

We use cookies to give you the best possible website experience. Learn more.