Meez.
Browse Prompts Backgrounds Pricing
Contact Sign in ·

Prompts · Mobile App

Mobile App build-prompts

7 prompts · paste into Lovable, Bolt, v0, Cursor or Claude · preview free · updated September 2026

Browse 7 mobile app build-prompts — each a complete, paste-ready spec (fonts, layout, color, motion and real asset URLs) for Lovable, Bolt, v0, Cursor and Claude. Preview every mobile app prompt free; unlock the full prompt with Unlimited. It’s the biggest mobile app prompt collection anywhere — and the whole library is $49/mo, about 3¢ per asset.

Coffee Rewards — Mobile App website build-promptCoffee Rewards Cross-Border — Mobile App website build-promptCross-Border Travel Journal — Mobile App website build-promptTravel Journal Wellness Companion — Mobile App website build-promptWellness Companion Forecast Center — Mobile App website build-promptForecast Center F1 Racing Hub — Mobile App website build-promptF1 Racing Hub Church Community — Mobile App website build-promptChurch Community

Try a free mobile app prompt

Coffee RewardsMobile App · free prompt · copy-paste

Prompt page

Build a mobile coffee profile screen inside a phone mockup frame. Use vanilla HTML, CSS, and JS with Vite as the bundler. The design is dark/warm-toned, inspired by iOS profile screens with glassmorphic UI elements.

**Phone mockup:**
- 390x844px at zoom 0.78, black background, 44px border-radius, overflow hidden, strong drop shadow
- Internal `.screen` div with dark background (`#180a06`), vertical scroll, hidden scrollbar

**Hero section (top):**
- Full-width, 430px tall
- Contains a looping muted autoplay video: `https://meez.design/web/media/bg-videos/278_kv7e7ksvfd.mp4`
- Video covers the area with `object-fit: cover; object-position: center top`
- Bottom gradient overlay fading from transparent at 52% to the background color at 100%
- Top bar with two glass circle buttons (edit icon on left, X close SVG on right), positioned absolute top 18px

**Identity section (overlaps hero by -112px margin-top):**
- Centered name "Dasha" (28px, weight 500)
- Left and right laurel images flanking the name (local assets `/assets/images/laurel-left.png` and `laurel-right.png`), 73px tall, 0.6 opacity, positioned via `right: calc(50% + 66px)` / `left: calc(50% + 66px)`
- Subtitle "Plum Parfait Latte" below (15px, muted color `rgba(235, 220, 205, 0.55)`)

**Achievements pill (centered, 30px below identity):**
- Glass pill button, 54px tall, 225px wide, 27px border-radius
- Trophy icon (local `/assets/images/icon-trophy.png`, 18px) + text "12 achievements" (18px, weight 500)

**Stats grid (3 columns, 12px gap, 26px top padding, 16px horizontal padding):**
- Each card: semi-transparent background `rgba(255,255,255,0.06)`, 24px border-radius
- Card 1: coffee image `https://meez.design/web/bg-previews/239_bp8i93exbq.jpg` (84x84), number "154", label "drinks consumed"
- Card 2: sandwich image `https://meez.design/web/bg-previews/240_bwuyzzssak.jpg` (84x84), number "36", label "sandwiches eaten"
- Card 3: cafe image `https://meez.design/web/bg-previews/241_bwxlb9yeti.jpg` (84x84), number "12", label "cafes visited"
- Numbers: 25px, weight 500. Labels: 13px, color `#BAAA9A8C`

**Favorite card (12px below stats, 16px horizontal margin):**
- Same card background, 24px radius, 110px height, flex row with 16px gap
- Latte image `https://meez.design/web/bg-previews/242_byzm6mnvgu.jpg` (108x108, object-fit contain)
- Text column: "Favorite" (13px, weight 500), "Latte" (19px, weight 500), "Ordered 73 times" (13px, color `#BAAA9A8C`)
- Shuffle button on right (glass circle, local icon `/assets/images/icon-shuffle.png` 19px)

**Partial next card:** Same card style but only 34px tall with flat bottom corners (teaser for scroll)

