axi0motion

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

EmberEvening light
TideOpen water
FernHigh ridges
CoralSoft shapes
IrisDeep color
DuneWarm sand

Controls

260 px
0.22

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>

The wrappers: ColorLens.tsx (React, TypeScript) and ColorLens.vue (Vue). Both render the same attributes as the HTML; the loader does the rest.

Options

AttributeDefaultWhat it does
data-motion-radius260the lens radius in pixels
data-motion-dim0.22how 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.