# Venetian Blind - Skill Reference > A full-bleed image slideshow whose transition splits the slide into N vertical strips that collapse (outgoing) or open (incoming) with a stagger, driven by one CSS `mask` of stacked gradients and one registered `@property` animated by `@keyframes`. A small TypeScript class (`mount()`) only sequences slides. ## What this teaches - CSS `mask` with one `linear-gradient` layer per strip, each sized `calc(100% / N + 1px)` and positioned along x - `@property` registration (``) so `@keyframes` can interpolate a custom property - Deriving per-strip progress from one global progress var with `clamp()` and `calc()` (stagger without N keyframe blocks) - A mask string built in JS and handed to CSS through a custom property (`--vm-vb-mask`) - zIndex swap + `animationend` listener for stateless slide sequencing - A `mount(container, options)` handle (`setParams`, `dispose`) and `[data-venetian-blind]` auto-init for the embed ## Core concept One property, `--vm-vb-p`, runs 0 to 1 over `duration`. Every strip reads it, subtracts its own start offset (`i * stagger`), divides by the time window it has left and clamps to 0..1, so the strip count and stagger are plain numbers in the mask string, not extra keyframes. Strip progress is squared (ease-in), which makes the edges of each strip snap shut faster than a linear move. CSS owns all interpolation; JS only decides which slide carries `.active` and which sits on top. ## Use it ### Embed (no build step) ```html
``` - Every key of `defaults` is a kebab-case `data-*` attribute: `data-direction`, `data-columns`, `data-stagger`, `data-duration`, `data-delay`. Numbers go through `Number`; strings are used as-is. Omit an attribute to keep its default. - There is no attribute for `images`: pass slides as child `` elements (the effect reads `currentSrc || src`), or use `mount()` with `options.images`. - With fewer than 2 slides nothing animates. - The global is `window.VenetianBlind`: `VenetianBlind.init(root?)` starts the effect on every `[data-venetian-blind]` not yet running (call it after inserting markup late), `VenetianBlind.get(el)` returns the running handle, and `VenetianBlind.mount`, `.defaults`, `.VenetianBlind` are re-exported. It auto-runs on `DOMContentLoaded`. ```js // elements added after load document.body.insertAdjacentHTML("beforeend", '
...
'); VenetianBlind.init(document.body); VenetianBlind.get(document.querySelector("[data-venetian-blind]")).setParams({ columns: 10 }); ``` ### Bundled apps Get the files with `npx vanilla-motion add venetian-blind` (they land in `./vanilla-motion/venetian-blind/`). ```ts import { mount } from "./vanilla-motion/venetian-blind/venetian-blind"; const container = document.querySelector("#hero")!; // give it a size // handle type: { setParams(next: Partial): void; dispose(): void } const fx = mount(container, { images: ["/img/one.jpg", "/img/two.jpg", "/img/three.jpg"], // omit to use the already inside direction: "reveal", columns: 8, }); fx.setParams({ stagger: 4, delay: 3500 }); // merged over the current params, applied live fx.dispose(); // stops the timer, restores the container's original children ``` Also exported: `defaults`, the `VenetianBlind` class (`static readonly version`), and the types `VenetianBlindParams`, `VenetianBlindDirection`, `VenetianBlindOptions` (= `Partial & { images?: string[] }`). No auto-init in the class. ### Params | Key | Default | Range / options | Meaning | | --- | --- | --- | --- | | `direction` | `"collapse"` | `"collapse"`, `"reveal"` | `collapse`: the outgoing slide's strips shrink to their centre. `reveal`: the incoming slide's strips grow from their centre. | | `columns` | `5` | 2 to 20, step 1 (the class clamps to at least 2) | Number of vertical strips. | | `stagger` | `10` | 0 to 20, step 0.5 | Start offset between neighbouring strips, as a percentage of the transition. `0` moves all strips together. | | `duration` | `800` | 100 to 4000 ms, step 50 | Length of one transition. | | `delay` | `2000` | 500 to 10000 ms, step 100 | Time a slide stays before the next transition. Changing it restarts the timer. | `setParams` applies `direction`, `columns`, `stagger` and `duration` immediately (the mask is rebuilt on every slide) and a running transition keeps going. Out-of-range values are not rejected by the class; the ranges are the control panel's. ## Key code ### HTML structure ```html
``` Slide images get `alt=""` (decorative); the first one loads eagerly with high fetch priority. Put real alt text on the surrounding content if the images carry meaning. ### CSS - the effect The class injects this once (`