From simple to advanced
Five steps from three images to a data-driven hero with your own controls. Each step is live; open any of them in the playground and keep going.
Step 1 — Three images
<!-- Step 1: three images. Responsive, swipeable, accessible. -->
<insigne-slider aria-label="Destinations">
<img src="https://picsum.photos/id/1015/1800/1100" alt="Fjord between mountains">
<img src="https://picsum.photos/id/1036/1800/1100" alt="Snowy forest">
<img src="https://picsum.photos/id/1043/1800/1100" alt="Rainy city street">
</insigne-slider>Already responsive (the height follows the photos, taller on phones), swipeable with momentum, keyboard-accessible, with dots, arrows on hover and a screen reader announcement per slide.
Step 2 — Backgrounds and content
Turn each image into a slide background with data-bg and put HTML on top. data-focus marks what must stay in frame; try the phone width in the playground.
<style>
.copy { position: absolute; left: 6%; bottom: 13%; right: 6%; color: #fff; }
.copy h2 { margin: 0; font-size: clamp(28px, 5.5vw, 58px); letter-spacing: -.03em; line-height: 1.04; }
.shade { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,.55)); }
</style>
<!-- Step 2: backgrounds with a focal point, and a headline on top. -->
<insigne-slider aria-label="Destinations">
<div>
<img data-bg src="https://picsum.photos/id/1015/1800/1100" alt="Fjord between mountains" data-focus="62% 45%">
<div class="shade"></div>
<div class="copy"><h2>Norway</h2></div>
</div>
<div>
<img data-bg src="https://picsum.photos/id/1036/1800/1100" alt="Snowy forest" data-focus="45% 55%">
<div class="shade"></div>
<div class="copy"><h2>Lapland</h2></div>
</div>
<div>
<img data-bg src="https://picsum.photos/id/1043/1800/1100" alt="Rainy city street">
<div class="shade"></div>
<div class="copy"><h2>Tokyo</h2></div>
</div>
</insigne-slider>Step 3 — Motion
An effect, autoplay with a pause ring, Ken Burns around the focus point, and two animated layers. Still only attributes.
<style>
.copy { position: absolute; left: 6%; bottom: 13%; right: 6%; color: #fff; }
.copy small { font: 600 12px system-ui; letter-spacing: .2em; text-transform: uppercase; opacity: .85; }
.copy h2 { margin: 10px 0 0; font-size: clamp(28px, 5.5vw, 58px); letter-spacing: -.03em; line-height: 1.04; }
.shade { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,.55)); }
</style>
<!-- Step 3: an effect, autoplay with a pause button, Ken Burns, and animated layers. -->
<insigne-slider aria-label="Destinations" effect="fade" autoplay="6000" ken-burns="alternate" pagination="fraction">
<div>
<img data-bg src="https://picsum.photos/id/1015/1800/1100" alt="Fjord between mountains" data-focus="62% 45%">
<div class="shade"></div>
<div class="copy"><small data-in="fade">01 · Scandinavia</small><h2 data-in="chars-up">Norway</h2></div>
</div>
<div>
<img data-bg src="https://picsum.photos/id/1036/1800/1100" alt="Snowy forest" data-focus="45% 55%">
<div class="shade"></div>
<div class="copy"><small data-in="fade">02 · Arctic</small><h2 data-in="chars-up">Lapland</h2></div>
</div>
<div>
<img data-bg src="https://picsum.photos/id/1043/1800/1100" alt="Rainy city street">
<div class="shade"></div>
<div class="copy"><small data-in="fade">03 · Japan</small><h2 data-in="chars-up">Tokyo</h2></div>
</div>
</insigne-slider>Step 4 — Premium touches
A WebGL transition on wide screens with a lighter CSS effect on narrow ones (breakpoints read the slider's width), sun beams, and brand colors through CSS custom properties.
<style>
.copy { position: absolute; left: 6%; bottom: 13%; right: 6%; color: #fff; }
.copy small { font: 600 12px system-ui; letter-spacing: .2em; text-transform: uppercase; opacity: .85; }
.copy h2 { margin: 10px 0 0; font-size: clamp(28px, 5.5vw, 58px); letter-spacing: -.03em; line-height: 1.04; }
.shade { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,.55)); }
/* Brand the controls from outside the shadow root */
insigne-slider { --ins-radius: 16px; --ins-arrow-bg: rgba(18,18,16,.72); --ins-arrow-fg: #f3f1ec; --ins-accent: #c9a35b; }
</style>
<!-- Step 4: a WebGL transition on desktop, a light CSS effect on narrow screens, and theming. -->
<insigne-slider aria-label="Destinations" effect="iris" autoplay="6000" ken-burns="alternate" pagination="fraction"
breakpoints='{"900":{"effect":"ripple"}}' overlay="beams">
<div>
<img data-bg src="https://picsum.photos/id/1015/1800/1100" alt="Fjord between mountains" data-focus="62% 45%">
<div class="shade"></div>
<div class="copy"><small data-in="fade">01 · Scandinavia</small><h2 data-in="chars-up">Norway</h2></div>
</div>
<div>
<img data-bg src="https://picsum.photos/id/1036/1800/1100" alt="Snowy forest" data-focus="45% 55%">
<div class="shade"></div>
<div class="copy"><small data-in="fade">02 · Arctic</small><h2 data-in="chars-up">Lapland</h2></div>
</div>
<div>
<img data-bg src="https://picsum.photos/id/1043/1800/1100" alt="Rainy city street">
<div class="shade"></div>
<div class="copy"><small data-in="fade">03 · Japan</small><h2 data-in="chars-up">Tokyo</h2></div>
</div>
</insigne-slider>Step 5 — Data, controls and events
The slides come from data (as they would from a CMS), the arrows are your own buttons, and events drive the UI around the slider. Text is inserted with textContent, so API data is safe.
<style>
.copy { position: absolute; left: 6%; bottom: 13%; right: 6%; color: #fff; }
.copy small { font: 600 12px system-ui; letter-spacing: .2em; text-transform: uppercase; opacity: .85; }
.copy h2 { margin: 10px 0 0; font-size: clamp(28px, 5.5vw, 58px); letter-spacing: -.03em; line-height: 1.04; }
.shade { position: absolute; inset: 0; background: linear-gradient(180deg, transparent 45%, rgba(0,0,0,.55)); }
.ui { display: flex; gap: 10px; align-items: center; margin-top: 14px; font: 14px system-ui; }
.ui button { font: inherit; padding: 6px 12px; border-radius: 99px; border: 1px solid #cfcac0; background: #fff; cursor: pointer; }
</style>
<!-- Step 5: slides from an API, your own controls, and events. -->
<div id="trip"></div>
<div class="ui">
<button id="prev">←</button><button id="next">→</button>
<button id="toggle">Pause</button>
<span id="status"></span>
</div>
<script type="module">
// Pretend this came from your CMS.
const places = [
{ id: 1015, name: 'Norway', region: 'Scandinavia', focus: '62% 45%' },
{ id: 1036, name: 'Lapland', region: 'Arctic', focus: '45% 55%' },
{ id: 1043, name: 'Tokyo', region: 'Japan', focus: '50% 50%' },
{ id: 1039, name: 'Oregon', region: 'Pacific Northwest', focus: '40% 50%' },
];
const slider = InsigneSlider.create('#trip', {
effect: 'fade',
autoplay: 6000,
kenBurns: 'alternate',
arrows: 'hidden',
pagination: 'progress',
label: 'Destinations',
slides: places.map((p, i) => ({
bg: { src: `https://picsum.photos/id/${p.id}/1800/1100`, alt: p.name, focus: p.focus, lazy: i > 0 },
html: '<div class="shade"></div>',
contentClass: 'copy',
layers: [
{ tag: 'small', text: `0${i + 1} · ${p.region}`, in: 'fade' },
{ tag: 'h2', text: p.name, in: 'chars-up' },
],
})),
});
const $ = (s) => document.querySelector(s);
$('#prev').onclick = () => slider.prev();
$('#next').onclick = () => slider.next();
$('#toggle').onclick = () => {
slider.isPlaying ? slider.pause() : slider.play();
$('#toggle').textContent = slider.isPlaying ? 'Pause' : 'Play';
};
slider.on('change', ({ index }) => ($('#status').textContent = `${places[index].name} — ${index + 1} of ${slider.count}`));
</script>Where to go from here
- Fine-tune any option in the options reference.
- Write your own animation recipes.
- Browse finished designs in templates.