Community widgets

A creation from the community

For Rowan — A Little Adventure

Made by ArkheOS

Open an illustrated birthday keepsake, personalize the recipient and wish, and unwrap a tiny present. Undo, Reset, and saved return are supported. Original by ArkheOS; the first form of the One idea. One change. birthday campaign.

Make your version

Play this widget, then change its rules, look, or purpose with Widget Canvas in ChatGPT.

Open Widget Canvas in ChatGPT

Copy the selected request. In ChatGPT, choose “Try in chat,” replace the example prompt, and paste it.

You are exploring the published defaults. Changes here stay in this browser and do not change the creator’s work.

Original creation

  1. For Rowan — A Little Adventure
    Made by ArkheOS
View published code

HTML

<svg class="definitions" aria-hidden="true" xmlns="http://www.w3.org/2000/svg"><defs>
<symbol id="sprig" viewBox="0 0 140 210"><g fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"><path d="M25 202Q75 132 91 18M53 155Q21 135 16 107M67 122Q109 112 123 84M79 84Q47 66 49 39M87 48Q113 40 119 16"/><path d="M52 156Q16 157 16 107Q48 116 52 156ZM67 123Q103 139 123 84Q84 86 67 123ZM79 84Q40 85 49 39Q78 53 79 84ZM87 49Q116 58 119 16Q94 24 87 49Z" fill="currentColor" fill-opacity=".08"/><path d="M32 189Q10 174 7 157Q32 164 32 189ZM42 175Q77 178 88 150Q57 147 42 175Z" fill="currentColor" fill-opacity=".12"/><circle cx="90" cy="18" r="4" fill="#b48943" stroke="none"/><circle cx="124" cy="80" r="3" fill="#b48943" stroke="none"/></g></symbol>
<symbol id="cake" viewBox="0 0 240 185"><g stroke="#164e50" stroke-width="1.7" stroke-linejoin="round" stroke-linecap="round"><path d="M38 159Q120 183 202 159" fill="none"/><ellipse cx="120" cy="153" rx="77" ry="10" fill="#e8d4a4" stroke="#ad8541"/><path d="M58 105L58 146Q120 165 182 146L182 105Z" fill="#e8d8b6"/><path d="M58 105Q120 86 182 105L182 121Q170 135 160 119Q149 136 138 121Q126 138 113 121Q100 136 88 121Q72 134 58 121Z" fill="#fff9e9"/><ellipse cx="120" cy="105" rx="62" ry="15" fill="#fff9e9"/><path d="M111 93L111 55L127 55L127 93" fill="#d0ac66"/><path d="M114 64L124 69M114 76L124 81" stroke="#fff9e9"/><path d="M119 48Q105 34 120 18Q136 38 119 48Z" fill="#c59a4b" stroke="#ad8541"/><path d="M119 43L120 34" stroke="#fff8de"/><path d="M82 141l2 1m20 5l2 1m24 0l2-1m23-5l2-1" stroke="#b48943" stroke-width="3"/><path d="M40 65l-6-5m9-12l1-7m145 26l7-3m-14-16l4-7" stroke="#b48943"/></g></symbol>
<symbol id="parcel" viewBox="0 0 150 130"><g stroke="#164e50" stroke-width="1.8" stroke-linejoin="round"><path d="M30 57L120 57L120 115Q75 125 30 115Z" fill="#d6b573"/><path d="M26 45L124 45L124 63Q75 70 26 63Z" fill="#e1c68d"/><path d="M67 46h16v73H67Z" fill="#285e5c"/><path d="M75 44Q24 43 36 21Q50 3 75 44ZM75 44Q125 44 114 21Q101 4 75 44Z" fill="#f7ecd2"/><path d="M75 44L55 66M75 44L96 66" fill="none"/></g></symbol>
<symbol id="compass" viewBox="0 0 120 120"><g stroke="#b18a45" fill="none"><circle cx="60" cy="60" r="44"/><circle cx="60" cy="60" r="38" stroke-dasharray="1 6"/><path d="M60 8V20M60 100v12M8 60h12m80 0h12"/></g><path d="M60 26L69 53L94 60L67 69L60 94L51 67L26 60L53 51Z" fill="#215759"/><path d="M60 26v34l34 0-27-7Z" fill="#d6b573"/><circle cx="60" cy="60" r="4" fill="#fff6df"/></symbol>
</defs></svg>
<main>
<header class="mast"><span class="eyebrow">THE LITTLE PAPER ATELIER</span><span class="edition">a birthday keepsake</span></header>
<div id="book" class="book">
<section id="cover" class="paper cover" aria-label="Birthday card cover">
<div class="border"></div><svg class="botanical left" aria-hidden="true"><use href="#sprig"/></svg><svg class="botanical right" aria-hidden="true"><use href="#sprig"/></svg>
<div class="ribbon" aria-hidden="true"></div><svg class="bow" viewBox="0 0 110 90" aria-hidden="true"><g stroke="#956e32" stroke-width="1.3" fill="#d1af70"><path d="M55 41Q0 49 8 17Q22-5 55 41ZM55 41Q111 49 103 17Q89-5 55 41Z"/><path d="M51 42L30 83L49 77L56 89L61 45M59 42L91 76L75 74L70 88L53 44"/><ellipse cx="56" cy="41" rx="9" ry="6"/></g></svg>
<p class="eyebrow cover-kicker">A DAY JUST FOR YOU</p><h1>Happy<br><em>Birthday</em></h1><p class="recipient" id="cover-name">Rowan</p>
<svg class="cake" aria-label="An original illustration of a little cake with a gold candle" role="img"><use href="#cake"/></svg>
<p class="small-note">a little joy, folded with love</p>
</section>
<section id="inside" class="paper inside" hidden aria-label="Opened birthday card">
<div class="left-page"><svg class="inner-sprig" aria-hidden="true"><use href="#sprig"/></svg><span class="eyebrow">ANOTHER YEAR OF</span><p class="wonder">wonder &<br><em>little adventures</em></p><svg class="inner-cake" aria-hidden="true"><use href="#cake"/></svg><span class="gold-star" aria-hidden="true">✦</span></div>
<div class="right-page"><p class="eyebrow">WITH A WISH FOR YOU</p><h2 id="dear">Dear Rowan,</h2><p id="letter" class="letter"></p><div class="gold-rule" aria-hidden="true">✦</div><p class="signoff">A very happy birthday.</p>
<button id="gift" class="gift" aria-expanded="false" aria-controls="surprise"><svg aria-hidden="true"><use href="#parcel"/></svg><span id="gift-label">A little something for you</span><small>Open your present</small></button>
<div id="surprise" class="surprise" hidden><svg aria-hidden="true"><use href="#compass"/></svg><p>A pocketful of possibility.</p><small>For wherever your next adventure leads.</small></div>
</div></section></div>
<nav class="actions" aria-label="Card controls"><button id="fold" class="primary" aria-expanded="false" aria-controls="inside">Untie & open</button><button id="personalize" aria-expanded="false" aria-controls="editor">Personalize</button><button id="undo" disabled>Undo</button><button id="reset">Reset</button></nav>
<section id="editor" class="editor paper" hidden aria-label="Personalize your card"><div class="editor-top"><h2>Your words, in ink</h2><button id="done">Done</button></div><label for="recipient">For</label><input id="recipient" maxlength="60" autocomplete="off" value="Rowan"><label for="message">A birthday wish</label><textarea id="message" maxlength="600" rows="4">Here’s to your next little adventure.</textarea><p class="editor-note">Your words appear inside the card. Edits save automatically.</p></section>
<footer><p id="status" role="status" aria-live="polite">Ready to unwrap a little joy.</p><p class="keyboard">Tab to explore · Enter or Space to activate · Esc to close editing</p></footer>
</main>