**Glass button system:**
- Shared `.glass` class: no border, `rgba(255,255,255,0.03)` background, subtle inset box-shadows for edge highlights, `backdrop-filter: blur(2px) saturate(1.3)`, scale-down on `:active`
- `.glass.circle`: 58x44px, 22px radius
- `.glass.pill`: 54px tall, pill-shaped

**Fonts:**
- Primary: "Neue Haas Unica" (loaded from local `/assets/fonts/neue-haas-unica/stylesheet.css`)
- Also load "Helvetica Now Display" from `/assets/fonts/helvetica-now/stylesheet.css`
- Fallbacks: -apple-system, SF Pro Display, Inter, Segoe UI, Roboto

**Page background (behind phone):**
- Multiple warm-toned radial gradients over `#070402`:
  - `radial-gradient(ellipse 65% 55% at 15% 52%, rgba(168, 78, 10, 0.22))`
  - `radial-gradient(ellipse 52% 48% at 83% 26%, rgba(122, 52, 8, 0.17))`
  - `radial-gradient(ellipse 44% 52% at 56% 92%, rgba(98, 36, 5, 0.14))`
  - `radial-gradient(ellipse 30% 30% at 72% 75%, rgba(60, 20, 5, 0.10))`

**Entry animations (respects prefers-reduced-motion):**
- Hero: `heroReveal` - opacity 0 + scale(1.01) to normal, 1.9s, cubic-bezier(0.16, 1, 0.3, 1)
- Top bar buttons: `dropIn` from translateY(-10px), 0.7s, staggered 0.35s/0.42s
- Laurels: `fadeIn` 0.8s, delay 0.5s
- Name: `fadeRise` from translateY(16px), 0.7s, delay 0.5s
- Subtitle: same, delay 0.58s
- Achievements: same, delay 0.66s
- Stat cards: staggered at 0.74s, 0.80s, 0.86s
- Favorite card: delay 0.94s
- Next card: fadeIn delay 1.02s
- All use cubic-bezier(0.16, 1, 0.3, 1) except simple fadeIn which uses ease-out

**JavaScript (liquid glass effect):**
- Generates a displacement map canvas for each `[data-liquid]` element based on its dimensions and border-radius
- Uses SDF (signed distance field) of a rounded rectangle to compute refraction vectors
- Creates SVG `<feDisplacementMap>` filters and applies them as `backdrop-filter: url(#id) blur(0.3px) saturate(1.3)`
- Falls back to standard blur if SVG filter not supported

**CSS variables:**
```
--bg: #180a06
--card: rgba(255, 255, 255, 0.06)
--text: #ede4d8
--muted: rgba(235, 220, 205, 0.55)
--radius-card: 24px
```

**Responsive:** At 440px viewport, phone scales to zoom 0.6. Body flex-wraps at 900px.

---

## Source Code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Coffee Profile</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
  <style>
* { margin: 0; padding: 0; box-sizing: border-box; }

:root {
  --bg: #180a06;
  --card: rgba(255, 255, 255, 0.06);
  --text: #ede4d8;
  --muted: rgba(235, 220, 205, 0.55);
  --radius-card: 24px;
}

html, body { height: 100%; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "SF Pro Text", "Inter", "Segoe UI", Roboto, sans-serif;
  line-height: 1.2;
  background:
    radial-gradient(ellipse 65% 55% at 15% 52%, rgba(168, 78, 10, 0.22) 0%, transparent 65%),
    radial-gradient(ellipse 52% 48% at 83% 26%, rgba(122, 52, 8, 0.17) 0%, transparent 60%),
    radial-gradient(ellipse 44% 52% at 56% 92%, rgba(98, 36, 5, 0.14) 0%, transparent 58%),
    radial-gradient(ellipse 30% 30% at 72% 75%, rgba(60, 20, 5, 0.10) 0%, transparent 50%),
    #070402;
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  align-items: center;
  padding: 36px 24px;
  color: var(--text);
}

.phone-mockup {
  position: relative;
  flex-shrink: 0;
  zoom: .78;
  width: 390px;
  height: 844px;
  background: #000;
  border-radius: 44px;
  overflow: hidden;
  box-shadow: 0 30px 80px rgba(0, 0, 0, .6);
}

