PocketPapi

Developer library · 68 free recipes

Effects, ready to ship.

Free motion recipes for real websites. Browse practical CSS and JavaScript patterns, copy a focused implementation, or let an LLM fetch the exact effect through the PocketPapi API or MCP.

const effect = "fade-up-reveal";
await ship(effect);

The foundation

Native recipes

Small, composable patterns you can paste into a plain HTML site, a CMS, or a generated staging build.

Interactive preview
Advanced interactionsCC0-style snippets

Hover lens

Move a soft lens across a visual card to reveal depth without loading a WebGL layer.

[data-sc-effect="hover-lens"] { --sc-x: 50%; --sc-y: 50%; position: relative; overflow: hidden; }
[data-sc-effect="hover-lens"]::after { content: ""; position: absolute; inset: 0; background: radial-gradient(100px circle at var(--sc-x) var(--sc-y), rgba(255,255,255,.7), transparent 64%); mix-blend-mode: screen; pointer-events: none; }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="hover-lens"]::after { display: none; } }

document.querySelectorAll('[data-sc-effect="hover-lens"]').forEach((card) => card.addEventListener('pointermove', (event) => { const box = card.getBoundingClientRect(); card.style.setProperty('--sc-x', `${event.clientX - box.left}px`); card.style.setProperty('--sc-y', `${event.clientY - box.top}px`); }));
Interactive preview
Advanced interactionsCC0-style snippets

Modal dialog

A focus-friendly native dialog pattern with a real close button and escape behavior.

[data-sc-effect="modal-dialog"] dialog { max-width: min(90vw, 32rem); border: 1px solid #dce3ee; border-radius: 12px; padding: 1.25rem; box-shadow: 0 24px 80px rgba(15,23,42,.2); }
[data-sc-effect="modal-dialog"] dialog::backdrop { background: rgba(15,23,42,.45); }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="modal-dialog"] dialog { scroll-behavior: auto; } }

<div data-sc-effect="modal-dialog"><button type="button" data-dialog-open aria-haspopup="dialog">Open details</button><dialog data-dialog><p>Short supporting content.</p><button type="button" data-dialog-close>Close</button></dialog></div>
<script>const modalRoot = document.querySelector('[data-sc-effect="modal-dialog"]'); const dialog = modalRoot.querySelector('[data-dialog]'); modalRoot.querySelector('[data-dialog-open]').addEventListener('click', () => dialog.showModal()); modalRoot.querySelector('[data-dialog-close]').addEventListener('click', () => dialog.close());</script>
Interactive preview
Advanced interactionsCC0-style snippets

Command palette

Expose a small keyboard-friendly command surface for power users without adding a framework.

