axi0motion

Masonry

Pictures in columns of their own heights that rise into place the first time they come into view, glide to new places when the columns change and open full size.

  • enhancer
  • gallery
  • images
  • grid
  • lightbox
  • columns
  • 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

Masonry example

A low sun over three rows of dunes at dusk
Dunes at dusk
A pale moon over a calm blue harbor
Harbor night
Red canyon walls under a peach morning sky
Canyon morning
Green hills rolling away under a soft sun
Green hills
Purple ridges under a pink sunset
Violet hour
Orange desert ridges at noon
High desert
Dark peaks under a moonlit sky
Moonlit peaks
Icy blue hills by a frozen lake
Frozen lake
Dusk falling over red dunes
Last light

Controls

3
150 px
14 px
55 ms

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 data-motion="masonry" aria-label="Field notes">
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='520' viewBox='0 0 400 520'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%231d1033'/%3E%3Cstop offset='1' stop-color='%23f08a4b'/%3E%3C/linearGradient%3E%3Crect width='400' height='520' fill='url(%23s)'/%3E%3Ccircle cx='137' cy='146' r='35' fill='%23ffd27a'/%3E%3Cpath d='M0 275 Q50 317 100 275 T200 289 T300 264 T400 275 V520 H0Z' fill='%237a3b52'/%3E%3Cpath d='M0 344 Q50 395 100 300 T200 342 T300 310 T400 344 V520 H0Z' fill='%234a2440'/%3E%3Cpath d='M0 419 Q50 377 100 397 T200 430 T300 391 T400 419 V520 H0Z' fill='%2324132b'/%3E%3C/svg%3E" alt="A low sun over three rows of dunes at dusk" width="400" height="520"><figcaption>Dunes at dusk</figcaption></figure>
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='300' viewBox='0 0 400 300'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%230b2340'/%3E%3Cstop offset='1' stop-color='%235fa8d3'/%3E%3C/linearGradient%3E%3Crect width='400' height='300' fill='url(%23s)'/%3E%3Ccircle cx='217' cy='79' r='29' fill='%23f4f1de'/%3E%3Cpath d='M0 153 Q50 125 100 135 T200 143 T300 138 T400 153 V300 H0Z' fill='%232f5d7c'/%3E%3Cpath d='M0 199 Q50 181 100 181 T200 191 T300 183 T400 199 V300 H0Z' fill='%231d3f5a'/%3E%3Cpath d='M0 249 Q50 269 100 235 T200 248 T300 234 T400 249 V300 H0Z' fill='%230f2638'/%3E%3C/svg%3E" alt="A pale moon over a calm blue harbor" width="400" height="300"><figcaption>Harbor night</figcaption></figure>
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='460' viewBox='0 0 400 460'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%23ffb48a'/%3E%3Cstop offset='1' stop-color='%23ffe3c4'/%3E%3C/linearGradient%3E%3Crect width='400' height='460' fill='url(%23s)'/%3E%3Ccircle cx='205' cy='147' r='47' fill='%23fff4e0'/%3E%3Cpath d='M0 236 Q50 196 100 209 T200 220 T300 235 T400 236 V460 H0Z' fill='%23e07a5f'/%3E%3Cpath d='M0 307 Q50 348 100 287 T200 300 T300 273 T400 307 V460 H0Z' fill='%23b85042'/%3E%3Cpath d='M0 389 Q50 357 100 387 T200 399 T300 361 T400 389 V460 H0Z' fill='%237a2e2e'/%3E%3C/svg%3E" alt="Red canyon walls under a peach morning sky" width="400" height="460"><figcaption>Canyon morning</figcaption></figure>
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='340' viewBox='0 0 400 340'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%230f2b1d'/%3E%3Cstop offset='1' stop-color='%237fb685'/%3E%3C/linearGradient%3E%3Crect width='400' height='340' fill='url(%23s)'/%3E%3Ccircle cx='250' cy='110' r='26' fill='%23f2e8cf'/%3E%3Cpath d='M0 186 Q50 166 100 185 T200 192 T300 183 T400 186 V340 H0Z' fill='%233a6b47'/%3E%3Cpath d='M0 233 Q50 203 100 220 T200 243 T300 204 T400 233 V340 H0Z' fill='%2324492f'/%3E%3Cpath d='M0 273 Q50 248 100 254 T200 272 T300 266 T400 273 V340 H0Z' fill='%23132a1b'/%3E%3C/svg%3E" alt="Green hills rolling away under a soft sun" width="400" height="340"><figcaption>Green hills</figcaption></figure>
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='560' viewBox='0 0 400 560'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%232b1055'/%3E%3Cstop offset='1' stop-color='%23d53a9d'/%3E%3C/linearGradient%3E%3Crect width='400' height='560' fill='url(%23s)'/%3E%3Ccircle cx='82' cy='121' r='35' fill='%23ffdd99'/%3E%3Cpath d='M0 299 Q50 331 100 291 T200 307 T300 294 T400 299 V560 H0Z' fill='%237e3aa1'/%3E%3Cpath d='M0 384 Q50 431 100 359 T200 382 T300 365 T400 384 V560 H0Z' fill='%234a2071'/%3E%3Cpath d='M0 475 Q50 506 100 447 T200 472 T300 469 T400 475 V560 H0Z' fill='%23200b3a'/%3E%3C/svg%3E" alt="Purple ridges under a pink sunset" width="400" height="560"><figcaption>Violet hour</figcaption></figure>
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='300' viewBox='0 0 400 300'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%23f6a04d'/%3E%3Cstop offset='1' stop-color='%23ffd8a8'/%3E%3C/linearGradient%3E%3Crect width='400' height='300' fill='url(%23s)'/%3E%3Ccircle cx='233' cy='79' r='31' fill='%23fff7ea'/%3E%3Cpath d='M0 150 Q50 176 100 135 T200 142 T300 142 T400 150 V300 H0Z' fill='%23d9824a'/%3E%3Cpath d='M0 199 Q50 179 100 189 T200 208 T300 187 T400 199 V300 H0Z' fill='%23a65a2e'/%3E%3Cpath d='M0 243 Q50 219 100 239 T200 243 T300 243 T400 243 V300 H0Z' fill='%235c2f17'/%3E%3C/svg%3E" alt="Orange desert ridges at noon" width="400" height="300"><figcaption>High desert</figcaption></figure>
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='420' viewBox='0 0 400 420'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%2310141f'/%3E%3Cstop offset='1' stop-color='%233c4f76'/%3E%3C/linearGradient%3E%3Crect width='400' height='420' fill='url(%23s)'/%3E%3Ccircle cx='145' cy='114' r='29' fill='%23e6e9f2'/%3E%3Cpath d='M0 217 Q50 239 100 210 T200 208 T300 210 T400 217 V420 H0Z' fill='%232a3550'/%3E%3Cpath d='M0 288 Q50 266 100 285 T200 295 T300 272 T400 288 V420 H0Z' fill='%231b2338'/%3E%3Cpath d='M0 340 Q50 301 100 337 T200 336 T300 313 T400 340 V420 H0Z' fill='%230d111c'/%3E%3C/svg%3E" alt="Dark peaks under a moonlit sky" width="400" height="420"><figcaption>Moonlit peaks</figcaption></figure>
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='360' viewBox='0 0 400 360'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%23a1d6e2'/%3E%3Cstop offset='1' stop-color='%23f1f1f2'/%3E%3C/linearGradient%3E%3Crect width='400' height='360' fill='url(%23s)'/%3E%3Ccircle cx='314' cy='74' r='38' fill='%23ffffff'/%3E%3Cpath d='M0 188 Q50 223 100 156 T200 176 T300 153 T400 188 V360 H0Z' fill='%237fb3c8'/%3E%3Cpath d='M0 241 Q50 262 100 227 T200 231 T300 233 T400 241 V360 H0Z' fill='%234f8aa3'/%3E%3Cpath d='M0 288 Q50 307 100 283 T200 283 T300 273 T400 288 V360 H0Z' fill='%232c5d73'/%3E%3C/svg%3E" alt="Icy blue hills by a frozen lake" width="400" height="360"><figcaption>Frozen lake</figcaption></figure>
  <figure><img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='480' viewBox='0 0 400 480'%3E%3ClinearGradient id='s' x2='0' y2='1'%3E%3Cstop stop-color='%231d1033'/%3E%3Cstop offset='1' stop-color='%23f08a4b'/%3E%3C/linearGradient%3E%3Crect width='400' height='480' fill='url(%23s)'/%3E%3Ccircle cx='133' cy='163' r='46' fill='%23ffd27a'/%3E%3Cpath d='M0 240 Q50 196 100 210 T200 218 T300 203 T400 240 V480 H0Z' fill='%237a3b52'/%3E%3Cpath d='M0 321 Q50 277 100 288 T200 330 T300 306 T400 321 V480 H0Z' fill='%234a2440'/%3E%3Cpath d='M0 400 Q50 352 100 357 T200 381 T300 369 T400 400 V480 H0Z' fill='%2324132b'/%3E%3C/svg%3E" alt="Dusk falling over red dunes" width="400" height="480"><figcaption>Last light</figcaption></figure>
</div>

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

Options

AttributeDefaultWhat it does
data-motion-columns3the most columns it uses
data-motion-width150the narrowest a column may get before it drops one
data-motion-gap14space between pictures
data-motion-stagger55the beat between pictures as they rise in

Accessibility

- Every picture becomes a button named after its alt text ("Open A low sun over three rows of dunes at dusk"); links keep their href, so without JavaScript they open the picture itself. - The viewer is the browser's own modal dialog: focus stays inside it, Escape or the backdrop closes it, Left and Right step through the pictures, and focus returns to the picture that opened it. - Pictures keep the reading order of the page; the columns come from CSS, so nothing waits for JavaScript to lay them out. - The entrance runs only for a gallery still below the screen; reduced motion skips it, and the glide when columns change. Reduced motion shows every state at once, with no movement, and stops anything that would move on its own. Frame loops and CSS animations pause off screen and in hidden tabs. Only transform, opacity and paint animate, so nothing around it moves.

Size

masonry.js is 1.6 KB gzip, with components-kit.js 3.9 KB it needs. A page loads the loader (2.7 KB) and core.js (1.4 KB) once, whatever it uses.