diff --git a/submissions/examples/image-comparison/README.md b/submissions/examples/image-comparison/README.md new file mode 100644 index 00000000..171b83c7 --- /dev/null +++ b/submissions/examples/image-comparison/README.md @@ -0,0 +1,35 @@ +# Image Comparison Component + +A before/after image comparison slider with a draggable vertical handle. Users can click and drag (or use arrow keys) to reveal more of one image or the other. Includes mouse, touch, and keyboard support with inline JavaScript. + +## Classes + +| Class | Description | +|---|---| +| `ease-image-comparison` | Container (`role="slider"`, keyboard-focusable) | +| `ease-image-comparison-after` | Full "after" image | +| `ease-image-comparison-before` | Clipped "before" wrapper | +| `ease-image-comparison-handle` | Draggable divider (line + circular grip) | +| `ease-image-comparison-label` | Shared label style | +| `ease-image-comparison-label--before` | "Before" label (bottom-left) | +| `ease-image-comparison-label--after` | "After" label (bottom-right) | + +## Usage + +```html +
+ After +
+ Before +
+
+ Before + After +
+``` + +See `demo.html` for the full JS setup (mouse drag, touch drag, arrow keys). + +## Why it fits EaseMotion CSS + +Pure CSS styling for the comparison container, clipped overlay, and handle with circular grip icon. Handle grip has a subtle hover scale effect. Fully responsive by default. Respects `prefers-reduced-motion`. diff --git a/submissions/examples/image-comparison/demo.html b/submissions/examples/image-comparison/demo.html new file mode 100644 index 00000000..1ebccdca --- /dev/null +++ b/submissions/examples/image-comparison/demo.html @@ -0,0 +1,81 @@ + + + + + + Image Comparison — EaseMotion CSS + + + + +
+ After +
+ Before +
+
+ Before + After +
+ + + + + diff --git a/submissions/examples/image-comparison/style.css b/submissions/examples/image-comparison/style.css new file mode 100644 index 00000000..17d805ce --- /dev/null +++ b/submissions/examples/image-comparison/style.css @@ -0,0 +1,125 @@ +/* ============================================================ + EaseMotion CSS — Before/After Image Comparison Slider + Issue #1199 + ============================================================ */ + +/* ── Container ────────────────────────────────────────────── */ + +.ease-image-comparison { + position: relative; + display: inline-block; + overflow: hidden; + cursor: ew-resize; + border-radius: var(--ease-radius-lg, 0.875rem); + user-select: none; + -webkit-user-select: none; + max-width: 100%; + outline: none; +} + +.ease-image-comparison:focus-visible { + box-shadow: 0 0 0 3px rgba(124, 108, 255, 0.35); +} + +/* ── After image (full width) ────────────────────────────── */ + +.ease-image-comparison-after { + display: block; + width: 100%; + height: auto; + pointer-events: none; +} + +/* ── Before image (clipped) ──────────────────────────────── */ + +.ease-image-comparison-before { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 50%; + overflow: hidden; +} + +.ease-image-comparison-before img { + display: block; + height: 100%; + width: auto; + max-width: none; + position: absolute; + left: 0; + top: 0; +} + +/* ── Handle ──────────────────────────────────────────────── */ + +.ease-image-comparison-handle { + position: absolute; + top: 0; + bottom: 0; + left: 50%; + width: 3px; + background: #fff; + box-shadow: 0 0 8px rgba(0, 0, 0, 0.35); + transform: translateX(-50%); + z-index: 2; + pointer-events: none; + transition: background var(--ease-speed-fast, 150ms) cubic-bezier(0.4, 0, 0.2, 1); +} + +.ease-image-comparison-handle::after { + content: ""; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 36px; + height: 36px; + border-radius: 50%; + background: #fff; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25); + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 5l-6 7 6 7'/%3E%3Cpath d='M16 5l6 7-6 7'/%3E%3C/svg%3E"); + background-size: 18px; + background-position: center; + background-repeat: no-repeat; + transition: transform var(--ease-speed-fast, 150ms) cubic-bezier(0.4, 0, 0.2, 1); +} + +.ease-image-comparison:hover .ease-image-comparison-handle::after { + transform: translate(-50%, -50%) scale(1.1); +} + +/* ── Labels ──────────────────────────────────────────────── */ + +.ease-image-comparison-label { + position: absolute; + bottom: 16px; + padding: 4px 12px; + border-radius: var(--ease-radius-sm, 4px); + background: rgba(0, 0, 0, 0.55); + color: #fff; + font-size: var(--ease-text-xs, 0.75rem); + font-weight: 500; + pointer-events: none; + z-index: 3; + backdrop-filter: blur(4px); +} + +.ease-image-comparison-label--before { + left: 16px; +} + +.ease-image-comparison-label--after { + right: 16px; +} + +/* ── Reduced motion ──────────────────────────────────────── */ + +@media (prefers-reduced-motion: reduce) { + .ease-image-comparison-handle::after { + transition: none; + } + .ease-image-comparison:hover .ease-image-comparison-handle::after { + transform: translate(-50%, -50%); + } +}