');for(const n of document.getElementsByTagName("script")){if(n.dataset.astroExec==="")continue;const o=n.getAttribute("type");if(o&&o!=="module"&&o!=="text/javascript")continue;const r=document.createElement("script");r.innerHTML=n.innerHTML;for(const i of n.attributes){if(i.name==="src"){const u=new Promise(a=>{r.onload=r.onerror=a});e=e.then(()=>u)}r.setAttribute(i.name,i.value)}r.dataset.astroExec="",n.replaceWith(r)}return e}const G=(e,t,n,o,r)=>{const i=W(t,e),u=document.title;document.title=o;let a=!1;if(e.href!==location.href&&!r)if(n.history==="replace"){const c=history.state;E({...n.state,index:c.index,scrollX:c.scrollX,scrollY:c.scrollY},"",e.href)}else be({...n.state,index:++v,scrollX:0,scrollY:0},"",e.href);if(document.title=u,R=e,i||(scrollTo({left:0,top:0,behavior:"instant"}),a=!0),r)scrollTo(r.scrollX,r.scrollY);else{if(e.hash){history.scrollRestoration="auto";const c=history.state;location.href=e.href,history.state||(E(c,""),i&&window.dispatchEvent(new PopStateEvent("popstate")))}else a||scrollTo({left:0,top:0,behavior:"instant"});history.scrollRestoration="manual"}};function Ee(e){const t=[];for(const n of e.querySelectorAll("head link[rel=stylesheet]"))if(!document.querySelector(`[${H}="${n.getAttribute(H)}"], link[rel=stylesheet][href="${n.getAttribute("href")}"]`)){const o=document.createElement("link");o.setAttribute("rel","preload"),o.setAttribute("as","style"),o.setAttribute("href",n.getAttribute("href")),t.push(new Promise(r=>{["load","error"].forEach(i=>o.addEventListener(i,r)),document.head.append(o)}))}return t}async function _(e,t,n,o,r){async function i(f){function l(d){const p=d.effect;return!p||!(p instanceof KeyframeEffect)||!p.target?!1:window.getComputedStyle(p.target,p.pseudoElement).animationIterationCount==="infinite"}const s=document.getAnimations();document.documentElement.setAttribute(P,f);const b=document.getAnimations().filter(d=>!s.includes(d)&&!l(d));return Promise.allSettled(b.map(d=>d.finished))}const u=async()=>{if(r==="animate"&&!n.transitionSkipped&&!e.signal.aborted)try{await i("old")}catch{}},a=document.title,c=await ye(e,n.viewTransition,u);G(c.to,c.from,t,a,o),V(me),r==="animate"&&(!n.transitionSkipped&&!c.signal.aborted?i("new").finally(()=>n.viewTransitionFinished()):n.viewTransitionFinished())}function Se(){return m?.controller.abort(),m={controller:new AbortController}}async function z(e,t,n,o,r){const i=Se();if(!N()||location.origin!==n.origin){i===m&&(m=void 0),location.href=n.href;return}const u=r?"traverse":o.history==="replace"?"replace":"push";if(u!=="traverse"&&x({scrollX,scrollY}),W(t,n)&&!o.formData&&(e!=="back"&&n.hash||e==="back"&&t.hash)){G(n,t,o,document.title,r),i===m&&(m=void 0);return}const a=await ge(t,n,e,u,o.sourceElement,o.info,i.controller.signal,o.formData,c);if(a.defaultPrevented||a.signal.aborted){i===m&&(m=void 0),a.signal.aborted||(location.href=n.href);return}async function c(s){const h=s.to.href,b={signal:s.signal};if(s.formData){b.method="POST";const w=s.sourceElement instanceof HTMLFormElement?s.sourceElement:s.sourceElement instanceof HTMLElement&&"form"in s.sourceElement?s.sourceElement.form:s.sourceElement?.closest("form");b.body=t!==void 0&&Reflect.get(HTMLFormElement.prototype,"attributes",w).getNamedItem("enctype")?.value==="application/x-www-form-urlencoded"?new URLSearchParams(s.formData):s.formData}const d=await Te(h,b);if(d===null){s.preventDefault();return}if(d.redirected){const w=new URL(d.redirected);if(w.origin!==s.to.origin){s.preventDefault();return}s.to=w}if(C??=new DOMParser,s.newDocument=C.parseFromString(d.html,d.mediaType),s.newDocument.querySelectorAll("noscript").forEach(w=>w.remove()),!s.newDocument.querySelector('[name="astro-view-transitions-enabled"]')&&!s.formData){s.preventDefault();return}const p=Ee(s.newDocument);p.length&&!s.signal.aborted&&await Promise.all(p)}async function f(){if(g&&g.viewTransition){try{g.viewTransition.skipTransition()}catch{}try{await g.viewTransition.updateCallbackDone}catch{}}return g={transitionSkipped:!1}}const l=await f();if(a.signal.aborted){i===m&&(m=void 0);return}if(document.documentElement.setAttribute(F,a.direction),I)l.viewTransition=document.startViewTransition(async()=>await _(a,o,l,r));else{const s=(async()=>{await Promise.resolve(),await _(a,o,l,r,K())})();l.viewTransition={updateCallbackDone:s,ready:s,finished:new Promise(h=>l.viewTransitionFinished=h),skipTransition:()=>{l.transitionSkipped=!0,document.documentElement.removeAttribute(P)},types:new Set}}l.viewTransition?.updateCallbackDone.finally(async()=>{await Ae(),j(),ve()}),l.viewTransition?.finished.finally(()=>{l.viewTransition=void 0,l===g&&(g=void 0),i===m&&(m=void 0),document.documentElement.removeAttribute(F),document.documentElement.removeAttribute(P)});try{await l.viewTransition?.updateCallbackDone}catch(s){const h=s;console.log("[astro]",h.name,h.message,h.stack)}}async function X(e,t){await z("forward",R,new URL(e,location.href),t??{})}function Re(e){if(!N()&&e.state){location.reload();return}if(e.state===null)return;const t=history.state,n=t.index,o=n>v?"forward":"back";v=n,z(o,R,new URL(location.href),{},t)}const Y=()=>{history.state&&(scrollX!==history.state.scrollX||scrollY!==history.state.scrollY)&&x({scrollX,scrollY})};{if(I||K()!=="none")if(R=new URL(location.href),addEventListener("popstate",Re),addEventListener("load",j),"onscrollend"in window)addEventListener("scrollend",Y);else{let e,t,n,o;const r=()=>{if(o!==history.state?.index){clearInterval(e),e=void 0;return}if(t===scrollY&&n===scrollX){clearInterval(e),e=void 0,Y();return}else t=scrollY,n=scrollX};addEventListener("scroll",()=>{e===void 0&&(o=history.state?.index,t=scrollY,n=scrollX,e=window.setInterval(r,50))},{passive:!0})}for(const e of document.getElementsByTagName("script"))U(e),e.dataset.astroExec=""}const J=new Set,S=new WeakSet;let D,Q,B=!1;function Le(e){B||(B=!0,D??=e?.prefetchAll,Q??=e?.defaultStrategy??"hover",ke(),Pe(),De(),Ie())}function ke(){for(const e of["touchstart","mousedown"])document.body.addEventListener(e,t=>{T(t.target,"tap")&&L(t.target.href,{ignoreSlowConnection:!0})},{passive:!0})}function Pe(){let e;document.body.addEventListener("focusin",o=>{T(o.target,"hover")&&t(o)},{passive:!0}),document.body.addEventListener("focusout",n,{passive:!0}),O(()=>{for(const o of document.getElementsByTagName("a"))S.has(o)||T(o,"hover")&&(S.add(o),o.addEventListener("mouseenter",t,{passive:!0}),o.addEventListener("mouseleave",n,{passive:!0}))});function t(o){const r=o.target.href;e&&clearTimeout(e),e=setTimeout(()=>{L(r)},80)}function n(){e&&(clearTimeout(e),e=0)}}function De(){let e;O(()=>{for(const t of document.getElementsByTagName("a"))S.has(t)||T(t,"viewport")&&(S.add(t),e??=xe(),e.observe(t))})}function xe(){const e=new WeakMap;return new IntersectionObserver((t,n)=>{for(const o of t){const r=o.target,i=e.get(r);o.isIntersecting?(i&&clearTimeout(i),e.set(r,setTimeout(()=>{n.unobserve(r),e.delete(r),L(r.href)},300))):i&&(clearTimeout(i),e.delete(r))}})}function Ie(){O(()=>{for(const e of document.getElementsByTagName("a"))T(e,"load")&&L(e.href)})}function L(e,t){e=e.replace(/#.*/,"");const n=t?.ignoreSlowConnection??!1;if(Ne(e,n))if(J.add(e),document.createElement("link").relList?.supports?.("prefetch")&&t?.with!=="fetch"){const o=document.createElement("link");o.rel="prefetch",o.setAttribute("href",e),document.head.append(o)}else fetch(e,{priority:"low"})}function Ne(e,t){if(!navigator.onLine||!t&&Z())return!1;try{const n=new URL(e,location.href);return location.origin===n.origin&&(location.pathname!==n.pathname||location.search!==n.search)&&!J.has(e)}catch{}return!1}function T(e,t){if(e?.tagName!=="A")return!1;const n=e.dataset.astroPrefetch;return n==="false"?!1:t==="tap"&&(n!=null||D)&&Z()?!0:n==null&&D||n===""?t===Q:n===t}function Z(){if("connection"in navigator){const e=navigator.connection;return e.saveData||/2g/.test(e.effectiveType)}return!1}function O(e){e();let t=!1;document.addEventListener("astro:page-load",()=>{if(!t){t=!0;return}e()})}let A=null;function Oe(){const e=document.querySelector('[name="astro-view-transitions-fallback"]');return e?e.getAttribute("content"):"animate"}function $(e){return e.dataset.astroReload!==void 0}const Me=e=>e.button&&e.button!==0||e.metaKey||e.ctrlKey||e.altKey||e.shiftKey;(I||Oe()!=="none")&&(document.addEventListener("click",e=>{let t=e.target;if(A=Me(e)?t:null,e.composed&&(t=e.composedPath()[0]),t instanceof Element&&(t=t.closest("a, area")),!(t instanceof HTMLAnchorElement)&&!(t instanceof SVGAElement)&&!(t instanceof HTMLAreaElement))return;const n=t instanceof HTMLElement?t.target:t.target.baseVal,o=t instanceof HTMLElement?t.href:t.href.baseVal,r=new URL(o,location.href).origin;$(t)||t.hasAttribute("download")||!t.href||n&&n!=="_self"||r!==location.origin||A||e.defaultPrevented||(e.preventDefault(),X(o,{history:t.dataset.astroHistory==="replace"?"replace":"auto",sourceElement:t}))}),document.addEventListener("submit",e=>{let t=e.target;const n=e.submitter,o=n&&n===A;if(A=null,t.tagName!=="FORM"||e.defaultPrevented||$(t)||o)return;const r=t,i=new FormData(r,n),u=typeof r.action=="string"?r.action:r.getAttribute("action"),a=typeof r.method=="string"?r.method:r.getAttribute("method");let c=n?.getAttribute("formaction")??u??location.pathname;const f=n?.getAttribute("formmethod")??a??"get";if(f==="dialog"||location.origin!==new URL(c,location.href).origin)return;const l={sourceElement:n??r};if(f==="get"){const s=new URLSearchParams(i),h=new URL(c);h.search=s.toString(),c=h.toString()}else l.formData=i;e.preventDefault(),X(c,l)}),Le({prefetchAll:!0}));
Over the past year, I’ve received a growing number of wedding invitations from friends which nearly all of them delivered as digital websites. While I appreciate the convenience and creativity of online invites, there are two recurring features that consistently frustrate me as a user.
Autoplay Audio Without Consent
Most digital invitations stay silent on the initial load. But as soon as I click the “Open Invitation” button, background music suddenly begins to play, often loudly and without warning.
This has led to several uncomfortable moments, especially in quiet places like libraries or trains, where unexpected audio can cause disruption and embarrassment.
In my opinion, audio should never play unless deliberately triggered by the user through a play button. Automatic playback should be the exception, not the rule.
If you must include background music, it should be subtle and ambient. A great example of tasteful audio integration is found on dogstudio.co .
Over-the-Top Animations
While animations can enhance a digital experience when used sparingly, I’ve found them to be excessive and distracting in many of the invitations I’ve received. Consider the following issues:
Overuse of animation: Every element seems to move and sometimes even nested layers animate simultaneously, creating visual chaos.
Slow transitions: Animations that exceed 500ms, especially those involving opacity, tend to feel sluggish and awkward.
Long transition distances: Elements that animate over 200px across the screen, regardless of speed, create a jarring user experience.
In contrast, great use of animation can be found on websites like bijakmemilih.id or Github ’s landing pages, where motion is meaningful and smooth.
These frustrations led me to create my own minimalist wedding invitation template. You can view the details of this project here .
My template features subtle animation and audio that plays only when the user chooses to, via a play button in the bottom-right corner. It’s built for elegance, clarity, and a more respectful user experience.