# Popping soap bubble

> A bubble with an iridescent film that wobbles and dodges the cursor; tap it and it bursts into droplets, then floats back.

- Category: [Liquid Glass](https://library.aimran.es/components/liquid-glass)
- Built with: HTML + CSS
- Live editor: https://library.aimran.es/blocks/uv-lg-soap-bubble
- Library: AIMRAN Library (https://library.aimran.es), free, no sign-up, by Aimran Oukham

## Where to use it

A soap bubble over a sunny meadow, made with a conic gradient masked to the rim for the thin film colors, an animated angle so the swirl keeps turning and two window reflections. The outline wobbles slowly, the bubble drifts away from a nearby pointer, and popping it sprays droplets before a new one grows from the wand.

The two colors steer the iridescence, and a counter keeps track of the bubbles you pop. It is a cheerful touch for kids’ apps, success screens, empty states or a playful call to action on a landing page.

## Code

### HTML

```html
<div class="uvq11-bub"><div class="bg" aria-hidden="true"></div><i class="wd" aria-hidden="true"></i><button type="button" class="bb" aria-label="Pop the bubble"><span class="fm"></span><span class="hl"></span></button><span class="sp" aria-hidden="true"><i style="--a:0deg;--d:3.4em"></i><i style="--a:30deg;--d:4.1em"></i><i style="--a:60deg;--d:4.8em"></i><i style="--a:90deg;--d:3.4em"></i><i style="--a:120deg;--d:4.1em"></i><i style="--a:150deg;--d:4.8em"></i><i style="--a:180deg;--d:3.4em"></i><i style="--a:210deg;--d:4.1em"></i><i style="--a:240deg;--d:4.8em"></i><i style="--a:270deg;--d:3.4em"></i><i style="--a:300deg;--d:4.1em"></i><i style="--a:330deg;--d:4.8em"></i></span><p class="ct"><output>0</output> <span>bubbles</span></p></div>
```

### CSS

```css
.uvq11-bub {
  --c1: #7ee8fa;
  --c2: #ff9bd2;
  --s: 16px;
  /* tamaño */
  --k: 1;
  /* velocidad */
  --fg: #1d1d1f;
  /* texto */
  --bg: #f5f5f7;
  /* fondo */
}

@property --uvq11-ba {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.uvq11-bub {
  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;
  position: relative;
  width: 20em;
  height: 14em;
  border-radius: 1.6em;
  overflow: hidden;
  color: #fff;
  isolation: isolate;
  -webkit-user-select: none;
  user-select: none;
  color: #24432a;
}

.uvq11-bub .bg {
  position: absolute;
  inset: 0;
  background: radial-gradient(18% 25% at 84% 16%,#fffbe0,rgba(255,240,180,.4) 50%,transparent 72%),radial-gradient(4% 6% at 22% 30%,rgba(255,255,255,.6),transparent),radial-gradient(3% 4% at 64% 22%,rgba(255,255,255,.5),transparent),radial-gradient(70% 40% at 30% 100%,#5fae58 60%,transparent 62%),radial-gradient(70% 40% at 90% 100%,#78c46a 60%,transparent 62%),linear-gradient(180deg,#a7dcff,#d9f2ff 55%,#bfe3a6 75%,#8fcf75);
}

.uvq11-bub .bb {
  font: inherit;
  color: inherit;
  border: 0;
  margin: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  position: absolute;
  left: 50%;
  top: 45%;
  width: 7.4em;
  height: 7.4em;
  margin: -3.7em 0 0 -3.7em;
  border-radius: 50%;
  translate: var(--px,0) var(--py,0);
  transition: translate calc(0.7s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.12s / var(--k)) cubic-bezier(.25,.1,.25,1),scale calc(0.12s / var(--k)) cubic-bezier(.25,.1,.25,1);
  background: radial-gradient(circle,transparent 58%,rgba(255,255,255,.14) 70%,rgba(255,255,255,.35) 100%);
  -webkit-backdrop-filter: saturate(1.3) brightness(1.05);
  backdrop-filter: saturate(1.3) brightness(1.05);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.4),inset 0 0 1.2em rgba(255,255,255,.25),0 1em 1.5em -1.2em rgba(40,80,40,.4);
  animation: uvq11-bub-w calc(5s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite,uvq11-bub-f calc(7s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite;
}

.uvq11-bub .bb:focus-visible {
  outline: 2px solid #24432a;
  outline-offset: 5px;
}

.uvq11-bub .fm {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: conic-gradient(from var(--uvq11-ba),var(--c1),var(--c2),#fff59d,#a8ffcf,#9ab8ff,var(--c1));
  -webkit-mask: radial-gradient(closest-side,transparent 64%,rgba(0,0,0,.35) 80%,#000 97%,transparent 100%);
  mask: radial-gradient(closest-side,transparent 64%,rgba(0,0,0,.35) 80%,#000 97%,transparent 100%);
  opacity: .75;
  mix-blend-mode: screen;
  animation: uvq11-bub-a calc(9s / var(--k)) linear infinite;
}

.uvq11-bub .hl,.uvq11-bub .hl::before,.uvq11-bub .hl::after {
  position: absolute;
  border-radius: .3em;
  pointer-events: none;
}

.uvq11-bub .hl {
  left: 22%;
  top: 17%;
  width: .95em;
  height: 1.15em;
  background: rgba(255,255,255,.85);
  transform: rotate(-24deg) skewY(8deg);
  filter: blur(.4px);
}

.uvq11-bub .hl::before {
  content: "";
  left: 1.15em;
  top: .1em;
  width: .75em;
  height: 1.05em;
  background: rgba(255,255,255,.7);
}

.uvq11-bub .hl::after {
  content: "";
  left: 3.9em;
  top: 4.5em;
  width: .7em;
  height: .45em;
  border-radius: 50%;
  background: rgba(255,255,255,.55);
}

.uvq11-bub.pop .bb {
  opacity: 0;
  scale: 1.18;
  pointer-events: none;
}

.uvq11-bub.grow .bb {
  animation: uvq11-bub-g calc(1.1s / var(--k)) cubic-bezier(.22,1,.36,1),uvq11-bub-w calc(5s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite;
}

.uvq11-bub .sp i {
  position: absolute;
  left: 50%;
  top: 45%;
  width: .32em;
  height: .32em;
  margin: -.16em;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%,#fff,color-mix(in srgb,var(--c1) 50%,rgba(255,255,255,.3)));
  opacity: 0;
  translate: var(--px,0) var(--py,0);
}

.uvq11-bub.pop .sp i {
  animation: uvq11-bub-s calc(0.6s / var(--k)) cubic-bezier(.25,.1,.25,1) forwards;
}

.uvq11-bub .wd {
  position: absolute;
  left: 1em;
  bottom: .5em;
  width: 2.2em;
  height: 2.2em;
  border-radius: 50%;
  box-shadow: inset 0 0 0 .32em var(--c2),0 .1em .2em rgba(0,0,0,.2);
  transform: rotate(-30deg);
}

.uvq11-bub .wd::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: .35em;
  height: 3em;
  margin-left: -.175em;
  border-radius: .2em;
  background: var(--c2);
}

.uvq11-bub .ct {
  position: absolute;
  right: 1em;
  bottom: .9em;
  margin: 0;
  padding: .35em .8em;
  border-radius: 2em;
  font-size: .75em;
  font-weight: 700;
  background: rgba(255,255,255,.55);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.8);
}

.uvq11-bub output {
  font-variant-numeric: tabular-nums;
}

@keyframes uvq11-bub-a {
  to {
    --uvq11-ba: 360deg;
  }
}

@keyframes uvq11-bub-w {
  0%,100% {
    border-radius: 50%;
  }
  25% {
    border-radius: 48% 52% 50% 50%/52% 48% 52% 48%;
  }
  50% {
    border-radius: 52% 48% 49% 51%/48% 52% 48% 52%;
  }
  75% {
    border-radius: 49% 51% 52% 48%/51% 49% 51% 49%;
  }
}

@keyframes uvq11-bub-f {
  0%,100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-.5em);
  }
}

@keyframes uvq11-bub-g {
  0% {
    transform: translate(-6em,5.4em) scale(.08);
  }
  100% {
    transform: none;
  }
}

@keyframes uvq11-bub-s {
  0% {
    opacity: 1;
    transform: rotate(var(--a)) translateY(-3.4em);
  }
  100% {
    opacity: 0;
    transform: rotate(var(--a)) translateY(calc(-1 * var(--d) - 1.4em)) scale(.4);
  }
}

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

### JavaScript

```js
document.querySelectorAll(".uvq11-bub").forEach(el => {
  const bb=el.querySelector(".bb"),out=el.querySelector("output");let n=0,t1=0,t2=0;
const pop=()=>{if(el.classList.contains("pop"))return;n+=1;out.textContent=String(n);el.classList.remove("grow");el.classList.add("pop");clearTimeout(t1);clearTimeout(t2);t1=setTimeout(()=>{el.style.setProperty("--px","0px");el.style.setProperty("--py","0px");el.classList.remove("pop");el.classList.add("grow");t2=setTimeout(()=>el.classList.remove("grow"),1200)},700)};
bb.addEventListener("click",pop);
el.addEventListener("pointermove",e=>{if(el.classList.contains("pop")||e.pointerType==="touch")return;const r=el.getBoundingClientRect(),s=r.width/el.offsetWidth||1,x=(e.clientX-r.left)/s,y=(e.clientY-r.top)/s,cx=el.offsetWidth/2,cy=el.offsetHeight*.45,dx=x-cx,dy=y-cy,d=Math.hypot(dx,dy)||1,R=el.offsetWidth*.32;if(d>R){el.style.setProperty("--px","0px");el.style.setProperty("--py","0px");return}const k=(R-d)*.35/d;el.style.setProperty("--px",(-dx*k).toFixed(1)+"px");el.style.setProperty("--py",(-dy*k).toFixed(1)+"px")});
el.addEventListener("pointerleave",()=>{el.style.setProperty("--px","0px");el.style.setProperty("--py","0px")});
return()=>{clearTimeout(t1);clearTimeout(t2)}
});
```

### 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 = `
@property --uvq11-ba {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.uvq11-bub {
  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;
  position: relative;
  width: 20em;
  height: 14em;
  border-radius: 1.6em;
  overflow: hidden;
  color: #fff;
  isolation: isolate;
  -webkit-user-select: none;
  user-select: none;
  color: #24432a;
}

.uvq11-bub .bg {
  position: absolute;
  inset: 0;
  background: radial-gradient(18% 25% at 84% 16%,#fffbe0,rgba(255,240,180,.4) 50%,transparent 72%),radial-gradient(4% 6% at 22% 30%,rgba(255,255,255,.6),transparent),radial-gradient(3% 4% at 64% 22%,rgba(255,255,255,.5),transparent),radial-gradient(70% 40% at 30% 100%,#5fae58 60%,transparent 62%),radial-gradient(70% 40% at 90% 100%,#78c46a 60%,transparent 62%),linear-gradient(180deg,#a7dcff,#d9f2ff 55%,#bfe3a6 75%,#8fcf75);
}

.uvq11-bub .bb {
  font: inherit;
  color: inherit;
  border: 0;
  margin: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  position: absolute;
  left: 50%;
  top: 45%;
  width: 7.4em;
  height: 7.4em;
  margin: -3.7em 0 0 -3.7em;
  border-radius: 50%;
  translate: var(--px,0) var(--py,0);
  transition: translate calc(0.7s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.12s / var(--k)) cubic-bezier(.25,.1,.25,1),scale calc(0.12s / var(--k)) cubic-bezier(.25,.1,.25,1);
  background: radial-gradient(circle,transparent 58%,rgba(255,255,255,.14) 70%,rgba(255,255,255,.35) 100%);
  -webkit-backdrop-filter: saturate(1.3) brightness(1.05);
  backdrop-filter: saturate(1.3) brightness(1.05);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.4),inset 0 0 1.2em rgba(255,255,255,.25),0 1em 1.5em -1.2em rgba(40,80,40,.4);
  animation: uvq11-bub-w calc(5s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite,uvq11-bub-f calc(7s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite;
}

.uvq11-bub .bb:focus-visible {
  outline: 2px solid #24432a;
  outline-offset: 5px;
}

.uvq11-bub .fm {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: conic-gradient(from var(--uvq11-ba),var(--c1),var(--c2),#fff59d,#a8ffcf,#9ab8ff,var(--c1));
  -webkit-mask: radial-gradient(closest-side,transparent 64%,rgba(0,0,0,.35) 80%,#000 97%,transparent 100%);
  mask: radial-gradient(closest-side,transparent 64%,rgba(0,0,0,.35) 80%,#000 97%,transparent 100%);
  opacity: .75;
  mix-blend-mode: screen;
  animation: uvq11-bub-a calc(9s / var(--k)) linear infinite;
}

.uvq11-bub .hl,.uvq11-bub .hl::before,.uvq11-bub .hl::after {
  position: absolute;
  border-radius: .3em;
  pointer-events: none;
}

.uvq11-bub .hl {
  left: 22%;
  top: 17%;
  width: .95em;
  height: 1.15em;
  background: rgba(255,255,255,.85);
  transform: rotate(-24deg) skewY(8deg);
  filter: blur(.4px);
}

.uvq11-bub .hl::before {
  content: "";
  left: 1.15em;
  top: .1em;
  width: .75em;
  height: 1.05em;
  background: rgba(255,255,255,.7);
}

.uvq11-bub .hl::after {
  content: "";
  left: 3.9em;
  top: 4.5em;
  width: .7em;
  height: .45em;
  border-radius: 50%;
  background: rgba(255,255,255,.55);
}

.uvq11-bub.pop .bb {
  opacity: 0;
  scale: 1.18;
  pointer-events: none;
}

.uvq11-bub.grow .bb {
  animation: uvq11-bub-g calc(1.1s / var(--k)) cubic-bezier(.22,1,.36,1),uvq11-bub-w calc(5s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite;
}

.uvq11-bub .sp i {
  position: absolute;
  left: 50%;
  top: 45%;
  width: .32em;
  height: .32em;
  margin: -.16em;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%,#fff,color-mix(in srgb,var(--c1) 50%,rgba(255,255,255,.3)));
  opacity: 0;
  translate: var(--px,0) var(--py,0);
}

.uvq11-bub.pop .sp i {
  animation: uvq11-bub-s calc(0.6s / var(--k)) cubic-bezier(.25,.1,.25,1) forwards;
}

.uvq11-bub .wd {
  position: absolute;
  left: 1em;
  bottom: .5em;
  width: 2.2em;
  height: 2.2em;
  border-radius: 50%;
  box-shadow: inset 0 0 0 .32em var(--c2),0 .1em .2em rgba(0,0,0,.2);
  transform: rotate(-30deg);
}

.uvq11-bub .wd::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: .35em;
  height: 3em;
  margin-left: -.175em;
  border-radius: .2em;
  background: var(--c2);
}

.uvq11-bub .ct {
  position: absolute;
  right: 1em;
  bottom: .9em;
  margin: 0;
  padding: .35em .8em;
  border-radius: 2em;
  font-size: .75em;
  font-weight: 700;
  background: rgba(255,255,255,.55);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.8);
}

.uvq11-bub output {
  font-variant-numeric: tabular-nums;
}

@keyframes uvq11-bub-a {
  to {
    --uvq11-ba: 360deg;
  }
}

@keyframes uvq11-bub-w {
  0%,100% {
    border-radius: 50%;
  }
  25% {
    border-radius: 48% 52% 50% 50%/52% 48% 52% 48%;
  }
  50% {
    border-radius: 52% 48% 49% 51%/48% 52% 48% 52%;
  }
  75% {
    border-radius: 49% 51% 52% 48%/51% 49% 51% 49%;
  }
}

@keyframes uvq11-bub-f {
  0%,100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-.5em);
  }
}

@keyframes uvq11-bub-g {
  0% {
    transform: translate(-6em,5.4em) scale(.08);
  }
  100% {
    transform: none;
  }
}

@keyframes uvq11-bub-s {
  0% {
    opacity: 1;
    transform: rotate(var(--a)) translateY(-3.4em);
  }
  100% {
    opacity: 0;
    transform: rotate(var(--a)) translateY(calc(-1 * var(--d) - 1.4em)) scale(.4);
  }
}

@media (prefers-reduced-motion:reduce) {
  .uvq11-bub,.uvq11-bub *,.uvq11-bub *::before,.uvq11-bub *::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 bb=el.querySelector(".bb"),out=el.querySelector("output");let n=0,t1=0,t2=0;
const pop=()=>{if(el.classList.contains("pop"))return;n+=1;out.textContent=String(n);el.classList.remove("grow");el.classList.add("pop");clearTimeout(t1);clearTimeout(t2);t1=setTimeout(()=>{el.style.setProperty("--px","0px");el.style.setProperty("--py","0px");el.classList.remove("pop");el.classList.add("grow");t2=setTimeout(()=>el.classList.remove("grow"),1200)},700)};
bb.addEventListener("click",pop);
el.addEventListener("pointermove",e=>{if(el.classList.contains("pop")||e.pointerType==="touch")return;const r=el.getBoundingClientRect(),s=r.width/el.offsetWidth||1,x=(e.clientX-r.left)/s,y=(e.clientY-r.top)/s,cx=el.offsetWidth/2,cy=el.offsetHeight*.45,dx=x-cx,dy=y-cy,d=Math.hypot(dx,dy)||1,R=el.offsetWidth*.32;if(d>R){el.style.setProperty("--px","0px");el.style.setProperty("--py","0px");return}const k=(R-d)*.35/d;el.style.setProperty("--px",(-dx*k).toFixed(1)+"px");el.style.setProperty("--py",(-dy*k).toFixed(1)+"px")});
el.addEventListener("pointerleave",()=>{el.style.setProperty("--px","0px");el.style.setProperty("--py","0px")});
return()=>{clearTimeout(t1);clearTimeout(t2)}
}

export default function LgSoapBubble() {
  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": "#7ee8fa",
          "--c2": "#ff9bd2",
          "--s": "16px",
          "--k": "1",
          "--fg": "#1d1d1f",
          "--bg": "#f5f5f7",
        } as CSSProperties}
      >
        <div className="uvq11-bub"><div className="bg" aria-hidden="true"></div><i className="wd" aria-hidden="true"></i><button type="button" className="bb" aria-label="Pop the bubble"><span className="fm"></span><span className="hl"></span></button><span className="sp" aria-hidden="true"><i style={{ "--a": "0deg", "--d": "3.4em" } as CSSProperties}></i><i style={{ "--a": "30deg", "--d": "4.1em" } as CSSProperties}></i><i style={{ "--a": "60deg", "--d": "4.8em" } as CSSProperties}></i><i style={{ "--a": "90deg", "--d": "3.4em" } as CSSProperties}></i><i style={{ "--a": "120deg", "--d": "4.1em" } as CSSProperties}></i><i style={{ "--a": "150deg", "--d": "4.8em" } as CSSProperties}></i><i style={{ "--a": "180deg", "--d": "3.4em" } as CSSProperties}></i><i style={{ "--a": "210deg", "--d": "4.1em" } as CSSProperties}></i><i style={{ "--a": "240deg", "--d": "4.8em" } as CSSProperties}></i><i style={{ "--a": "270deg", "--d": "3.4em" } as CSSProperties}></i><i style={{ "--a": "300deg", "--d": "4.1em" } as CSSProperties}></i><i style={{ "--a": "330deg", "--d": "4.8em" } as CSSProperties}></i></span><p className="ct"><output>0</output> <span>bubbles</span></p></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 “Popping soap bubble” block (Liquid Glass) from AIMRAN Library to my project.

## What it is
A bubble with an iridescent film that wobbles and dodges the cursor; tap it and it bursts into droplets, then floats back.

## Chosen settings
- Main color: #7ee8fa
- Secondary color: #ff9bd2
- 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 = `
@property --uvq11-ba {
  syntax: "<angle>";
  inherits: false;
  initial-value: 0deg;
}

.uvq11-bub {
  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;
  position: relative;
  width: 20em;
  height: 14em;
  border-radius: 1.6em;
  overflow: hidden;
  color: #fff;
  isolation: isolate;
  -webkit-user-select: none;
  user-select: none;
  color: #24432a;
}

.uvq11-bub .bg {
  position: absolute;
  inset: 0;
  background: radial-gradient(18% 25% at 84% 16%,#fffbe0,rgba(255,240,180,.4) 50%,transparent 72%),radial-gradient(4% 6% at 22% 30%,rgba(255,255,255,.6),transparent),radial-gradient(3% 4% at 64% 22%,rgba(255,255,255,.5),transparent),radial-gradient(70% 40% at 30% 100%,#5fae58 60%,transparent 62%),radial-gradient(70% 40% at 90% 100%,#78c46a 60%,transparent 62%),linear-gradient(180deg,#a7dcff,#d9f2ff 55%,#bfe3a6 75%,#8fcf75);
}

.uvq11-bub .bb {
  font: inherit;
  color: inherit;
  border: 0;
  margin: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  position: absolute;
  left: 50%;
  top: 45%;
  width: 7.4em;
  height: 7.4em;
  margin: -3.7em 0 0 -3.7em;
  border-radius: 50%;
  translate: var(--px,0) var(--py,0);
  transition: translate calc(0.7s / var(--k)) cubic-bezier(.34,1.56,.64,1),opacity calc(0.12s / var(--k)) cubic-bezier(.25,.1,.25,1),scale calc(0.12s / var(--k)) cubic-bezier(.25,.1,.25,1);
  background: radial-gradient(circle,transparent 58%,rgba(255,255,255,.14) 70%,rgba(255,255,255,.35) 100%);
  -webkit-backdrop-filter: saturate(1.3) brightness(1.05);
  backdrop-filter: saturate(1.3) brightness(1.05);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.4),inset 0 0 1.2em rgba(255,255,255,.25),0 1em 1.5em -1.2em rgba(40,80,40,.4);
  animation: uvq11-bub-w calc(5s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite,uvq11-bub-f calc(7s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite;
}

.uvq11-bub .bb:focus-visible {
  outline: 2px solid #24432a;
  outline-offset: 5px;
}

.uvq11-bub .fm {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: conic-gradient(from var(--uvq11-ba),var(--c1),var(--c2),#fff59d,#a8ffcf,#9ab8ff,var(--c1));
  -webkit-mask: radial-gradient(closest-side,transparent 64%,rgba(0,0,0,.35) 80%,#000 97%,transparent 100%);
  mask: radial-gradient(closest-side,transparent 64%,rgba(0,0,0,.35) 80%,#000 97%,transparent 100%);
  opacity: .75;
  mix-blend-mode: screen;
  animation: uvq11-bub-a calc(9s / var(--k)) linear infinite;
}

.uvq11-bub .hl,.uvq11-bub .hl::before,.uvq11-bub .hl::after {
  position: absolute;
  border-radius: .3em;
  pointer-events: none;
}

.uvq11-bub .hl {
  left: 22%;
  top: 17%;
  width: .95em;
  height: 1.15em;
  background: rgba(255,255,255,.85);
  transform: rotate(-24deg) skewY(8deg);
  filter: blur(.4px);
}

.uvq11-bub .hl::before {
  content: "";
  left: 1.15em;
  top: .1em;
  width: .75em;
  height: 1.05em;
  background: rgba(255,255,255,.7);
}

.uvq11-bub .hl::after {
  content: "";
  left: 3.9em;
  top: 4.5em;
  width: .7em;
  height: .45em;
  border-radius: 50%;
  background: rgba(255,255,255,.55);
}

.uvq11-bub.pop .bb {
  opacity: 0;
  scale: 1.18;
  pointer-events: none;
}

.uvq11-bub.grow .bb {
  animation: uvq11-bub-g calc(1.1s / var(--k)) cubic-bezier(.22,1,.36,1),uvq11-bub-w calc(5s / var(--k)) cubic-bezier(.25,.1,.25,1) infinite;
}

.uvq11-bub .sp i {
  position: absolute;
  left: 50%;
  top: 45%;
  width: .32em;
  height: .32em;
  margin: -.16em;
  border-radius: 50%;
  background: radial-gradient(circle at 35% 35%,#fff,color-mix(in srgb,var(--c1) 50%,rgba(255,255,255,.3)));
  opacity: 0;
  translate: var(--px,0) var(--py,0);
}

.uvq11-bub.pop .sp i {
  animation: uvq11-bub-s calc(0.6s / var(--k)) cubic-bezier(.25,.1,.25,1) forwards;
}

.uvq11-bub .wd {
  position: absolute;
  left: 1em;
  bottom: .5em;
  width: 2.2em;
  height: 2.2em;
  border-radius: 50%;
  box-shadow: inset 0 0 0 .32em var(--c2),0 .1em .2em rgba(0,0,0,.2);
  transform: rotate(-30deg);
}

.uvq11-bub .wd::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 100%;
  width: .35em;
  height: 3em;
  margin-left: -.175em;
  border-radius: .2em;
  background: var(--c2);
}

.uvq11-bub .ct {
  position: absolute;
  right: 1em;
  bottom: .9em;
  margin: 0;
  padding: .35em .8em;
  border-radius: 2em;
  font-size: .75em;
  font-weight: 700;
  background: rgba(255,255,255,.55);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.8);
}

.uvq11-bub output {
  font-variant-numeric: tabular-nums;
}

@keyframes uvq11-bub-a {
  to {
    --uvq11-ba: 360deg;
  }
}

@keyframes uvq11-bub-w {
  0%,100% {
    border-radius: 50%;
  }
  25% {
    border-radius: 48% 52% 50% 50%/52% 48% 52% 48%;
  }
  50% {
    border-radius: 52% 48% 49% 51%/48% 52% 48% 52%;
  }
  75% {
    border-radius: 49% 51% 52% 48%/51% 49% 51% 49%;
  }
}

@keyframes uvq11-bub-f {
  0%,100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-.5em);
  }
}

@keyframes uvq11-bub-g {
  0% {
    transform: translate(-6em,5.4em) scale(.08);
  }
  100% {
    transform: none;
  }
}

@keyframes uvq11-bub-s {
  0% {
    opacity: 1;
    transform: rotate(var(--a)) translateY(-3.4em);
  }
  100% {
    opacity: 0;
    transform: rotate(var(--a)) translateY(calc(-1 * var(--d) - 1.4em)) scale(.4);
  }
}

@media (prefers-reduced-motion:reduce) {
  .uvq11-bub,.uvq11-bub *,.uvq11-bub *::before,.uvq11-bub *::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 bb=el.querySelector(".bb"),out=el.querySelector("output");let n=0,t1=0,t2=0;
const pop=()=>{if(el.classList.contains("pop"))return;n+=1;out.textContent=String(n);el.classList.remove("grow");el.classList.add("pop");clearTimeout(t1);clearTimeout(t2);t1=setTimeout(()=>{el.style.setProperty("--px","0px");el.style.setProperty("--py","0px");el.classList.remove("pop");el.classList.add("grow");t2=setTimeout(()=>el.classList.remove("grow"),1200)},700)};
bb.addEventListener("click",pop);
el.addEventListener("pointermove",e=>{if(el.classList.contains("pop")||e.pointerType==="touch")return;const r=el.getBoundingClientRect(),s=r.width/el.offsetWidth||1,x=(e.clientX-r.left)/s,y=(e.clientY-r.top)/s,cx=el.offsetWidth/2,cy=el.offsetHeight*.45,dx=x-cx,dy=y-cy,d=Math.hypot(dx,dy)||1,R=el.offsetWidth*.32;if(d>R){el.style.setProperty("--px","0px");el.style.setProperty("--py","0px");return}const k=(R-d)*.35/d;el.style.setProperty("--px",(-dx*k).toFixed(1)+"px");el.style.setProperty("--py",(-dy*k).toFixed(1)+"px")});
el.addEventListener("pointerleave",()=>{el.style.setProperty("--px","0px");el.style.setProperty("--py","0px")});
return()=>{clearTimeout(t1);clearTimeout(t2)}
}

export default function LgSoapBubble() {
  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": "#7ee8fa",
          "--c2": "#ff9bd2",
          "--s": "16px",
          "--k": "1",
          "--fg": "#1d1d1f",
          "--bg": "#f5f5f7",
        } as CSSProperties}
      >
        <div className="uvq11-bub"><div className="bg" aria-hidden="true"></div><i className="wd" aria-hidden="true"></i><button type="button" className="bb" aria-label="Pop the bubble"><span className="fm"></span><span className="hl"></span></button><span className="sp" aria-hidden="true"><i style={{ "--a": "0deg", "--d": "3.4em" } as CSSProperties}></i><i style={{ "--a": "30deg", "--d": "4.1em" } as CSSProperties}></i><i style={{ "--a": "60deg", "--d": "4.8em" } as CSSProperties}></i><i style={{ "--a": "90deg", "--d": "3.4em" } as CSSProperties}></i><i style={{ "--a": "120deg", "--d": "4.1em" } as CSSProperties}></i><i style={{ "--a": "150deg", "--d": "4.8em" } as CSSProperties}></i><i style={{ "--a": "180deg", "--d": "3.4em" } as CSSProperties}></i><i style={{ "--a": "210deg", "--d": "4.1em" } as CSSProperties}></i><i style={{ "--a": "240deg", "--d": "4.8em" } as CSSProperties}></i><i style={{ "--a": "270deg", "--d": "3.4em" } as CSSProperties}></i><i style={{ "--a": "300deg", "--d": "4.1em" } as CSSProperties}></i><i style={{ "--a": "330deg", "--d": "4.8em" } as CSSProperties}></i></span><p className="ct"><output>0</output> <span>bubbles</span></p></div>
      </div>
    </>
  );
}
```

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