A Pen by Stan Williams on CodePen.
Created
December 16, 2025 08:36
-
-
Save stanwmusic/da4465dda9b625fd5851151a3c938771 to your computer and use it in GitHub Desktop.
Squircles Gallery with view-transition
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <div class="grid"> | |
| <div class="item" data-title="The Street"> | |
| <img src="https://picsum.photos/id/57/300/300" alt=""> | |
| </div> | |
| <div class="item" data-title="Looking Out"> | |
| <img src="https://picsum.photos/id/447/300/300" alt=""> | |
| </div> | |
| <div class="item" data-title="Freedom Above"> | |
| <img src="https://picsum.photos/id/315/300/300" alt=""> | |
| </div> | |
| <div class="item" data-title="Greener Grass"> | |
| <img src="https://picsum.photos/id/89/300/300" alt=""> | |
| </div> | |
| <div class="item" data-title="Relax"> | |
| <img src="https://picsum.photos/id/103/300/300" alt=""> | |
| </div> | |
| <div class="item" data-title="Road to Nowwhere" data-active="true"> | |
| <img src="https://picsum.photos/id/314/300/300" alt=""> | |
| </div> | |
| <div class="item" data-title="Spiraling"> | |
| <img src="https://picsum.photos/id/137/300/300" alt=""> | |
| </div> | |
| <div class="item" data-title="Abandoned"> | |
| <img src="https://picsum.photos/id/478/300/300" alt=""> | |
| </div> | |
| <div class="item" data-title="Path to Paradise"> | |
| <img src="https://picsum.photos/id/316/300/300" alt=""> | |
| </div> | |
| </div> | |
| <svg width="0" height="0" style="position: absolute;"> | |
| <defs> | |
| <clipPath id="squircleClip" clipPathUnits="objectBoundingBox"> | |
| <path d=" | |
| M 0.5 0.05 | |
| C 0.8 0.05 0.95 0.2 0.95 0.5 | |
| C 0.95 0.8 0.8 0.95 0.5 0.95 | |
| C 0.2 0.95 0.05 0.8 0.05 0.5 | |
| C 0.05 0.2 0.2 0.05 0.5 0.05 | |
| Z | |
| " /> | |
| </clipPath> | |
| </defs> | |
| </svg> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const grid = document.querySelector(".grid"); | |
| const items = Array.from(grid.children); | |
| let activeItem = grid.querySelector('.item[data-active="true"]'); | |
| // swap current item with new clicked item | |
| function swapItems(clickedItem) { | |
| if (clickedItem === activeItem) return; | |
| // store sibling reference for repositioning | |
| const activeNext = activeItem.nextElementSibling; | |
| // re-order DOM nodes: move clicked to active's position, active to clicked's position | |
| if (clickedItem === activeNext) { | |
| // if clicked is immediately after active, just swap positions | |
| grid.insertBefore(clickedItem, activeItem); | |
| } else { | |
| grid.insertBefore(activeItem, clickedItem); | |
| grid.insertBefore(clickedItem, activeNext); | |
| } | |
| // update active state | |
| activeItem.removeAttribute("data-active"); | |
| clickedItem.setAttribute("data-active", "true"); | |
| activeItem = clickedItem; | |
| } | |
| function handleClick(event) { | |
| const clickedItem = event.target.closest(".item"); | |
| if (!clickedItem || clickedItem === activeItem) return; | |
| if (document.startViewTransition) { | |
| document.startViewTransition(() => swapItems(clickedItem)); | |
| } else { | |
| // no transition for browsers that don't support viewTransition | |
| swapItems(clickedItem); | |
| } | |
| } | |
| items.forEach((item, index) => { | |
| item.style.viewTransitionName = `item-${index}`; | |
| item.addEventListener("click", handleClick); | |
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @import url(https://fonts.bunny.net/css?family=amatic-sc:400); | |
| @layers general,demo; | |
| @layer demo { | |
| :root { | |
| --grid-cols: repeat(2, 1fr); | |
| --grid-rows: 100px; | |
| --grid-cols-active: 1 / -1; | |
| --grid-rows-active: 3 / 5; | |
| --trans-swap-duration: 300ms; | |
| --trans-image-zoom: 1000ms; | |
| @media (700px < width) { | |
| --grid-cols: repeat(6, 1fr); | |
| --grid-rows: 140px; | |
| --grid-cols-active: 3 / 5; | |
| --grid-rows-active: 1 / 3; | |
| } | |
| } | |
| .grid { | |
| max-width: 1200px; | |
| display: grid; | |
| gap: 1rem; | |
| grid-template-columns: var(--grid-cols); | |
| grid-auto-rows: var(--grid-rows); | |
| } | |
| .grid:has(.item:hover) > .item:not(:hover) { | |
| /*--img-opacity: .27;*/ | |
| } | |
| .item { | |
| aspect-ratio: 1; | |
| overflow: hidden; | |
| -webkit-clip-path: url(#squircleClip); | |
| clip-path: url(#squircleClip); | |
| /*border: 1px solid light-dark(rgba(0 0 0 / 0.15), rgba(255 255 255 / 0.5));*/ | |
| transition: filter var(--trans-swap-duration) ease-in-out; | |
| filter: sepia(var(--img-filter, 0.75)); | |
| cursor: pointer; | |
| isolation: isolate; | |
| & > img { | |
| width: 100%; | |
| height: 100%; | |
| object-fit: cover; | |
| scale: var(--img-zoom, 1.5); | |
| opacity: var(--img-opacity, 1); | |
| transition: scale var(--trans-image-zoom) ease-in-out, | |
| opacity 150ms ease-in-out; | |
| } | |
| &:hover { | |
| --img-zoom: 1; | |
| } | |
| /* active item in center */ | |
| &[data-active="true"] { | |
| --img-filter: 0; | |
| grid-column: var(--grid-cols-active); | |
| grid-row: var(--grid-rows-active); | |
| z-index: 2; | |
| position: relative; | |
| &::before { | |
| content: ""; | |
| position: absolute; | |
| inset: 0; | |
| background: radial-gradient(circle at 50% 300%, rgb(0 0 0), transparent); | |
| z-index: 1; | |
| } | |
| &::after { | |
| content: attr(data-title); | |
| position: absolute; | |
| bottom: 0; | |
| left: 50%; | |
| translate: -50% -2rem; | |
| font-family: "Amatic SC", handwriting; | |
| font-size: 1.6rem; | |
| color: white; | |
| z-index: 2; | |
| transition: translate 500ms 300ms; | |
| transition-timing-function: linear( | |
| 0, | |
| 0.197 6.9%, | |
| 1 37.8%, | |
| 0.888 44.2%, | |
| 0.862 47.1%, | |
| 0.853 50%, | |
| 0.86 52.6%, | |
| 0.881 55.4%, | |
| 1 65.5%, | |
| 0.97 69.3%, | |
| 0.96 73%, | |
| 0.967 76.4%, | |
| 1 84.5%, | |
| 0.993 89.2%, | |
| 1 | |
| ); | |
| @starting-style { | |
| translate: -50% 90px; | |
| opacity: 0; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| /* general styling not relevant for this demo */ | |
| @layer base { | |
| * { | |
| box-sizing: border-box; | |
| } | |
| :root { | |
| color-scheme: light dark; | |
| --bg-dark: rgb(16, 24, 40); | |
| --bg-light: rgb(255, 237, 212); | |
| --txt-light: rgb(10, 10, 10); | |
| --txt-dark: rgb(245, 245, 245);); | |
| --line-light: rgba(0 0 0 / .25); | |
| --line-dark: rgba(255 255 255 / .25); | |
| --clr-bg: light-dark(var(--bg-light), var(--bg-dark)); | |
| --clr-txt: light-dark(var(--txt-light), var(--txt-dark)); | |
| --clr-lines: light-dark(var(--line-light), var(--line-dark)); | |
| } | |
| body { | |
| background-color: var(--clr-bg); | |
| color: var(--clr-txt); | |
| min-height: 100svh; | |
| margin: 0; | |
| padding: 2rem; | |
| font-family: "Jura", sans-serif; | |
| font-size: 1rem; | |
| line-height: 1.5; | |
| display: grid; | |
| place-items: center; | |
| gap: 2rem; | |
| & > * { | |
| /*outline: 1px dashed red;*/ | |
| } | |
| } | |
| h1 { | |
| margin: 0; | |
| font-size: 1.2rem; | |
| } | |
| /* screen-reader only */ | |
| .sr-only { | |
| position: absolute !important; | |
| width: 1px; height: 1px; | |
| padding: 0; margin: -1px; | |
| overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; | |
| border: 0; | |
| } | |
| .msg-supports { | |
| font-size: 0.8rem; | |
| /* | |
| @supports (animation-timeline: scroll()) { | |
| display: none; | |
| } | |
| @supports (order:sibling-index()) { | |
| display: none; | |
| } | |
| @supports (x: attr(x type(*))) { | |
| display: none; | |
| } | |
| */ | |
| } | |
| } | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment