<h2>Choose a character</h2>
<div class="gallery">
<div class="gallery__large">
<img src="https://assets.codepen.io/89905/matroshka-01.svg" alt="" title="" width="222" height="184" draggable="false">
</div>
<ul class="gallery__previews">
<li><a href="#" class="active"><img src="https://assets.codepen.io/89905/matroshka-01.svg" alt="" title="" width="222" height="184" draggable="false"></a></li>
<li><a href="#"><img src="https://assets.codepen.io/89905/matroshka-02.svg" alt="" title="" width="222" height="184" draggable="false"></a></li>
<li><a href="#"><img src="https://assets.codepen.io/89905/matroshka-03.svg" alt="" title="" width="222" height="184" draggable="false"></a></li>
<li><a href="#"><img src="https://assets.codepen.io/89905/matroshka-04.svg" alt="" title="" width="222" height="184" draggable="false"></a></li>
<li><a href="#"><img src="https://assets.codepen.io/89905/matroshka-05.svg" alt="" title="" width="222" height="184" draggable="false"></a></li>
</ul>
</div>
<p><label><input type="checkbox" id="skipViewTransitions"> Skip View Transitions</label></p>
<footer>
<p>This demo is part of <a href="https://goo.gle/css-wrapped-2023" target="_top">#CSSWrapped2023</a></p>
</footer>
:root {
view-transition-name: none;
}
::view-transition-group(*) {
animation-duration: 0.5s;
}
.gallery {
display: flex;
flex-direction: column;
gap: 0.5em;
padding: 1em;
border-radius: 0.5em;
background: transparent
linear-gradient(to bottom right, #ff00fa 0%, #0ff 100%);
border: 0.25em solid #6300ff;
}
.gallery__large {
& img {
width: 100%;
height: auto;
}
}
.gallery__previews {
display: flex;
flex-direction: row;
gap: 0.5em;
& a {
display: block;
border-radius: 0.5em;
background: #f4f4f4;
color: #6300ff;
border: 0.25em solid currentcolor;
text-decoration: none;
&:hover {
color: #eb16c4;
}
&.active {
color: #eb16c4;
position: relative;
&::before {
content: "✔";
position: absolute;
inset: 0;
font-size: 1.5em;
display: grid;
place-content: center;
}
& img {
opacity: 0;
}
}
}
& img {
max-width: 100%;
height: auto;
display: block;
}
}
@layer reset {
*,
*:after,
*:before {
box-sizing: border-box;
}
* {
margin: 0;
padding: 0;
}
ul[class] {
list-style: none;
}
label {
cursor: pointer;
max-width: max-content;
user-select: none;
}
}
@layer baselayout {
html {
margin: auto;
line-height: 1.75;
font-size: 1.25em;
font-family: "Syne", sans-serif;
min-height: 100%;
background: white;
}
body {
width: 100vmin;
max-width: 75ch;
margin: 0 auto;
min-height: 100dvh;
display: grid;
gap: 2em;
padding: 2em;
place-content: safe center;
}
footer {
font-style: italic;
}
h2 {
font-family: "Anybody", sans-serif;
text-decoration: underline;
text-decoration-color: hsl(156deg 100% 50% / 50%);
text-decoration-thickness: 0.2rem;
text-decoration-style: wavy;
text-decoration-skip-ink: none;
}
}
const loadImage = (path) => {
return new Promise((resolve, reject) => {
const img = new Image();
// img.crossOrigin = "Anonymous";
img.src = path;
img.onload = () => {
resolve(img);
};
img.onerror = (e) => {
reject(e);
};
});
};
const handleClick = async (e) => {
// Get references to the elements
const $link = e.target.closest("a");
const $gallery = e.target.closest(".gallery");
// Bail out
if (!$link || !$gallery) return false;
// Remove active from previous active link
$gallery.querySelector("a.active")?.classList.remove("active");
// Add .active to clicked link
$link.classList.add("active");
// Update main image
const $img = $link.querySelector("img");
const url = $img.getAttribute("src");
const img = await loadImage(url);
$gallery.querySelector(".gallery__large img").setAttribute("src", url);
};
document.querySelectorAll(".gallery").forEach(($gallery) => {
$gallery.querySelectorAll("a").forEach(($link) => {
$link.addEventListener("click", async (e) => {
// Prevent double clicks
if ($link.classList.contains("active")) return false;
e.preventDefault();
e.stopPropagation();
if (!document.startViewTransition || skipViewTransitions?.checked) {
handleClick(e);
} else {
const $curImage = $gallery.querySelector("a.active img");
const $newImage = $link.querySelector("img");
const $largeImage = $gallery.querySelector(".gallery__large img");
$newImage.style.viewTransitionName = "grow";
$largeImage.style.viewTransitionName = "shrink";
const t = document.startViewTransition(async () => {
$largeImage.style.viewTransitionName = "grow";
$curImage.style.viewTransitionName = "shrink";
$newImage.style.viewTransitionName = "none";
await handleClick(e);
});
await t.finished;
$curImage.style.viewTransitionName = "none";
$newImage.style.viewTransitionName = "none";
$largeImage.style.viewTransitionName = "none";
}
});
});
});
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.