Color lens
Grayscale cards get their color back under a soft lens that follows the pointer.
- enhancer
- hover
- pointer
- grid
- cards
- images
- since 0.2
Background Studio: tune it below, then copy the code
Your system asks for reduced motion: the demo shows its final state and nothing moves.
Live demo
Color lens example
Code
It follows the controls above: change an option and the code changes with it.
<link rel="stylesheet" href="/axi0/motion/motion.css?v=6">
<script src="/axi0/motion/motion.js?v=10" defer></script>
<div class="m-color-lens" data-motion="color-lens">
<figure data-color="#f6a04d"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%233b2a6b'/%3E%3Cstop offset='.55' stop-color='%23ff6b8b'/%3E%3Cstop offset='1' stop-color='%23ffb36b'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Ccircle cx='190' cy='238' r='44' fill='%23ffe2b0'/%3E%3Cpath d='M0 290 Q70 240 140 285 T300 270 V400 H0Z' fill='%238a3b6e'/%3E%3Cpath d='M0 330 Q90 290 170 330 T300 320 V400 H0Z' fill='%234a2152'/%3E%3Cpath d='M0 365 Q120 340 300 372 V400 H0Z' fill='%2324122e'/%3E%3C/svg%3E" alt=""><figcaption><b>Ember</b><span>Evening light</span></figcaption></figure>
<figure data-color="#3b82f6"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%239be7ff'/%3E%3Cstop offset='.5' stop-color='%233b82f6'/%3E%3Cstop offset='1' stop-color='%23172554'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Ccircle cx='220' cy='90' r='26' fill='%23f0fbff'/%3E%3Cpath d='M0 220 Q40 205 75 220 T150 220 T225 220 T300 220 V400 H0Z' fill='%232563eb' opacity='.7'/%3E%3Cpath d='M0 270 Q40 252 80 270 T160 270 T240 270 T320 270 V400 H0Z' fill='%231d4ed8' opacity='.8'/%3E%3Cpath d='M0 330 Q50 310 100 330 T200 330 T300 330 V400 H0Z' fill='%230f1f5c'/%3E%3C/svg%3E" alt=""><figcaption><b>Tide</b><span>Open water</span></figcaption></figure>
<figure data-color="#22c55e"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23e6fbd6'/%3E%3Cstop offset='1' stop-color='%237dd3a8'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Cpath d='M0 230 L60 160 L120 225 L190 140 L300 240 V400 H0Z' fill='%233f9f73'/%3E%3Cpath d='M0 290 L80 215 L150 280 L230 205 L300 260 V400 H0Z' fill='%23227552'/%3E%3Cpath d='M0 350 L100 290 L190 345 L300 300 V400 H0Z' fill='%230f3d2b'/%3E%3C/svg%3E" alt=""><figcaption><b>Fern</b><span>High ridges</span></figcaption></figure>
<figure data-color="#ff6b8b"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Crect width='300' height='400' fill='%23ffd6c2'/%3E%3Ccircle cx='70' cy='90' r='110' fill='%23ff9f8a'/%3E%3Ccircle cx='250' cy='300' r='130' fill='%23ff6b8b' opacity='.85'/%3E%3Ccircle cx='190' cy='150' r='60' fill='%23ffe9a8'/%3E%3Ccircle cx='90' cy='320' r='50' fill='%23f6a04d' opacity='.9'/%3E%3C/svg%3E" alt=""><figcaption><b>Coral</b><span>Soft shapes</span></figcaption></figure>
<figure data-color="#8a7dff"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%23c4b5fd'/%3E%3Cstop offset='1' stop-color='%234c1d95'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Ccircle cx='220' cy='110' r='90' fill='%238a7dff' opacity='.8'/%3E%3Ccircle cx='80' cy='280' r='110' fill='%236d28d9' opacity='.7'/%3E%3Ccircle cx='170' cy='250' r='40' fill='%23f5d0fe'/%3E%3C/svg%3E" alt=""><figcaption><b>Iris</b><span>Deep color</span></figcaption></figure>
<figure data-color="#f5b13d"><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 300 400' width='300' height='400' preserveAspectRatio='xMidYMid slice'%3E%3Cdefs%3E%3ClinearGradient id='a' x1='0' y1='0' x2='0' y2='1'%3E%3Cstop offset='0' stop-color='%23ffd9a0'/%3E%3Cstop offset='1' stop-color='%23f6a04d'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='300' height='400' fill='url%28%23a%29'/%3E%3Ccircle cx='80' cy='120' r='30' fill='%23fff4dc'/%3E%3Cpath d='M0 250 C80 200 150 300 300 230 V400 H0Z' fill='%23e07b39'/%3E%3Cpath d='M0 310 C110 260 180 340 300 300 V400 H0Z' fill='%23b9541f'/%3E%3Cpath d='M0 360 C90 330 200 380 300 350 V400 H0Z' fill='%237a3212'/%3E%3C/svg%3E" alt=""><figcaption><b>Dune</b><span>Warm sand</span></figcaption></figure>
</div>
import { ColorLens } from "./motion/react/ColorLens";
<ColorLens>Your content</ColorLens>
<script setup lang="ts">
import ColorLens from "./motion/vue/ColorLens.vue";
</script>
<template>
<ColorLens>Your content</ColorLens>
</template>
const el = document.querySelector("#title");
el.dataset.motion = "color-lens";
The wrappers: ColorLens.tsx (React, TypeScript) and ColorLens.vue (Vue). Both render the same attributes as the HTML; the loader does the rest.
Options
| Attribute | Default | What it does |
|---|---|---|
data-motion-radius | 260 | the lens radius in pixels |
data-motion-dim | 0.22 | how much darker the gray part is, 0 to 1 |
Accessibility
One overlay over the grid grays the backdrop through a mask, so cards keep their markup, links and text as they are (screen readers hear them unchanged). Keyboard focus on a card opens the lens over it; a tap opens it for a moment. Reduced motion: the lens jumps to the pointer without easing.
Size
color-lens.js is 1.7 KB gzip. A page loads the loader (2.7 KB) and core.js (1.4 KB) once, whatever it uses.