{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "liquid-glass-pill",
  "type": "registry:block",
  "title": "Liquid Glass Pill",
  "description": "A pill button made of refracting glass: the backdrop bends at its rim through a per-channel displacement map, so edges fringe like a lens. A specular highlight slides round the edge toward the pointer, and the pill stretches toward the cursor and squashes when pressed.",
  "docs": "Liquid Glass Pill was added to public/vividsites/liquid-glass-pill.html. Paste its markup, <style> and <script> where you want it on the page (or open the file to see it alone). Customise it with the CSS variables at the top. Docs and live demo: https://vividsites.app/c/liquid-glass-pill",
  "categories": [
    "ui",
    "buttons-inputs"
  ],
  "files": [
    {
      "path": "components/vividsites/liquid-glass-pill.html",
      "type": "registry:file",
      "target": "public/vividsites/liquid-glass-pill.html",
      "content": "<!--\nvs-liquid-glass-pill: a pill of real refracting glass. The backdrop bends at the rim\n  (an SVG displacement map, split per colour channel so edges fringe like a lens), a\n  specular highlight slides round the edge toward the pointer, and the pill stretches\n  toward the cursor and squashes when pressed.\n\n  Markup:  <button class=\"vs-liquid-glass-pill\">Label</button>   (or an <a>)\n  Add .vs-liquid-glass-pill--round for a circular icon button.\n\n  Attributes (optional, live — change them any time):\n    data-refraction  rim displacement in px            (default 38)\n    data-bevel       width of the curved rim in px     (default: 45% of the height)\n    data-dispersion  colour fringe, 0 = none           (default .08)\n\n    data-theme       \"auto\" (default), \"dark\" or \"light\". Glass is made to sit over imagery or\n                     colour (a photo, a video, a gradient): there it bends what is behind it and\n                     wears light ink. On a plain light page there is nothing to bend, so \"auto\"\n                     looks at what is behind the pill and, if it is a flat light colour, switches\n                     to the light look: dark ink, a frosted body, a hairline rim and a soft floor\n                     shadow, so it stays readable. Force either look with \"dark\" / \"light\".\n\n  CSS variables below set tint, ink, rim, frost, font and size (font-size scales the pill).\n  --vs-liquid-glass-pill-font defaults to a system stack; set it to inherit to use your page's font.\n  After changing --vs-liquid-glass-pill-blur / -saturate, or moving the pill onto a different\n  backdrop, call vsLiquidGlassPill.refresh(el).\n  Refraction runs in Chromium; Safari and Firefox get a frosted blur with the same rim and\n  highlight. Reduced motion: no stretch.\n-->\n<button class=\"vs-liquid-glass-pill\" type=\"button\">\n  <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M12 5v14M5 12h14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"/></svg>\n  Add to library\n</button>\n\n<style>\n.vs-liquid-glass-pill {\n  --vs-liquid-glass-pill-tint: rgba(255,255,255,.10);   /* glass body colour */\n  --vs-liquid-glass-pill-ink: #fffaf2;                  /* label colour */\n  --vs-liquid-glass-pill-rim: rgba(255,255,255,.34);    /* resting edge */\n  --vs-liquid-glass-pill-spec: #ffffff;                 /* pointer highlight */\n  --vs-liquid-glass-pill-blur: 1.2px;                   /* frost inside the lens */\n  --vs-liquid-glass-pill-saturate: 1.5;\n  --vs-liquid-glass-pill-font-size: 1rem;              /* scales height and padding too */\n  --vs-liquid-glass-pill-height: 3.75em;\n  --vs-liquid-glass-pill-pad: 0 1.9em;\n  --vs-liquid-glass-pill-focus: #fffaf2;\n  --vs-liquid-glass-pill-font: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n\n  position: relative; display: inline-flex; align-items: center; justify-content: center;\n  height: var(--vs-liquid-glass-pill-height); padding: var(--vs-liquid-glass-pill-pad);\n  border: 0; border-radius: 999px; background: none; color: var(--vs-liquid-glass-pill-ink);\n  font: inherit; font-family: var(--vs-liquid-glass-pill-font); font-size: var(--vs-liquid-glass-pill-font-size); box-sizing: border-box; font-weight: 500; letter-spacing: .005em;\n  text-decoration: none; white-space: nowrap; cursor: pointer; isolation: isolate; outline: none;\n  -webkit-tap-highlight-color: transparent; touch-action: manipulation; will-change: transform;\n  box-shadow: 0 14px 34px -14px rgba(0,0,0,.45), 0 2px 6px -2px rgba(0,0,0,.18);\n}\n.vs-liquid-glass-pill--round { width: var(--vs-liquid-glass-pill-height); padding: 0; }\n.vs-liquid-glass-pill-glass, .vs-liquid-glass-pill-rim, .vs-liquid-glass-pill-sheen {\n  position: absolute; inset: 0; border-radius: inherit; pointer-events: none;\n}\n.vs-liquid-glass-pill-glass {\n  z-index: -2; background: var(--vs-liquid-glass-pill-tint);\n  -webkit-backdrop-filter: blur(14px) saturate(1.6) brightness(1.06);\n  backdrop-filter: blur(14px) saturate(1.6) brightness(1.06);\n}\n/* body light: a lit top lip, a darker belly, a faint caustic along the bottom */\n.vs-liquid-glass-pill-sheen {\n  z-index: -1;\n  box-shadow:\n    inset 0 1px 0 rgba(255,255,255,.55),\n    inset 0 -1px 0 rgba(255,255,255,.18),\n    inset 0 10px 18px -12px rgba(255,255,255,.55),\n    inset 0 -12px 20px -14px rgba(255,255,255,.35);\n  background: linear-gradient(180deg, rgba(255,255,255,.10), rgba(255,255,255,0) 45%, rgba(0,0,0,.05));\n  overflow: hidden;\n}\n/* the same highlight, soft and inside the body, so the light reads as passing through */\n.vs-liquid-glass-pill-glint {\n  position: absolute; left: 0; top: 0; width: 9rem; height: 9rem; margin: -4.5rem 0 0 -4.5rem; border-radius: 50%;\n  background: radial-gradient(closest-side, rgba(255,255,255,.42), rgba(255,255,255,.12) 50%, transparent);\n  mix-blend-mode: screen; opacity: 0; will-change: transform, opacity;\n}\n.vs-liquid-glass-pill-rim {\n  z-index: 1; padding: 1.5px; overflow: hidden;\n  background: linear-gradient(160deg, var(--vs-liquid-glass-pill-rim), rgba(255,255,255,.06) 45%, rgba(255,255,255,.2));\n  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n  -webkit-mask-composite: xor; mask-composite: exclude;\n}\n.vs-liquid-glass-pill-spec {\n  position: absolute; left: 0; top: 0; width: 7rem; height: 7rem; margin: -3.5rem 0 0 -3.5rem; border-radius: 50%;\n  background: radial-gradient(closest-side, var(--vs-liquid-glass-pill-spec), rgba(255,255,255,.35) 45%, transparent);\n  opacity: 0; will-change: transform, opacity;\n}\n.vs-liquid-glass-pill-label {\n  position: relative; z-index: 2; display: inline-flex; align-items: center; gap: .6em;\n  text-shadow: 0 1px 2px rgba(0,0,0,.14), 0 2px 14px rgba(0,0,0,.22); will-change: transform;\n}\n.vs-liquid-glass-pill:focus-visible { box-shadow: 0 0 0 2px rgba(0,0,0,.15), 0 0 0 4px var(--vs-liquid-glass-pill-focus), 0 14px 34px -14px rgba(0,0,0,.45); }\n.vs-liquid-glass-pill *, .vs-liquid-glass-pill *::before { box-sizing: border-box; }\n.vs-liquid-glass-pill-label svg { width: 1.15em; height: 1.15em; flex: none; }\n.vs-liquid-glass-pill:disabled, .vs-liquid-glass-pill[aria-disabled=\"true\"] { cursor: not-allowed; opacity: .5; }\n/* light look: data-theme=\"light\", or picked by \"auto\" when the pill sits on a flat light colour */\n.vs-liquid-glass-pill[data-theme=\"light\"], .vs-liquid-glass-pill.is-on-light {\n  --vs-liquid-glass-pill-tint: rgba(250,250,252,.55);\n  --vs-liquid-glass-pill-ink: #17181d;\n  --vs-liquid-glass-pill-rim: rgba(255,255,255,.95);\n  --vs-liquid-glass-pill-focus: #2d5bff;\n  box-shadow: 0 0 0 .5px rgba(20,22,34,.16), 0 1px 1.5px rgba(20,22,34,.08), 0 6px 14px -6px rgba(20,22,34,.14), 0 18px 36px -18px rgba(20,22,34,.3);\n}\n.vs-liquid-glass-pill[data-theme=\"light\"] .vs-liquid-glass-pill-sheen, .vs-liquid-glass-pill.is-on-light .vs-liquid-glass-pill-sheen {\n  background: linear-gradient(180deg, rgba(255,255,255,.75), rgba(255,255,255,.1) 48%, rgba(24,28,44,.07));\n  box-shadow: inset 0 1px 0 #fff, inset 0 -1px 0 rgba(24,28,44,.06), inset 0 10px 16px -12px #fff, inset 0 -12px 18px -14px rgba(24,28,44,.14);\n}\n.vs-liquid-glass-pill[data-theme=\"light\"] .vs-liquid-glass-pill-rim, .vs-liquid-glass-pill.is-on-light .vs-liquid-glass-pill-rim {\n  background: linear-gradient(160deg, var(--vs-liquid-glass-pill-rim), rgba(24,28,44,.1) 45%, rgba(24,28,44,.22));\n}\n.vs-liquid-glass-pill[data-theme=\"light\"] .vs-liquid-glass-pill-label, .vs-liquid-glass-pill.is-on-light .vs-liquid-glass-pill-label { text-shadow: 0 1px 0 rgba(255,255,255,.7); }\n.vs-liquid-glass-pill[data-theme=\"light\"]:focus-visible, .vs-liquid-glass-pill.is-on-light:focus-visible { box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--vs-liquid-glass-pill-focus), 0 14px 30px -14px rgba(20,22,34,.3); }\n@media (forced-colors: active) { .vs-liquid-glass-pill { border: 1px solid ButtonText; } .vs-liquid-glass-pill:focus-visible { outline: 2px solid Highlight; } }\n</style>\n\n<script>\n(() => {\n  if (window.vsLiquidGlassPill) return;\n  const reduce = matchMedia('(prefers-reduced-motion: reduce)');\n  const brands = (navigator.userAgentData && navigator.userAgentData.brands) || [];\n  const refracts = brands.some(b => /Chromium|Chrome|Edge/.test(b.brand)); // backdrop-filter:url() is Chromium-only\n  const NS = 'http://www.w3.org/2000/svg';\n  let defs = null, uid = 0;\n  const getDefs = () => {\n    if (defs) return defs;\n    const svg = document.createElementNS(NS, 'svg');\n    svg.setAttribute('aria-hidden', 'true'); svg.setAttribute('width', '0'); svg.setAttribute('height', '0');\n    svg.style.cssText = 'position:absolute;width:0;height:0;overflow:hidden;pointer-events:none';\n    defs = document.createElementNS(NS, 'defs'); svg.appendChild(defs); document.body.appendChild(svg); return defs;\n  };\n\n  // displacement map: every pixel in the rim samples the backdrop from further in,\n  // with a circular-arc profile so the bend is steepest right at the edge (a lens lip)\n  function lensMap(W, H, B) {\n    const c = document.createElement('canvas'); c.width = W; c.height = H;\n    const g = c.getContext('2d'), img = g.createImageData(W, H), d = img.data;\n    const r = H / 2, hw = W / 2, hh = H / 2;\n    for (let y = 0; y < H; y++) for (let x = 0; x < W; x++) {\n      const px = x + .5 - hw, py = y + .5 - hh;\n      const qx = Math.abs(px) - (hw - r), qy = Math.abs(py) - (hh - r);\n      const ox = Math.max(qx, 0), oy = Math.max(qy, 0);\n      const sd = Math.hypot(ox, oy) + Math.min(Math.max(qx, qy), 0) - r;\n      let nx = 0, ny = 0, m = 0;\n      if (sd < 0 && -sd < B) {\n        if (qx > 0 && qy > 0) { const l = Math.hypot(qx, qy) || 1; nx = qx / l * Math.sign(px); ny = qy / l * Math.sign(py); }\n        else if (qx > qy) nx = Math.sign(px); else ny = Math.sign(py);\n        const t = 1 + sd / B;                    // 1 at the edge → 0 at the inner end of the rim\n        m = 1 - Math.sqrt(Math.max(0, 1 - t * t));\n      }\n      const i = (y * W + x) * 4;\n      d[i] = 128 - nx * m * 127; d[i + 1] = 128 - ny * m * 127; d[i + 2] = 128; d[i + 3] = 255;\n    }\n    g.putImageData(img, 0, 0); return c.toDataURL();\n  }\n  function buildFilter(id, W, H, url, S, disp, blur, sat) {\n    const f = document.createElementNS(NS, 'filter');\n    f.setAttribute('id', id);\n    Object.entries({ x: 0, y: 0, width: W, height: H, filterUnits: 'userSpaceOnUse', primitiveUnits: 'userSpaceOnUse', 'color-interpolation-filters': 'sRGB' }).forEach(([k, v]) => f.setAttribute(k, v));\n    const ch = [['1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0', 1 - disp], ['0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 0', 1], ['0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0', 1 + disp]];\n    f.innerHTML = `<feGaussianBlur in=\"SourceGraphic\" stdDeviation=\"${blur}\" result=\"b\"/>\n      <feImage href=\"${url}\" x=\"0\" y=\"0\" width=\"${W}\" height=\"${H}\" preserveAspectRatio=\"none\" result=\"m\"/>` +\n      ch.map(([mat, k], i) => `<feDisplacementMap in=\"b\" in2=\"m\" scale=\"${(S * 2 * k).toFixed(2)}\" xChannelSelector=\"R\" yChannelSelector=\"G\" result=\"d${i}\"/>\n      <feColorMatrix in=\"d${i}\" type=\"matrix\" values=\"${mat}\" result=\"c${i}\"/>`).join('') +\n      `<feBlend in=\"c0\" in2=\"c1\" mode=\"screen\" result=\"rg\"/><feBlend in=\"rg\" in2=\"c2\" mode=\"screen\" result=\"rgb\"/>\n      <feColorMatrix in=\"rgb\" type=\"saturate\" values=\"${sat}\"/>`;\n    return f;\n  }\n\n  // data-theme=\"auto\": is the pill sitting on a flat light colour (nothing to refract)?\n  const rgba = c => { const m = String(c).match(/[\\d.]+/g); if (!m || m.length < 3) return null; const [r, g, b, a = 1] = m.map(Number); return { a, l: (.2126 * r + .7152 * g + .0722 * b) / 255 }; };\n  function onLight(el) {\n    const r = el.getBoundingClientRect(); let stack = [];\n    if (r.width && r.bottom > 0 && r.right > 0 && r.top < innerHeight && r.left < innerWidth) {\n      const x = Math.min(innerWidth - 1, Math.max(0, r.left + r.width / 2)), y = Math.min(innerHeight - 1, Math.max(0, r.top + r.height / 2));\n      stack = document.elementsFromPoint(x, y).filter(n => !el.contains(n));\n    } else for (let p = el.parentElement; p; p = p.parentElement) stack.push(p);\n    for (const n of stack) {\n      if (/^(img|video|canvas|picture|iframe|svg)$/i.test(n.tagName)) return false;   // imagery behind: keep the glass look\n      const cs = getComputedStyle(n);\n      if (cs.backgroundImage && cs.backgroundImage !== 'none') return false;\n      const c = rgba(cs.backgroundColor); if (c && c.a > .5) return c.l > .62;\n    }\n    return !/dark/.test(getComputedStyle(document.documentElement).colorScheme || '');   // bare canvas: white\n  }\n  const theme = el => {\n    const t = el.dataset.theme;\n    el.classList.toggle('is-on-light', !(t === 'dark' || t === 'light') && onLight(el));\n  };\n\n  const spring = (x, k, c) => ({ x, v: 0, g: x, k, c });\n  const live = new Set(); let raf = 0, last = 0;\n  const tick = now => {\n    const dt = Math.min(.064, (now - last) / 1000 || .016); last = now;\n    for (const p of live) if (!p.update(dt)) live.delete(p);\n    raf = live.size ? requestAnimationFrame(tick) : 0;\n  };\n  const wake = p => { live.add(p); if (!raf) { last = performance.now(); raf = requestAnimationFrame(tick); } };\n\n  function mount(el) {\n    if (el.__vsLgp) return; el.__vsLgp = true;\n    const label = document.createElement('span'); label.className = 'vs-liquid-glass-pill-label';\n    while (el.firstChild) label.appendChild(el.firstChild);\n    const glass = document.createElement('span'); glass.className = 'vs-liquid-glass-pill-glass';\n    const sheen = document.createElement('span'); sheen.className = 'vs-liquid-glass-pill-sheen';\n    const rim = document.createElement('span'); rim.className = 'vs-liquid-glass-pill-rim';\n    const spec = document.createElement('span'); spec.className = 'vs-liquid-glass-pill-spec'; rim.appendChild(spec);\n    const glint = document.createElement('span'); glint.className = 'vs-liquid-glass-pill-glint'; sheen.appendChild(glint);\n    [glass, sheen, rim].forEach(n => n.setAttribute('aria-hidden', 'true'));\n    el.append(glass, sheen, label, rim);\n\n    const id = 'vs-lgp-' + (++uid); let filter = null, W = 0, H = 0, SIG = '';\n    const rebuild = () => {\n      const w = Math.round(el.offsetWidth), h = Math.round(el.offsetHeight);\n      const cs = getComputedStyle(el), sig = [el.dataset.refraction, el.dataset.bevel, el.dataset.dispersion, cs.getPropertyValue('--vs-liquid-glass-pill-blur'), cs.getPropertyValue('--vs-liquid-glass-pill-saturate')].join('|');\n      if (!refracts || !w || !h || (w === W && h === H && sig === SIG)) return; W = w; H = h; SIG = sig;\n      const S = +(el.dataset.refraction || 38), B = +(el.dataset.bevel || H * .45), disp = +(el.dataset.dispersion || .08);\n      const blur = parseFloat(getComputedStyle(el).getPropertyValue('--vs-liquid-glass-pill-blur')) || 1.2;\n      const sat = parseFloat(getComputedStyle(el).getPropertyValue('--vs-liquid-glass-pill-saturate')) || 1.5;\n      const nf = buildFilter(id, W, H, lensMap(W, H, B), S, disp, blur, sat);\n      filter ? filter.replaceWith(nf) : getDefs().appendChild(nf); filter = nf;\n      glass.style.backdropFilter = glass.style.webkitBackdropFilter = `url(#${id})`;\n    };\n    new ResizeObserver(rebuild).observe(el); el.__vsLgpRebuild = () => { theme(el); rebuild(); };\n    new MutationObserver(() => { theme(el); rebuild(); }).observe(el, { attributes: true, attributeFilter: ['data-refraction', 'data-bevel', 'data-dispersion', 'data-theme'] });\n    theme(el); requestAnimationFrame(() => theme(el));\n\n    const S = { tx: spring(0, 220, 18), ty: spring(0, 220, 18), st: spring(0, 220, 16), px: spring(1, 380, 14), py: spring(1, 380, 14),\n      sx: spring(0, 260, 26), sy: spring(0, 260, 26), so: spring(.22, 160, 24) };\n    let ang = 0, visible = true, hover = false, down = false;\n    let themed = false;\n    new IntersectionObserver(e => { visible = e[0].isIntersecting; if (visible && !themed) { themed = true; theme(el); } }).observe(el);\n\n    function aim(cx, cy) {\n      const r = el.getBoundingClientRect(); if (!r.width) return;\n      const w = el.offsetWidth, h = el.offsetHeight, rad = h / 2;\n      const lx = (cx - r.left) * w / r.width, ly = (cy - r.top) * h / r.height;\n      // nearest point on the pill's outline\n      const ax = Math.min(Math.max(lx, rad), w - rad), ay = h / 2;\n      let dx = lx - ax, dy = ly - ay; const dl = Math.hypot(dx, dy);\n      if (dl < .001) { dx = 0; dy = -1; } else { dx /= dl; dy /= dl; }\n      S.sx.g = ax + dx * rad; S.sy.g = ay + dy * rad;\n      const out = Math.max(0, dl - rad);\n      const near = Math.max(0, 1 - out / 260);\n      S.so.g = .22 + .78 * near * near;\n      if (hover && !reduce.matches) {\n        const mx = lx - w / 2, my = ly - h / 2;\n        S.tx.g = Math.tanh(mx / w) * 5; S.ty.g = Math.tanh(my / h) * 3;\n        ang = Math.atan2(my, mx); S.st.g = .035 * Math.min(1, Math.hypot(mx / (w / 2), my / (h / 2)));\n      }\n      wake(api);\n    }\n    const settle = () => { S.tx.g = S.ty.g = S.st.g = 0; S.so.g = .22; wake(api); };\n\n    const api = { update(dt) {\n      let moving = false; const n = Math.max(1, Math.round(dt * 120)), h = dt / n;\n      for (const k in S) { const s = S[k];\n        if (reduce.matches && k !== 'sx' && k !== 'sy' && k !== 'so') { s.x = s.g; s.v = 0; continue; }\n        if (Math.abs(s.v) < .002 && Math.abs(s.x - s.g) < .002) { s.x = s.g; s.v = 0; continue; }\n        moving = true;\n        for (let i = 0; i < n; i++) { s.v += (-s.k * (s.x - s.g) - s.c * s.v) * h; s.x += s.v * h; }\n      }\n      const a = ang * 180 / Math.PI;\n      el.style.transform = `translate3d(${S.tx.x.toFixed(2)}px,${S.ty.x.toFixed(2)}px,0) rotate(${a.toFixed(2)}deg) scaleX(${(1 + S.st.x).toFixed(4)}) rotate(${(-a).toFixed(2)}deg) scale(${S.px.x.toFixed(4)},${S.py.x.toFixed(4)})`;\n      spec.style.transform = `translate3d(${S.sx.x.toFixed(1)}px,${S.sy.x.toFixed(1)}px,0)`;\n      const o = Math.max(0, Math.min(1, S.so.x));\n      spec.style.opacity = o.toFixed(3);\n      glint.style.transform = spec.style.transform; glint.style.opacity = (o * o * .9).toFixed(3);\n      return moving;\n    }};\n    // rest highlight: upper-left shoulder\n    requestAnimationFrame(() => { S.sx.x = S.sx.g = el.offsetHeight * .55; S.sy.x = S.sy.g = 1; wake(api); });\n\n    addEventListener('pointermove', e => { if (visible && e.pointerType === 'mouse') aim(e.clientX, e.clientY); }, { passive: true });\n    el.addEventListener('pointerenter', e => { if (e.pointerType === 'mouse') { hover = true; aim(e.clientX, e.clientY); } });\n    el.addEventListener('pointerleave', e => { hover = false; if (!down) settle(); });\n    const press = () => { down = true; S.px.g = 1.06; S.py.g = .9; wake(api); };\n    const lift = () => { if (!down) return; down = false; S.px.g = 1; S.py.g = 1; S.px.v -= 1.4; S.py.v += 2.2; wake(api); };\n    el.addEventListener('pointerdown', e => { press(); if (e.pointerType !== 'mouse') { aim(e.clientX, e.clientY); S.so.g = 1; } });\n    el.addEventListener('pointerup', e => { lift(); if (e.pointerType !== 'mouse') settle(); });\n    el.addEventListener('pointercancel', () => { lift(); settle(); });\n    el.addEventListener('keydown', e => { if ((e.key === ' ' || e.key === 'Enter') && !e.repeat) press(); });\n    el.addEventListener('keyup', lift); el.addEventListener('blur', lift);\n  }\n  const init = () => document.querySelectorAll('.vs-liquid-glass-pill').forEach(mount);\n  document.readyState === 'loading' ? document.addEventListener('DOMContentLoaded', init) : init();\n  window.vsLiquidGlassPill = { mount, refresh: el => el.__vsLgpRebuild && el.__vsLgpRebuild() };\n})();\n</script>\n"
    }
  ],
  "meta": {
    "category": "ui",
    "group": "Buttons & inputs",
    "tags": [
      "button",
      "glass",
      "liquid glass",
      "refraction",
      "backdrop-filter",
      "svg filter",
      "specular"
    ],
    "tech": [
      "CSS",
      "JS",
      "SVG"
    ],
    "deps": [],
    "source": null,
    "free": true,
    "accent": "#1d2cc9",
    "added": "2026-09-30",
    "demo": "https://vividsites.app/api/component/liquid-glass-pill/index.html",
    "thumbnail": "https://vividsites.app/api/component/liquid-glass-pill/thumb.jpg",
    "video": "https://vividsites.app/api/component/liquid-glass-pill/thumb.mp4",
    "page": "https://vividsites.app/c/liquid-glass-pill"
  }
}