# Camera lens with aperture

> The front element of a 50 mm lens with multicoated reflections; pick an f-number and the iris blades close.

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

## Where to use it

A camera lens seen from the front: knurled focus ring, engraved markings on an SVG text path and a deep front element with purple and green coating reflections. A seven-blade iris drawn in SVG opens and closes smoothly, the reflections shift with the pointer, and a small viewfinder shows the bokeh shrinking as you stop down.

Use the two colors to tint the coatings and the selected chip. The f-stops are native radio buttons, so arrow keys work, and the block fits photography courses, camera shops or a settings panel with character.

## Code

### HTML

```html
<div class="uvq11-lens" style="--bk:.5"><div class="ln" aria-hidden="true"><svg class="tx" viewBox="0 0 200 200"><defs><path id="uvq11-lens-p" d="M100 100m-80 0a80 80 0 1 1 160 0a80 80 0 1 1-160 0"/></defs><text><textPath href="#uvq11-lens-p">AIMRAN · 50 mm · 1:1.4 · Ø58 · AIMRAN · 50 mm · 1:1.4 · Ø58 ·</textPath></text></svg><div class="gl"><svg class="ir" viewBox="-50 -50 100 100"><path class="bl" fill-rule="evenodd" d="M-50 0a50 50 0 1 0 100 0a50 50 0 1 0-100 0ZM22.50 13.34L3.60 25.91L-18.02 18.97L-26.07 -2.26L-14.49 -21.79L8.00 -24.91L24.46 -9.28Z"/><path class="ed" d="M22.50 13.34L-10.64 48.86M3.60 25.91L-44.83 22.15M-18.02 18.97L-45.26 -21.24M-26.07 -2.26L-11.62 -48.63M-14.49 -21.79L30.78 -39.40M8.00 -24.91L50.00 -0.50M24.46 -9.28L31.57 38.78"/></svg><i class="ct"></i><i class="r1"></i><i class="r2"></i><i class="r3"></i><i class="sb"></i></div></div><div class="pn"><p class="fm">50 mm</p><output class="fv">f/2.8</output><div class="vf" aria-hidden="true"><i style="--x:8%;--y:26%;--c:#ffd166;--z:1.08"></i><i style="--x:19%;--y:72%;--c:#ff6b6b;--z:1.18"></i><i style="--x:44%;--y:20%;--c:#7bdff2;--z:0.67"></i><i style="--x:43%;--y:46%;--c:#ffd166;--z:1.00"></i><i style="--x:48%;--y:36%;--c:#f7aef8;--z:0.89"></i><i style="--x:72%;--y:15%;--c:#b8f2e6;--z:0.64"></i><i style="--x:46%;--y:58%;--c:#ffe8a3;--z:1.05"></i><b></b></div><div class="ap" role="radiogroup" aria-label="Aperture"><label><input type="radio" name="uvq11-lens-ap" value="1.4"><span>f/1.4</span></label><label><input type="radio" name="uvq11-lens-ap" value="2"><span>f/2</span></label><label><input type="radio" name="uvq11-lens-ap" value="2.8" checked><span>f/2.8</span></label><label><input type="radio" name="uvq11-lens-ap" value="4"><span>f/4</span></label><label><input type="radio" name="uvq11-lens-ap" value="8"><span>f/8</span></label><label><input type="radio" name="uvq11-lens-ap" value="16"><span>f/16</span></label></div></div></div>
```

### CSS

