# Scrubbable A to Z index

> A contact list with an A to Z rail: slide your finger along the letters and a bubble shows where you will jump.

- Category: [Navigation](https://library.aimran.es/components/navigation)
- Built with: HTML + CSS
- Live editor: https://library.aimran.es/blocks/uv-nav-az-scrubber
- Library: AIMRAN Library (https://library.aimran.es), free, no sign-up, by Aimran Oukham

## Where to use it

The alphabet index from phone contact lists: a rail of letters runs down the side, and sliding a finger or the mouse along it jumps the list to that letter while a teardrop bubble shows the letter next to your finger. Letters with no entries are dimmed, headers stick to the top while you scroll, and the rail works as a slider from the keyboard.

The main color sets the letters and bubble, the secondary color the sticky headers. Sort your data alphabetically, and the empty letters are worked out from it.

## Code

### HTML

```html
<div class="uvq12-az"><p class="tt">Contacts</p><div class="bx"><div class="ls" tabindex="0" aria-label="Contacts"><section data-l="A"><h5>A</h5><ul><li><i style="--h:#e11d48" aria-hidden="true">AR</i>Ana Ruiz</li><li><i style="--h:#eab308" aria-hidden="true">AG</i>Alberto Gil</li></ul></section><section data-l="B"><h5>B</h5><ul><li><i style="--h:#0ea5e9" aria-hidden="true">BS</i>Bea Soto</li></ul></section><section data-l="C"><h5>C</h5><ul><li><i style="--h:#f97316" aria-hidden="true">CV</i>Carla Vidal</li><li><i style="--h:#f97316" aria-hidden="true">CM</i>Carlos Mena</li></ul></section><section data-l="D"><h5>D</h5><ul><li><i style="--h:#f97316" aria-hidden="true">DP</i>Diego Paz</li></ul></section><section data-l="E"><h5>E</h5><ul><li><i style="--h:#eab308" aria-hidden="true">EO</i>Elena Ortiz</li></ul></section><section data-l="G"><h5>G</h5><ul><li><i style="--h:#f97316" aria-hidden="true">GR</i>Gabriel Ros</li></ul></section><section data-l="I"><h5>I</h5><ul><li><i style="--h:#e11d48" aria-hidden="true">IS</i>Irene Sanz</li></ul></section><section data-l="J"><h5>J</h5><ul><li><i style="--h:#f97316" aria-hidden="true">JL</i>Javier León</li></ul></section><section data-l="L"><h5>L</h5><ul><li><i style="--h:#eab308" aria-hidden="true">LP</i>Laura Peña</li><li><i style="--h:#a855f7" aria-hidden="true">LM</i>Lucía Martín</li></ul></section><section data-l="M"><h5>M</h5><ul><li><i style="--h:#f97316" aria-hidden="true">MG</i>Mario Gómez</li><li><i style="--h:#eab308" aria-hidden="true">MC</i>Marta Cano</li></ul></section><section data-l="N"><h5>N</h5><ul><li><i style="--h:#0ea5e9" aria-hidden="true">NV</i>Nuria Vega</li></ul></section><section data-l="P"><h5>P</h5><ul><li><i style="--h:#e11d48" aria-hidden="true">PR</i>Pablo Rey</li></ul></section><section data-l="R"><h5>R</h5><ul><li><i style="--h:#e11d48" aria-hidden="true">RD</i>Raúl Díaz</li><li><i style="--h:#eab308" aria-hidden="true">RG</i>Rosa Gil</li></ul></section><section data-l="S"><h5>S</h5><ul><li><i style="--h:#f97316" aria-hidden="true">SM</i>Sara Molina</li></ul></section><section data-l="T"><h5>T</h5><ul><li><i style="--h:#22c55e" aria-hidden="true">TR</i>Tomás Rivas</li></ul></section><section data-l="V"><h5>V</h5><ul><li><i style="--h:#22c55e" aria-hidden="true">VL</i>Vera Luna</li></ul></section></div><div class="rl" role="slider" tabindex="0" aria-label="Contents" aria-valuemin="0" aria-valuemax="25" aria-valuenow="0" aria-valuetext="A"><span>A</span><span>B</span><span>C</span><span>D</span><span>E</span><span class="no">F</span><span>G</span><span class="no">H</span><span>I</span><span>J</span><span class="no">K</span><span>L</span><span>M</span><span>N</span><span class="no">O</span><span>P</span><span class="no">Q</span><span>R</span><span>S</span><span>T</span><span class="no">U</span><span>V</span><span class="no">W</span><span class="no">X</span><span class="no">Y</span><span class="no">Z</span><b class="bb" aria-hidden="true"><span>A</span></b></div></div></div>
```

### CSS

```css
.uvq12-az {
  --c1: #2563eb;
  --c2: #dbeafe;
  --s: 16px;
  /* tamaño */
  --k: 1;
  /* velocidad */
  --fg: #1d1d1f;
  /* texto */
  --bg: #f5f5f7;
  /* fondo */
}

.uvq12-az {
  font-family: -apple-system,BlinkMacSystemFont,"SF Pro Text","Inter",system-ui,sans-serif;
  font-size: var(--s);
  color: var(--fg);
  -webkit-tap-highlight-color: transparent;
  width: 16em;
}

.uvq12-az .tt {
  margin: 0 0 .5em;
  font-size: 1.3em;
  font-weight: 800;
  letter-spacing: -.02em;
}

.uvq12-az .bx {
  position: relative;
  display: flex;
  height: 13em;
  border-radius: 1em;
  background: color-mix(in srgb,var(--fg) 2%,var(--bg));
  box-shadow: 0 0 0 1px color-mix(in srgb,var(--fg) 9%,transparent),0 1.4em 2.8em -1.4em rgba(0,0,0,.45);
}

.uvq12-az .ls {
  position: relative;
  flex: 1;
  overflow-y: auto;
  scrollbar-width: none;
  border-radius: 1em 0 0 1em;
  outline: 0;
}

.uvq12-az .ls::-webkit-scrollbar {
  display: none;
}

.uvq12-az .ls:focus-visible {
  box-shadow: inset 0 0 0 2px var(--c1);
}

.uvq12-az h5 {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: .3em .9em;
  font-size: .72em;
  font-weight: 800;
  color: var(--c1);
  background: color-mix(in srgb,var(--c2) 40%,var(--bg));
  backdrop-filter: blur(6px);
}

.uvq12-az ul {
  margin: 0;
  padding: 0 .4em;
  list-style: none;
}

.uvq12-az li {
  display: flex;
  align-items: center;
  gap: .6em;
  padding: .45em .5em;
  font-size: .85em;
  border-bottom: 1px solid color-mix(in srgb,var(--fg) 6%,transparent);
}

.uvq12-az li i {
  display: grid;
  place-items: center;
  flex: none;
  width: 2em;
  height: 2em;
  border-radius: 50%;
  font-size: .7em;
  font-weight: 800;
  font-style: normal;
  color: #fff;
  background: var(--h);
}

.uvq12-az .rl {
  position: relative;
  flex: none;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  width: 1.5em;
  padding: .5em 0;
  border-radius: 0 1em 1em 0;
  cursor: pointer;
  touch-action: none;
  outline: 0;
  user-select: none;
}

.uvq12-az .rl:focus-visible {
  box-shadow: inset 0 0 0 2px var(--c1);
}

.uvq12-az .rl>span {
  display: block;
  text-align: center;
  font-size: .5em;
  line-height: 1;
  font-weight: 800;
  color: var(--c1);
  transition: transform calc(0.15s / var(--k)) cubic-bezier(.34,1.56,.64,1);
}

.uvq12-az .rl>span.no {
  color: color-mix(in srgb,var(--fg) 55%,transparent);
  opacity: .5;
}

.uvq12-az .rl>span.on {
  transform: scale(1.7);
}

.uvq12-az .bb {
  position: absolute;
  right: 2.2em;
  top: 0;
  display: grid;
  place-items: center;
  width: 3em;
  height: 3em;
  margin-top: -1.5em;
  border-radius: 50% 50% 0 50%;
  color: #fff;
  background: var(--c1);
  box-shadow: 0 .5em 1em -.3em color-mix(in srgb,var(--c1) 60%,transparent);
  transform: translateY(var(--y,0px)) rotate(-45deg) scale(0);
  transform-origin: 100% 100%;
  opacity: 0;
  pointer-events: none;
  transition: transform calc(0.2s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.15s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq12-az .bb span {
  font-size: 1.4em;
  font-weight: 800;
  transform: rotate(45deg);
}

.uvq12-az.act .bb {
  opacity: 1;
  transform: translateY(var(--y,0px)) rotate(-45deg) scale(1);
}

@media (prefers-reduced-motion:reduce) {
  .uvq12-az,.uvq12-az *,.uvq12-az *::before,.uvq12-az *::after {
    animation-duration: .01ms!important;
    animation-iteration-count: 1!important;
    transition-duration: .01ms!important;
  }
}
```

### JavaScript

```js
document.querySelectorAll(".uvq12-az").forEach(el => {
  const ls=el.querySelector(".ls"),rl=el.querySelector(".rl"),bb=el.querySelector(".bb"),bs=bb.querySelector("span"),lt=[...rl.querySelectorAll(":scope>span")],secs=[...ls.querySelectorAll("section")];let k=0,dn=false,hide=0;
const L=i=>lt[i].textContent;
const pick=(i,show)=>{k=Math.max(0,Math.min(25,i));const l=L(k);bs.textContent=l;rl.setAttribute("aria-valuenow",k);rl.setAttribute("aria-valuetext",l);const sp=lt[k];el.style.setProperty("--y",(sp.offsetTop+sp.offsetHeight/2)+"px");lt.forEach((s,j)=>s.classList.toggle("on",j===k&&dn));const s=secs.find(x=>x.dataset.l>=l)||secs[secs.length-1];ls.scrollTop=s.offsetTop;if(show){el.classList.add("act");clearTimeout(hide);if(!dn)hide=setTimeout(()=>el.classList.remove("act"),700)}};
const at=e=>{const r=rl.getBoundingClientRect();return Math.floor((e.clientY-r.top)/r.height*26)};
rl.addEventListener("pointerdown",e=>{dn=true;rl.setPointerCapture(e.pointerId);pick(at(e),1)});rl.addEventListener("pointermove",e=>{if(dn&&at(e)!==k)pick(at(e),1)});
const up=()=>{if(!dn)return;dn=false;lt.forEach(s=>s.classList.remove("on"));hide=setTimeout(()=>el.classList.remove("act"),350)};rl.addEventListener("pointerup",up);rl.addEventListener("pointercancel",up);
rl.addEventListener("keydown",e=>{const m={ArrowDown:1,ArrowRight:1,ArrowUp:-1,ArrowLeft:-1,PageDown:5,PageUp:-5}[e.key];if(m){e.preventDefault();pick(k+m,1)}if(e.key==="Home"){e.preventDefault();pick(0,1)}if(e.key==="End"){e.preventDefault();pick(25,1)}});
return()=>clearTimeout(hide)
});
```

### React

```tsx
// @ts-nocheck: setup() es JavaScript del DOM sin tipos, el mismo de la pestaña JS.
import { useEffect, useRef, type CSSProperties } from "react";

const css = `
.uvq12-az {
  font-family: -apple-system,BlinkMacSystemFont,"SF Pro Text","Inter",system-ui,sans-serif;
  font-size: var(--s);
  color: var(--fg);
  -webkit-tap-highlight-color: transparent;
  width: 16em;
}

.uvq12-az .tt {
  margin: 0 0 .5em;
  font-size: 1.3em;
  font-weight: 800;
  letter-spacing: -.02em;
}

.uvq12-az .bx {
  position: relative;
  display: flex;
  height: 13em;
  border-radius: 1em;
  background: color-mix(in srgb,var(--fg) 2%,var(--bg));
  box-shadow: 0 0 0 1px color-mix(in srgb,var(--fg) 9%,transparent),0 1.4em 2.8em -1.4em rgba(0,0,0,.45);
}

.uvq12-az .ls {
  position: relative;
  flex: 1;
  overflow-y: auto;
  scrollbar-width: none;
  border-radius: 1em 0 0 1em;
  outline: 0;
}

.uvq12-az .ls::-webkit-scrollbar {
  display: none;
}

.uvq12-az .ls:focus-visible {
  box-shadow: inset 0 0 0 2px var(--c1);
}

.uvq12-az h5 {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: .3em .9em;
  font-size: .72em;
  font-weight: 800;
  color: var(--c1);
  background: color-mix(in srgb,var(--c2) 40%,var(--bg));
  backdrop-filter: blur(6px);
}

.uvq12-az ul {
  margin: 0;
  padding: 0 .4em;
  list-style: none;
}

.uvq12-az li {
  display: flex;
  align-items: center;
  gap: .6em;
  padding: .45em .5em;
  font-size: .85em;
  border-bottom: 1px solid color-mix(in srgb,var(--fg) 6%,transparent);
}

.uvq12-az li i {
  display: grid;
  place-items: center;
  flex: none;
  width: 2em;
  height: 2em;
  border-radius: 50%;
  font-size: .7em;
  font-weight: 800;
  font-style: normal;
  color: #fff;
  background: var(--h);
}

.uvq12-az .rl {
  position: relative;
  flex: none;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  width: 1.5em;
  padding: .5em 0;
  border-radius: 0 1em 1em 0;
  cursor: pointer;
  touch-action: none;
  outline: 0;
  user-select: none;
}

.uvq12-az .rl:focus-visible {
  box-shadow: inset 0 0 0 2px var(--c1);
}

.uvq12-az .rl>span {
  display: block;
  text-align: center;
  font-size: .5em;
  line-height: 1;
  font-weight: 800;
  color: var(--c1);
  transition: transform calc(0.15s / var(--k)) cubic-bezier(.34,1.56,.64,1);
}

.uvq12-az .rl>span.no {
  color: color-mix(in srgb,var(--fg) 55%,transparent);
  opacity: .5;
}

.uvq12-az .rl>span.on {
  transform: scale(1.7);
}

.uvq12-az .bb {
  position: absolute;
  right: 2.2em;
  top: 0;
  display: grid;
  place-items: center;
  width: 3em;
  height: 3em;
  margin-top: -1.5em;
  border-radius: 50% 50% 0 50%;
  color: #fff;
  background: var(--c1);
  box-shadow: 0 .5em 1em -.3em color-mix(in srgb,var(--c1) 60%,transparent);
  transform: translateY(var(--y,0px)) rotate(-45deg) scale(0);
  transform-origin: 100% 100%;
  opacity: 0;
  pointer-events: none;
  transition: transform calc(0.2s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.15s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq12-az .bb span {
  font-size: 1.4em;
  font-weight: 800;
  transform: rotate(45deg);
}

.uvq12-az.act .bb {
  opacity: 1;
  transform: translateY(var(--y,0px)) rotate(-45deg) scale(1);
}

@media (prefers-reduced-motion:reduce) {
  .uvq12-az,.uvq12-az *,.uvq12-az *::before,.uvq12-az *::after {
    animation-duration: .01ms!important;
    animation-iteration-count: 1!important;
    transition-duration: .01ms!important;
  }
}
`;

// Interacción en JavaScript del DOM: la misma que en la pestaña JS.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function setup(el: any) {
  const ls=el.querySelector(".ls"),rl=el.querySelector(".rl"),bb=el.querySelector(".bb"),bs=bb.querySelector("span"),lt=[...rl.querySelectorAll(":scope>span")],secs=[...ls.querySelectorAll("section")];let k=0,dn=false,hide=0;
const L=i=>lt[i].textContent;
const pick=(i,show)=>{k=Math.max(0,Math.min(25,i));const l=L(k);bs.textContent=l;rl.setAttribute("aria-valuenow",k);rl.setAttribute("aria-valuetext",l);const sp=lt[k];el.style.setProperty("--y",(sp.offsetTop+sp.offsetHeight/2)+"px");lt.forEach((s,j)=>s.classList.toggle("on",j===k&&dn));const s=secs.find(x=>x.dataset.l>=l)||secs[secs.length-1];ls.scrollTop=s.offsetTop;if(show){el.classList.add("act");clearTimeout(hide);if(!dn)hide=setTimeout(()=>el.classList.remove("act"),700)}};
const at=e=>{const r=rl.getBoundingClientRect();return Math.floor((e.clientY-r.top)/r.height*26)};
rl.addEventListener("pointerdown",e=>{dn=true;rl.setPointerCapture(e.pointerId);pick(at(e),1)});rl.addEventListener("pointermove",e=>{if(dn&&at(e)!==k)pick(at(e),1)});
const up=()=>{if(!dn)return;dn=false;lt.forEach(s=>s.classList.remove("on"));hide=setTimeout(()=>el.classList.remove("act"),350)};rl.addEventListener("pointerup",up);rl.addEventListener("pointercancel",up);
rl.addEventListener("keydown",e=>{const m={ArrowDown:1,ArrowRight:1,ArrowUp:-1,ArrowLeft:-1,PageDown:5,PageUp:-5}[e.key];if(m){e.preventDefault();pick(k+m,1)}if(e.key==="Home"){e.preventDefault();pick(0,1)}if(e.key==="End"){e.preventDefault();pick(25,1)}});
return()=>clearTimeout(hide)
}

export default function NavAzScrubber() {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = ref.current?.firstElementChild;
    return el ? setup(el) : undefined;
  }, []);

  return (
    <>
      <style>{css}</style>
      <div
        ref={ref} style={{
          "--c1": "#2563eb",
          "--c2": "#dbeafe",
          "--s": "16px",
          "--k": "1",
          "--fg": "#1d1d1f",
          "--bg": "#f5f5f7",
        } as CSSProperties}
      >
        <div className="uvq12-az"><p className="tt">Contacts</p><div className="bx"><div className="ls" tabIndex={0} aria-label="Contacts"><section data-l="A"><h5>A</h5><ul><li><i style={{ "--h": "#e11d48" } as CSSProperties} aria-hidden="true">AR</i>Ana Ruiz</li><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">AG</i>Alberto Gil</li></ul></section><section data-l="B"><h5>B</h5><ul><li><i style={{ "--h": "#0ea5e9" } as CSSProperties} aria-hidden="true">BS</i>Bea Soto</li></ul></section><section data-l="C"><h5>C</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">CV</i>Carla Vidal</li><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">CM</i>Carlos Mena</li></ul></section><section data-l="D"><h5>D</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">DP</i>Diego Paz</li></ul></section><section data-l="E"><h5>E</h5><ul><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">EO</i>Elena Ortiz</li></ul></section><section data-l="G"><h5>G</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">GR</i>Gabriel Ros</li></ul></section><section data-l="I"><h5>I</h5><ul><li><i style={{ "--h": "#e11d48" } as CSSProperties} aria-hidden="true">IS</i>Irene Sanz</li></ul></section><section data-l="J"><h5>J</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">JL</i>Javier León</li></ul></section><section data-l="L"><h5>L</h5><ul><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">LP</i>Laura Peña</li><li><i style={{ "--h": "#a855f7" } as CSSProperties} aria-hidden="true">LM</i>Lucía Martín</li></ul></section><section data-l="M"><h5>M</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">MG</i>Mario Gómez</li><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">MC</i>Marta Cano</li></ul></section><section data-l="N"><h5>N</h5><ul><li><i style={{ "--h": "#0ea5e9" } as CSSProperties} aria-hidden="true">NV</i>Nuria Vega</li></ul></section><section data-l="P"><h5>P</h5><ul><li><i style={{ "--h": "#e11d48" } as CSSProperties} aria-hidden="true">PR</i>Pablo Rey</li></ul></section><section data-l="R"><h5>R</h5><ul><li><i style={{ "--h": "#e11d48" } as CSSProperties} aria-hidden="true">RD</i>Raúl Díaz</li><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">RG</i>Rosa Gil</li></ul></section><section data-l="S"><h5>S</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">SM</i>Sara Molina</li></ul></section><section data-l="T"><h5>T</h5><ul><li><i style={{ "--h": "#22c55e" } as CSSProperties} aria-hidden="true">TR</i>Tomás Rivas</li></ul></section><section data-l="V"><h5>V</h5><ul><li><i style={{ "--h": "#22c55e" } as CSSProperties} aria-hidden="true">VL</i>Vera Luna</li></ul></section></div><div className="rl" role="slider" tabIndex={0} aria-label="Contents" aria-valuemin="0" aria-valuemax="25" aria-valuenow="0" aria-valuetext="A"><span>A</span><span>B</span><span>C</span><span>D</span><span>E</span><span className="no">F</span><span>G</span><span className="no">H</span><span>I</span><span>J</span><span className="no">K</span><span>L</span><span>M</span><span>N</span><span className="no">O</span><span>P</span><span className="no">Q</span><span>R</span><span>S</span><span>T</span><span className="no">U</span><span>V</span><span className="no">W</span><span className="no">X</span><span className="no">Y</span><span className="no">Z</span><b className="bb" aria-hidden="true"><span>A</span></b></div></div></div>
      </div>
    </>
  );
}
```

## Prompt for AI assistants

Paste this into ChatGPT, Claude, Cursor, v0 or Lovable to adapt the block to your stack (Tailwind, Vue, Svelte…):

````text
I want to add the “Scrubbable A to Z index” block (Navigation) from AIMRAN Library to my project.

## What it is
A contact list with an A to Z rail: slide your finger along the letters and a bubble shows where you will jump.

## Chosen settings
- Main color: #2563eb
- Secondary color: #dbeafe
- Size: 16px (from 12 to 24px)
- Speed: 1x (from 0.5 to 2x)
- Text: "Contactos"

## What I need
1. Add it to my project with the same look and animation as the reference code: same sizes, colours, radii, shadows, timings and easing.
2. It is built with plain HTML and CSS (the reference wraps it in a React component with its CSS). If my project uses something else (Vue, Svelte, Astro, plain HTML…), translate it without losing any behaviour. If I use Tailwind, turn the styles into classes; if not, keep the CSS in its own file.
3. Expose the settings above as props (or CSS variables) with those values as defaults.
4. Make it work well on phones: nothing that depends only on hover, touch targets of at least 44 px and no overflow on 320 px screens.
5. Make it look right in light and dark mode.
6. Keep it accessible: semantic HTML, roles and aria-* where needed, visible focus and keyboard support.
7. Respect prefers-reduced-motion: with reduced motion, no big animations but the same end result.

## Reference code
```tsx
// @ts-nocheck: setup() es JavaScript del DOM sin tipos, el mismo de la pestaña JS.
import { useEffect, useRef, type CSSProperties } from "react";

const css = `
.uvq12-az {
  font-family: -apple-system,BlinkMacSystemFont,"SF Pro Text","Inter",system-ui,sans-serif;
  font-size: var(--s);
  color: var(--fg);
  -webkit-tap-highlight-color: transparent;
  width: 16em;
}

.uvq12-az .tt {
  margin: 0 0 .5em;
  font-size: 1.3em;
  font-weight: 800;
  letter-spacing: -.02em;
}

.uvq12-az .bx {
  position: relative;
  display: flex;
  height: 13em;
  border-radius: 1em;
  background: color-mix(in srgb,var(--fg) 2%,var(--bg));
  box-shadow: 0 0 0 1px color-mix(in srgb,var(--fg) 9%,transparent),0 1.4em 2.8em -1.4em rgba(0,0,0,.45);
}

.uvq12-az .ls {
  position: relative;
  flex: 1;
  overflow-y: auto;
  scrollbar-width: none;
  border-radius: 1em 0 0 1em;
  outline: 0;
}

.uvq12-az .ls::-webkit-scrollbar {
  display: none;
}

.uvq12-az .ls:focus-visible {
  box-shadow: inset 0 0 0 2px var(--c1);
}

.uvq12-az h5 {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: .3em .9em;
  font-size: .72em;
  font-weight: 800;
  color: var(--c1);
  background: color-mix(in srgb,var(--c2) 40%,var(--bg));
  backdrop-filter: blur(6px);
}

.uvq12-az ul {
  margin: 0;
  padding: 0 .4em;
  list-style: none;
}

.uvq12-az li {
  display: flex;
  align-items: center;
  gap: .6em;
  padding: .45em .5em;
  font-size: .85em;
  border-bottom: 1px solid color-mix(in srgb,var(--fg) 6%,transparent);
}

.uvq12-az li i {
  display: grid;
  place-items: center;
  flex: none;
  width: 2em;
  height: 2em;
  border-radius: 50%;
  font-size: .7em;
  font-weight: 800;
  font-style: normal;
  color: #fff;
  background: var(--h);
}

.uvq12-az .rl {
  position: relative;
  flex: none;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  width: 1.5em;
  padding: .5em 0;
  border-radius: 0 1em 1em 0;
  cursor: pointer;
  touch-action: none;
  outline: 0;
  user-select: none;
}

.uvq12-az .rl:focus-visible {
  box-shadow: inset 0 0 0 2px var(--c1);
}

.uvq12-az .rl>span {
  display: block;
  text-align: center;
  font-size: .5em;
  line-height: 1;
  font-weight: 800;
  color: var(--c1);
  transition: transform calc(0.15s / var(--k)) cubic-bezier(.34,1.56,.64,1);
}

.uvq12-az .rl>span.no {
  color: color-mix(in srgb,var(--fg) 55%,transparent);
  opacity: .5;
}

.uvq12-az .rl>span.on {
  transform: scale(1.7);
}

.uvq12-az .bb {
  position: absolute;
  right: 2.2em;
  top: 0;
  display: grid;
  place-items: center;
  width: 3em;
  height: 3em;
  margin-top: -1.5em;
  border-radius: 50% 50% 0 50%;
  color: #fff;
  background: var(--c1);
  box-shadow: 0 .5em 1em -.3em color-mix(in srgb,var(--c1) 60%,transparent);
  transform: translateY(var(--y,0px)) rotate(-45deg) scale(0);
  transform-origin: 100% 100%;
  opacity: 0;
  pointer-events: none;
  transition: transform calc(0.2s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.15s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq12-az .bb span {
  font-size: 1.4em;
  font-weight: 800;
  transform: rotate(45deg);
}

.uvq12-az.act .bb {
  opacity: 1;
  transform: translateY(var(--y,0px)) rotate(-45deg) scale(1);
}

@media (prefers-reduced-motion:reduce) {
  .uvq12-az,.uvq12-az *,.uvq12-az *::before,.uvq12-az *::after {
    animation-duration: .01ms!important;
    animation-iteration-count: 1!important;
    transition-duration: .01ms!important;
  }
}
`;

// Interacción en JavaScript del DOM: la misma que en la pestaña JS.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function setup(el: any) {
  const ls=el.querySelector(".ls"),rl=el.querySelector(".rl"),bb=el.querySelector(".bb"),bs=bb.querySelector("span"),lt=[...rl.querySelectorAll(":scope>span")],secs=[...ls.querySelectorAll("section")];let k=0,dn=false,hide=0;
const L=i=>lt[i].textContent;
const pick=(i,show)=>{k=Math.max(0,Math.min(25,i));const l=L(k);bs.textContent=l;rl.setAttribute("aria-valuenow",k);rl.setAttribute("aria-valuetext",l);const sp=lt[k];el.style.setProperty("--y",(sp.offsetTop+sp.offsetHeight/2)+"px");lt.forEach((s,j)=>s.classList.toggle("on",j===k&&dn));const s=secs.find(x=>x.dataset.l>=l)||secs[secs.length-1];ls.scrollTop=s.offsetTop;if(show){el.classList.add("act");clearTimeout(hide);if(!dn)hide=setTimeout(()=>el.classList.remove("act"),700)}};
const at=e=>{const r=rl.getBoundingClientRect();return Math.floor((e.clientY-r.top)/r.height*26)};
rl.addEventListener("pointerdown",e=>{dn=true;rl.setPointerCapture(e.pointerId);pick(at(e),1)});rl.addEventListener("pointermove",e=>{if(dn&&at(e)!==k)pick(at(e),1)});
const up=()=>{if(!dn)return;dn=false;lt.forEach(s=>s.classList.remove("on"));hide=setTimeout(()=>el.classList.remove("act"),350)};rl.addEventListener("pointerup",up);rl.addEventListener("pointercancel",up);
rl.addEventListener("keydown",e=>{const m={ArrowDown:1,ArrowRight:1,ArrowUp:-1,ArrowLeft:-1,PageDown:5,PageUp:-5}[e.key];if(m){e.preventDefault();pick(k+m,1)}if(e.key==="Home"){e.preventDefault();pick(0,1)}if(e.key==="End"){e.preventDefault();pick(25,1)}});
return()=>clearTimeout(hide)
}

export default function NavAzScrubber() {
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const el = ref.current?.firstElementChild;
    return el ? setup(el) : undefined;
  }, []);

  return (
    <>
      <style>{css}</style>
      <div
        ref={ref} style={{
          "--c1": "#2563eb",
          "--c2": "#dbeafe",
          "--s": "16px",
          "--k": "1",
          "--fg": "#1d1d1f",
          "--bg": "#f5f5f7",
        } as CSSProperties}
      >
        <div className="uvq12-az"><p className="tt">Contacts</p><div className="bx"><div className="ls" tabIndex={0} aria-label="Contacts"><section data-l="A"><h5>A</h5><ul><li><i style={{ "--h": "#e11d48" } as CSSProperties} aria-hidden="true">AR</i>Ana Ruiz</li><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">AG</i>Alberto Gil</li></ul></section><section data-l="B"><h5>B</h5><ul><li><i style={{ "--h": "#0ea5e9" } as CSSProperties} aria-hidden="true">BS</i>Bea Soto</li></ul></section><section data-l="C"><h5>C</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">CV</i>Carla Vidal</li><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">CM</i>Carlos Mena</li></ul></section><section data-l="D"><h5>D</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">DP</i>Diego Paz</li></ul></section><section data-l="E"><h5>E</h5><ul><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">EO</i>Elena Ortiz</li></ul></section><section data-l="G"><h5>G</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">GR</i>Gabriel Ros</li></ul></section><section data-l="I"><h5>I</h5><ul><li><i style={{ "--h": "#e11d48" } as CSSProperties} aria-hidden="true">IS</i>Irene Sanz</li></ul></section><section data-l="J"><h5>J</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">JL</i>Javier León</li></ul></section><section data-l="L"><h5>L</h5><ul><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">LP</i>Laura Peña</li><li><i style={{ "--h": "#a855f7" } as CSSProperties} aria-hidden="true">LM</i>Lucía Martín</li></ul></section><section data-l="M"><h5>M</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">MG</i>Mario Gómez</li><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">MC</i>Marta Cano</li></ul></section><section data-l="N"><h5>N</h5><ul><li><i style={{ "--h": "#0ea5e9" } as CSSProperties} aria-hidden="true">NV</i>Nuria Vega</li></ul></section><section data-l="P"><h5>P</h5><ul><li><i style={{ "--h": "#e11d48" } as CSSProperties} aria-hidden="true">PR</i>Pablo Rey</li></ul></section><section data-l="R"><h5>R</h5><ul><li><i style={{ "--h": "#e11d48" } as CSSProperties} aria-hidden="true">RD</i>Raúl Díaz</li><li><i style={{ "--h": "#eab308" } as CSSProperties} aria-hidden="true">RG</i>Rosa Gil</li></ul></section><section data-l="S"><h5>S</h5><ul><li><i style={{ "--h": "#f97316" } as CSSProperties} aria-hidden="true">SM</i>Sara Molina</li></ul></section><section data-l="T"><h5>T</h5><ul><li><i style={{ "--h": "#22c55e" } as CSSProperties} aria-hidden="true">TR</i>Tomás Rivas</li></ul></section><section data-l="V"><h5>V</h5><ul><li><i style={{ "--h": "#22c55e" } as CSSProperties} aria-hidden="true">VL</i>Vera Luna</li></ul></section></div><div className="rl" role="slider" tabIndex={0} aria-label="Contents" aria-valuemin="0" aria-valuemax="25" aria-valuenow="0" aria-valuetext="A"><span>A</span><span>B</span><span>C</span><span>D</span><span>E</span><span className="no">F</span><span>G</span><span className="no">H</span><span>I</span><span>J</span><span className="no">K</span><span>L</span><span>M</span><span>N</span><span className="no">O</span><span>P</span><span className="no">Q</span><span>R</span><span>S</span><span>T</span><span className="no">U</span><span>V</span><span className="no">W</span><span className="no">X</span><span className="no">Y</span><span className="no">Z</span><b className="bb" aria-hidden="true"><span>A</span></b></div></div></div>
      </div>
    </>
  );
}
```

Give me the complete final code, ready to paste, and tell me in one line where to put it in my project.
````