CSS

*{box-sizing:border-box}html{color-scheme:light}body{margin:0;color:#164e50;background:#e9e9df;font:14px/1.5 system-ui,sans-serif}button,input,textarea{font:inherit}button{cursor:pointer;min-height:44px;border:1px solid #a4b4a7;border-radius:24px;padding:10px 17px;color:#164e50;background:#f9f3e4}button:hover{background:#eee2c9}button:disabled{opacity:.45;cursor:default}button:focus-visible,input:focus-visible,textarea:focus-visible{outline:3px solid #8b6228;outline-offset:4px}main{max-width:860px;margin:auto;padding:24px 18px 18px;min-width:0}.definitions{position:absolute;width:0;height:0;overflow:hidden}.mast{display:flex;justify-content:space-between;gap:12px;flex-wrap:wrap;margin:0 0 23px}.eyebrow{font:10px/1.7 system-ui,sans-serif;letter-spacing:2.3px}.edition{font:italic 13px Georgia,serif;color:#5a7068}.book{max-width:380px;margin:0 auto;perspective:1400px;transition:max-width .6s ease}.book.open{max-width:760px}.paper{background-color:#fbf3e1;background-image:radial-gradient(#9d7a3d14 .6px,transparent .8px),repeating-linear-gradient(92deg,transparent 0px,transparent 8px,#b6a17405 9px);background-size:5px 5px,100% 100%;box-shadow:0 16px 36px #354e4020,2px 3px 2px #5b574820;border:1px solid #d8c9a6}.cover{min-height:478px;position:relative;text-align:center;padding:42px 38px 27px;isolation:isolate;overflow:hidden;border-radius:2px 6px 6px 2px;border-left:7px solid #e6d8bb}.cover:after{content:"";position:absolute;inset:0;box-shadow:inset 10px 0 15px #6e57300b;pointer-events:none}.border{position:absolute;inset:18px;border:1px solid #b18a4566;z-index:-1}.border:after{content:"";position:absolute;inset:4px;border:1px solid #b18a4529}.cover-kicker{margin:5px 0 18px;font-size:9px}.cover h1{font:normal clamp(36px,8vw,48px)/1.06 Georgia,serif;margin:0;letter-spacing:-1px}.cover h1 em{font-weight:normal}.recipient{font:italic 27px/1.3 Georgia,serif;margin:18px 0 0;overflow-wrap:anywhere;position:relative;z-index:1}.cake{display:block;width:210px;max-width:100%;height:164px;margin:9px auto 0}.small-note{font:italic 12px Georgia,serif;margin:6px 0 3px}.botanical{position:absolute;width:95px;height:170px;opacity:.85;z-index:-1}.botanical.left{left:-7px;bottom:12px;transform:rotate(-15deg)}.botanical.right{right:-7px;top:80px;transform:rotate(175deg)}.ribbon{position:absolute;top:0;bottom:0;right:28px;width:13px;background:linear-gradient(90deg,#ab8046,#e0c893 42%,#bb965a);opacity:.8;z-index:-1}.bow{position:absolute;width:83px;height:70px;right:-4px;top:19px;transform:rotate(12deg)}.inside{display:grid;grid-template-columns:.9fr 1.1fr;min-height:478px;border-radius:3px;transform-origin:left center;animation:unfold .65s ease both}.left-page{position:relative;padding:60px 22px 25px;text-align:center;border-right:1px solid #c2b28f;background:linear-gradient(90deg,transparent 92%,#79633f10);overflow:hidden}.left-page>.eyebrow{font-size:9px}.wonder{font:normal 32px/1.2 Georgia,serif;margin:16px 0 7px}.wonder em{font-size:25px}.inner-sprig{position:absolute;width:90px;height:135px;left:-7px;top:-10px;transform:rotate(45deg);opacity:.5}.inner-cake{width:190px;max-width:100%;height:165px}.gold-star{display:block;color:#a57b37;font-size:24px}.right-page{padding:36px 30px 24px;text-align:center;min-width:0;background:linear-gradient(90deg,#806b3c09,transparent 6%)}.right-page>.eyebrow{font-size:9px}.right-page h2{font:italic 27px/1.3 Georgia,serif;margin:21px 0 17px;overflow-wrap:anywhere}.letter{font:21px/1.55 Georgia,serif;white-space:pre-wrap;overflow-wrap:anywhere;margin:0;min-height:66px}.gold-rule{color:#a77e3d;display:flex;align-items:center;gap:12px;justify-content:center;margin:20px 0 13px}.gold-rule:before,.gold-rule:after{content:"";width:40px;height:1px;background:#bd9d63}.signoff{font:italic 14px Georgia,serif;margin:0 0 13px}.gift{display:flex;flex-direction:column;align-items:center;border:0;background:transparent;border-radius:10px;margin:0 auto;padding:2px 6px 8px;max-width:100%}.gift:hover{background:#e8dcb747}.gift svg{width:93px;height:85px;transition:transform .3s}.gift:hover svg{transform:rotate(-4deg)}.gift span{font:italic 14px Georgia,serif}.gift small{font-size:10px;letter-spacing:.6px;margin-top:4px}.surprise{padding:8px 12px 15px;border:1px solid #bc9a5e;margin-top:10px;animation:appear .45s ease both;background:#fff9eb;box-shadow:2px 3px 0 #e5d8b7}.surprise svg{width:80px;height:80px}.surprise p{font:italic 20px/1.3 Georgia,serif;margin:3px 0 8px}.surprise small{display:block;font-size:11px}.actions{display:flex;justify-content:center;gap:8px;flex-wrap:wrap;margin:24px 0 13px}.primary{background:#164e50;color:#fff7e6;border-color:#164e50}.primary:hover{background:#286365}.editor{max-width:580px;margin:18px auto 0;padding:20px 22px;border-radius:3px}.editor-top{display:flex;justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px}.editor h2{font:italic 23px Georgia,serif;margin:0}.editor-top button{padding:5px 14px}.editor label{display:block;font-size:12px;margin:12px 0 6px}.editor input,.editor textarea{display:block;width:100%;min-width:0;border:0;border-bottom:1px solid #80998c;background:#ffffff55;color:#164e50;padding:10px 8px;border-radius:0;font:18px/1.5 Georgia,serif}.editor textarea{resize:vertical}.editor-note{font-size:11px;color:#4d635b;margin:12px 0 0}footer{text-align:center;color:#4d635b;font-size:11px}footer p{margin:5px 0}.keyboard{font-size:10px;padding-top:4px}[hidden]{display:none!important}@keyframes unfold{from{opacity:.5;transform:rotateY(-13deg)}to{opacity:1;transform:rotateY(0)}}@keyframes appear{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@media(max-width:540px){main{padding:18px 12px}.mast{margin-bottom:18px}.edition{font-size:11px}.inside{grid-template-columns:1fr}.left-page{padding:24px 20px 14px;border-right:0;border-bottom:1px solid #c2b28f;min-height:170px;background:linear-gradient(0deg,#79633f10,transparent 8%)}.wonder{font-size:25px;margin:9px 0}.wonder br{display:none}.wonder em{font-size:23px}.inner-cake{width:110px;height:86px}.gold-star{position:absolute;right:25px;top:60px}.right-page{padding:28px 24px}.actions{gap:7px}.actions button{padding:9px 14px}.cover{padding-left:27px;padding-right:27px}.editor{padding:17px}.eyebrow{letter-spacing:1.6px}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation:none!important;transition:none!important;scroll-behavior:auto!important}.gift:hover svg{transform:none}}

JavaScript

(()=>{
'use strict';
const $=id=>document.getElementById(id), KEY='rowan-birthday-paper-v1';
const defaults=()=>({name:'Rowan',message:'Here’s to your next little adventure.',open:false,gift:false});
let s=defaults(),history=[],editGroup=null,generation=0,saveChain=Promise.resolve(),contextChain=Promise.resolve(),restoreEpoch=0;
const copy=o=>({...o});
const equal=(a,b)=>a.name===b.name&&a.message===b.message&&a.open===b.open&&a.gift===b.gift;
const wordsEqual=(a,b)=>a.name===b.name&&a.message===b.message;
function clean(v){if(!v||typeof v!=='object'||Array.isArray(v))return null;return {name:typeof v.name==='string'?v.name.slice(0,60):'Rowan',message:typeof v.message==='string'?v.message.slice(0,600):defaults().message,open:v.open===true,gift:v.gift===true};}
function render(syncFields=true){
$('book').classList.toggle('open',s.open);$('cover').hidden=s.open;$('inside').hidden=!s.open;
$('cover-name').textContent=s.name.trim()||'you';$('dear').textContent='Dear '+(s.name.trim()||'you')+',';
$('letter').textContent=s.message;$('fold').textContent=s.open?'Fold card closed':'Untie & open';$('fold').setAttribute('aria-expanded',String(s.open));
$('gift').setAttribute('aria-expanded',String(s.gift));$('surprise').hidden=!s.gift;
$('gift-label').textContent=s.gift?'A little adventure, just for you':'A little something for you';
$('gift').querySelector('small').textContent=s.gift?'Wrap it again':'Open your present';
$('undo').disabled=history.length===0;
if(syncFields){$('recipient').value=s.name;$('message').value=s.message;}
}
function snapshot(){if(!history.length||!equal(history[history.length-1],s)){history.push(copy(s));if(history.length>40)history.shift();}}
function publishContext(){
const context={kind:'birthday-card',recipient:s.name,wish:s.message},epoch=restoreEpoch;
contextChain=contextChain.catch(()=>{}).then(async()=>{if(epoch!==restoreEpoch)return;try{if(window.WidgetCanvas&&typeof WidgetCanvas.setContext==='function')await WidgetCanvas.setContext(context);}catch(e){}});
}
function persist(){
const stamp=++generation,payload={kind:KEY,version:1,card:copy(s),history:history.map(copy)};
$('status').textContent='Saving your keepsake…';
saveChain=saveChain.catch(()=>{}).then(async()=>{if(stamp!==generation)return;
try{if(!window.WidgetCanvas||typeof WidgetCanvas.setState!=='function')throw Error('No save bridge');await WidgetCanvas.setState(payload);if(stamp===generation)$('status').textContent='Your keepsake is saved.';}
catch(e){if(stamp===generation)$('status').textContent='Saving is unavailable. Keep this card open to retain your edits.';}});
}
function change(fn){
const before=copy(s),next=copy(s);fn(next);if(equal(before,next))return false;
editGroup=null;snapshot();s=next;render();persist();if(!wordsEqual(before,s))publishContext();return true;
}
function restore(v){
if(!v||v.kind!==KEY||v.version!==1)return false;
const next=clean(v.card);if(!next)return false;
++generation;++restoreEpoch;editGroup=null;s=next;
history=Array.isArray(v.history)?v.history.slice(-40).map(clean).filter(Boolean):[];
while(history.length&&equal(history[history.length-1],s))history.pop();
render();$('status').textContent='Welcome back. Your keepsake is restored.';return true;
}
$('fold').onclick=()=>change(next=>{next.open=!next.open;});
$('gift').onclick=()=>change(next=>{next.gift=!next.gift;});
function editor(open){$('editor').hidden=!open;$('personalize').setAttribute('aria-expanded',String(open));editGroup=null;if(open)$('recipient').focus();else $('personalize').focus();}
$('personalize').onclick=()=>editor($('editor').hidden);
$('done').onclick=()=>editor(false);
$('editor').addEventListener('keydown',e=>{if(e.key==='Escape'){e.preventDefault();editor(false);}});
for(const [id,key] of [['recipient','name'],['message','message']]){
$(id).addEventListener('focus',()=>{editGroup=null;});
$(id).addEventListener('input',e=>{
const value=e.target.value.slice(0,key==='name'?60:600);if(value===s[key])return;
if(editGroup!==id){snapshot();editGroup=id;}s[key]=value;
if(history.length&&equal(history[history.length-1],s)){history.pop();editGroup=null;}
render(false);persist();publishContext();
});
$(id).addEventListener('blur',()=>{editGroup=null;});
}
$('undo').onclick=()=>{while(history.length&&equal(history[history.length-1],s))history.pop();if(!history.length){render(false);return;}const before=copy(s);editGroup=null;s=history.pop();render();persist();if(!wordsEqual(before,s))publishContext();};
$('reset').onclick=()=>{change(next=>Object.assign(next,defaults()));$('editor').hidden=true;$('personalize').setAttribute('aria-expanded','false');$('fold').focus();};
render();
window.addEventListener('widgetcanvas:restore',e=>restore(e.detail));
async function loadStartup(){
const startupGeneration=generation;
try{if(!window.WidgetCanvas)return;
const saved=await WidgetCanvas.getState();
if(generation===startupGeneration)restore(saved);
}catch(e){if(generation===startupGeneration)$('status').textContent='Your card is ready. Saved edits could not be loaded.';}
}
try{if(window.WidgetCanvas){WidgetCanvas.setControlsPosition('top-right');WidgetCanvas.setResetEnabled(false);WidgetCanvas.setSharingEnabled(false);}}
catch(e){$('status').textContent='Your card is ready. Saving is unavailable.';}
loadStartup();
})();