[data-sc-effect="command-palette"] [role="dialog"] { border: 1px solid #dce3ee; border-radius: 12px; background: #fff; box-shadow: 0 24px 70px rgba(15,23,42,.18); }
[data-sc-effect="command-palette"] [aria-selected="true"] { background: #edf4ff; color: #155eef; }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="command-palette"] [role="dialog"] { transition: none; } }

const palette = document.querySelector('[data-sc-effect="command-palette"]'); const paletteDialog = palette.querySelector('[role="dialog"]'); const paletteButton = palette.querySelector('[data-palette-open]'); paletteButton.addEventListener('click', () => paletteDialog.hidden = false); palette.addEventListener('keydown', (event) => { if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === 'k') { event.preventDefault(); paletteDialog.hidden = false; paletteDialog.querySelector('input')?.focus(); } if (event.key === 'Escape') paletteDialog.hidden = true; });
Interactive preview
Advanced interactionsCC0-style snippets

Filter chip group

A compact filter group with real pressed state and a live result label.

[data-sc-effect="filter-chip-group"] { display: flex; flex-wrap: wrap; gap: .5rem; }
[data-sc-effect="filter-chip-group"] button[aria-pressed="true"] { background: #155eef; border-color: #155eef; color: #fff; }
Interactive preview
Advanced interactionsCC0-style snippets

Theme switcher

Switch a component between light and dark visual tokens while keeping the state explicit and keyboard accessible.

[data-sc-effect="theme-switcher"] { --sc-surface: #fff; --sc-ink: #172033; padding: 1rem; border-radius: 10px; background: var(--sc-surface); color: var(--sc-ink); transition: background 180ms ease, color 180ms ease; }
[data-sc-effect="theme-switcher"].is-dark { --sc-surface: #172033; --sc-ink: #fff; }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="theme-switcher"] { transition: none; } }

const themeRoot = document.querySelector('[data-sc-effect="theme-switcher"]'); themeRoot.querySelector('button').addEventListener('click', () => { const dark = themeRoot.classList.toggle('is-dark'); themeRoot.querySelector('[data-theme-label]').textContent = dark ? 'Dark' : 'Light'; });
Interactive preview
Advanced interactionsCC0-style snippets

Cinematic object drift

Gently floats up to eight direct child objects on a selected section with varied timing and depth.

/* CSS */
[data-sc-effect="cinematic-float"] > [data-sc-float-object] { animation: sc-object-drift var(--sc-motion-duration, var(--sc-float-duration, 8s)) ease-in-out var(--sc-float-delay, 0s) infinite var(--sc-motion-direction, alternate); transform: translate3d(0, 0, 0); }
[data-sc-effect="cinematic-float"] > [data-sc-float-object][data-sc-motion-type="spin"] { animation-name: sc-object-spin; }
[data-sc-effect="cinematic-float"] > [data-sc-float-object][data-sc-motion-type="pulse"] { animation-name: sc-object-pulse; }
[data-sc-effect="cinematic-float"] > [data-sc-float-object][data-sc-motion-type="slide"] { animation-name: sc-object-slide; }
@keyframes sc-object-drift { to { transform: translate3d(var(--sc-float-x, 0px), var(--sc-float-y, -14px), 0) rotate(var(--sc-float-rotate, 1deg)); } }
@keyframes sc-object-spin { to { transform: translate3d(0, var(--sc-float-y, -14px), 0) rotate(var(--sc-float-spin, 16deg)); } }
@keyframes sc-object-pulse { to { transform: translate3d(0, var(--sc-float-y, -14px), 0) scale(var(--sc-float-scale, 1.035)); } }
@keyframes sc-object-slide { to { transform: translate3d(var(--sc-float-x, 8px), 0, 0); } }
@media (prefers-reduced-motion: reduce) { [data-sc-effect="cinematic-float"] > [data-sc-float-object] { animation: none; transform: none; } }

/* JS */
document.querySelectorAll('[data-sc-effect="cinematic-float"]').forEach((scene) => {
  const mode = getComputedStyle(scene).getPropertyValue('--sc-object-mode').replace(/["']/g, '').trim() || 'all';
  const objects = Array.from(scene.children).filter((item) => mode === 'media' ? item.matches('img,video,picture,svg,canvas') : mode === 'text-media' ? item.matches('h1,h2,h3,h4,p,img,video,picture,svg,canvas') : !item.matches('button,input,select,textarea,a,[contenteditable="true"]'));
  const intensity = Math.max(.5, Math.min(1.5, parseFloat(getComputedStyle(scene).getPropertyValue('--sc-motion-intensity')) || 1));
  const choreography = getComputedStyle(scene).getPropertyValue('--sc-object-motion').replace(/["']/g, '').trim() || 'float';
  const stagger = Math.max(0, Math.min(800, parseFloat(getComputedStyle(scene).getPropertyValue('--sc-motion-stagger')) || 180)) / 1000;
  const motionTypes = ['float', 'spin', 'pulse', 'slide'];
  objects.slice(0, 12).forEach((item, index) => {
    item.setAttribute('data-sc-float-object', '');
    item.setAttribute('data-sc-motion-type', choreography === 'mixed' ? motionTypes[index % motionTypes.length] : choreography);
    item.style.setProperty('--sc-float-duration', `${7 + (index % 4) * 1.4}s`);
    item.style.setProperty('--sc-float-delay', `${index * stagger}s`);
    item.style.setProperty('--sc-float-x', `${(index % 2 ? -8 : 8) * intensity}px`);
    item.style.setProperty('--sc-float-y', `${(-10 - (index % 3) * 5) * intensity}px`);
    item.style.setProperty('--sc-float-rotate', `${(index % 2 ? -1.2 : 1.2) * intensity}deg`);
    item.style.setProperty('--sc-float-spin', `${(index % 2 ? -14 : 14) * intensity}deg`);
    item.style.setProperty('--sc-float-scale', String(1 + .035 * intensity));
  });
});

Bring a library when it earns its weight

Free library starters

Version-pinned starting points for open-source libraries. Review the license and performance tradeoff before adding another dependency.

For GPTs and other agents

Tell the LLM how to use the library.

Give the model a clear selection loop. It should discover, fetch, compose, then explain where it placed the effect instead of inventing a new dependency.

Recommended instruction

Use this in a system or developer prompt when the model can access the REST API or MCP.

When a user asks for website motion or an interaction effect:
1. Search PocketPapi’s effects catalog before writing a new animation.
2. Prefer a native recipe when it meets the request; use a free MIT library only when it materially reduces complexity.
3. Fetch the chosen effect by effect_id and inspect its reduced-motion and accessibility notes.
4. Read `studio_compatibility` before choosing an effect. `available=true` means Site Studio can stage it on one unique existing element; `available=false` means it is copy-code/MCP composition only and needs markup or reviewed dependency setup.
5. For a connected site, use `sitecommander_site_studio_media` with `media_type=video` to find an approved MP4/WebM and `media_type=poster` for an optional still image, then call `sitecommander_site_studio_effect_apply` with the exact staged page, unique selector, and chosen animation settings. This returns a review preview and never publishes. Authorized REST clients can use `/api/control-plane/site-studio/media` and `/api/control-plane/site-studio/effects` with the same site scope.
6. For standalone code, compose the effect for the user’s real selector. Keep the selector scoped and do not rewrite unrelated styles.
7. Return the exact files or insertion points, explain the dependency (if any), and mention keyboard, touch, and prefers-reduced-motion behavior.
8. Never add a library from an untrusted CDN or use a versionless URL when a pinned URL is available.

Use `sitecommander_effects_search`, `sitecommander_effects_fetch`, and `sitecommander_effects_compose` for discovery and code. Use `sitecommander_site_studio_media` plus `sitecommander_site_studio_effect_apply` only for an authorized site's staging workflow.
Public catalog, connected-site staging.The REST endpoints are public and read-only. An authorized MCP site connection can stage compatible effects for review; publishing remains a separate approval.