Open demo

CSS code

HTML
<div><span class="lbl">Time zone with a toggle button</span><div class="card">
<div class="cb" data-combobox>
  <label class="cb-label" for="cb-tz-input">Time zone</label>
  <div class="cb-field">
    <input class="cb-input" id="cb-tz-input" type="text" value="Riga" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="cb-tz-list" aria-describedby="cb-tz-hint">
    <button type="button" class="cb-toggle" tabindex="-1" aria-label="Show time zones" aria-expanded="false" aria-controls="cb-tz-list" data-toggle><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m4 6 4 4 4-4"/></svg></button>
  </div>
  <div class="cb-pop" data-popup hidden>
    <ul class="cb-list" role="listbox" id="cb-tz-list" aria-label="Time zones">
        <li class="cb-opt" role="option" id="cb-tz-1" data-value="Pacific/Honolulu"><span class="cb-main"><span data-label>Honolulu</span> <span class="cb-sub">Pacific/Honolulu</span></span><span class="cb-off">UTC−10:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-2" data-value="America/Los_Angeles"><span class="cb-main"><span data-label>Los Angeles</span> <span class="cb-sub">America/Los Angeles</span></span><span class="cb-off">UTC−08:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-3" data-value="America/Denver"><span class="cb-main"><span data-label>Denver</span> <span class="cb-sub">America/Denver</span></span><span class="cb-off">UTC−07:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-4" data-value="America/Chicago"><span class="cb-main"><span data-label>Chicago</span> <span class="cb-sub">America/Chicago</span></span><span class="cb-off">UTC−06:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-5" data-value="America/New_York"><span class="cb-main"><span data-label>New York</span> <span class="cb-sub">America/New York</span></span><span class="cb-off">UTC−05:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-6" data-value="America/Sao_Paulo"><span class="cb-main"><span data-label>São Paulo</span> <span class="cb-sub">America/Sao Paulo</span></span><span class="cb-off">UTC−03:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-7" data-value="Europe/London"><span class="cb-main"><span data-label>London</span> <span class="cb-sub">Europe/London</span></span><span class="cb-off">UTC+00:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-8" data-value="Europe/Lisbon"><span class="cb-main"><span data-label>Lisbon</span> <span class="cb-sub">Europe/Lisbon</span></span><span class="cb-off">UTC+00:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-9" data-value="Europe/Berlin"><span class="cb-main"><span data-label>Berlin</span> <span class="cb-sub">Europe/Berlin</span></span><span class="cb-off">UTC+01:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-10" data-value="Europe/Riga"><span class="cb-main"><span data-label>Riga</span> <span class="cb-sub">Europe/Riga</span></span><span class="cb-off">UTC+02:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-11" data-value="Africa/Nairobi"><span class="cb-main"><span data-label>Nairobi</span> <span class="cb-sub">Africa/Nairobi</span></span><span class="cb-off">UTC+03:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-12" data-value="Asia/Dubai"><span class="cb-main"><span data-label>Dubai</span> <span class="cb-sub">Asia/Dubai</span></span><span class="cb-off">UTC+04:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-13" data-value="Asia/Kolkata"><span class="cb-main"><span data-label>Mumbai</span> <span class="cb-sub">Asia/Kolkata</span></span><span class="cb-off">UTC+05:30</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-14" data-value="Asia/Singapore"><span class="cb-main"><span data-label>Singapore</span> <span class="cb-sub">Asia/Singapore</span></span><span class="cb-off">UTC+08:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-15" data-value="Asia/Tokyo"><span class="cb-main"><span data-label>Tokyo</span> <span class="cb-sub">Asia/Tokyo</span></span><span class="cb-off">UTC+09:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-16" data-value="Australia/Sydney"><span class="cb-main"><span data-label>Sydney</span> <span class="cb-sub">Australia/Sydney</span></span><span class="cb-off">UTC+10:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-tz-17" data-value="Pacific/Auckland"><span class="cb-main"><span data-label>Auckland</span> <span class="cb-sub">Pacific/Auckland</span></span><span class="cb-off">UTC+12:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
    </ul>
    <p class="cb-msg" data-empty hidden>No matching time zones.</p>
  </div>
  <input type="hidden" name="timezone" value="Europe/Riga">
  <p class="cb-hint" id="cb-tz-hint">Search by city, region or UTC offset.</p>
  <p class="sr-only" role="status"></p>
</div>
</div></div>

<div><span class="lbl">Airport search with codes and a clear button</span><div class="card">
<div class="cb" data-combobox>
  <label class="cb-label" for="cb-ap-input">Flying from</label>
  <div class="cb-field">
    <svg class="cb-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/></svg>
    <input class="cb-input has-ico" id="cb-ap-input" type="text" placeholder="City or airport code" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="cb-ap-list">
    <button type="button" class="cb-clear" aria-label="Clear airport" data-clear hidden><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="m4.5 4.5 7 7m0-7-7 7"/></svg></button>
  </div>
  <div class="cb-pop" data-popup hidden>
    <ul class="cb-list" role="listbox" id="cb-ap-list" aria-label="Airports">
        <li class="cb-opt" role="option" id="cb-ap-1" data-value="LIS"><span class="cb-code">LIS</span><span class="cb-main"><span data-label>Lisbon</span> <span class="cb-sub">Humberto Delgado, Portugal</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-2" data-value="LHR"><span class="cb-code">LHR</span><span class="cb-main"><span data-label>London Heathrow</span> <span class="cb-sub">United Kingdom</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-3" data-value="LGW"><span class="cb-code">LGW</span><span class="cb-main"><span data-label>London Gatwick</span> <span class="cb-sub">United Kingdom</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-4" data-value="LAX"><span class="cb-code">LAX</span><span class="cb-main"><span data-label>Los Angeles</span> <span class="cb-sub">California, United States</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-5" data-value="MAD"><span class="cb-code">MAD</span><span class="cb-main"><span data-label>Madrid Barajas</span> <span class="cb-sub">Spain</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-6" data-value="MXP"><span class="cb-code">MXP</span><span class="cb-main"><span data-label>Milan Malpensa</span> <span class="cb-sub">Italy</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-7" data-value="MUC"><span class="cb-code">MUC</span><span class="cb-main"><span data-label>Munich</span> <span class="cb-sub">Franz Josef Strauss, Germany</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-8" data-value="OSL"><span class="cb-code">OSL</span><span class="cb-main"><span data-label>Oslo Gardermoen</span> <span class="cb-sub">Norway</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-9" data-value="CDG"><span class="cb-code">CDG</span><span class="cb-main"><span data-label>Paris Charles de Gaulle</span> <span class="cb-sub">France</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-10" data-value="PRG"><span class="cb-code">PRG</span><span class="cb-main"><span data-label>Prague</span> <span class="cb-sub">Václav Havel, Czechia</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-11" data-value="RIX"><span class="cb-code">RIX</span><span class="cb-main"><span data-label>Riga</span> <span class="cb-sub">Latvia</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-12" data-value="FCO"><span class="cb-code">FCO</span><span class="cb-main"><span data-label>Rome Fiumicino</span> <span class="cb-sub">Italy</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-13" data-value="ARN"><span class="cb-code">ARN</span><span class="cb-main"><span data-label>Stockholm Arlanda</span> <span class="cb-sub">Sweden</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-14" data-value="VIE"><span class="cb-code">VIE</span><span class="cb-main"><span data-label>Vienna</span> <span class="cb-sub">Austria</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="cb-opt" role="option" id="cb-ap-15" data-value="ZRH"><span class="cb-code">ZRH</span><span class="cb-main"><span data-label>Zürich</span> <span class="cb-sub">Switzerland</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
    </ul>
    <p class="cb-msg" data-empty hidden>No matching airports.</p>
  </div>
  <input type="hidden" name="from" value="">
  <p class="sr-only" role="status"></p>
