Welcome to Vanilla Breeze
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
This bell pulls live notifications from /go/notify/messages — the same contract documented at /docs/concepts/service-contracts/. Static articles like this one are the no-JS / no-backend fallback.
Before/after image comparison slider with drag handle, keyboard support, and progressive enhancement.
The <compare-surface> component creates a before/after comparison slider. Two children overlay each other, and a draggable divider reveals one side. Works with any two children — images, divs, or any other elements.
<compare-surface> <img src="before.jpg" alt="Before" /> <img src="after.jpg" alt="After" /></compare-surface>
| Attribute | Type | Default | Description |
|---|---|---|---|
position |
number | 50 |
Initial slider position from 0 (fully left) to 100 (fully right). |
Set position to control where the divider starts. A value of 25 means the divider starts 25% from the left, showing mostly the “after” content.
<!-- Start at 25% --><compare-surface position="25"> <img src="before.jpg" alt="Before" /> <img src="after.jpg" alt="After" /></compare-surface>
The component works with any two child elements, not just images. Use divs, text blocks, or any HTML content.
Draft
The original version before revisions.
Final
The polished version with improved clarity.
<compare-surface> <div class="panel-before">Draft version</div> <div class="panel-after">Final version</div></compare-surface>
| Key | Action |
|---|---|
| ArrowLeft / ArrowDown | Move divider 1% left |
| ArrowRight / ArrowUp | Move divider 1% right |
| Shift + Arrow | Move divider 10% in the arrow direction |
| Event | Detail | Description |
|---|---|---|
compare-surface:change |
{ position: number } |
Fired when the slider position changes (drag or keyboard). |
const slider = document.querySelector('compare-surface'); slider.addEventListener('compare-surface:change', (event) => { console.log('Position:', event.detail.position);});
The divider has role="slider" with aria-valuemin, aria-valuemax, and aria-valuenow. Screen readers announce position changes as the slider moves.
The divider is focusable (tabindex="0") and responds to arrow keys. Shift + arrow provides larger steps for faster navigation.
The divider uses touch-action: none and setPointerCapture for reliable touch/pointer drag behavior across devices.
The divider and handle can be styled with CSS. The divider is a .comparison-divider element with a ::after pseudo-element for the circular handle.
/* Custom divider color */.comparison-divider { background: var(--color-interactive);} /* Custom handle */.comparison-divider::after { background: var(--color-interactive); border-color: white;}
Without JavaScript, both children display side-by-side in a natural grid. The :not(:defined) selector provides the fallback layout. Once JS registers the component, children overlap and the clip-path slider takes over.
/* Without JS: side-by-side grid */compare-surface:not(:defined) { display: grid; grid-template-columns: 1fr 1fr; gap: var(--size-s);}
<content-swap> — Two-face content toggle with transitions<tab-set> — Tab panels for content switching