picola
Docs

Introduction

picola is a fast, type-safe, extensible lightbox and image viewer — a dependency-free core with a first-class Svelte 5 adapter.

picola is a fast, type-safe, extensible lightbox and image viewer. It pairs a framework-agnostic, dependency-free core with a first-class Svelte 5 adapter, so you get modern, gesture-driven image viewing without fighting your framework or your CSS.

<script lang="ts">
import { Lightbox, type Slide } from 'picola/svelte';
import 'picola/picola.css';
const slides: Slide[] = [
{ src: '/photos/1.jpg', width: 2400, height: 1600, alt: 'A river at dusk.' }
];
let open = $state(false);
</script>
<button onclick={() => (open = true)}>Open gallery</button>
<Lightbox bind:open {slides} />

That is a complete, keyboard- and gesture-driven viewer. Everything else in these docs is optional refinement.

Why picola

  • Compositor-driven transitions — opening, closing, the clip reveal, backdrop fades and slide-stepping run on the compositor via the Web Animations API, so they keep their frame rate even while images decode on the main thread.
  • Fly from the thumbnail — open and close animate from the source thumbnail. When it is an object-fit: cover crop, the transition reveals the full image with an animated clip instead of a plain scale.
  • Memory-disciplined — only the active slide ± 1 is mounted; images that leave the window are detached so the browser can reclaim their decoded pixels, and a source swap keeps the previous image up until the new one has decoded.
  • Progressive loading — show a placeholder instantly, defer full resolution to a zoomintent event, then upgrade the source with zoom and pan preserved. The slides array is reactive, so pagination syncs into the open viewer.
  • Native-feeling gestures — focal-point pinch / double-tap / wheel zoom, rubber-band overscroll, velocity flicks, and drag- or pinch-to-dismiss with a 01 progress value you can read from CSS.
  • Genuinely Svelte 5 — snippets and real components for the toolbar and caption (wired through {@attach}), and a generic Slide<T> that carries your own per-slide data through typed events. No SVG-string injection, no manual mount/unmount.
  • Styling that doesn’t fight back — flat single-class selectors and zero-specificity (:where) custom-property defaults, so you override by normal cascade order — no !important, and no @layer to lose against global resets.
  • Dependency-free core — the core has zero dependencies and owns gestures, physics and transforms; the picola/svelte adapter is ~15.6 KB min+gzip with icons, on a ~11 KB core (the stylesheet adds ~1.3 KB). Build an adapter for any framework.

How it’s built

The core owns gesture recognition, zoom/pan physics and transform rendering; an adapter owns the DOM structure. The Svelte adapter under picola/svelte is the reference implementation — it renders the chrome, wires up snippets, and delegates all interaction to the core.

Because the core is framework-agnostic and dependency-free, you can drive it directly (see Framework-agnostic core) or build an adapter for any other framework.

Next steps

  • Installation — add picola to your project.
  • Quick start — a complete viewer in a handful of lines.
  • Recipes — fly-from-thumbnail, custom toolbars, deferred loading, headless mode and more.
  • API reference — every prop, event, snippet and type.

Early development. picola is pre-1.0 and its APIs may change before the stable release.