webdevpuneet.com

Floating Label Select — Free HTML CSS JS Form Field Snippet

Floating Label Select · Forms · Plain HTML, CSS & JS · Live preview

CategoryForms

Copy & paste

Source Code

<form class="fs-form" id="fsForm">
  <div class="fs-field" data-empty="true">
    <select class="fs-select" id="fsCountry">
      <option value="" disabled selected hidden></option>
      <option value="us">United States</option>
      <option value="uk">United Kingdom</option>
      <option value="de">Germany</option>
      <option value="jp">Japan</option>
      <option value="in">India</option>
    </select>
    <label class="fs-label" for="fsCountry">Country</label>
    <svg class="fs-caret" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg>
  </div>

  <div class="fs-field" data-empty="true">
    <select class="fs-select" id="fsPlan">
      <option value="" disabled selected hidden></option>
      <option value="free">Free</option>
      <option value="pro">Pro</option>
      <option value="team">Team</option>
    </select>
    <label class="fs-label" for="fsPlan">Plan</label>
    <svg class="fs-caret" viewBox="0 0 24 24"><path d="M6 9l6 6 6-6"/></svg>
  </div>

  <button type="submit" class="fs-submit">Continue</button>
</form>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Real native select
Keyboard, mobile, and a11y come free.
Floating label
Rises on focus or when a value is set.
Custom caret
Inline SVG that flips 180° on focus.
Placeholder option
Hidden empty option keeps it truly empty.
Focus ring
Accent border and soft box-shadow.
State sync
data-empty attribute drives the float.
Associated label
label for connects to the select id.
Smooth motion
Transform and color transitions.

About this UI Snippet

Floating Label Select — Material-Style Dropdowns That Stay Compact

Screenshot of the Floating Label Select snippet rendered live

The floating label select brings the popular floating-label pattern — where the placeholder shrinks and rises into a label once the field is in use — to native <select> dropdowns. This snippet styles a real, accessible select element with plain CSS and a tiny vanilla JavaScript state sync, keeping forms compact while always showing what each field is for.

Taming the native select

A real <select> is kept for accessibility and free keyboard and mobile behaviour, but its default chrome is removed with appearance: none (and the -webkit- prefix) so it can be restyled. The browser's arrow is replaced by a custom inline SVG caret, positioned absolutely on the right and made pointer-events: none so clicks fall through to the select beneath it. Generous top padding leaves room for the label to sit above the chosen value.

The floating label logic

The label floats up in two situations: when the field is focused, and when it holds a value. Focus is handled purely in CSS with :focus + .fs-label. Value presence can't be detected by CSS on a select, so a wrapper carries a data-empty attribute that JavaScript keeps in sync on every change; the selector .fs-field:not([data-empty="true"]) .fs-label then floats the label whenever a real option is chosen. A hidden, disabled, empty first option acts as the placeholder so the field starts genuinely empty.

The animated caret

The custom caret rotates 180° on focus via :focus ~ .fs-caret, mirroring the open/closed affordance of a dropdown, and shifts to the accent colour to reinforce the focus state. Because it's a sibling of the select in the same relative wrapper, no JavaScript is needed for the flip.

Focus ring and motion

On focus the border switches to the accent colour and a soft box-shadow ring appears — the same focus affordance as a good text input — and the label transition (transform, colour, and font-size over ~160ms) makes the rise feel smooth rather than snapping. All of it is GPU-friendly transform and colour work.

Accessible by construction

Because the control is a genuine <select> with a properly associated <label for>, it works with screen readers, keyboard navigation, and native mobile pickers out of the box — you get the custom look without sacrificing the platform behaviour that a div-based fake dropdown would throw away.

Customizing it

Swap the accent colour, adjust the padding to change the float distance, or restyle the caret. The same wrapper pattern scales to as many selects as a form needs, and the submit handler shows how to read every value. Pair it with a floating label text input and an inline validation form for a complete styled form.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSTwo labelled selects render with placeholders.
  2. 2
    Focus a selectThe label floats up and the caret flips.
  3. 3
    Choose an optionThe label stays floated to show the value.
  4. 4
    Submit the formThe handler logs every selected value.
  5. 5
    Add a fieldCopy a wrapper with its own select and label.
  6. 6
    Recolor itChange the accent used for focus and label.

Real-world uses

Common Use Cases

Signup form dropdowns
Sit beside a floating label text input so selects and inputs share the same rising-label behaviour.
Checkout country and region
Offer country and region choices in a checkout form, keeping the real native select for mobile pickers and accessibility.
Settings preferences
Place preference dropdowns in a settings panel, with a caret that flips 180 degrees on focus.
Table filters
Use compact selects above a filterable table, where a hidden empty option keeps the control truly blank until chosen.
Multi-step and validated forms
Collect profile fields in a multi-step form, and combine with an inline validation form for error feedback.
Related: Mood Picker
See the Mood Picker for a related forms pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

A native select gives keyboard navigation, screen-reader support, and the platform's mobile picker for free. This snippet removes its default chrome with appearance: none and adds a custom caret, so you get the styled look without rebuilding all the accessibility a div-based fake dropdown would lose.

CSS can detect focus on a select but not whether it has a value, so a wrapper carries a data-empty attribute that JavaScript updates on every change event. The selector .fs-field:not([data-empty="true"]) .fs-label floats the label whenever a real option is chosen, and the focus case is handled separately with :focus + .fs-label.

The first option is hidden, disabled, selected, and has an empty value, so it acts as a placeholder and the select starts with no real value. The sync function reads select.value on load, finds it empty, and leaves the label in its resting position.

The custom SVG caret is a sibling of the select inside the same relative wrapper, so the CSS rule .fs-select:focus ~ .fs-caret rotates it 180 degrees and recolours it on focus. It is also pointer-events: none so clicks pass through to the select beneath it.

Drive the empty state from your bound value rather than a data attribute — render the wrapper class from whether the value is truthy (a computed in Vue, a className expression in React, ngClass in Angular). Keep the element a real select with onChange/v-model/ngModel, and port the caret and label CSS unchanged. In Tailwind use appearance-none with peer-focus utilities for the float.