20 Tailwind CSS Custom Selects

20 Tailwind + JSPublished

Country and currency pickers, fintech card selectors, SaaS plan switchers, account menus, and dashboard filters — plus multi-select tag chips, status dropdowns, date-range presets, color-swatch previews, floating-label fields, and a glassmorphism showpiece. Scoped under .tcs-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

Related20 CSS Custom Select Dropdowns28 Tailwind CSS Input Fields24 CSS Radio Buttons29 CSS Checkboxes30 Tailwind CSS Login Forms24 Tailwind CSS Radio Buttons

Premium upgradePartner: Untitled UI

Affiliate link

Building a whole Tailwind app, not just this component?

You just browsed 20 Tailwind custom selects. Untitled UI Pro unlocks 5,000+ components and sections and 250+ page examples for React, styled with Tailwind CSS v4. One licence is a one-time $349 with lifetime updates; teams start at $999.

Get Untitled UI Pro

Untitled UI React. We may earn a commission, at no extra cost to you.

01. Tailwind Country Selector Dropdown With Flags (opens in a new tab)

Published

Tailwind + JS
The checkout country picker, ported to variants: the panel carries its closed and open geometry as utilities on one element, the selected row styles itself off aria-selected, and the roving highlight styles itself off data-active. The flags are still gradients — the one thing that stays in the stylesheet, because a flag is a recipe, not a design token.

02. Tailwind Currency Picker Dropdown With Symbols (opens in a new tab)

Published

Tailwind + JS
A fintech currency select whose gradient border is a before: utility, not a stylesheet rule: the conic ring, the mask pair and the hover fade all live in the trigger's class list. Options are a three-column grid so $, € and C$ stay aligned, and the panel unfolds with an animated clip-path inset.

03. Tailwind Credit Card Type Selector Dropdown (opens in a new tab)

Published

Tailwind + JS
A saved-cards picker on a glass panel: backdrop-blur-[14px] is a utility, the spring open curve is a token, and the selected row's accent rail is aria-selected:border-l-tcs03-rail — the indicator and the semantics are the same attribute. The four brand marks stay in CSS, where attribute-keyed artwork belongs.

04. Tailwind Pricing Plan Switcher Dropdown (opens in a new tab)

Published

Tailwind + JS
A plan picker whose highlight glides instead of jumping: one absolutely-positioned bar reads --n from the list and moves with [translate:0_calc(var(--n)*60px)]. The accent itself is a runtime variable the script rewrites per tier, which is exactly the case for a plain custom property rather than an @theme token.

05. Tailwind User Avatar Profile Dropdown Menu (opens in a new tab)

Published

Tailwind + JS
The navbar account menu, and the port's win is that there is no state attribute to invent: the panel opens off the button's own aria-expanded through group-has-aria-expanded:. Actions, not values — so this one is a role="menu" with roving focus, not a listbox, and the sign-out row tints itself from the danger token.

06. Tailwind Minimalist Language Switcher Dropdown (opens in a new tab)

Published

Tailwind + JS
The quiet one: an icon pill in a light header that opens a 210px list of languages with their endonyms. Almost the entire demo is utilities — the only stylesheet rule is the CSS globe, and the only new markup idea is lang on each option so a screen reader pronounces Français in French.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

07. Tailwind Account Workspace Switcher Dropdown (opens in a new tab)

Published

Tailwind + JS
A B2B workspace switcher: scrollable team list, plan badges, and a pinned Create workspace action that stays reachable at the bottom. Each team's logo colour is one number — style="--h:265" read by an arbitrary hsl() gradient utility, so a new team needs no new CSS at all.

08. Tailwind Dark Mode Theme Selector Dropdown (opens in a new tab)

Published

Tailwind + JS
Light / Dark / System, and the port makes an argument: @theme is a build-time mint, so a theme the user switches at runtime belongs in a plain [data-theme] block read through bg-[var(--tcs08-surf)]. One attribute repaints the whole stage — including color-scheme, so native controls follow.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

17. Tailwind Utility First Select Dropdown (opens in a new tab)

Published

Tailwind + JS
The one demo whose CSS original was an imitation of Tailwind — hand-written shadows emulating ring-2, hex greys standing in for the palette. Here it is simply the thing itself: ring-3 ring-tcs17-acc/40, shadow-xs, shadow-lg, and a stylesheet that holds nothing but tokens.

