You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
consttl=createTimeline({loop: true,alternate: true});tl.add('.box1',{x: 100}).add('.box2',{y: 50},'-=200')// 200ms before previous ends.add('.box3',{scale: 2},'+=100')// 100ms after previous.add('.box4',{rotate: 90},500);// At 500ms absolutetl.label('myLabel',1000);tl.call(()=>console.log('done'),2000);
Playback Control & Cancellation
constanim=animate('.el',{x: 100,autoplay: false});anim.play();anim.pause();anim.reverse();anim.restart();anim.seek(500);// Seek to 500msanim.seek('50%');// Seek to 50%
functionanimateDragStart(card){returnanimate(card,{scale: 1.12,rotate: 8,boxShadow: '0 20px 50px rgba(0,0,0,0.3)',duration: 150,ease: 'out(3)'});}
During Drag — Use Direct Transform (60fps)
// Don't use animate() for pointer trackingfunctionupdateDragPosition(el,x,y){el.style.transform=`translate(${x}px, ${y}px) scale(1.12) rotate(8deg)`;}
---name: idb-state-persistencedescription: Use IndexedDB via the `idb` library for persistent browser state that survives refreshes. Use when building rapid prototypes, games, or SPAs that need client-side state persistence beyond localStorage limitations.---# IDB State Persistence for Rapid Prototyping## When to Use This Skill
Use IDB when you need:
-**State that survives browser refresh** (the primary use case)
-**Structured data storage** (objects, arrays, not just strings)
-**Async non-blocking storage** (localStorage is synchronous)
-**Larger storage limits** (~50MB+ vs localStorage's ~5MB)
-**Indexed queries** on stored data
## Quick Decision: localStorage vs IDB
localStorage: Simple key-value, <100 items, strings only, sync OK
IDB: Structured data, many records, async required, need queries
**Rule of thumb**: If you're doing `JSON.parse(localStorage.getItem(...))` more than twice, switch to IDB.
---
## Installation
```bash
npm install idb
The keyval pattern - drop-in replacement for localStorage with async API:
// services/StateStore.jsimport{openDB}from'idb';constDB_NAME='app-state';constSTORE_NAME='state';constDB_VERSION=1;letdbPromise=null;functiongetDB(){if(!dbPromise){dbPromise=openDB(DB_NAME,DB_VERSION,{upgrade(db){if(!db.objectStoreNames.contains(STORE_NAME)){db.createObjectStore(STORE_NAME);}},});}returndbPromise;}exportconstStateStore={asyncget(key){constdb=awaitgetDB();returndb.get(STORE_NAME,key);},asyncset(key,value){constdb=awaitgetDB();returndb.put(STORE_NAME,value,key);},asyncdelete(key){constdb=awaitgetDB();returndb.delete(STORE_NAME,key);},asyncclear(){constdb=awaitgetDB();returndb.clear(STORE_NAME);},asynckeys(){constdb=awaitgetDB();returndb.getAllKeys(STORE_NAME);},asyncgetAll(){constdb=awaitgetDB();constkeys=awaitdb.getAllKeys(STORE_NAME);constvalues=awaitdb.getAll(STORE_NAME);returnObject.fromEntries(keys.map((k,i)=>[k,values[i]]));},asyncgetOrDefault(key,defaultValue){constvalue=awaitthis.get(key);returnvalue!==undefined ? value : defaultValue;},asyncupdate(key,updater){constcurrent=awaitthis.get(key);constupdated=updater(current);awaitthis.set(key,updated);returnupdated;}};
Usage
import{StateStore}from'./services/StateStore.js';// Set values (no JSON.stringify needed!)awaitStateStore.set('playerName','ACE');awaitStateStore.set('gameState',{streak: 5,round: 12,mode: 'greater'});// Get valuesconstname=awaitStateStore.get('playerName');conststate=awaitStateStore.get('gameState');// Get with defaultconstvolume=awaitStateStore.getOrDefault('volume',0.8);// Update atomicallyawaitStateStore.update('gameState',(state)=>({
...state,streak: state.streak+1}));
Pattern 2: Typed State Manager (Recommended for Games/Apps)
import{gameState}from'./services/GameStateManager.js';classGameContainerextendsHTMLElement{asyncconnectedCallback(){awaitgameState.ready();// Sync reads after ready()this._streak=gameState.get('streak');this._mode=gameState.get('mode');// Subscribe to changesthis._unsubStreak=gameState.onChange('streak',(newVal)=>{this._streak=newVal;this.updateUI();});this.render();}disconnectedCallback(){this._unsubStreak?.();}asynchandleWin(){awaitgameState.set('streak',this._streak+1);}}
// ✅ CORRECT - bump version for schema changesopenDB('db',2,{upgrade(db,oldVersion){if(oldVersion<1){db.createObjectStore('users');}if(oldVersion<2){db.createObjectStore('posts');}}});
3. Incognito Mode Fallback
asyncfunctiongetStorage(){try{constdb=awaitopenDB('test',1,{upgrade(db){db.createObjectStore('test');}});awaitdb.put('test','test','test');awaitdb.delete('test','test');return'idb';}catch{console.warn('IDB unavailable, falling back to memory');return'memory';}}
Debugging Tips
// View all IDB databasesconstdatabases=awaitindexedDB.databases();console.log('Available databases:',databases);// Clear everything (useful in dev)awaitindexedDB.deleteDatabase('my-app-db');// Dev tools: Application > IndexedDB
When writing JavaScript, scan the "Package" column for your need. If it's a native API (backticks), use it directly—zero dependencies. If it's a just-* package, install it for a tested, zero-dep micro-utility. The "When to Use" column describes the exact trigger condition. Import using ESM syntax.
Collections {}[]
Package
When to Use
just-diff
Get a JSON-patch style diff between two objects or arrays
just-diff-apply
Apply a diff/patch object to mutate a target object
just-compare
Deep equality check for objects, arrays, primitives, NaN-safe
structuredClone(obj)
Deep copy objects, arrays, maps, sets, dates, regexes
arr.map(x => x.prop)
Extract one property from every item in array
arr.filter(x => x != null)
Remove null/undefined from arrays
Objects {}
Package
When to Use
just-extend
Deep merge objects into target; use spread for shallow
In this AI world, there are so many oppertunties to build amazing prototypes; however, we want to make sure that the prototypes that we're making are still conforming to best practices. Below is a list of libraries that we should use building rapid prototypes.