앱 클립 카드

App Clip Card

앱을 설치하지 않고도 QR·NFC·링크로 특정 기능 하나만 즉시 열 수 있게, 화면 아래에서 작게 떠오르는 카드.

다른 이름: 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는 NFC 태그·QR 코드·App Clip 코드·사파리 스마트 배너·메시지 링크 등으로 이 작은 카드(앱 아이콘, 이름, 짧은 설명, 둥근 "열기" 버튼)를 띄운다. 전체 화면 설치 화면을 먼저 거치지 않고 앱의 아주 작은 조각(용량 제한이 있는 경량 실행 파일)만 즉시 실행시켜 필요한 기능 하나만 바로 쓰게 하고, 정식 설치는 그다음에 원하면 하는 선택지로 남긴다.

Android의 인스턴트 앱(Instant App)도 같은 발상이다 — 구글 플레이를 거치지 않고 "지금 실행" 배너로 설치 없이 앱 일부를 실행한다. 두 방식 모두 핵심은 "이게 전체 앱이 아니다"를 카드 자체가 분명히 드러내는 것 — 원래 있던 페이지(식당 테이블의 QR, 주차 미터기)를 완전히 가리지 않고 그 위에 살짝 얹히며, 닫기 버튼으로 언제든 원래 맥락으로 돌아갈 수 있어야 한다.

일반 배너 광고와 혼동되기 쉬운 지점이라, 아이콘·이름은 실제 앱스토어에 등록된 것과 정확히 일치해야 하고 "열기"를 눌렀을 때는 광고 랜딩이 아니라 정말로 그 기능이 바로 실행돼야 신뢰를 잃지 않는다.

언제 쓰나

설치 장벽 없이 결제·주차·메뉴 주문처럼 단발성 기능 하나만 즉시 실행시키고 싶은 물리적 접점(QR, NFC 태그)에 씁니다. 앱의 핵심 기능 전체를 대체하려는 목적에는 맞지 않습니다.