19. Tailwind Floating Label Select Dropdown Field (opens in a new tab)

Published

Tailwind + JS
The floating label, and the port deletes its state machine. The label floats when the field is focused, when the panel is open, or when a value exists — group-focus-within:, group-data-open: and group-has-[[aria-selected=true]]:. The has-value class and the two focus listeners are simply gone.
FAQ

Frequently asked questions

How do I make these custom select dropdowns keyboard-accessible instead of just clickable?
A custom select is a listbox, so it needs the roles native form controls give you for free. Put role="combobox" and aria-expanded on the trigger, role="listbox" on the panel, and role="option" with aria-selected on each row. The user avatar menu in Demo 05 and the language switcher in Demo 06 open on click, but a real select must also open on Enter, Space and Arrow keys, move a highlight with Up/Down, commit on Enter and close on Escape returning focus to the trigger. The product sort in Demo 12 already tracks the keyboard focus with its highlight bar, so wire the bar's index to a roving tabindex rather than to hover alone. Never leave the options as bare <div> elements a screen reader reads as plain text with no selected state.
How does the sliding highlight bar in the plan switcher follow the active option without a class per row?
Demo 04 and Demo 12 use one absolutely-positioned bar instead of toggling a background on each row. Every option row sets a custom property — the plan switcher reads --n as its index — and the bar is translated by calc(var(--n) * row-height), so moving selection just rewrites one number and the bar glides on a transition. The win is that five rows share one pill class list; you are not adding and removing an active class in JavaScript. The common bug is a jump on first paint: if the bar starts at translate zero before --n is set, it animates in from the top. Set the initial index inline on the panel and guard the transition with a class you add after the first frame, or the bar slides across the whole list every time the dropdown opens.
How does the multi-select tag dropdown keep chips in the field and a live count in sync?
Demo 09 puts the chosen tags as chips inside the trigger itself and shows a count, and the checkbox tick is drawn entirely in utilities rather than a native input. The mechanism is one source of truth — an array of selected values — that renders both the chips in the field and the checked state of each row. Toggling a row pushes or splices that array, then you re-render chips and rewrite the count text. Two bugs bite here: removing a chip must also uncheck its row, so the chip's remove control has to mutate the same array the list reads, not a copy; and an empty selection should restore the placeholder, not leave a zero-width gap where chips were. For accessibility, mark the panel aria-multiselectable="true" and each row role="option" with aria-selected, because a checkmark alone is invisible to a screen reader.
Why does the dark mode theme selector use @theme, and can I swap themes at runtime with it?
Demo 08 offers Light, Dark and System, and the port makes a deliberate point: @theme in Tailwind is a build-time mint — it generates your utility classes and design tokens when the CSS compiles, so you cannot rewrite it in the browser to flip a theme live. The runtime switch has to run on CSS custom properties instead. Define your palette as variables on a root element, let the utilities reference those variables, and change the theme by setting a data-theme attribute or toggling a class that redefines the variables. The System option reads prefers-color-scheme through a media query. The trap is expecting @theme to behave like a live token store; if you try to mutate it after load nothing happens, and the selector looks broken while the compiled classes never change.
My dropdown won't close when I click outside it — what am I missing?
None of these panels close on their own; the open panels in Demo 01, Demo 05 and Demo 07 stay open until something toggles them shut. You need a document-level listener that closes the dropdown when a click lands outside both the trigger and the panel. The usual bug is that the same click which opens the menu immediately bubbles up to that document handler and closes it again, so the panel never appears. Fix it by checking whether the event target is contained inside your root element before closing, or stop propagation on the trigger. Also close on Escape and on selecting an option. For the scrollable workspace list in Demo 07, do not close on a scroll or on clicks inside the pinned Create workspace action — scope the outside check to the panel's bounding element, and remember to tear the listener down when the component unmounts or you leak handlers.
When should I not build a custom select and just use the native element instead?
Reach for a native <select> when the options are plain text and the value is all that matters — a native control gives you keyboard support, mobile's built-in wheel picker, form submission and screen-reader semantics with zero code, and these ports cannot fully match the native mobile experience. Build a custom one only when the option needs rich content a native <option> cannot render: the flags in Demo 01, the currency symbols in Demo 02, the avatar rows in Demo 05, the per-row typefaces in Demo 14 or the hex swatches in Demo 16. Even then, keep a real hidden native input carrying the value so the field submits inside a <form> and validation still works. If your dropdown is really navigation — the account menu in Demo 05 — it is a menu, not a select, and should use links and role="menu", not option semantics.
How does the color swatch picker render any hex without writing a new class per color?
Demo 16 avoids a class explosion by driving the swatch from a variable. Each row sets --c to its hex value, and the swatch element is a single bg-[var(--c)] — so one class list paints an unlimited palette because the color lives in the property, not the utility. Adding a color is adding a row that sets --c; you never touch the stylesheet. The same trick powers the font preview in Demo 14, where each row sets its own face and the trigger repaints from the chosen row. The pitfall is contrast: a light swatch on a light panel needs a ring or border so it does not vanish, and a hex you inject from user input must be validated before it reaches --c or a malformed value silently falls back to transparent. Give each option an aria-label with the color name, since a screen reader cannot announce a background color.
How is the slide-and-fade open animation built, and why does it feel springy rather than linear?
Demo 18 resolves the panel from blurred-and-small to sharp, and the rows cascade in behind it. The panel carries its closed and open geometry as utilities — a small scale, an opacity of zero and a blur in the closed state, transitioning to full scale and sharp when an open class is added, as Demo 01 does with its closed and open geometry. The spring feel comes from the timing function, not extra keyframes: a custom cubic-bezier that overshoots past one before settling reads as a spring. Demo 20's aurora uses a named --animate-tcs20-drift for the same reason. Two things to guard: set transform-origin toward the trigger so the panel grows out of the field instead of its own center, and wrap the motion in prefers-reduced-motion so users who opt out get an instant open with no scale or blur. Stagger rows with a per-row delay, but cap it or the last row lags noticeably.

