# Floating filter button

> A round floating button that grows into a small filter panel, with options sliding in one by one.

- Category: [Filters](https://library.aimran.es/components/filters)
- Built with: HTML + CSS
- Live editor: https://library.aimran.es/blocks/uv-f-mob-fab
- Library: AIMRAN Library (https://library.aimran.es), free, no sign-up, by Aimran Oukham

## Where to use it

Useful when the screen is mostly content and filters are occasional. A directory of coworking spaces, for instance, where people scroll through listings and only sometimes want Lowest price, Best rated, Near me or Available. The button waits in the corner until someone needs it. People looking for a floating action button menu usually want something like this.

A floating button can cover the last item of a list, so add some bottom padding to the content. Keep the panel to four or five options; anything longer belongs in a proper sheet. Tapping outside should close it, as it does here.

## Code

### HTML

```html
<div class="uv-fmf"><div class="ph"><div class="body"><span></span><span></span><span></span><span></span></div><div class="fb"><button class="tg"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/></svg></button><div class="pn"><div class="tt">Filter by</div><button style="--i:0"><span class="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>Low price</button><button style="--i:1"><span class="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>Top rated</button><button style="--i:2"><span class="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>Near me</button><button style="--i:3"><span class="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5"/></svg></span>Available</button></div></div></div></div>
```

### CSS

```css
.uv-fmf {
  --c1: #0071e3;
  --c2: #8ec5ff;
  --s: 16px;
  /* tamaño */
  --k: 1;
  /* velocidad */
  --fg: #1d1d1f;
  /* texto */
  --bg: #f5f5f7;
  /* fondo */
}

.uv-fmf {
  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;
}

.uv-fmf .ph {
  position: relative;
  width: 16em;
  height: 26em;
  padding: .9em;
  border-radius: 2.2em;
  background: var(--bg);
  box-shadow: 0 0 0 2px color-mix(in srgb,var(--fg) 14%,transparent),0 24px 50px rgba(0,0,0,.18);
  overflow: hidden;
  padding: 1em;
}

.uv-fmf .body {
  display: flex;
  flex-direction: column;
  gap: .7em;
}

.uv-fmf .body span {
  height: 4.2em;
  border-radius: 1em;
  background: color-mix(in srgb,var(--fg) 6%,transparent);
}

.uv-fmf .fb {
  position: absolute;
  right: 1em;
  bottom: 1em;
}

.uv-fmf .tg {
  display: grid;
  place-items: center;
  width: 3.2em;
  height: 3.2em;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: #fff;
  background: var(--c1);
  box-shadow: 0 12px 26px color-mix(in srgb,var(--c1) 42%,transparent);
  transition: transform calc(0.4s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.25s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .tg svg {
  width: 1.4em;
  height: 1.4em;
}

.uv-fmf .tg:active {
  transform: scale(.92);
}

.uv-fmf.open .tg {
  transform: scale(.4);
  opacity: 0;
  pointer-events: none;
}

.uv-fmf .pn {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 12.5em;
  display: flex;
  flex-direction: column;
  gap: .15em;
  padding: .7em;
  border-radius: 1.3em;
  background: var(--bg);
  box-shadow: 0 18px 44px rgba(0,0,0,.22);
  transform-origin: bottom right;
  scale: .2;
  opacity: 0;
  pointer-events: none;
  transition: scale calc(0.42s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.28s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf.open .pn {
  scale: 1;
  opacity: 1;
  pointer-events: auto;
}

.uv-fmf .tt {
  padding: .2em .6em .5em;
  font-size: calc(var(--s) * .76);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  opacity: .45;
}

.uv-fmf .pn button {
  font-family: -apple-system,BlinkMacSystemFont,"SF Pro Text","Inter",system-ui,sans-serif;
  font-size: var(--s);
  font-weight: 550;
  line-height: 1;
  padding: .72em 1.25em;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  background: transparent;
  color: var(--fg);
  position: relative;
  transition: color calc(0.32s / var(--k)) cubic-bezier(.25,.1,.25,1),background calc(0.32s / var(--k)) cubic-bezier(.25,.1,.25,1),transform calc(0.32s / var(--k)) cubic-bezier(.34,1.56,.64,1);
  display: flex;
  align-items: center;
  gap: .6em;
  font-size: calc(var(--s) * .85);
  padding: .5em .6em;
  border-radius: .7em;
  text-align: left;
  opacity: 0;
}

.uv-fmf.open .pn button {
  animation: uv-fmf calc(0.35s / var(--k)) cubic-bezier(.25,.1,.25,1) forwards;
  animation-delay: calc(.08s + var(--i) * .05s / var(--k));
}

.uv-fmf .ck {
  display: grid;
  place-items: center;
  width: 1.1em;
  height: 1.1em;
  flex: 0 0 auto;
  border-radius: .3em;
  box-shadow: inset 0 0 0 1.6px color-mix(in srgb,var(--fg) 25%,transparent);
  transition: background calc(0.25s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .ck svg {
  width: .75em;
  height: .75em;
  color: #fff;
}

.uv-fmf .ck svg path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset calc(0.3s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .pn button.on .ck {
  background: var(--c1);
  box-shadow: inset 0 0 0 1.6px var(--c1);
}

.uv-fmf .pn button.on .ck svg path {
  stroke-dashoffset: 0;
}

@keyframes uv-fmf {
  from {
    opacity: 0;
    transform: translateY(.4em);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
```

### JavaScript

```js
document.querySelectorAll(".uv-fmf").forEach(el => {
  el.querySelector(".tg").addEventListener("click",()=>el.classList.add("open"));el.querySelectorAll(".pn button").forEach(b=>b.addEventListener("click",()=>b.classList.toggle("on")));el.addEventListener("pointerdown",e=>{if(!e.target.closest(".fb"))el.classList.remove("open")})
});
```

### 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 = `
.uv-fmf {
  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;
}

.uv-fmf .ph {
  position: relative;
  width: 16em;
  height: 26em;
  padding: .9em;
  border-radius: 2.2em;
  background: var(--bg);
  box-shadow: 0 0 0 2px color-mix(in srgb,var(--fg) 14%,transparent),0 24px 50px rgba(0,0,0,.18);
  overflow: hidden;
  padding: 1em;
}

.uv-fmf .body {
  display: flex;
  flex-direction: column;
  gap: .7em;
}

.uv-fmf .body span {
  height: 4.2em;
  border-radius: 1em;
  background: color-mix(in srgb,var(--fg) 6%,transparent);
}

.uv-fmf .fb {
  position: absolute;
  right: 1em;
  bottom: 1em;
}

.uv-fmf .tg {
  display: grid;
  place-items: center;
  width: 3.2em;
  height: 3.2em;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: #fff;
  background: var(--c1);
  box-shadow: 0 12px 26px color-mix(in srgb,var(--c1) 42%,transparent);
  transition: transform calc(0.4s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.25s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .tg svg {
  width: 1.4em;
  height: 1.4em;
}

.uv-fmf .tg:active {
  transform: scale(.92);
}

.uv-fmf.open .tg {
  transform: scale(.4);
  opacity: 0;
  pointer-events: none;
}

.uv-fmf .pn {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 12.5em;
  display: flex;
  flex-direction: column;
  gap: .15em;
  padding: .7em;
  border-radius: 1.3em;
  background: var(--bg);
  box-shadow: 0 18px 44px rgba(0,0,0,.22);
  transform-origin: bottom right;
  scale: .2;
  opacity: 0;
  pointer-events: none;
  transition: scale calc(0.42s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.28s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf.open .pn {
  scale: 1;
  opacity: 1;
  pointer-events: auto;
}

.uv-fmf .tt {
  padding: .2em .6em .5em;
  font-size: calc(var(--s) * .76);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  opacity: .45;
}

.uv-fmf .pn button {
  font-family: -apple-system,BlinkMacSystemFont,"SF Pro Text","Inter",system-ui,sans-serif;
  font-size: var(--s);
  font-weight: 550;
  line-height: 1;
  padding: .72em 1.25em;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  background: transparent;
  color: var(--fg);
  position: relative;
  transition: color calc(0.32s / var(--k)) cubic-bezier(.25,.1,.25,1),background calc(0.32s / var(--k)) cubic-bezier(.25,.1,.25,1),transform calc(0.32s / var(--k)) cubic-bezier(.34,1.56,.64,1);
  display: flex;
  align-items: center;
  gap: .6em;
  font-size: calc(var(--s) * .85);
  padding: .5em .6em;
  border-radius: .7em;
  text-align: left;
  opacity: 0;
}

.uv-fmf.open .pn button {
  animation: uv-fmf calc(0.35s / var(--k)) cubic-bezier(.25,.1,.25,1) forwards;
  animation-delay: calc(.08s + var(--i) * .05s / var(--k));
}

.uv-fmf .ck {
  display: grid;
  place-items: center;
  width: 1.1em;
  height: 1.1em;
  flex: 0 0 auto;
  border-radius: .3em;
  box-shadow: inset 0 0 0 1.6px color-mix(in srgb,var(--fg) 25%,transparent);
  transition: background calc(0.25s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .ck svg {
  width: .75em;
  height: .75em;
  color: #fff;
}

.uv-fmf .ck svg path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset calc(0.3s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .pn button.on .ck {
  background: var(--c1);
  box-shadow: inset 0 0 0 1.6px var(--c1);
}

.uv-fmf .pn button.on .ck svg path {
  stroke-dashoffset: 0;
}

@keyframes uv-fmf {
  from {
    opacity: 0;
    transform: translateY(.4em);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
`;

// 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) {
  el.querySelector(".tg").addEventListener("click",()=>el.classList.add("open"));el.querySelectorAll(".pn button").forEach(b=>b.addEventListener("click",()=>b.classList.toggle("on")));el.addEventListener("pointerdown",e=>{if(!e.target.closest(".fb"))el.classList.remove("open")})
}

export default function FMobFab() {
  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": "#0071e3",
          "--c2": "#8ec5ff",
          "--s": "16px",
          "--k": "1",
          "--fg": "#1d1d1f",
          "--bg": "#f5f5f7",
        } as CSSProperties}
      >
        <div className="uv-fmf"><div className="ph"><div className="body"><span></span><span></span><span></span><span></span></div><div className="fb"><button className="tg"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M4 7h10M18 7h2M4 17h4M12 17h8" /><circle cx="16" cy="7" r="2" /><circle cx="10" cy="17" r="2" /></svg></button><div className="pn"><div className="tt">Filter by</div><button style={{ "--i": "0" } as CSSProperties}><span className="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg></span>Low price</button><button style={{ "--i": "1" } as CSSProperties}><span className="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg></span>Top rated</button><button style={{ "--i": "2" } as CSSProperties}><span className="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg></span>Near me</button><button style={{ "--i": "3" } as CSSProperties}><span className="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg></span>Available</button></div></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 “Floating filter button” block (Filters) from AIMRAN Library to my project.

## What it is
A round floating button that grows into a small filter panel, with options sliding in one by one.

## Chosen settings
- Main color: #0071e3
- Secondary color: #8ec5ff
- Size: 16px (from 12 to 24px)
- Speed: 1x (from 0.5 to 2x)

## 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 = `
.uv-fmf {
  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;
}

.uv-fmf .ph {
  position: relative;
  width: 16em;
  height: 26em;
  padding: .9em;
  border-radius: 2.2em;
  background: var(--bg);
  box-shadow: 0 0 0 2px color-mix(in srgb,var(--fg) 14%,transparent),0 24px 50px rgba(0,0,0,.18);
  overflow: hidden;
  padding: 1em;
}

.uv-fmf .body {
  display: flex;
  flex-direction: column;
  gap: .7em;
}

.uv-fmf .body span {
  height: 4.2em;
  border-radius: 1em;
  background: color-mix(in srgb,var(--fg) 6%,transparent);
}

.uv-fmf .fb {
  position: absolute;
  right: 1em;
  bottom: 1em;
}

.uv-fmf .tg {
  display: grid;
  place-items: center;
  width: 3.2em;
  height: 3.2em;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: #fff;
  background: var(--c1);
  box-shadow: 0 12px 26px color-mix(in srgb,var(--c1) 42%,transparent);
  transition: transform calc(0.4s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.25s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .tg svg {
  width: 1.4em;
  height: 1.4em;
}

.uv-fmf .tg:active {
  transform: scale(.92);
}

.uv-fmf.open .tg {
  transform: scale(.4);
  opacity: 0;
  pointer-events: none;
}

.uv-fmf .pn {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 12.5em;
  display: flex;
  flex-direction: column;
  gap: .15em;
  padding: .7em;
  border-radius: 1.3em;
  background: var(--bg);
  box-shadow: 0 18px 44px rgba(0,0,0,.22);
  transform-origin: bottom right;
  scale: .2;
  opacity: 0;
  pointer-events: none;
  transition: scale calc(0.42s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.28s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf.open .pn {
  scale: 1;
  opacity: 1;
  pointer-events: auto;
}

.uv-fmf .tt {
  padding: .2em .6em .5em;
  font-size: calc(var(--s) * .76);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  opacity: .45;
}

.uv-fmf .pn button {
  font-family: -apple-system,BlinkMacSystemFont,"SF Pro Text","Inter",system-ui,sans-serif;
  font-size: var(--s);
  font-weight: 550;
  line-height: 1;
  padding: .72em 1.25em;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  background: transparent;
  color: var(--fg);
  position: relative;
  transition: color calc(0.32s / var(--k)) cubic-bezier(.25,.1,.25,1),background calc(0.32s / var(--k)) cubic-bezier(.25,.1,.25,1),transform calc(0.32s / var(--k)) cubic-bezier(.34,1.56,.64,1);
  display: flex;
  align-items: center;
  gap: .6em;
  font-size: calc(var(--s) * .85);
  padding: .5em .6em;
  border-radius: .7em;
  text-align: left;
  opacity: 0;
}

.uv-fmf.open .pn button {
  animation: uv-fmf calc(0.35s / var(--k)) cubic-bezier(.25,.1,.25,1) forwards;
  animation-delay: calc(.08s + var(--i) * .05s / var(--k));
}

.uv-fmf .ck {
  display: grid;
  place-items: center;
  width: 1.1em;
  height: 1.1em;
  flex: 0 0 auto;
  border-radius: .3em;
  box-shadow: inset 0 0 0 1.6px color-mix(in srgb,var(--fg) 25%,transparent);
  transition: background calc(0.25s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .ck svg {
  width: .75em;
  height: .75em;
  color: #fff;
}

.uv-fmf .ck svg path {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset calc(0.3s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uv-fmf .pn button.on .ck {
  background: var(--c1);
  box-shadow: inset 0 0 0 1.6px var(--c1);
}

.uv-fmf .pn button.on .ck svg path {
  stroke-dashoffset: 0;
}

@keyframes uv-fmf {
  from {
    opacity: 0;
    transform: translateY(.4em);
  }
  to {
    opacity: 1;
    transform: none;
  }
}
`;

// 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) {
  el.querySelector(".tg").addEventListener("click",()=>el.classList.add("open"));el.querySelectorAll(".pn button").forEach(b=>b.addEventListener("click",()=>b.classList.toggle("on")));el.addEventListener("pointerdown",e=>{if(!e.target.closest(".fb"))el.classList.remove("open")})
}

export default function FMobFab() {
  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": "#0071e3",
          "--c2": "#8ec5ff",
          "--s": "16px",
          "--k": "1",
          "--fg": "#1d1d1f",
          "--bg": "#f5f5f7",
        } as CSSProperties}
      >
        <div className="uv-fmf"><div className="ph"><div className="body"><span></span><span></span><span></span><span></span></div><div className="fb"><button className="tg"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M4 7h10M18 7h2M4 17h4M12 17h8" /><circle cx="16" cy="7" r="2" /><circle cx="10" cy="17" r="2" /></svg></button><div className="pn"><div className="tt">Filter by</div><button style={{ "--i": "0" } as CSSProperties}><span className="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg></span>Low price</button><button style={{ "--i": "1" } as CSSProperties}><span className="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg></span>Top rated</button><button style={{ "--i": "2" } as CSSProperties}><span className="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg></span>Near me</button><button style={{ "--i": "3" } as CSSProperties}><span className="ck"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12.5l4.5 4.5L19 7.5" /></svg></span>Available</button></div></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.
````