.screen {
  position: relative;
  width: 100%;
  height: 100%;
  background: var(--bg);
  overflow-y: auto;
  overflow-x: hidden;
  scrollbar-width: none;
}
.screen::-webkit-scrollbar { display: none; }

@media (max-width: 900px) {
  body { flex-wrap: wrap; }
}
@media (max-width: 440px) {
  .phone-mockup { zoom: .6; }
}

.hero {
  position: relative;
  width: 100%;
  height: 430px;
}
.hero img,
.hero video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom,
    rgba(24, 10, 6, 0) 52%,
    rgba(24, 10, 6, 0.55) 78%,
    var(--bg) 100%);
  pointer-events: none;
}

.glass {
  border: none;
  cursor: pointer;
  font-family: inherit;
  color: var(--text);
  background: rgba(255, 255, 255, 0.03);
  backdrop-filter: blur(2px) saturate(1.3);
  -webkit-backdrop-filter: blur(2px) saturate(1.3);
  box-shadow:
    0 8px 22px rgba(0, 0, 0, 0.22),
    inset 0 0 0 0.6px rgba(255, 255, 255, 0.02),
    inset 1.5px 2px 0 -1px rgba(255, 255, 255, 0.3),
    inset -1.5px -2px 0 -1px rgba(255, 255, 255, 0.3),
    inset 0 0 12px 3px rgba(255, 255, 255, 0.0);
  transition: transform 0.16s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.glass:active { transform: scale(0.92); }
.glass:focus-visible { outline: 2px solid rgba(255,255,255,0.7); outline-offset: 2px; }

.glass.circle {
  width: 58px;
  height: 44px;
  border-radius: 22px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.glass.circle svg { width: 22px; height: 22px; }
.glass.circle img {
  width: 19px;
  height: 19px;
  object-fit: contain;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

.top-bar {
  position: absolute;
  top: 18px;
  left: 18px;
  right: 18px;
  display: flex;
  justify-content: space-between;
  z-index: 3;
}
.top-bar .glass.circle {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: rgba(255, 255, 255, 0.05);
}

.identity {
  position: relative;
  margin-top: -112px;
  z-index: 2;
  text-align: center;
  padding: 0 24px;
}
.name-row {
  display: flex;
  align-items: center;
  justify-content: center;
}
.laurel {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  height: 73px;
  width: auto;
  opacity: 0.6;
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.95) 45%, rgba(0,0,0,0.95) 100%);
  mask-image: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.95) 45%, rgba(0,0,0,0.95) 100%);
}
.laurel.left  { right: calc(50% + 66px); }
.laurel.right { left:  calc(50% + 66px); }
.name {
  font-size: 28px;
  font-weight: 500;
  letter-spacing: 0.01em;
}
.subtitle {
  margin-top: 6px;
  font-size: 15px;
  color: var(--muted);
  font-weight: 400;
}