Related collections

15 CSS 3D Tilt Hover Cards preview

15 CSS 3D Tilt Hover Cards

15 mouse-aware 3D tilt hover cards — e-commerce product spotlight, glassmorphism parallax team card, interactive pricing tier, holographic NFT collectible, pop-out mascot, dark tech grid with border glow, media player album art, blog article preview, cyberpunk neon glow, dashboard KPI widget, minimalist real estate, flip-to-back tilt, mobile app showcase, course learning card, and a Pure CSS touch-friendly tilt. Vanilla JS writes --rx/--ry/--mx/--my; all rendering stays in CSS on the GPU.

24 CSS Animated Cards preview

24 CSS Animated Cards

24 hand-coded CSS animated card patterns organised by what triggers the motion, not by card style. Scroll and entrance triggers: staggered grid reveals, @starting-style mount-in, scroll-driven scale and fade on animation-timeline: view(), View Transitions API card-to-detail morphs, blur-to-sharp lazy image loading, and FLIP re-layout when a filter changes. State-change triggers: add-to-cart success morph, like and save particle burst, expand and collapse to intrinsic height with calc-size(), swipe-to-dismiss on Pointer Events, and skeleton-to-loaded crossfade. Data-driven triggers: count-up KPI metrics, SVG sparkline draw-on-enter, and progress ring fill on load. Ambient idle loops: breathing glow, floating drift, animated gradient mesh backgrounds, conic rotating borders, auto-cycling testimonial decks, and live status pulses. Plus four hover patterns where the effect is the topic itself: cursor spotlight, holographic foil glare, corner ribbon slide, and horizontal accordion expand. Every card is scoped under a .ac-NN prefix for no-collision pasting, guards prefers-reduced-motion, animates compositor-only properties, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

22 CSS Avatars preview

22 CSS Avatars

22 hand-coded CSS avatars for chat apps, team dashboards, comment threads, account menus, and social profiles. Covers circular and squircle shapes, gradient and conic story rings, hexagon clip-path crops, online status dots, notification count badges, verified checkmarks, stacked facepiles with plus-N overflow, initials fallbacks for users without photos, broken-image recovery, avatar pickers, and a size scale system where the ring, badge, and status dot all scale from a single custom property.

Search CodeFronts

Loading…