App Clip Card

앱 클립 카드

A small card that rises from the bottom, letting a QR code, NFC tag, or link launch one specific feature instantly — with no app install.

Also known as: App ClipInstant App banner인스턴트 앱 카드
···
html
<div class="stage"><div class="phone"><div class="notch"></div><div class="screen" id="screen">
<div class="acbg"><div class="bar w80"></div><div class="bar w60"></div><div class="bar w70"></div><div class="bar w50"></div></div>
<div class="acshade" id="acshade"></div>
<div class="accard" id="accard">
  <button class="acclose" id="acclose">&#10005;</button>
  <div class="acicon" id="acicon"></div>
  <div class="actxt"><div class="acname">Cafe Lumen</div><div class="acdesc">Order &amp; pay — no install</div></div>
  <button class="acopen" id="acopen">Open</button>
</div></div><div class="home"></div></div></div>
css
.stage{position:absolute;inset:0;display:grid;place-items:center;overflow:hidden}
.phone{position:relative;height:90vmin;max-height:640px;aspect-ratio:9/19.5;background:#08080c;
  border-radius:20%/8.6%;box-shadow:0 0 0 2px rgba(255,255,255,.08) inset,0 12px 30px rgba(0,0,0,.35)}
.phone .notch{position:absolute;left:50%;top:0;transform:translateX(-50%);width:38%;height:5.6%;
  background:#08080c;border-radius:0 0 46% 46%;z-index:80}
.phone .screen{position:absolute;top:7.6%;left:3%;right:3%;bottom:3%;border-radius:11%/4.6%;overflow:hidden;
  background:var(--surface);color:var(--fg)}
.phone .home{position:absolute;left:50%;bottom:1.1%;transform:translateX(-50%);width:32%;height:3px;
  border-radius:2px;background:rgba(255,255,255,.45);z-index:80}
.pfx{position:absolute;left:0;top:0;width:16px;height:16px;margin:-8px 0 0 -8px;border-radius:50%;
  background:color-mix(in srgb,var(--accent) 55%,transparent);
  box-shadow:0 0 0 6px color-mix(in srgb,var(--accent) 15%,transparent);pointer-events:none;z-index:999;
  transition:opacity .2s}
.pfx.hold{width:30px;height:30px;margin:-15px 0 0 -15px;
  box-shadow:0 0 0 9px color-mix(in srgb,var(--accent) 20%,transparent);
  transition:width .5s linear,height .5s linear,margin .5s linear,box-shadow .5s linear,opacity .2s}

.acbg{position:absolute;inset:0;padding:16% 8% 0;display:grid;grid-auto-rows:clamp(3px,2.6vmin,7px);gap:8%;filter:blur(1.4px);opacity:.7}
.bar{border-radius:3px;background:var(--line)}
.w80{width:80%}.w60{width:60%}.w70{width:70%}.w50{width:50%}
.acshade{position:absolute;inset:0;background:#000;opacity:0;transition:opacity .3s;z-index:10;pointer-events:none}
.acshade.show{opacity:.22}
.accard{position:absolute;left:4%;right:4%;bottom:4%;background:var(--surface);border-radius:16px;
  box-shadow:0 -6px 20px rgba(0,0,0,.22);padding:8% 7%;display:grid;grid-template-columns:auto 1fr;
  align-items:center;gap:5%;transform:translateY(140%);transition:transform .35s cubic-bezier(.2,.8,.2,1);z-index:20}
.accard.show{transform:translateY(0)}
.acclose{position:absolute;top:6%;right:5%;border:none;background:none;color:var(--muted);font-size:clamp(6px,2vmin,8px)}
.acicon{width:clamp(20px,13vmin,34px);height:clamp(20px,13vmin,34px);border-radius:24%;background:var(--accent);transition:transform .2s}
.acicon.pulse{transform:scale(1.15)}
.actxt{min-width:0}
.acname{font-weight:800;font-size:clamp(6.5px,2.4vmin,9px);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.acdesc{font-size:clamp(5.5px,1.9vmin,7.5px);color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.acopen{grid-column:1/3;margin-top:4%;border:none;border-radius:999px;background:var(--accent);color:#fff;
  font-weight:800;font-size:clamp(6.5px,2.4vmin,9px);padding:4% 0}
js
const pfx=document.createElement('div');pfx.className='pfx';document.body.appendChild(pfx);
const screenEl=document.getElementById('screen');
let pfxAuto=true;
function pfxHide(){pfx.style.opacity='0';}
addEventListener('pointerdown',()=>{pfxAuto=false;pfxHide();},{capture:true});
function pfxAt(fx,fy){
  const r=screenEl.getBoundingClientRect();
  const x=r.left+r.width*fx, y=r.top+r.height*fy;
  pfx.style.transform='translate('+x+'px,'+y+'px)';
  return {x,y};
}
function pfxWalk(x0,y0,x1,y1,ms){
  return new Promise(res=>{
    const start=performance.now();
    function step(now){
      if(!pfxAuto) return res();
      const p=Math.min(1,(now-start)/ms), e=1-Math.pow(1-p,3);
      pfxAt(x0+(x1-x0)*e, y0+(y1-y0)*e);
      if(p<1) requestAnimationFrame(step); else res();
    }
    requestAnimationFrame(step);
  });
}
function pfxWait(ms){
  return new Promise(res=>{
    const start=performance.now();
    function step(now){ if(!pfxAuto) return res(); if(now-start<ms) requestAnimationFrame(step); else res(); }
    requestAnimationFrame(step);
  });
}

const shade=document.getElementById('acshade'), card=document.getElementById('accard'), icon=document.getElementById('acicon'),
  openBtn=document.getElementById('acopen'), closeBtn=document.getElementById('acclose');
function open(){ shade.classList.add('show'); card.classList.add('show'); }
function close(){ shade.classList.remove('show'); card.classList.remove('show'); }
closeBtn.addEventListener('pointerdown',()=>{ pfxAuto=false; pfxHide(); close(); });
shade.addEventListener('pointerdown',close);
function launch(){
  icon.classList.add('pulse'); openBtn.textContent='Opening...';
  setTimeout(()=>{ icon.classList.remove('pulse'); openBtn.textContent='Open'; close(); },650);
}
openBtn.addEventListener('pointerdown',()=>{ pfxAuto=false; pfxHide(); launch(); });
function fracOf(el){ const r=el.getBoundingClientRect(), sr=screenEl.getBoundingClientRect();
  return {x:(r.left+r.width/2-sr.left)/sr.width,y:(r.top+r.height/2-sr.top)/sr.height}; }
async function autoSeq(){
  while(pfxAuto){
    close();
    await pfxWalk(0.5,0.9,0.5,0.9,350); if(!pfxAuto)break;
    open();
    await pfxWait(700); if(!pfxAuto)break;
    const f=fracOf(openBtn);
    await pfxWalk(0.5,0.9,f.x,f.y,300); if(!pfxAuto)break;
    launch();
    await pfxWait(1000);
  }
}
autoSeq();

iOS surfaces this small card — app icon, name, a short subtitle, a rounded "Open" button — through an NFC tag, a QR code, an App Clip Code, a Safari smart banner, or a Messages link. Instead of a full-screen install flow first, it launches a tiny, size-capped slice of the real app instantly so one specific task just works, leaving a full install as something to opt into afterward, if at all.

Android's Instant Apps run on the same idea — a "Try Now" banner that launches part of an app with no Google Play install. In both cases, the card itself has to make unmistakably clear this isn't the full app — it sits lightly over whatever triggered it (a restaurant table's QR code, a parking meter), never fully covering that context, and a close button always returns to it.

It's easy to mistake for an ad banner, so the icon and name have to match what's actually listed on the app store exactly, and tapping "Open" needs to genuinely launch the feature on the spot rather than an ad landing page — otherwise trust in the pattern erodes fast.

When to use

Use it at a physical touchpoint — a QR code, an NFC tag — where one single-use task (paying, parking, ordering) needs to run instantly with no install barrier. It's not meant to replace the full app's core experience.