.achievements-wrap {
  display: flex;
  justify-content: center;
  margin-top: 30px;
  padding: 0 24px;
}
.glass.pill {
  height: 54px;
  padding: 0 26px;
  border-radius: 27px;
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 18px;
  font-weight: 500;
  letter-spacing: 0.01em;
  width: 225px;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: rgba(255, 255, 255, 0.05);
}
.glass.pill svg {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.stats {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12px;
  padding: 26px 16px 0;
}
.stat-card {
  background: var(--card);
  border-radius: var(--radius-card);
  padding: 14px 8px 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
}
.stat-card img {
  width: 84px;
  height: 84px;
  object-fit: contain;
  margin-bottom: 8px;
  user-select: none;
  -webkit-user-drag: none;
}
.stat-num {
  font-size: 25px;
  font-weight: 500;
  line-height: 1;
}
.stat-label {
  margin-top: 7px;
  font-size: 13px;
  line-height: 1.1;
  font-weight: 400;
  color: #BAAA9A8C;
}

.favorite {
  margin: 12px 16px 0;
  background: var(--card);
  border-radius: var(--radius-card);
  padding: 16px;
  display: flex;
  align-items: center;
  gap: 16px;
  height: 110px;
}
.favorite > img {
  width: 108px;
  height: 108px;
  object-fit: contain;
  flex-shrink: 0;
  margin-left: -4px;
  margin-right: 2px;
  user-select: none;
  -webkit-user-drag: none;
}
.fav-text { flex: 1; min-width: 0; }
.fav-eyebrow {
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  opacity: 0.9;
  padding-top: 2px;
  padding-bottom: 2px;
}
.fav-title {
  font-size: 19px;
  font-weight: 500;
  padding-top: 3px;
  padding-bottom: 3px;
}
.fav-sub {
  font-size: 13px;
  font-weight: 400;
  color: #BAAA9A8C;
  margin-top: 3px;
  line-height: 1.1;
}
.favorite .glass.circle {
  flex-shrink: 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: rgba(255, 255, 255, 0.05);
}

.next-card {
  margin: 12px 16px 0;
  background: var(--card);
  border-radius: var(--radius-card) var(--radius-card) 0 0;
  height: 34px;
}

@media (prefers-reduced-motion: reduce) {
  .glass { transition: none; }
}

@media (prefers-reduced-motion: no-preference) {
  @keyframes heroReveal {
    from { opacity: 0; transform: scale(1.01); }
    to   { opacity: 1; transform: scale(1); }
  }
  @keyframes fadeRise {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  @keyframes fadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  @keyframes dropIn {
    from { opacity: 0; transform: translateY(-10px); }
    to   { opacity: 1; transform: translateY(0); }
  }

  .top-bar .glass.circle:nth-child(1) {
    animation: dropIn 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.35s both;
  }
  .top-bar .glass.circle:nth-child(2) {
    animation: dropIn 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.42s both;
  }
  .hero img,
  .hero video {
    animation: heroReveal 1.9s cubic-bezier(0.16, 1, 0.3, 1) both;
  }
  .laurel.left, .laurel.right {
    animation: fadeIn 0.8s ease-out 0.5s both;
  }
  .name {
    animation: fadeRise 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.5s both;
  }
  .subtitle {
    animation: fadeRise 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.58s both;
  }
  .achievements-wrap {
    animation: fadeRise 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.66s both;
  }
  .stat-card:nth-child(1) {
    animation: fadeRise 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.74s both;
  }
  .stat-card:nth-child(2) {
    animation: fadeRise 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.80s both;
  }
  .stat-card:nth-child(3) {
    animation: fadeRise 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.86s both;
  }
  .favorite {
    animation: fadeRise 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.94s both;
  }
  .next-card {
    animation: fadeIn 0.6s ease-out 1.02s both;
  }
}
  </style>
</head>
<body>

<div class="phone-mockup">
  <div class="screen">

    <div class="hero">
      <video src="https://meez.design/web/media/bg-videos/281_mag4s8xjit.mp4" autoplay muted loop playsinline></video>
      <div class="top-bar">
        <button class="glass circle" type="button" aria-label="Edit profile">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
            <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/>
            <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/>
          </svg>
        </button>
        <button class="glass circle" type="button" aria-label="Close">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" style="width: 26px; height: 26px">
            <path d="M18 6 6 18M6 6l12 12"></path>
          </svg>
        </button>
      </div>
    </div>

    <div class="identity">
      <svg class="laurel left" viewBox="0 0 40 80" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M38 75C34 65 30 55 28 45C26 35 26 25 30 15C32 10 35 6 38 3" stroke="#ede4d8" stroke-width="1" stroke-linecap="round" opacity="0.7"/>
        <path d="M36 70C30 62 26 52 25 42" stroke="#ede4d8" stroke-width="0.8" stroke-linecap="round" opacity="0.5"/>
        <ellipse cx="30" cy="18" rx="5" ry="8" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(-15 30 18)"/>
        <ellipse cx="26" cy="30" rx="5" ry="8" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(-10 26 30)"/>
        <ellipse cx="25" cy="43" rx="5" ry="8" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(-5 25 43)"/>
        <ellipse cx="27" cy="56" rx="5" ry="8" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(5 27 56)"/>
        <ellipse cx="32" cy="67" rx="4" ry="7" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(10 32 67)"/>
      </svg>
      <svg class="laurel right" viewBox="0 0 40 80" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M2 75C6 65 10 55 12 45C14 35 14 25 10 15C8 10 5 6 2 3" stroke="#ede4d8" stroke-width="1" stroke-linecap="round" opacity="0.7"/>
        <path d="M4 70C10 62 14 52 15 42" stroke="#ede4d8" stroke-width="0.8" stroke-linecap="round" opacity="0.5"/>
        <ellipse cx="10" cy="18" rx="5" ry="8" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(15 10 18)"/>
        <ellipse cx="14" cy="30" rx="5" ry="8" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(10 14 30)"/>
        <ellipse cx="15" cy="43" rx="5" ry="8" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(5 15 43)"/>
        <ellipse cx="13" cy="56" rx="5" ry="8" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(-5 13 56)"/>
        <ellipse cx="8" cy="67" rx="4" ry="7" fill="none" stroke="#ede4d8" stroke-width="0.8" opacity="0.6" transform="rotate(-10 8 67)"/>
      </svg>
      <div class="name-row">
        <h1 class="name">Dasha</h1>
      </div>
      <p class="subtitle">Plum Parfait Latte</p>
    </div>

    <div class="achievements-wrap">
      <button class="glass pill" type="button">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
          <path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"/>
          <path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"/>
          <path d="M4 22h16"/>
          <path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"/>
          <path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"/>
          <path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"/>
        </svg>
        12 achievements
      </button>
    </div>

    <div class="stats">
      <div class="stat-card">
        <img src="https://meez.design/web/bg-previews/243_c6rpndcf3k.jpg" alt="">
        <div class="stat-num">154</div>
        <div class="stat-label">drinks<br>consumed</div>
      </div>
      <div class="stat-card">
        <img src="https://meez.design/web/bg-previews/244_ca7j1h1ztr.jpg" alt="">
        <div class="stat-num">36</div>
        <div class="stat-label">sandwiches<br>eaten</div>
      </div>
      <div class="stat-card">
        <img src="https://meez.design/web/bg-previews/245_cyhp8jgs3b.jpg" alt="">
        <div class="stat-num">12</div>
        <div class="stat-label">cafes<br>visited</div>
      </div>
    </div>

    <div class="favorite">
      <img src="https://meez.design/web/bg-previews/246_d1scdtfz8a.jpg" alt="">
      <div class="fav-text">
        <div class="fav-eyebrow">Favorite</div>
        <div class="fav-title">Latte</div>
        <div class="fav-sub">Ordered 73 times</div>
      </div>
      <button class="glass circle" type="button" aria-label="Change favorite">
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
          <polyline points="16 3 21 3 21 8"/>
          <line x1="4" y1="20" x2="21" y2="3"/>
          <polyline points="21 16 21 21 16 21"/>
          <line x1="15" y1="15" x2="21" y2="21"/>
          <line x1="4" y1="4" x2="9" y2="9"/>
        </svg>
      </button>
    </div>

    <div class="next-card"></div>

  </div>
</div>

<svg width="0" height="0" style="position:absolute" aria-hidden="true">
  <defs id="liquid-defs"></defs>
</svg>

<script>
const SVG_NS = 'http://www.w3.org/2000/svg';
const XLINK_NS = 'http://www.w3.org/1999/xlink';
const defs = document.getElementById('liquid-defs');
const mapCache = {};
let uid = 0;

function smoothstep(a, b, t) {
  t = Math.min(1, Math.max(0, (t - a) / (b - a)));
  return t * t * (3 - 2 * t);
}

function sdRoundedRect(px, py, hw, hh, r) {
  const qx = Math.abs(px) - hw + r;
  const qy = Math.abs(py) - hh + r;
  return Math.min(Math.max(qx, qy), 0) +
         Math.hypot(Math.max(qx, 0), Math.max(qy, 0)) - r;
}

function buildMap(w, h, radius) {
  const key = w + 'x' + h + 'r' + radius;
  if (mapCache[key]) return mapCache[key];

  const scale = 2;
  const canvas = document.createElement('canvas');
  canvas.width = w * scale;
  canvas.height = h * scale;
  const ctx = canvas.getContext('2d');
  const img = ctx.createImageData(canvas.width, canvas.height);
  const data = img.data;

  const hw = canvas.width / 2, hh = canvas.height / 2, r = radius * scale;
  const edgeBand = Math.min(16, Math.min(w, h) * 0.32) * scale;

  let i = 0;
  for (let y = 0; y < canvas.height; y++) {
    for (let x = 0; x < canvas.width; x++) {
      const px = x - hw, py = y - hh;
      const d = sdRoundedRect(px, py, hw, hh, r);
      const t = smoothstep(-edgeBand, 0, d);
      const strength = t * t;
      const len = Math.hypot(px, py) || 1;
      data[i++] = 128 + (px / len) * strength * 127;
      data[i++] = 128 + (py / len) * strength * 127;
      data[i++] = 128;
      data[i++] = 255;
    }
  }
  ctx.putImageData(img, 0, 0);
  mapCache[key] = canvas.toDataURL();
  return mapCache[key];
}

function attachGlass(el) {
  const rect = el.getBoundingClientRect();
  const w = Math.round(rect.width), h = Math.round(rect.height);
  if (!w || !h) return;
  const cs = getComputedStyle(el);
  let radius = parseFloat(cs.borderTopLeftRadius) || 0;
  radius = Math.min(radius, Math.min(w, h) / 2);

  const id = 'lg-' + (uid++);
  const filter = document.createElementNS(SVG_NS, 'filter');
  filter.setAttribute('id', id);
  filter.setAttribute('x', '-20%');
  filter.setAttribute('y', '-20%');
  filter.setAttribute('width', '140%');
  filter.setAttribute('height', '140%');
  filter.setAttribute('color-interpolation-filters', 'sRGB');

  const feImage = document.createElementNS(SVG_NS, 'feImage');
  const url = buildMap(w, h, radius);
  feImage.setAttribute('href', url);
  feImage.setAttributeNS(XLINK_NS, 'href', url);
  feImage.setAttribute('x', '0');
  feImage.setAttribute('y', '0');
  feImage.setAttribute('width', w);
  feImage.setAttribute('height', h);
  feImage.setAttribute('preserveAspectRatio', 'none');
  feImage.setAttribute('result', 'map');

  const feDisp = document.createElementNS(SVG_NS, 'feDisplacementMap');
  feDisp.setAttribute('in', 'SourceGraphic');
  feDisp.setAttribute('in2', 'map');
  feDisp.setAttribute('scale', '26');
  feDisp.setAttribute('xChannelSelector', 'R');
  feDisp.setAttribute('yChannelSelector', 'G');

  filter.appendChild(feImage);
  filter.appendChild(feDisp);
  defs.appendChild(filter);

  const value = `url(#${id}) blur(0.3px) saturate(1.3)`;
  el.style.backdropFilter = value;
  el.style.webkitBackdropFilter = value;

  const applied = getComputedStyle(el).backdropFilter ||
                  getComputedStyle(el).webkitBackdropFilter || '';
  if (!applied.includes('url')) {
    el.style.backdropFilter = 'blur(6px) saturate(1.4) brightness(1.08)';
    el.style.webkitBackdropFilter = 'blur(6px) saturate(1.4) brightness(1.08)';
    el.style.background = 'rgba(255,255,255,0.10)';
  }
}

requestAnimationFrame(() => {
  document.querySelectorAll('[data-liquid]').forEach(attachGlass);
});
</script>
</body>
</html>

Browse & preview free → Unlock everything — from $49/mo

More categories: Hero Section · Landing Page · Design System · SaaS · Component · Background · Agency · Features Section · Form · Card · Portfolio · Text Effect · Pricing · Navigation · CTA · About · Footer Section

Meez.

The biggest library of AI website build-prompts & motion backgrounds — from $49/mo, ≈5¢ per asset.

Library
Browse all Free prompts Prompts by category Section prompts Motion backgrounds Pricing
Learn & contact
How it works Why AI sites look generic Compare AI builders Terms Privacy Contact Affiliates
© Meez — an open archive. Start free · Unlimited from $49/mo · cancel anytime.