</div>
</div></div>
CSS
/* Searchable combobox: an editable combobox with list autocomplete (WAI-ARIA APG). The input filters a listbox of
   options; the highlighted row, the pick and the open state come from data-active, data-chosen and aria-expanded,
   which the script sets. A hidden input carries the picked id to the form. */
.cb{--cb-accent:#1d4ed8;--cb-mark:#1e40af;--cb-tint:#eaf0ff;--cb-ring:rgba(29,78,216,.25);--cb-line:#858ea3;--cb-ink:#152033;--cb-muted:#566079;
  position:relative;display:grid;gap:7px;color:var(--cb-ink)}
.cb [hidden]{display:none!important}
.cb .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
.cb-label{font-size:.9rem;font-weight:600}
.cb-hint{font-size:.82rem;color:var(--cb-muted)}
.cb-field{position:relative}
.cb-input{display:block;width:100%;height:46px;margin:0;padding:0 48px 0 14px;font:inherit;font-size:.95rem;color:inherit;background:#fff;
  border:1px solid var(--cb-line);border-radius:12px;box-shadow:0 1px 2px rgba(21,32,51,.06);transition:border-color .15s,box-shadow .15s}
.cb-input.has-ico{padding-left:44px}
.cb-input::placeholder{color:var(--cb-muted);opacity:1}
.cb-input:hover{border-color:#646d84}
.cb-input:focus{outline:none;border-color:var(--cb-accent);box-shadow:0 0 0 4px var(--cb-ring)}
.cb-ico{position:absolute;left:14px;top:50%;width:19px;height:19px;margin-top:-9.5px;color:var(--cb-muted);pointer-events:none}
.cb-toggle,.cb-clear{position:absolute;top:50%;display:grid;place-items:center;padding:0;color:var(--cb-muted);background:none;border:0;cursor:pointer}
.cb-toggle{right:5px;width:38px;height:36px;margin-top:-18px;border-radius:9px}
.cb-toggle:hover{background:#eef2f8;color:var(--cb-ink)}
.cb-toggle svg{width:16px;height:16px;transition:transform .2s}
.cb-toggle[aria-expanded="true"] svg{transform:rotate(180deg)}
.cb-clear{right:9px;width:28px;height:28px;margin-top:-14px;border-radius:50%}
.cb-clear:hover{background:#eef2f8;color:var(--cb-ink)}
.cb-clear:focus-visible{outline:2px solid var(--cb-accent);outline-offset:1px}
.cb-clear svg{width:14px;height:14px}
/* the popup */
.cb-pop{position:absolute;z-index:30;left:0;right:0;top:calc(100% + 6px);padding:6px;background:#fff;border:1px solid #d6dce8;border-radius:12px;
  box-shadow:0 18px 40px -12px rgba(21,32,51,.3)}
.cb-list{max-height:280px;overflow-y:auto;overscroll-behavior:contain;list-style:none}
.cb-opt{display:flex;align-items:center;gap:12px;padding:8px 10px;border-radius:8px;cursor:pointer}
.cb-opt[data-active]{background:var(--cb-tint)}
.cb-main{flex:1;min-width:0;font-size:.93rem}
.cb-main mark{background:none;color:var(--cb-mark);font-weight:700}
.cb-opt[data-chosen] .cb-main>[data-label]{font-weight:600}
.cb-sub{display:block;font-size:.78rem;color:var(--cb-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cb-off{flex:none;font-size:.78rem;font-weight:600;color:var(--cb-muted);font-variant-numeric:tabular-nums}
.cb-code{flex:none;display:grid;place-items:center;width:44px;height:30px;border-radius:7px;background:#eef2f8;color:#334155;font-size:.74rem;font-weight:700;letter-spacing:.06em}
.cb-opt[data-active] .cb-code{background:#dbe5ff;color:var(--cb-mark)}
.cb-check{flex:none;width:16px;height:16px;color:var(--cb-accent);visibility:hidden}
.cb-opt[data-chosen] .cb-check{visibility:visible}
.cb-msg{padding:10px;font-size:.88rem;color:var(--cb-muted)}
@media (prefers-reduced-motion:reduce){.cb-input,.cb-toggle svg{transition:none}}
JavaScript
// Searchable combobox: an editable combobox with list autocomplete (WAI-ARIA APG). Typing filters the list and
// highlights the match. Down / Up open the list and move through it (aria-activedescendant, wrapping round),
// Alt+Down opens it without moving, Enter picks, Escape closes the list or, when it is closed, clears the field.
// The picked option's data-value goes into the hidden input, so the form submits an id rather than the label.
(() => {
  document.querySelectorAll('[data-combobox]').forEach(root => {
    const input = root.querySelector('[role="combobox"]');
    const popup = root.querySelector('[data-popup]');
    const list = root.querySelector('[role="listbox"]');
    const empty = root.querySelector('[data-empty]');
    const value = root.querySelector('input[type="hidden"]');
    const toggle = root.querySelector('[data-toggle]');
    const clear = root.querySelector('[data-clear]');
    const status = root.querySelector('[role="status"]');
    const opts = [...list.querySelectorAll('[role="option"]')];
    const names = new Map(opts.map(o => [o, o.querySelector('[data-label]').textContent]));
    const isOpen = () => input.getAttribute('aria-expanded') === 'true';
    let active = null;
    let chosen = opts.find(o => o.dataset.value === value.value) || null;

    function choose(o) {
      chosen = o;
      value.value = o ? o.dataset.value : '';
      opts.forEach(x => x.toggleAttribute('data-chosen', x === o));
      if (clear) clear.hidden = !input.value;
    }
    function filter(all) {
      const q = all ? '' : input.value.trim().toLowerCase();
      const hits = opts.filter(o => {
        const hit = o.textContent.toLowerCase().includes(q);
        const el = o.querySelector('[data-label]'), name = names.get(o), at = q ? name.toLowerCase().indexOf(q) : -1;
        o.hidden = !hit;
        if (at < 0) el.textContent = name;
        else {
          const mark = document.createElement('mark');
          mark.textContent = name.slice(at, at + q.length);
          el.replaceChildren(name.slice(0, at), mark, name.slice(at + q.length));
        }
        return hit;
      });
      empty.hidden = hits.length > 0;
      return hits;
    }
    function setActive(o) {
      active = o;
      opts.forEach(x => { x.toggleAttribute('data-active', x === o); x.setAttribute('aria-selected', x === o); });
      if (!o) return input.removeAttribute('aria-activedescendant');
      input.setAttribute('aria-activedescendant', o.id);
      const r = o.getBoundingClientRect(), b = list.getBoundingClientRect(); // keep it in view without scrolling the page
      if (r.top < b.top) list.scrollTop -= b.top - r.top;
      else if (r.bottom > b.bottom) list.scrollTop += r.bottom - b.bottom;
    }
    function open(announce, all) {
      const hits = filter(all);
      popup.hidden = false;
      input.setAttribute('aria-expanded', 'true');
      if (toggle) toggle.setAttribute('aria-expanded', 'true');
      if (announce) status.textContent = hits.length ? hits.length + (hits.length === 1 ? ' result' : ' results') : 'No results';
    }
    function close() {
      popup.hidden = true;
      input.setAttribute('aria-expanded', 'false');
      if (toggle) toggle.setAttribute('aria-expanded', 'false');
      setActive(null);
    }
    function pick(o) {
      input.value = names.get(o);
      choose(o);
      close();
      value.dispatchEvent(new Event('change', { bubbles: true }));
    }
    // opening on a field that still shows the pick lists everything, with the pick highlighted
    const showsPick = () => chosen && input.value === names.get(chosen);

    input.addEventListener('keydown', e => {
      switch (e.key) {
        case 'ArrowDown': case 'ArrowUp': {
          e.preventDefault();
          const down = e.key === 'ArrowDown';
          if (e.altKey) { if (!down) { if (active) pick(active); else close(); } else if (!isOpen()) open(false, showsPick()); break; }
          if (!isOpen()) {
            open(false, showsPick());
            if (showsPick()) { setActive(chosen); break; }
          }
          const vis = opts.filter(o => !o.hidden), i = vis.indexOf(active);
          if (vis.length) setActive(vis[i < 0 ? (down ? 0 : vis.length - 1) : (i + (down ? 1 : -1) + vis.length) % vis.length]);
          break;
        }
        case 'Enter':
          if (isOpen() && active) { e.preventDefault(); pick(active); }
          else if (isOpen()) close();
          break;
        case 'Escape':
          if (isOpen()) { e.preventDefault(); close(); }
          else if (input.value) { e.preventDefault(); input.value = ''; choose(null); }
          break;
        case 'Tab': close(); break;
        case 'ArrowLeft': case 'ArrowRight': case 'Home': case 'End': setActive(null); break;
      }
    });
    input.addEventListener('input', () => {
      const q = input.value.trim().toLowerCase();
      choose(opts.find(o => names.get(o).toLowerCase() === q) || null);
      setActive(null);
      open(true);
    });
    input.addEventListener('click', () => { if (!isOpen()) open(false, showsPick()); });
    input.addEventListener('blur', close);
    if (toggle) {
      toggle.addEventListener('mousedown', e => e.preventDefault());
      toggle.addEventListener('click', () => {
        if (isOpen()) close();
        else { open(false, showsPick()); if (showsPick()) setActive(chosen); }
        input.focus();
      });
    }
    if (clear) clear.addEventListener('click', () => {
      input.value = '';
      choose(null);
      value.dispatchEvent(new Event('change', { bubbles: true }));
      input.focus();
    });
    popup.addEventListener('mousedown', e => e.preventDefault()); // keep focus in the input
    list.addEventListener('click', e => { const o = e.target.closest('[role="option"]'); if (o) { pick(o); input.focus(); } });
    list.addEventListener('pointermove', e => {
      const o = e.target.closest('[role="option"]');
      if (o && o !== active) setActive(o);
    });
    document.addEventListener('pointerdown', e => { if (isOpen() && !root.contains(e.target)) close(); });
    if (input.form) input.form.addEventListener('reset', () => setTimeout(() => {
      choose(opts.find(o => names.get(o) === input.value) || null); // follow the text the reset put back
    }));
    if (chosen) input.value = names.get(chosen);
    choose(chosen);
  });
})();

Bootstrap 5 code

Requires: Bootstrap 5.3.8 CSS

HTML
<div><span class="lbl">Time zone with a toggle button</span><div class="card demo-card"><div class="card-body">
<div class="cb" data-combobox>
  <label class="form-label" for="cb-tz-input">Time zone</label>
  <div class="position-relative">
    <input class="form-control cb-input" id="cb-tz-input" type="text" value="Riga" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="cb-tz-list" aria-describedby="cb-tz-hint">
    <button type="button" class="btn cb-toggle" tabindex="-1" aria-label="Show time zones" aria-expanded="false" aria-controls="cb-tz-list" data-toggle><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m4 6 4 4 4-4"/></svg></button>
  </div>
  <div class="dropdown-menu cb-pop" data-popup hidden>
    <ul class="cb-list list-unstyled m-0" role="listbox" id="cb-tz-list" aria-label="Time zones">
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-1" data-value="Pacific/Honolulu"><span class="cb-main"><span data-label>Honolulu</span> <span class="cb-sub">Pacific/Honolulu</span></span><span class="cb-off">UTC−10:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-2" data-value="America/Los_Angeles"><span class="cb-main"><span data-label>Los Angeles</span> <span class="cb-sub">America/Los Angeles</span></span><span class="cb-off">UTC−08:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-3" data-value="America/Denver"><span class="cb-main"><span data-label>Denver</span> <span class="cb-sub">America/Denver</span></span><span class="cb-off">UTC−07:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-4" data-value="America/Chicago"><span class="cb-main"><span data-label>Chicago</span> <span class="cb-sub">America/Chicago</span></span><span class="cb-off">UTC−06:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-5" data-value="America/New_York"><span class="cb-main"><span data-label>New York</span> <span class="cb-sub">America/New York</span></span><span class="cb-off">UTC−05:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-6" data-value="America/Sao_Paulo"><span class="cb-main"><span data-label>São Paulo</span> <span class="cb-sub">America/Sao Paulo</span></span><span class="cb-off">UTC−03:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-7" data-value="Europe/London"><span class="cb-main"><span data-label>London</span> <span class="cb-sub">Europe/London</span></span><span class="cb-off">UTC+00:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-8" data-value="Europe/Lisbon"><span class="cb-main"><span data-label>Lisbon</span> <span class="cb-sub">Europe/Lisbon</span></span><span class="cb-off">UTC+00:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-9" data-value="Europe/Berlin"><span class="cb-main"><span data-label>Berlin</span> <span class="cb-sub">Europe/Berlin</span></span><span class="cb-off">UTC+01:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-10" data-value="Europe/Riga"><span class="cb-main"><span data-label>Riga</span> <span class="cb-sub">Europe/Riga</span></span><span class="cb-off">UTC+02:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-11" data-value="Africa/Nairobi"><span class="cb-main"><span data-label>Nairobi</span> <span class="cb-sub">Africa/Nairobi</span></span><span class="cb-off">UTC+03:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-12" data-value="Asia/Dubai"><span class="cb-main"><span data-label>Dubai</span> <span class="cb-sub">Asia/Dubai</span></span><span class="cb-off">UTC+04:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-13" data-value="Asia/Kolkata"><span class="cb-main"><span data-label>Mumbai</span> <span class="cb-sub">Asia/Kolkata</span></span><span class="cb-off">UTC+05:30</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-14" data-value="Asia/Singapore"><span class="cb-main"><span data-label>Singapore</span> <span class="cb-sub">Asia/Singapore</span></span><span class="cb-off">UTC+08:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-15" data-value="Asia/Tokyo"><span class="cb-main"><span data-label>Tokyo</span> <span class="cb-sub">Asia/Tokyo</span></span><span class="cb-off">UTC+09:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-16" data-value="Australia/Sydney"><span class="cb-main"><span data-label>Sydney</span> <span class="cb-sub">Australia/Sydney</span></span><span class="cb-off">UTC+10:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-tz-17" data-value="Pacific/Auckland"><span class="cb-main"><span data-label>Auckland</span> <span class="cb-sub">Pacific/Auckland</span></span><span class="cb-off">UTC+12:00</span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
    </ul>
    <p class="cb-msg" data-empty hidden>No matching time zones.</p>
  </div>
  <input type="hidden" name="timezone" value="Europe/Riga">
  <p class="form-text" id="cb-tz-hint">Search by city, region or UTC offset.</p>
  <p class="visually-hidden" role="status"></p>
</div>
</div></div></div>

<div><span class="lbl">Airport search with codes and a clear button</span><div class="card demo-card"><div class="card-body">
<div class="cb" data-combobox>
  <label class="form-label" for="cb-ap-input">Flying from</label>
  <div class="position-relative">
    <svg class="cb-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/></svg>
    <input class="form-control cb-input has-ico" id="cb-ap-input" type="text" placeholder="City or airport code" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="cb-ap-list">
    <button type="button" class="btn-close cb-clear" aria-label="Clear airport" data-clear hidden></button>
  </div>
  <div class="dropdown-menu cb-pop" data-popup hidden>
    <ul class="cb-list list-unstyled m-0" role="listbox" id="cb-ap-list" aria-label="Airports">
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-1" data-value="LIS"><span class="badge cb-code">LIS</span><span class="cb-main"><span data-label>Lisbon</span> <span class="cb-sub">Humberto Delgado, Portugal</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-2" data-value="LHR"><span class="badge cb-code">LHR</span><span class="cb-main"><span data-label>London Heathrow</span> <span class="cb-sub">United Kingdom</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-3" data-value="LGW"><span class="badge cb-code">LGW</span><span class="cb-main"><span data-label>London Gatwick</span> <span class="cb-sub">United Kingdom</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-4" data-value="LAX"><span class="badge cb-code">LAX</span><span class="cb-main"><span data-label>Los Angeles</span> <span class="cb-sub">California, United States</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-5" data-value="MAD"><span class="badge cb-code">MAD</span><span class="cb-main"><span data-label>Madrid Barajas</span> <span class="cb-sub">Spain</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-6" data-value="MXP"><span class="badge cb-code">MXP</span><span class="cb-main"><span data-label>Milan Malpensa</span> <span class="cb-sub">Italy</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-7" data-value="MUC"><span class="badge cb-code">MUC</span><span class="cb-main"><span data-label>Munich</span> <span class="cb-sub">Franz Josef Strauss, Germany</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-8" data-value="OSL"><span class="badge cb-code">OSL</span><span class="cb-main"><span data-label>Oslo Gardermoen</span> <span class="cb-sub">Norway</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-9" data-value="CDG"><span class="badge cb-code">CDG</span><span class="cb-main"><span data-label>Paris Charles de Gaulle</span> <span class="cb-sub">France</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-10" data-value="PRG"><span class="badge cb-code">PRG</span><span class="cb-main"><span data-label>Prague</span> <span class="cb-sub">Václav Havel, Czechia</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-11" data-value="RIX"><span class="badge cb-code">RIX</span><span class="cb-main"><span data-label>Riga</span> <span class="cb-sub">Latvia</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-12" data-value="FCO"><span class="badge cb-code">FCO</span><span class="cb-main"><span data-label>Rome Fiumicino</span> <span class="cb-sub">Italy</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-13" data-value="ARN"><span class="badge cb-code">ARN</span><span class="cb-main"><span data-label>Stockholm Arlanda</span> <span class="cb-sub">Sweden</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-14" data-value="VIE"><span class="badge cb-code">VIE</span><span class="cb-main"><span data-label>Vienna</span> <span class="cb-sub">Austria</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="dropdown-item cb-opt d-flex align-items-center" role="option" id="cb-ap-15" data-value="ZRH"><span class="badge cb-code">ZRH</span><span class="cb-main"><span data-label>Zürich</span> <span class="cb-sub">Switzerland</span></span><svg class="cb-check" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
    </ul>
    <p class="cb-msg" data-empty hidden>No matching airports.</p>
  </div>
  <input type="hidden" name="from" value="">
  <p class="visually-hidden" role="status"></p>
</div>
</div></div></div>
CSS
/* Searchable combobox on Bootstrap parts: a .form-control as the editable combobox (WAI-ARIA APG list autocomplete),
   a .dropdown-menu with .dropdown-item options, .btn-close to clear. The script sets data-active, data-chosen and
   aria-expanded; a hidden input carries the picked id to the form. */
.cb{--cb-accent:#1d4ed8;--cb-mark:#1e40af;--cb-tint:#eaf0ff;--cb-ring:rgba(29,78,216,.25);--cb-line:#858ea3;--cb-ink:#152033;--cb-muted:#566079;
  position:relative;display:grid;gap:7px;color:var(--cb-ink)}
.cb [hidden]{display:none!important} /* beats .d-flex on filtered options */
.cb .form-label{margin:0;font-size:.9rem;font-weight:600}
.cb .form-text{margin:0;font-size:.82rem;color:var(--cb-muted)}
.cb .cb-input{height:46px;padding:0 48px 0 14px;font-size:.95rem;line-height:1.45;color:var(--cb-ink);border-color:var(--cb-line);border-radius:12px;
  box-shadow:0 1px 2px rgba(21,32,51,.06)}
.cb .cb-input.has-ico{padding-left:44px}
.cb .cb-input::placeholder{color:var(--cb-muted)}
.cb .cb-input:hover{border-color:#646d84}
.cb .cb-input:focus{border-color:var(--cb-accent);box-shadow:0 0 0 4px var(--cb-ring)}
.cb-ico{position:absolute;left:14px;top:50%;width:19px;height:19px;margin-top:-9.5px;color:var(--cb-muted);pointer-events:none;z-index:1}
.cb .cb-toggle{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-color:var(--cb-muted);--bs-btn-hover-color:var(--cb-ink);--bs-btn-hover-bg:#eef2f8;
  --bs-btn-active-bg:#eef2f8;--bs-btn-border-width:0;position:absolute;right:5px;top:50%;display:grid;place-items:center;width:38px;height:36px;margin-top:-18px;border-radius:9px}
.cb-toggle svg{width:16px;height:16px;transition:transform .2s}
.cb-toggle[aria-expanded="true"] svg{transform:rotate(180deg)}
.cb .cb-clear{--bs-btn-close-bg:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23566079' stroke-width='2.2' stroke-linecap='round'%3E%3Cpath d='m4.5 4.5 7 7m0-7-7 7'/%3E%3C/svg%3E");
  --bs-btn-close-opacity:1;--bs-btn-close-hover-opacity:1;--bs-btn-close-focus-opacity:1;--bs-btn-close-focus-shadow:none;
  position:absolute;right:9px;top:50%;width:28px;height:28px;margin-top:-14px;padding:0;background-size:14px;border-radius:50%}
.cb .cb-clear:hover{background-color:#eef2f8}
.cb .cb-clear:focus-visible{outline:2px solid var(--cb-accent);outline-offset:1px}
.cb .cb-pop{--bs-dropdown-padding-x:6px;--bs-dropdown-padding-y:6px;--bs-dropdown-border-color:#d6dce8;--bs-dropdown-border-radius:12px;
  --bs-dropdown-link-color:var(--cb-ink);--bs-dropdown-link-hover-color:var(--cb-ink);--bs-dropdown-link-hover-bg:transparent;
  --bs-dropdown-link-active-color:var(--cb-ink);--bs-dropdown-link-active-bg:transparent;
  display:block;left:0;right:0;top:calc(100% + 6px);z-index:30;box-shadow:0 18px 40px -12px rgba(21,32,51,.3)}
.cb-list{max-height:280px;overflow-y:auto;overscroll-behavior:contain}
.cb .cb-opt{gap:12px;padding:8px 10px;border-radius:8px;white-space:normal;cursor:pointer}
.cb .cb-opt[data-active]{background:var(--cb-tint)}
.cb-main{flex:1;min-width:0;font-size:.93rem}
.cb-main mark{padding:0;background:none;color:var(--cb-mark);font-weight:700}
.cb-opt[data-chosen] .cb-main>[data-label]{font-weight:600}
.cb-sub{display:block;font-size:.78rem;color:var(--cb-muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.cb-off{flex:none;font-size:.78rem;font-weight:600;color:var(--cb-muted);font-variant-numeric:tabular-nums}
.cb .cb-code{flex:none;display:grid;place-items:center;width:44px;height:30px;border-radius:7px;background:#eef2f8;color:#334155;font-size:.74rem;font-weight:700;letter-spacing:.06em}
.cb-opt[data-active] .cb-code{background:#dbe5ff;color:var(--cb-mark)}
.cb-check{flex:none;width:16px;height:16px;color:var(--cb-accent);visibility:hidden}
.cb-opt[data-chosen] .cb-check{visibility:visible}
.cb-msg{padding:10px;margin:0;font-size:.88rem;color:var(--cb-muted)}
@media (prefers-reduced-motion:reduce){.cb .cb-input,.cb-toggle svg{transition:none}}
JavaScript
// Searchable combobox: an editable combobox with list autocomplete (WAI-ARIA APG). Typing filters the list and
// highlights the match. Down / Up open the list and move through it (aria-activedescendant, wrapping round),
// Alt+Down opens it without moving, Enter picks, Escape closes the list or, when it is closed, clears the field.
// The picked option's data-value goes into the hidden input, so the form submits an id rather than the label.
(() => {
  document.querySelectorAll('[data-combobox]').forEach(root => {
    const input = root.querySelector('[role="combobox"]');
    const popup = root.querySelector('[data-popup]');
    const list = root.querySelector('[role="listbox"]');
    const empty = root.querySelector('[data-empty]');
    const value = root.querySelector('input[type="hidden"]');
    const toggle = root.querySelector('[data-toggle]');
    const clear = root.querySelector('[data-clear]');
    const status = root.querySelector('[role="status"]');
    const opts = [...list.querySelectorAll('[role="option"]')];
    const names = new Map(opts.map(o => [o, o.querySelector('[data-label]').textContent]));
    const isOpen = () => input.getAttribute('aria-expanded') === 'true';
    let active = null;
    let chosen = opts.find(o => o.dataset.value === value.value) || null;

    function choose(o) {
      chosen = o;
      value.value = o ? o.dataset.value : '';
      opts.forEach(x => x.toggleAttribute('data-chosen', x === o));
      if (clear) clear.hidden = !input.value;
    }
    function filter(all) {
      const q = all ? '' : input.value.trim().toLowerCase();
      const hits = opts.filter(o => {
        const hit = o.textContent.toLowerCase().includes(q);
        const el = o.querySelector('[data-label]'), name = names.get(o), at = q ? name.toLowerCase().indexOf(q) : -1;
        o.hidden = !hit;
        if (at < 0) el.textContent = name;
        else {
          const mark = document.createElement('mark');
          mark.textContent = name.slice(at, at + q.length);
          el.replaceChildren(name.slice(0, at), mark, name.slice(at + q.length));
        }
        return hit;
      });
      empty.hidden = hits.length > 0;
      return hits;
    }
    function setActive(o) {
      active = o;
      opts.forEach(x => { x.toggleAttribute('data-active', x === o); x.setAttribute('aria-selected', x === o); });
      if (!o) return input.removeAttribute('aria-activedescendant');
      input.setAttribute('aria-activedescendant', o.id);
      const r = o.getBoundingClientRect(), b = list.getBoundingClientRect(); // keep it in view without scrolling the page
      if (r.top < b.top) list.scrollTop -= b.top - r.top;
      else if (r.bottom > b.bottom) list.scrollTop += r.bottom - b.bottom;
    }
    function open(announce, all) {
      const hits = filter(all);
      popup.hidden = false;
      input.setAttribute('aria-expanded', 'true');
      if (toggle) toggle.setAttribute('aria-expanded', 'true');
      if (announce) status.textContent = hits.length ? hits.length + (hits.length === 1 ? ' result' : ' results') : 'No results';
    }
    function close() {
      popup.hidden = true;
      input.setAttribute('aria-expanded', 'false');
      if (toggle) toggle.setAttribute('aria-expanded', 'false');
      setActive(null);
    }
    function pick(o) {
      input.value = names.get(o);
      choose(o);
      close();
      value.dispatchEvent(new Event('change', { bubbles: true }));
    }
    // opening on a field that still shows the pick lists everything, with the pick highlighted
    const showsPick = () => chosen && input.value === names.get(chosen);

    input.addEventListener('keydown', e => {
      switch (e.key) {
        case 'ArrowDown': case 'ArrowUp': {
          e.preventDefault();
          const down = e.key === 'ArrowDown';
          if (e.altKey) { if (!down) { if (active) pick(active); else close(); } else if (!isOpen()) open(false, showsPick()); break; }
          if (!isOpen()) {
            open(false, showsPick());
            if (showsPick()) { setActive(chosen); break; }
          }
          const vis = opts.filter(o => !o.hidden), i = vis.indexOf(active);
          if (vis.length) setActive(vis[i < 0 ? (down ? 0 : vis.length - 1) : (i + (down ? 1 : -1) + vis.length) % vis.length]);
          break;
        }
        case 'Enter':
          if (isOpen() && active) { e.preventDefault(); pick(active); }
          else if (isOpen()) close();
          break;
        case 'Escape':
          if (isOpen()) { e.preventDefault(); close(); }
          else if (input.value) { e.preventDefault(); input.value = ''; choose(null); }
          break;
        case 'Tab': close(); break;
        case 'ArrowLeft': case 'ArrowRight': case 'Home': case 'End': setActive(null); break;
      }
    });
    input.addEventListener('input', () => {
      const q = input.value.trim().toLowerCase();
      choose(opts.find(o => names.get(o).toLowerCase() === q) || null);
      setActive(null);
      open(true);
    });
    input.addEventListener('click', () => { if (!isOpen()) open(false, showsPick()); });
    input.addEventListener('blur', close);
    if (toggle) {
      toggle.addEventListener('mousedown', e => e.preventDefault());
      toggle.addEventListener('click', () => {
        if (isOpen()) close();
        else { open(false, showsPick()); if (showsPick()) setActive(chosen); }
        input.focus();
      });
    }
    if (clear) clear.addEventListener('click', () => {
      input.value = '';
      choose(null);
      value.dispatchEvent(new Event('change', { bubbles: true }));
      input.focus();
    });
    popup.addEventListener('mousedown', e => e.preventDefault()); // keep focus in the input
    list.addEventListener('click', e => { const o = e.target.closest('[role="option"]'); if (o) { pick(o); input.focus(); } });
    list.addEventListener('pointermove', e => {
      const o = e.target.closest('[role="option"]');
      if (o && o !== active) setActive(o);
    });
    document.addEventListener('pointerdown', e => { if (isOpen() && !root.contains(e.target)) close(); });
    if (input.form) input.form.addEventListener('reset', () => setTimeout(() => {
      choose(opts.find(o => names.get(o) === input.value) || null); // follow the text the reset put back
    }));
    if (chosen) input.value = names.get(chosen);
    choose(chosen);
  });
})();

Tailwind 4 code

Requires: Tailwind CSS 4

HTML
<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#566079] uppercase">Time zone with a toggle button</span><div class="rounded-2xl border border-[#e0e6f0] bg-white px-6 pt-[22px] pb-6 shadow-[0_1px_2px_rgba(21,32,51,.04)]">
<div class="relative grid gap-[7px] text-cb-ink" data-combobox>
  <label class="text-[.9rem] font-semibold" for="cb-tz-input">Time zone</label>
  <div class="relative">
    <input class="block h-[46px] w-full rounded-xl border border-cb-line bg-white pr-12 pl-3.5 text-[.95rem] shadow-[0_1px_2px_rgba(21,32,51,.06)] transition-[border-color,box-shadow] duration-150 placeholder:text-cb-muted hover:border-[#646d84] focus:border-cb-accent focus:shadow-[0_0_0_4px_rgba(29,78,216,.25)] focus:outline-none motion-reduce:transition-none" id="cb-tz-input" type="text" value="Riga" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="cb-tz-list" aria-describedby="cb-tz-hint">
    <button type="button" class="group absolute top-1/2 right-[5px] grid h-9 w-[38px] -translate-y-1/2 cursor-pointer place-items-center rounded-[9px] text-cb-muted hover:bg-[#eef2f8] hover:text-cb-ink" tabindex="-1" aria-label="Show time zones" aria-expanded="false" aria-controls="cb-tz-list" data-toggle><svg class="size-4 transition-transform duration-200 group-aria-expanded:rotate-180 motion-reduce:transition-none" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m4 6 4 4 4-4"/></svg></button>
  </div>
  <div class="absolute top-[calc(100%+6px)] right-0 left-0 z-30 rounded-xl border border-[#d6dce8] bg-white p-1.5 shadow-[0_18px_40px_-12px_rgba(21,32,51,.3)]" data-popup hidden>
    <ul class="max-h-[280px] overflow-y-auto overscroll-contain" role="listbox" id="cb-tz-list" aria-label="Time zones">
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-1" data-value="Pacific/Honolulu"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Honolulu</span> <span class="block truncate text-[.78rem] text-cb-muted">Pacific/Honolulu</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC−10:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-2" data-value="America/Los_Angeles"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Los Angeles</span> <span class="block truncate text-[.78rem] text-cb-muted">America/Los Angeles</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC−08:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-3" data-value="America/Denver"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Denver</span> <span class="block truncate text-[.78rem] text-cb-muted">America/Denver</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC−07:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-4" data-value="America/Chicago"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Chicago</span> <span class="block truncate text-[.78rem] text-cb-muted">America/Chicago</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC−06:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-5" data-value="America/New_York"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>New York</span> <span class="block truncate text-[.78rem] text-cb-muted">America/New York</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC−05:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-6" data-value="America/Sao_Paulo"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>São Paulo</span> <span class="block truncate text-[.78rem] text-cb-muted">America/Sao Paulo</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC−03:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-7" data-value="Europe/London"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>London</span> <span class="block truncate text-[.78rem] text-cb-muted">Europe/London</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+00:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-8" data-value="Europe/Lisbon"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Lisbon</span> <span class="block truncate text-[.78rem] text-cb-muted">Europe/Lisbon</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+00:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-9" data-value="Europe/Berlin"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Berlin</span> <span class="block truncate text-[.78rem] text-cb-muted">Europe/Berlin</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+01:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-10" data-value="Europe/Riga"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Riga</span> <span class="block truncate text-[.78rem] text-cb-muted">Europe/Riga</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+02:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-11" data-value="Africa/Nairobi"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Nairobi</span> <span class="block truncate text-[.78rem] text-cb-muted">Africa/Nairobi</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+03:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-12" data-value="Asia/Dubai"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Dubai</span> <span class="block truncate text-[.78rem] text-cb-muted">Asia/Dubai</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+04:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-13" data-value="Asia/Kolkata"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Mumbai</span> <span class="block truncate text-[.78rem] text-cb-muted">Asia/Kolkata</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+05:30</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-14" data-value="Asia/Singapore"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Singapore</span> <span class="block truncate text-[.78rem] text-cb-muted">Asia/Singapore</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+08:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-15" data-value="Asia/Tokyo"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Tokyo</span> <span class="block truncate text-[.78rem] text-cb-muted">Asia/Tokyo</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+09:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-16" data-value="Australia/Sydney"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Sydney</span> <span class="block truncate text-[.78rem] text-cb-muted">Australia/Sydney</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+10:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-tz-17" data-value="Pacific/Auckland"><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Auckland</span> <span class="block truncate text-[.78rem] text-cb-muted">Pacific/Auckland</span></span><span class="flex-none text-[.78rem] font-semibold text-cb-muted tabular-nums">UTC+12:00</span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
    </ul>
    <p class="p-2.5 text-[.88rem] text-cb-muted" data-empty hidden>No matching time zones.</p>
  </div>
  <input type="hidden" name="timezone" value="Europe/Riga">
  <p class="text-[.82rem] text-cb-muted" id="cb-tz-hint">Search by city, region or UTC offset.</p>
  <p class="sr-only" role="status"></p>
</div>
</div></div>

<div><span class="mb-3 block text-[.72rem] tracking-[.12em] text-[#566079] uppercase">Airport search with codes and a clear button</span><div class="rounded-2xl border border-[#e0e6f0] bg-white px-6 pt-[22px] pb-6 shadow-[0_1px_2px_rgba(21,32,51,.04)]">
<div class="relative grid gap-[7px] text-cb-ink" data-combobox>
  <label class="text-[.9rem] font-semibold" for="cb-ap-input">Flying from</label>
  <div class="relative">
    <svg class="pointer-events-none absolute top-1/2 left-3.5 size-[19px] -translate-y-1/2 text-cb-muted" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M17.8 19.2 16 11l3.5-3.5C21 6 21.5 4 21 3c-1-.5-3 0-4.5 1.5L13 8 4.8 6.2c-.5-.1-.9.1-1.1.5l-.3.5c-.2.5-.1 1 .3 1.3L9 12l-2 3H4l-1 1 3 2 2 3 1-1v-3l3-2 3.5 5.3c.3.4.8.5 1.3.3l.5-.2c.4-.3.6-.7.5-1.2z"/></svg>
    <input class="block h-[46px] w-full rounded-xl border border-cb-line bg-white pr-12 pl-11 text-[.95rem] shadow-[0_1px_2px_rgba(21,32,51,.06)] transition-[border-color,box-shadow] duration-150 placeholder:text-cb-muted hover:border-[#646d84] focus:border-cb-accent focus:shadow-[0_0_0_4px_rgba(29,78,216,.25)] focus:outline-none motion-reduce:transition-none" id="cb-ap-input" type="text" placeholder="City or airport code" autocomplete="off" spellcheck="false" role="combobox" aria-autocomplete="list" aria-expanded="false" aria-controls="cb-ap-list">
    <button type="button" class="absolute top-1/2 right-[9px] grid size-7 -translate-y-1/2 cursor-pointer place-items-center rounded-full text-cb-muted hover:bg-[#eef2f8] hover:text-cb-ink focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-cb-accent" aria-label="Clear airport" data-clear hidden><svg class="size-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" aria-hidden="true" focusable="false"><path d="m4.5 4.5 7 7m0-7-7 7"/></svg></button>
  </div>
  <div class="absolute top-[calc(100%+6px)] right-0 left-0 z-30 rounded-xl border border-[#d6dce8] bg-white p-1.5 shadow-[0_18px_40px_-12px_rgba(21,32,51,.3)]" data-popup hidden>
    <ul class="max-h-[280px] overflow-y-auto overscroll-contain" role="listbox" id="cb-ap-list" aria-label="Airports">
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-1" data-value="LIS"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">LIS</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Lisbon</span> <span class="block truncate text-[.78rem] text-cb-muted">Humberto Delgado, Portugal</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-2" data-value="LHR"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">LHR</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>London Heathrow</span> <span class="block truncate text-[.78rem] text-cb-muted">United Kingdom</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-3" data-value="LGW"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">LGW</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>London Gatwick</span> <span class="block truncate text-[.78rem] text-cb-muted">United Kingdom</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-4" data-value="LAX"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">LAX</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Los Angeles</span> <span class="block truncate text-[.78rem] text-cb-muted">California, United States</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-5" data-value="MAD"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">MAD</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Madrid Barajas</span> <span class="block truncate text-[.78rem] text-cb-muted">Spain</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-6" data-value="MXP"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">MXP</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Milan Malpensa</span> <span class="block truncate text-[.78rem] text-cb-muted">Italy</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-7" data-value="MUC"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">MUC</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Munich</span> <span class="block truncate text-[.78rem] text-cb-muted">Franz Josef Strauss, Germany</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-8" data-value="OSL"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">OSL</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Oslo Gardermoen</span> <span class="block truncate text-[.78rem] text-cb-muted">Norway</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-9" data-value="CDG"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">CDG</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Paris Charles de Gaulle</span> <span class="block truncate text-[.78rem] text-cb-muted">France</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-10" data-value="PRG"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">PRG</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Prague</span> <span class="block truncate text-[.78rem] text-cb-muted">Václav Havel, Czechia</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-11" data-value="RIX"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">RIX</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Riga</span> <span class="block truncate text-[.78rem] text-cb-muted">Latvia</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-12" data-value="FCO"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">FCO</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Rome Fiumicino</span> <span class="block truncate text-[.78rem] text-cb-muted">Italy</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-13" data-value="ARN"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">ARN</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Stockholm Arlanda</span> <span class="block truncate text-[.78rem] text-cb-muted">Sweden</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-14" data-value="VIE"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">VIE</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Vienna</span> <span class="block truncate text-[.78rem] text-cb-muted">Austria</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
        <li class="group flex cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2 data-active:bg-cb-tint" role="option" id="cb-ap-15" data-value="ZRH"><span class="grid h-[30px] w-11 flex-none place-items-center rounded-[7px] bg-[#eef2f8] text-[.74rem] font-bold tracking-[.06em] text-[#334155] group-data-active:bg-[#dbe5ff] group-data-active:text-cb-mark">ZRH</span><span class="min-w-0 flex-1 text-[.93rem] group-data-chosen:[&>[data-label]]:font-semibold [&_mark]:bg-transparent [&_mark]:font-bold [&_mark]:text-cb-mark"><span data-label>Zürich</span> <span class="block truncate text-[.78rem] text-cb-muted">Switzerland</span></span><svg class="invisible size-4 flex-none text-cb-accent group-data-chosen:visible" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="m3.5 8.5 3 3 6-7"/></svg></li>
    </ul>
    <p class="p-2.5 text-[.88rem] text-cb-muted" data-empty hidden>No matching airports.</p>
  </div>
  <input type="hidden" name="from" value="">
  <p class="sr-only" role="status"></p>
</div>
</div></div>
CSS
@theme {
  --color-cb-accent: #1d4ed8;
  --color-cb-mark: #1e40af;
  --color-cb-tint: #eaf0ff;
  --color-cb-line: #858ea3;
  --color-cb-ink: #152033;
  --color-cb-muted: #566079;
}
JavaScript
// Searchable combobox: an editable combobox with list autocomplete (WAI-ARIA APG). Typing filters the list and
// highlights the match. Down / Up open the list and move through it (aria-activedescendant, wrapping round),
// Alt+Down opens it without moving, Enter picks, Escape closes the list or, when it is closed, clears the field.
// The picked option's data-value goes into the hidden input, so the form submits an id rather than the label.
(() => {
  document.querySelectorAll('[data-combobox]').forEach(root => {
    const input = root.querySelector('[role="combobox"]');
    const popup = root.querySelector('[data-popup]');
    const list = root.querySelector('[role="listbox"]');
    const empty = root.querySelector('[data-empty]');
    const value = root.querySelector('input[type="hidden"]');
    const toggle = root.querySelector('[data-toggle]');
    const clear = root.querySelector('[data-clear]');
    const status = root.querySelector('[role="status"]');
    const opts = [...list.querySelectorAll('[role="option"]')];
    const names = new Map(opts.map(o => [o, o.querySelector('[data-label]').textContent]));
    const isOpen = () => input.getAttribute('aria-expanded') === 'true';
    let active = null;
    let chosen = opts.find(o => o.dataset.value === value.value) || null;

    function choose(o) {
      chosen = o;
      value.value = o ? o.dataset.value : '';
      opts.forEach(x => x.toggleAttribute('data-chosen', x === o));
      if (clear) clear.hidden = !input.value;
    }
    function filter(all) {
      const q = all ? '' : input.value.trim().toLowerCase();
      const hits = opts.filter(o => {
        const hit = o.textContent.toLowerCase().includes(q);
        const el = o.querySelector('[data-label]'), name = names.get(o), at = q ? name.toLowerCase().indexOf(q) : -1;
        o.hidden = !hit;
        if (at < 0) el.textContent = name;
        else {
          const mark = document.createElement('mark');
          mark.textContent = name.slice(at, at + q.length);
          el.replaceChildren(name.slice(0, at), mark, name.slice(at + q.length));
        }
        return hit;
      });
      empty.hidden = hits.length > 0;
      return hits;
    }
    function setActive(o) {
      active = o;
      opts.forEach(x => { x.toggleAttribute('data-active', x === o); x.setAttribute('aria-selected', x === o); });
      if (!o) return input.removeAttribute('aria-activedescendant');
      input.setAttribute('aria-activedescendant', o.id);
      const r = o.getBoundingClientRect(), b = list.getBoundingClientRect(); // keep it in view without scrolling the page
      if (r.top < b.top) list.scrollTop -= b.top - r.top;
      else if (r.bottom > b.bottom) list.scrollTop += r.bottom - b.bottom;
    }
    function open(announce, all) {
      const hits = filter(all);
      popup.hidden = false;
      input.setAttribute('aria-expanded', 'true');
      if (toggle) toggle.setAttribute('aria-expanded', 'true');
      if (announce) status.textContent = hits.length ? hits.length + (hits.length === 1 ? ' result' : ' results') : 'No results';
    }
    function close() {
      popup.hidden = true;
      input.setAttribute('aria-expanded', 'false');
      if (toggle) toggle.setAttribute('aria-expanded', 'false');
      setActive(null);
    }
    function pick(o) {
      input.value = names.get(o);
      choose(o);
      close();
      value.dispatchEvent(new Event('change', { bubbles: true }));
    }
    // opening on a field that still shows the pick lists everything, with the pick highlighted
    const showsPick = () => chosen && input.value === names.get(chosen);

    input.addEventListener('keydown', e => {
      switch (e.key) {
        case 'ArrowDown': case 'ArrowUp': {
          e.preventDefault();
          const down = e.key === 'ArrowDown';
          if (e.altKey) { if (!down) { if (active) pick(active); else close(); } else if (!isOpen()) open(false, showsPick()); break; }
          if (!isOpen()) {
            open(false, showsPick());
            if (showsPick()) { setActive(chosen); break; }
          }
          const vis = opts.filter(o => !o.hidden), i = vis.indexOf(active);
          if (vis.length) setActive(vis[i < 0 ? (down ? 0 : vis.length - 1) : (i + (down ? 1 : -1) + vis.length) % vis.length]);
          break;
        }
        case 'Enter':
          if (isOpen() && active) { e.preventDefault(); pick(active); }
          else if (isOpen()) close();
          break;
        case 'Escape':
          if (isOpen()) { e.preventDefault(); close(); }
          else if (input.value) { e.preventDefault(); input.value = ''; choose(null); }
          break;
        case 'Tab': close(); break;
        case 'ArrowLeft': case 'ArrowRight': case 'Home': case 'End': setActive(null); break;
      }
    });
    input.addEventListener('input', () => {
      const q = input.value.trim().toLowerCase();
      choose(opts.find(o => names.get(o).toLowerCase() === q) || null);
      setActive(null);
      open(true);
    });
    input.addEventListener('click', () => { if (!isOpen()) open(false, showsPick()); });
    input.addEventListener('blur', close);
    if (toggle) {
      toggle.addEventListener('mousedown', e => e.preventDefault());
      toggle.addEventListener('click', () => {
        if (isOpen()) close();
        else { open(false, showsPick()); if (showsPick()) setActive(chosen); }
        input.focus();
      });
    }
    if (clear) clear.addEventListener('click', () => {
      input.value = '';
      choose(null);
      value.dispatchEvent(new Event('change', { bubbles: true }));
      input.focus();
    });
    popup.addEventListener('mousedown', e => e.preventDefault()); // keep focus in the input
    list.addEventListener('click', e => { const o = e.target.closest('[role="option"]'); if (o) { pick(o); input.focus(); } });
    list.addEventListener('pointermove', e => {
      const o = e.target.closest('[role="option"]');
      if (o && o !== active) setActive(o);
    });
    document.addEventListener('pointerdown', e => { if (isOpen() && !root.contains(e.target)) close(); });
    if (input.form) input.form.addEventListener('reset', () => setTimeout(() => {
      choose(opts.find(o => names.get(o) === input.value) || null); // follow the text the reset put back
    }));
    if (chosen) input.value = names.get(chosen);
    choose(chosen);
  });
})();

About this set

Single-choice selects you can type into, for lists too long to scroll. Time zone with a toggle button lists seventeen zones with their IANA names and UTC offsets; typing a city, a region or an offset such as +05 narrows the list, and the chevron button opens the full list with the current zone highlighted. Airport search with codes and a clear button puts a plane icon in the field, shows three-letter codes as badges beside each airport, highlights the typed letters and offers a clear button once there is text. Both follow the WAI-ARIA editable combobox with list autocomplete: typing filters, Down and Up move through the matches and wrap round, Alt+Down opens without moving, Enter picks, and Escape closes the list or clears the field. The option’s name stays in the field while its id, such as Europe/Riga or LIS, goes into a hidden input, so the form submits a stable value rather than display text. A polite status line reports how many results match.

What’s included

  • Filter as you type with the match highlighted
  • Toggle button that opens the full list
  • Clear button that appears with text
  • WAI-ARIA editable combobox with list autocomplete
  • Hidden input submits the option id, not the label
  • Result count announced in a status line

Accessibility

The input is a role="combobox" with aria-autocomplete="list", aria-expanded, aria-controls and aria-activedescendant, labelled by a real label element; the highlighted option carries aria-selected as the pattern specifies. The toggle button is named and kept out of the tab order as in the APG example, the clear button is named and reachable with Tab, and a role="status" line announces the number of results. Focus shows a ring on the field.

Customise it

Change --cb-accent, --cb-mark, --cb-tint and --cb-line on .cb; in Tailwind edit the --color-cb-* theme colours. Put your own options in the listbox with data-value for the submitted id and a data-label span for the text that is matched and shown.