Footer Locale & Currency Switcher — Free Snippet
Footer Locale & Currency Switcher · Footers · Plain HTML, CSS & JS · Live preview
What's included
Features
About this UI Snippet
Footer Locale & Currency Switcher — Working Language and Currency Pickers

Global products need a place to let a visitor pick their language and their currency without leaving the page, and the footer is the conventional home for that control — out of the way of primary navigation, but always reachable from anywhere on the site. This snippet builds two independent dropdown pickers styled as compact footer buttons, each opening a popover menu upward (since footer dropdowns near the bottom of the viewport have nowhere to open downward), and wires the currency picker to actually recompute a sample price so the interaction feels real rather than decorative.
Two independent popovers, one shared open/close mechanism
Both pickers — language and currency — are built from the same wirePicker(rootId, btnId, menuId, onSelect) function, called twice with different element ids and a different onSelect callback. Clicking a button toggles its own .fls-picker.open class after first calling closeAll(), so opening one picker always closes the other — a small but important detail, since two open dropdowns stacked in a footer row would overlap and confuse the eye. A document level click listener and an Escape keydown listener both call the same closeAll(), covering the two most common ways users expect a popover to dismiss: clicking elsewhere or pressing escape.
Opening upward, not downward
The .fls-menu is positioned with bottom: calc(100% + 8px) rather than top, anchoring it above the trigger button. This is the detail that makes it a genuine footer component rather than a generic dropdown dropped into a footer: a footer sits at the very bottom of the viewport, so a menu that opened downward would frequently render off-screen or get clipped by the page boundary. Opening upward is the correct default for any control positioned in the last 100px of a page.
A currency conversion that actually runs
Selecting a language option is cosmetic — it swaps the flag and label text on the button. Selecting a currency option is not: each <li> carries a data-rate and data-sym attribute, and the onSelect callback multiplies the base priceUSD (29) by the selected rate, rounds it, and writes the new symbol and amount into the sample price on the page, along with a brief color flash so the change registers as a live update rather than a silent DOM mutation. This demonstrates the full flow a real implementation would need: a rate table, a selection handler, and a re-render of every price on the page — here scoped to one element for clarity.
Accessible listbox semantics
Each button carries aria-haspopup="listbox" and toggles aria-expanded, and each menu uses role="listbox" with role="option" list items, so assistive technology announces the control as a genuine picker rather than a plain link list. The hidden attribute — not just a CSS class — is toggled on the menu, which keeps it out of the accessibility tree entirely when closed, not merely visually hidden.
Extending it for production
Wire the language selection to your i18n library's locale-switch function (e.g. reload the page with a ?lang= query param, or call i18n.changeLanguage() in a React app) and drive every price on the page — not just the sample — from a shared currency-rate store so the whole storefront updates in sync. The rate table here is hardcoded for demonstration; in production it should come from a live exchange-rate API refreshed periodically, since currency rates that are even a day stale can meaningfully mislead international customers on price.
Build with AI
Build, Understand, Optimize, and Extend It With AI
Rather than tracing the popover open/close logic by hand, paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the menus anchor with bottom: calc(100% + 8px) instead of the more common top offset, and how the shared wirePicker function keeps the two independent dropdowns from ever being open at the same time. The same assistant can help you optimize it — for instance asking whether the currency rate table should be fetched once on load and cached, or refetched periodically for accuracy. It is also useful for extending the component: ask it to persist the selected language and currency to localStorage so the choice survives a reload, drive every price on the page from the selected currency rather than just the one sample, or wire the language picker into a real i18n routing scheme. Treat the code less like a finished artifact and more like a starting point for a conversation.
Prompt to recreate it
Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:
Build a site footer with two independent dropdown pickers — language and currency — in plain HTML, CSS, and JavaScript, no libraries.
Requirements:
- A footer row with a brand mark, a few nav links, and two picker buttons on the right, each opening its own popover menu when clicked.
- Menus must anchor above their trigger button (not below), since the footer sits at the bottom of the viewport and a downward-opening menu would be clipped.
- Opening one picker must automatically close the other if it is open, and clicking anywhere outside any open menu, or pressing Escape, must close whichever menu is open.
- The currency picker's options must each carry a numeric conversion rate; selecting one must recompute a sample price shown elsewhere on the page by multiplying a fixed base price by that rate, updating the displayed currency symbol, and briefly flashing a color change on the price to signal the live update.
- Use aria-haspopup and a toggled aria-expanded attribute on each trigger button, and role="listbox" with role="option" list items on each menu, with the menu's hidden attribute (not just a CSS class) toggled so it is fully removed from the accessibility tree when closed.
- Provide one shared JavaScript function that wires up both pickers' open/close/selection behavior rather than duplicating the logic for each.Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.
Step by step
How to Use
- 1Paste HTML, CSS, and JSA footer renders with brand, links, and two dropdown pickers on the right.
- 2Open a pickerClick the language or currency button — its menu opens upward above the button.
- 3Pick a currencySelecting a currency recalculates and flashes the sample price above the footer.
- 4Close itClick elsewhere on the page, press Escape, or pick an option — all three dismiss the open menu.
- 5Swap in real ratesReplace the hardcoded data-rate attributes with a live exchange-rate feed and apply the conversion to every price on the page.
- 6Export in your formatClick HTML, JSX, or Tailwind to download the version you need.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
The footer sits at the very bottom of the page, so a menu opening downward from a footer button would frequently render past the viewport edge or get clipped. Anchoring with bottom: calc(100% + 8px) instead of top opens the menu above the button, which always has room in a footer context.
In this snippet only the one sample price element updates, to demonstrate the mechanism clearly. In production, drive every price on the page from a shared rate value in state (or a global store) so all of them recompute together when the currency changes.
Replace the data-rate attributes with values fetched from a live exchange-rate API (refreshed on an interval or on page load) and store them in a JS object keyed by currency code, then look up the current rate in the onSelect callback instead of reading a static attribute.
Both listeners call the same closeAll() function, which is idempotent — calling it when nothing is open simply does nothing. The click listener is attached to document and fires after the button click listener (which uses stopPropagation to prevent immediately reclosing the menu it just opened).
Each trigger button has aria-haspopup="listbox" and a toggled aria-expanded state, and the menu itself uses role="listbox" with role="option" items. The menu is hidden via the hidden attribute (not just CSS), which removes it from the accessibility tree entirely when closed.
Yes — copy an existing <li> inside the relevant .fls-menu, update its data attributes and label, and it works immediately since the click handler is delegated across every list item in the menu rather than bound to specific options.