Prompts · Mobile App
Mobile App build-prompts
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
Cross-Border
Travel Journal
Wellness Companion
Forecast Center
F1 Racing Hub
Church Community
Try a free mobile app prompt
Coffee RewardsMobile App · free prompt · copy-paste
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