```css
.uvq11-lens {
  --c1: #8b5cf6;
  --c2: #2ee59d;
  --s: 16px;
  /* tamaño */
  --k: 1;
  /* velocidad */
  --fg: #1d1d1f;
  /* texto */
  --bg: #f5f5f7;
  /* fondo */
}

.uvq11-lens {
  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;
  --mx: 0;
  --my: 0;
  background: radial-gradient(50% 60% at 30% 40%,#2a2a30,#0c0c0e 75%);
}

.uvq11-lens .ln {
  position: absolute;
  left: .9em;
  top: 1.5em;
  width: 11em;
  height: 11em;
  border-radius: 50%;
  background: repeating-conic-gradient(#1a1a1d 0 2deg,#303035 2deg 4deg);
  box-shadow: 0 1em 2em -.6em rgba(0,0,0,.9),inset 0 0 0 .08em #555;
}

.uvq11-lens .ln::before {
  content: "";
  position: absolute;
  inset: .6em;
  border-radius: 50%;
  background: radial-gradient(circle,#0d0d10 60%,#1d1d22 61%,#141417 100%);
  box-shadow: 0 0 0 .06em #77777f,inset 0 0 0 .05em #c33;
}

.uvq11-lens .tx {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.uvq11-lens text {
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .8px;
  fill: #c9ccd3;
}

.uvq11-lens .gl {
  position: absolute;
  inset: 1.9em;
  border-radius: 50%;
  overflow: hidden;
  background: radial-gradient(circle,#06070b 0 30%,#0e1219 62%,#1d2433 88%,#07080b 100%);
  box-shadow: inset 0 0 0 .14em #050506,inset 0 0 1.2em rgba(0,0,0,.9);
}

.uvq11-lens .ir {
  position: absolute;
  inset: 9%;
  width: 82%;
  height: 82%;
}

.uvq11-lens .bl {
  fill: #17171b;
}

.uvq11-lens .ed {
  fill: none;
  stroke: rgba(255,255,255,.1);
  stroke-width: .6;
}

.uvq11-lens .gl i {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  transition: transform calc(0.3s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens .ct {
  left: 38%;
  top: 38%;
  width: 24%;
  height: 24%;
  background: radial-gradient(circle,color-mix(in srgb,var(--c1) 70%,transparent),transparent 70%);
  transform: translate(calc(var(--mx) * -.3em),calc(var(--my) * -.3em));
}

.uvq11-lens .r1 {
  inset: 4%;
  background: conic-gradient(from 200deg,transparent 0 30deg,color-mix(in srgb,var(--c1) 85%,transparent) 60deg,transparent 110deg,transparent 220deg,color-mix(in srgb,var(--c2) 70%,transparent) 250deg,transparent 290deg);
  -webkit-mask: radial-gradient(closest-side,transparent 82%,#000 88% 92%,transparent 97%);
  mask: radial-gradient(closest-side,transparent 82%,#000 88% 92%,transparent 97%);
  transform: rotate(calc(var(--mx) * 25deg));
}

.uvq11-lens .r2 {
  left: 56%;
  top: 58%;
  width: 1.2em;
  height: 1.2em;
  background: radial-gradient(circle,color-mix(in srgb,var(--c2) 80%,#fff) 0 15%,color-mix(in srgb,var(--c2) 60%,transparent) 40%,transparent 70%);
  transform: translate(calc(var(--mx) * .7em),calc(var(--my) * .7em));
}

.uvq11-lens .r3 {
  left: 30%;
  top: 62%;
  width: .5em;
  height: .5em;
  background: radial-gradient(circle,#ffcf7a,transparent 70%);
  transform: translate(calc(var(--mx) * .4em),calc(var(--my) * .4em));
}

.uvq11-lens .sb {
  left: 20%;
  top: 16%;
  width: 1.7em;
  height: 1.05em;
  border-radius: .35em;
  background: linear-gradient(160deg,rgba(255,255,255,.95),rgba(255,255,255,.55));
  filter: blur(.4px);
  transform: translate(calc(var(--mx) * -1em),calc(var(--my) * -1em)) skewX(-8deg);
}

.uvq11-lens .pn {
  position: absolute;
  left: 12.6em;
  top: 1.1em;
  width: 6.7em;
}

.uvq11-lens .fm {
  margin: 0;
  font-size: .62em;
  font-weight: 700;
  letter-spacing: .14em;
  color: #9a9aa5;
}

.uvq11-lens .fv {
  display: block;
  margin: .05em 0 .35em;
  font-size: 1.65em;
  font-weight: 250;
  letter-spacing: -.02em;
}

.uvq11-lens .vf {
  position: relative;
  height: 3.6em;
  border-radius: .6em;
  overflow: hidden;
  background: linear-gradient(180deg,#22354a,#0f1722);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.15);
}

.uvq11-lens .vf i {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: calc((.3em + 1.5em * var(--bk)) * var(--z));
  aspect-ratio: 1;
  border-radius: 50%;
  translate: -50% -50%;
  background: radial-gradient(circle,color-mix(in srgb,var(--c) 45%,transparent) 0 55%,var(--c) 66%,transparent 72%);
  filter: blur(calc(var(--bk) * 1.2px));
  opacity: calc(.5 + .45 * var(--bk));
  transition: all calc(0.4s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens .vf b {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 1.3em;
  height: 1.9em;
  margin-left: -.65em;
  border-radius: .65em .65em 0 0;
  background: #0a0d12;
  box-shadow: 0 -1.15em 0 -.25em #0a0d12;
}

.uvq11-lens .vf::after {
  content: "";
  position: absolute;
  inset: .4em;
  background: linear-gradient(#fff,#fff) 0 0/.5em 1px,linear-gradient(#fff,#fff) 0 0/1px .5em,linear-gradient(#fff,#fff) 100% 0/.5em 1px,linear-gradient(#fff,#fff) 100% 0/1px .5em,linear-gradient(#fff,#fff) 0 100%/.5em 1px,linear-gradient(#fff,#fff) 0 100%/1px .5em,linear-gradient(#fff,#fff) 100% 100%/.5em 1px,linear-gradient(#fff,#fff) 100% 100%/1px .5em;
  background-repeat: no-repeat;
  opacity: .7;
}

.uvq11-lens .ap {
  display: grid;
  grid-template-columns: repeat(3,1fr);
  gap: .3em;
  margin-top: .55em;
}

.uvq11-lens label {
  position: relative;
  cursor: pointer;
}

.uvq11-lens input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}

.uvq11-lens label span {
  display: block;
  padding: .45em 0;
  border-radius: .5em;
  text-align: center;
  font-size: .62em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #c9ccd3;
  background: rgba(255,255,255,.07);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12);
  transition: all calc(0.2s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens label:hover span {
  background: rgba(255,255,255,.14);
}

.uvq11-lens input:checked+span {
  color: #111;
  background: #f2f2f5;
  box-shadow: 0 0 0 1px #fff,0 .3em .8em -.3em var(--c1);
}

.uvq11-lens input:focus-visible+span {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.uvq11-lens label:active span {
  transform: scale(.94);
}

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

### JavaScript

```js
document.querySelectorAll(".uvq11-lens").forEach(el => {
  const rm=matchMedia("(prefers-reduced-motion: reduce)").matches;const ln=el.querySelector(".ln"),bl=el.querySelector(".bl"),ed=el.querySelector(".ed"),fv=el.querySelector(".fv");const R=f=>44*Math.pow(1.4/f,.75);let r=R(2.8),to=r,raf=0;
const path=r=>{let d="M-50 0a50 50 0 1 0 100 0a50 50 0 1 0-100 0Z",e="";const rot=r/44*.9;for(let k=0;k<7;k++){const a=rot+k*Math.PI*2/7,x=r*Math.cos(a),y=r*Math.sin(a),b=a+1.25;d+=(k?"L":"M")+x.toFixed(2)+" "+y.toFixed(2);e+="M"+x.toFixed(2)+" "+y.toFixed(2)+"L"+(50*Math.cos(b)).toFixed(2)+" "+(50*Math.sin(b)).toFixed(2)}return[d+"Z",e]};
const draw=()=>{const p=path(r);bl.setAttribute("d",p[0]);ed.setAttribute("d",p[1])};
const step=()=>{r+=(to-r)*.18;if(Math.abs(to-r)<.05)r=to;draw();raf=r===to?0:requestAnimationFrame(step)};
el.querySelectorAll("input").forEach(i=>i.addEventListener("change",()=>{const f=parseFloat(i.value);to=R(f);fv.textContent="f/"+i.value;el.style.setProperty("--bk",((to-6)/38).toFixed(3));if(rm){r=to;draw()}else if(!raf)raf=requestAnimationFrame(step)}));
ln.addEventListener("pointermove",e=>{const b=ln.getBoundingClientRect();el.style.setProperty("--mx",((e.clientX-b.left)/b.width*2-1).toFixed(3));el.style.setProperty("--my",((e.clientY-b.top)/b.height*2-1).toFixed(3))});
ln.addEventListener("pointerleave",()=>{el.style.setProperty("--mx","0");el.style.setProperty("--my","0")});
return()=>cancelAnimationFrame(raf)
});
```

### 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 = `
.uvq11-lens {
  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;
  --mx: 0;
  --my: 0;
  background: radial-gradient(50% 60% at 30% 40%,#2a2a30,#0c0c0e 75%);
}

.uvq11-lens .ln {
  position: absolute;
  left: .9em;
  top: 1.5em;
  width: 11em;
  height: 11em;
  border-radius: 50%;
  background: repeating-conic-gradient(#1a1a1d 0 2deg,#303035 2deg 4deg);
  box-shadow: 0 1em 2em -.6em rgba(0,0,0,.9),inset 0 0 0 .08em #555;
}

.uvq11-lens .ln::before {
  content: "";
  position: absolute;
  inset: .6em;
  border-radius: 50%;
  background: radial-gradient(circle,#0d0d10 60%,#1d1d22 61%,#141417 100%);
  box-shadow: 0 0 0 .06em #77777f,inset 0 0 0 .05em #c33;
}

.uvq11-lens .tx {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.uvq11-lens text {
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .8px;
  fill: #c9ccd3;
}

.uvq11-lens .gl {
  position: absolute;
  inset: 1.9em;
  border-radius: 50%;
  overflow: hidden;
  background: radial-gradient(circle,#06070b 0 30%,#0e1219 62%,#1d2433 88%,#07080b 100%);
  box-shadow: inset 0 0 0 .14em #050506,inset 0 0 1.2em rgba(0,0,0,.9);
}

.uvq11-lens .ir {
  position: absolute;
  inset: 9%;
  width: 82%;
  height: 82%;
}

.uvq11-lens .bl {
  fill: #17171b;
}

.uvq11-lens .ed {
  fill: none;
  stroke: rgba(255,255,255,.1);
  stroke-width: .6;
}

.uvq11-lens .gl i {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  transition: transform calc(0.3s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens .ct {
  left: 38%;
  top: 38%;
  width: 24%;
  height: 24%;
  background: radial-gradient(circle,color-mix(in srgb,var(--c1) 70%,transparent),transparent 70%);
  transform: translate(calc(var(--mx) * -.3em),calc(var(--my) * -.3em));
}

.uvq11-lens .r1 {
  inset: 4%;
  background: conic-gradient(from 200deg,transparent 0 30deg,color-mix(in srgb,var(--c1) 85%,transparent) 60deg,transparent 110deg,transparent 220deg,color-mix(in srgb,var(--c2) 70%,transparent) 250deg,transparent 290deg);
  -webkit-mask: radial-gradient(closest-side,transparent 82%,#000 88% 92%,transparent 97%);
  mask: radial-gradient(closest-side,transparent 82%,#000 88% 92%,transparent 97%);
  transform: rotate(calc(var(--mx) * 25deg));
}

.uvq11-lens .r2 {
  left: 56%;
  top: 58%;
  width: 1.2em;
  height: 1.2em;
  background: radial-gradient(circle,color-mix(in srgb,var(--c2) 80%,#fff) 0 15%,color-mix(in srgb,var(--c2) 60%,transparent) 40%,transparent 70%);
  transform: translate(calc(var(--mx) * .7em),calc(var(--my) * .7em));
}

.uvq11-lens .r3 {
  left: 30%;
  top: 62%;
  width: .5em;
  height: .5em;
  background: radial-gradient(circle,#ffcf7a,transparent 70%);
  transform: translate(calc(var(--mx) * .4em),calc(var(--my) * .4em));
}

.uvq11-lens .sb {
  left: 20%;
  top: 16%;
  width: 1.7em;
  height: 1.05em;
  border-radius: .35em;
  background: linear-gradient(160deg,rgba(255,255,255,.95),rgba(255,255,255,.55));
  filter: blur(.4px);
  transform: translate(calc(var(--mx) * -1em),calc(var(--my) * -1em)) skewX(-8deg);
}

.uvq11-lens .pn {
  position: absolute;
  left: 12.6em;
  top: 1.1em;
  width: 6.7em;
}

.uvq11-lens .fm {
  margin: 0;
  font-size: .62em;
  font-weight: 700;
  letter-spacing: .14em;
  color: #9a9aa5;
}

.uvq11-lens .fv {
  display: block;
  margin: .05em 0 .35em;
  font-size: 1.65em;
  font-weight: 250;
  letter-spacing: -.02em;
}

.uvq11-lens .vf {
  position: relative;
  height: 3.6em;
  border-radius: .6em;
  overflow: hidden;
  background: linear-gradient(180deg,#22354a,#0f1722);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.15);
}

.uvq11-lens .vf i {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: calc((.3em + 1.5em * var(--bk)) * var(--z));
  aspect-ratio: 1;
  border-radius: 50%;
  translate: -50% -50%;
  background: radial-gradient(circle,color-mix(in srgb,var(--c) 45%,transparent) 0 55%,var(--c) 66%,transparent 72%);
  filter: blur(calc(var(--bk) * 1.2px));
  opacity: calc(.5 + .45 * var(--bk));
  transition: all calc(0.4s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens .vf b {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 1.3em;
  height: 1.9em;
  margin-left: -.65em;
  border-radius: .65em .65em 0 0;
  background: #0a0d12;
  box-shadow: 0 -1.15em 0 -.25em #0a0d12;
}

.uvq11-lens .vf::after {
  content: "";
  position: absolute;
  inset: .4em;
  background: linear-gradient(#fff,#fff) 0 0/.5em 1px,linear-gradient(#fff,#fff) 0 0/1px .5em,linear-gradient(#fff,#fff) 100% 0/.5em 1px,linear-gradient(#fff,#fff) 100% 0/1px .5em,linear-gradient(#fff,#fff) 0 100%/.5em 1px,linear-gradient(#fff,#fff) 0 100%/1px .5em,linear-gradient(#fff,#fff) 100% 100%/.5em 1px,linear-gradient(#fff,#fff) 100% 100%/1px .5em;
  background-repeat: no-repeat;
  opacity: .7;
}

.uvq11-lens .ap {
  display: grid;
  grid-template-columns: repeat(3,1fr);
  gap: .3em;
  margin-top: .55em;
}

.uvq11-lens label {
  position: relative;
  cursor: pointer;
}

.uvq11-lens input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}

.uvq11-lens label span {
  display: block;
  padding: .45em 0;
  border-radius: .5em;
  text-align: center;
  font-size: .62em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #c9ccd3;
  background: rgba(255,255,255,.07);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12);
  transition: all calc(0.2s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens label:hover span {
  background: rgba(255,255,255,.14);
}

.uvq11-lens input:checked+span {
  color: #111;
  background: #f2f2f5;
  box-shadow: 0 0 0 1px #fff,0 .3em .8em -.3em var(--c1);
}

.uvq11-lens input:focus-visible+span {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.uvq11-lens label:active span {
  transform: scale(.94);
}

@media (prefers-reduced-motion:reduce) {
  .uvq11-lens,.uvq11-lens *,.uvq11-lens *::before,.uvq11-lens *::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 rm=matchMedia("(prefers-reduced-motion: reduce)").matches;const ln=el.querySelector(".ln"),bl=el.querySelector(".bl"),ed=el.querySelector(".ed"),fv=el.querySelector(".fv");const R=f=>44*Math.pow(1.4/f,.75);let r=R(2.8),to=r,raf=0;
const path=r=>{let d="M-50 0a50 50 0 1 0 100 0a50 50 0 1 0-100 0Z",e="";const rot=r/44*.9;for(let k=0;k<7;k++){const a=rot+k*Math.PI*2/7,x=r*Math.cos(a),y=r*Math.sin(a),b=a+1.25;d+=(k?"L":"M")+x.toFixed(2)+" "+y.toFixed(2);e+="M"+x.toFixed(2)+" "+y.toFixed(2)+"L"+(50*Math.cos(b)).toFixed(2)+" "+(50*Math.sin(b)).toFixed(2)}return[d+"Z",e]};
const draw=()=>{const p=path(r);bl.setAttribute("d",p[0]);ed.setAttribute("d",p[1])};
const step=()=>{r+=(to-r)*.18;if(Math.abs(to-r)<.05)r=to;draw();raf=r===to?0:requestAnimationFrame(step)};
el.querySelectorAll("input").forEach(i=>i.addEventListener("change",()=>{const f=parseFloat(i.value);to=R(f);fv.textContent="f/"+i.value;el.style.setProperty("--bk",((to-6)/38).toFixed(3));if(rm){r=to;draw()}else if(!raf)raf=requestAnimationFrame(step)}));
ln.addEventListener("pointermove",e=>{const b=ln.getBoundingClientRect();el.style.setProperty("--mx",((e.clientX-b.left)/b.width*2-1).toFixed(3));el.style.setProperty("--my",((e.clientY-b.top)/b.height*2-1).toFixed(3))});
ln.addEventListener("pointerleave",()=>{el.style.setProperty("--mx","0");el.style.setProperty("--my","0")});
return()=>cancelAnimationFrame(raf)
}

export default function LgCameraLens() {
  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": "#8b5cf6",
          "--c2": "#2ee59d",
          "--s": "16px",
          "--k": "1",
          "--fg": "#1d1d1f",
          "--bg": "#f5f5f7",
        } as CSSProperties}
      >
        <div className="uvq11-lens" style={{ "--bk": ".5" } as CSSProperties}><div className="ln" aria-hidden="true"><svg className="tx" viewBox="0 0 200 200"><defs><path id="uvq11-lens-p" d="M100 100m-80 0a80 80 0 1 1 160 0a80 80 0 1 1-160 0" /></defs><text><textPath href="#uvq11-lens-p">AIMRAN · 50 mm · 1:1.4 · Ø58 · AIMRAN · 50 mm · 1:1.4 · Ø58 ·</textPath></text></svg><div className="gl"><svg className="ir" viewBox="-50 -50 100 100"><path className="bl" fillRule="evenodd" d="M-50 0a50 50 0 1 0 100 0a50 50 0 1 0-100 0ZM22.50 13.34L3.60 25.91L-18.02 18.97L-26.07 -2.26L-14.49 -21.79L8.00 -24.91L24.46 -9.28Z" /><path className="ed" d="M22.50 13.34L-10.64 48.86M3.60 25.91L-44.83 22.15M-18.02 18.97L-45.26 -21.24M-26.07 -2.26L-11.62 -48.63M-14.49 -21.79L30.78 -39.40M8.00 -24.91L50.00 -0.50M24.46 -9.28L31.57 38.78" /></svg><i className="ct"></i><i className="r1"></i><i className="r2"></i><i className="r3"></i><i className="sb"></i></div></div><div className="pn"><p className="fm">50 mm</p><output className="fv">f/2.8</output><div className="vf" aria-hidden="true"><i style={{ "--x": "8%", "--y": "26%", "--c": "#ffd166", "--z": "1.08" } as CSSProperties}></i><i style={{ "--x": "19%", "--y": "72%", "--c": "#ff6b6b", "--z": "1.18" } as CSSProperties}></i><i style={{ "--x": "44%", "--y": "20%", "--c": "#7bdff2", "--z": "0.67" } as CSSProperties}></i><i style={{ "--x": "43%", "--y": "46%", "--c": "#ffd166", "--z": "1.00" } as CSSProperties}></i><i style={{ "--x": "48%", "--y": "36%", "--c": "#f7aef8", "--z": "0.89" } as CSSProperties}></i><i style={{ "--x": "72%", "--y": "15%", "--c": "#b8f2e6", "--z": "0.64" } as CSSProperties}></i><i style={{ "--x": "46%", "--y": "58%", "--c": "#ffe8a3", "--z": "1.05" } as CSSProperties}></i><b></b></div><div className="ap" role="radiogroup" aria-label="Aperture"><label><input type="radio" name="uvq11-lens-ap" value="1.4" /><span>f/1.4</span></label><label><input type="radio" name="uvq11-lens-ap" value="2" /><span>f/2</span></label><label><input type="radio" name="uvq11-lens-ap" value="2.8" checked={true} /><span>f/2.8</span></label><label><input type="radio" name="uvq11-lens-ap" value="4" /><span>f/4</span></label><label><input type="radio" name="uvq11-lens-ap" value="8" /><span>f/8</span></label><label><input type="radio" name="uvq11-lens-ap" value="16" /><span>f/16</span></label></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 “Camera lens with aperture” block (Liquid Glass) from AIMRAN Library to my project.

## What it is
The front element of a 50 mm lens with multicoated reflections; pick an f-number and the iris blades close.

## Chosen settings
- Main color: #8b5cf6
- Secondary color: #2ee59d
- 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 = `
.uvq11-lens {
  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;
  --mx: 0;
  --my: 0;
  background: radial-gradient(50% 60% at 30% 40%,#2a2a30,#0c0c0e 75%);
}

.uvq11-lens .ln {
  position: absolute;
  left: .9em;
  top: 1.5em;
  width: 11em;
  height: 11em;
  border-radius: 50%;
  background: repeating-conic-gradient(#1a1a1d 0 2deg,#303035 2deg 4deg);
  box-shadow: 0 1em 2em -.6em rgba(0,0,0,.9),inset 0 0 0 .08em #555;
}

.uvq11-lens .ln::before {
  content: "";
  position: absolute;
  inset: .6em;
  border-radius: 50%;
  background: radial-gradient(circle,#0d0d10 60%,#1d1d22 61%,#141417 100%);
  box-shadow: 0 0 0 .06em #77777f,inset 0 0 0 .05em #c33;
}

.uvq11-lens .tx {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.uvq11-lens text {
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .8px;
  fill: #c9ccd3;
}

.uvq11-lens .gl {
  position: absolute;
  inset: 1.9em;
  border-radius: 50%;
  overflow: hidden;
  background: radial-gradient(circle,#06070b 0 30%,#0e1219 62%,#1d2433 88%,#07080b 100%);
  box-shadow: inset 0 0 0 .14em #050506,inset 0 0 1.2em rgba(0,0,0,.9);
}

.uvq11-lens .ir {
  position: absolute;
  inset: 9%;
  width: 82%;
  height: 82%;
}

.uvq11-lens .bl {
  fill: #17171b;
}

.uvq11-lens .ed {
  fill: none;
  stroke: rgba(255,255,255,.1);
  stroke-width: .6;
}

.uvq11-lens .gl i {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  transition: transform calc(0.3s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens .ct {
  left: 38%;
  top: 38%;
  width: 24%;
  height: 24%;
  background: radial-gradient(circle,color-mix(in srgb,var(--c1) 70%,transparent),transparent 70%);
  transform: translate(calc(var(--mx) * -.3em),calc(var(--my) * -.3em));
}

.uvq11-lens .r1 {
  inset: 4%;
  background: conic-gradient(from 200deg,transparent 0 30deg,color-mix(in srgb,var(--c1) 85%,transparent) 60deg,transparent 110deg,transparent 220deg,color-mix(in srgb,var(--c2) 70%,transparent) 250deg,transparent 290deg);
  -webkit-mask: radial-gradient(closest-side,transparent 82%,#000 88% 92%,transparent 97%);
  mask: radial-gradient(closest-side,transparent 82%,#000 88% 92%,transparent 97%);
  transform: rotate(calc(var(--mx) * 25deg));
}

.uvq11-lens .r2 {
  left: 56%;
  top: 58%;
  width: 1.2em;
  height: 1.2em;
  background: radial-gradient(circle,color-mix(in srgb,var(--c2) 80%,#fff) 0 15%,color-mix(in srgb,var(--c2) 60%,transparent) 40%,transparent 70%);
  transform: translate(calc(var(--mx) * .7em),calc(var(--my) * .7em));
}

.uvq11-lens .r3 {
  left: 30%;
  top: 62%;
  width: .5em;
  height: .5em;
  background: radial-gradient(circle,#ffcf7a,transparent 70%);
  transform: translate(calc(var(--mx) * .4em),calc(var(--my) * .4em));
}

.uvq11-lens .sb {
  left: 20%;
  top: 16%;
  width: 1.7em;
  height: 1.05em;
  border-radius: .35em;
  background: linear-gradient(160deg,rgba(255,255,255,.95),rgba(255,255,255,.55));
  filter: blur(.4px);
  transform: translate(calc(var(--mx) * -1em),calc(var(--my) * -1em)) skewX(-8deg);
}

.uvq11-lens .pn {
  position: absolute;
  left: 12.6em;
  top: 1.1em;
  width: 6.7em;
}

.uvq11-lens .fm {
  margin: 0;
  font-size: .62em;
  font-weight: 700;
  letter-spacing: .14em;
  color: #9a9aa5;
}

.uvq11-lens .fv {
  display: block;
  margin: .05em 0 .35em;
  font-size: 1.65em;
  font-weight: 250;
  letter-spacing: -.02em;
}

.uvq11-lens .vf {
  position: relative;
  height: 3.6em;
  border-radius: .6em;
  overflow: hidden;
  background: linear-gradient(180deg,#22354a,#0f1722);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.15);
}

.uvq11-lens .vf i {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: calc((.3em + 1.5em * var(--bk)) * var(--z));
  aspect-ratio: 1;
  border-radius: 50%;
  translate: -50% -50%;
  background: radial-gradient(circle,color-mix(in srgb,var(--c) 45%,transparent) 0 55%,var(--c) 66%,transparent 72%);
  filter: blur(calc(var(--bk) * 1.2px));
  opacity: calc(.5 + .45 * var(--bk));
  transition: all calc(0.4s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens .vf b {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 1.3em;
  height: 1.9em;
  margin-left: -.65em;
  border-radius: .65em .65em 0 0;
  background: #0a0d12;
  box-shadow: 0 -1.15em 0 -.25em #0a0d12;
}

.uvq11-lens .vf::after {
  content: "";
  position: absolute;
  inset: .4em;
  background: linear-gradient(#fff,#fff) 0 0/.5em 1px,linear-gradient(#fff,#fff) 0 0/1px .5em,linear-gradient(#fff,#fff) 100% 0/.5em 1px,linear-gradient(#fff,#fff) 100% 0/1px .5em,linear-gradient(#fff,#fff) 0 100%/.5em 1px,linear-gradient(#fff,#fff) 0 100%/1px .5em,linear-gradient(#fff,#fff) 100% 100%/.5em 1px,linear-gradient(#fff,#fff) 100% 100%/1px .5em;
  background-repeat: no-repeat;
  opacity: .7;
}

.uvq11-lens .ap {
  display: grid;
  grid-template-columns: repeat(3,1fr);
  gap: .3em;
  margin-top: .55em;
}

.uvq11-lens label {
  position: relative;
  cursor: pointer;
}

.uvq11-lens input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}

.uvq11-lens label span {
  display: block;
  padding: .45em 0;
  border-radius: .5em;
  text-align: center;
  font-size: .62em;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: #c9ccd3;
  background: rgba(255,255,255,.07);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.12);
  transition: all calc(0.2s / var(--k)) cubic-bezier(.25,.1,.25,1);
}

.uvq11-lens label:hover span {
  background: rgba(255,255,255,.14);
}

.uvq11-lens input:checked+span {
  color: #111;
  background: #f2f2f5;
  box-shadow: 0 0 0 1px #fff,0 .3em .8em -.3em var(--c1);
}

.uvq11-lens input:focus-visible+span {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.uvq11-lens label:active span {
  transform: scale(.94);
}

@media (prefers-reduced-motion:reduce) {
  .uvq11-lens,.uvq11-lens *,.uvq11-lens *::before,.uvq11-lens *::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 rm=matchMedia("(prefers-reduced-motion: reduce)").matches;const ln=el.querySelector(".ln"),bl=el.querySelector(".bl"),ed=el.querySelector(".ed"),fv=el.querySelector(".fv");const R=f=>44*Math.pow(1.4/f,.75);let r=R(2.8),to=r,raf=0;
const path=r=>{let d="M-50 0a50 50 0 1 0 100 0a50 50 0 1 0-100 0Z",e="";const rot=r/44*.9;for(let k=0;k<7;k++){const a=rot+k*Math.PI*2/7,x=r*Math.cos(a),y=r*Math.sin(a),b=a+1.25;d+=(k?"L":"M")+x.toFixed(2)+" "+y.toFixed(2);e+="M"+x.toFixed(2)+" "+y.toFixed(2)+"L"+(50*Math.cos(b)).toFixed(2)+" "+(50*Math.sin(b)).toFixed(2)}return[d+"Z",e]};
const draw=()=>{const p=path(r);bl.setAttribute("d",p[0]);ed.setAttribute("d",p[1])};
const step=()=>{r+=(to-r)*.18;if(Math.abs(to-r)<.05)r=to;draw();raf=r===to?0:requestAnimationFrame(step)};
el.querySelectorAll("input").forEach(i=>i.addEventListener("change",()=>{const f=parseFloat(i.value);to=R(f);fv.textContent="f/"+i.value;el.style.setProperty("--bk",((to-6)/38).toFixed(3));if(rm){r=to;draw()}else if(!raf)raf=requestAnimationFrame(step)}));
ln.addEventListener("pointermove",e=>{const b=ln.getBoundingClientRect();el.style.setProperty("--mx",((e.clientX-b.left)/b.width*2-1).toFixed(3));el.style.setProperty("--my",((e.clientY-b.top)/b.height*2-1).toFixed(3))});
ln.addEventListener("pointerleave",()=>{el.style.setProperty("--mx","0");el.style.setProperty("--my","0")});
return()=>cancelAnimationFrame(raf)
}

export default function LgCameraLens() {
  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": "#8b5cf6",
          "--c2": "#2ee59d",
          "--s": "16px",
          "--k": "1",
          "--fg": "#1d1d1f",
          "--bg": "#f5f5f7",
        } as CSSProperties}
      >
        <div className="uvq11-lens" style={{ "--bk": ".5" } as CSSProperties}><div className="ln" aria-hidden="true"><svg className="tx" viewBox="0 0 200 200"><defs><path id="uvq11-lens-p" d="M100 100m-80 0a80 80 0 1 1 160 0a80 80 0 1 1-160 0" /></defs><text><textPath href="#uvq11-lens-p">AIMRAN · 50 mm · 1:1.4 · Ø58 · AIMRAN · 50 mm · 1:1.4 · Ø58 ·</textPath></text></svg><div className="gl"><svg className="ir" viewBox="-50 -50 100 100"><path className="bl" fillRule="evenodd" d="M-50 0a50 50 0 1 0 100 0a50 50 0 1 0-100 0ZM22.50 13.34L3.60 25.91L-18.02 18.97L-26.07 -2.26L-14.49 -21.79L8.00 -24.91L24.46 -9.28Z" /><path className="ed" d="M22.50 13.34L-10.64 48.86M3.60 25.91L-44.83 22.15M-18.02 18.97L-45.26 -21.24M-26.07 -2.26L-11.62 -48.63M-14.49 -21.79L30.78 -39.40M8.00 -24.91L50.00 -0.50M24.46 -9.28L31.57 38.78" /></svg><i className="ct"></i><i className="r1"></i><i className="r2"></i><i className="r3"></i><i className="sb"></i></div></div><div className="pn"><p className="fm">50 mm</p><output className="fv">f/2.8</output><div className="vf" aria-hidden="true"><i style={{ "--x": "8%", "--y": "26%", "--c": "#ffd166", "--z": "1.08" } as CSSProperties}></i><i style={{ "--x": "19%", "--y": "72%", "--c": "#ff6b6b", "--z": "1.18" } as CSSProperties}></i><i style={{ "--x": "44%", "--y": "20%", "--c": "#7bdff2", "--z": "0.67" } as CSSProperties}></i><i style={{ "--x": "43%", "--y": "46%", "--c": "#ffd166", "--z": "1.00" } as CSSProperties}></i><i style={{ "--x": "48%", "--y": "36%", "--c": "#f7aef8", "--z": "0.89" } as CSSProperties}></i><i style={{ "--x": "72%", "--y": "15%", "--c": "#b8f2e6", "--z": "0.64" } as CSSProperties}></i><i style={{ "--x": "46%", "--y": "58%", "--c": "#ffe8a3", "--z": "1.05" } as CSSProperties}></i><b></b></div><div className="ap" role="radiogroup" aria-label="Aperture"><label><input type="radio" name="uvq11-lens-ap" value="1.4" /><span>f/1.4</span></label><label><input type="radio" name="uvq11-lens-ap" value="2" /><span>f/2</span></label><label><input type="radio" name="uvq11-lens-ap" value="2.8" checked={true} /><span>f/2.8</span></label><label><input type="radio" name="uvq11-lens-ap" value="4" /><span>f/4</span></label><label><input type="radio" name="uvq11-lens-ap" value="8" /><span>f/8</span></label><label><input type="radio" name="uvq11-lens-ap" value="16" /><span>f/16</span></label></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.
````
