{"id":8857,"date":"2026-03-19T10:24:50","date_gmt":"2026-03-19T15:24:50","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=8857"},"modified":"2026-03-19T10:24:51","modified_gmt":"2026-03-19T15:24:51","slug":"image-gallery-with-popovers-and-aim-anchor-interpolated-morph","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/image-gallery-with-popovers-and-aim-anchor-interpolated-morph\/","title":{"rendered":"Image Gallery with Popovers and AIM (Anchor-Interpolated Morph)"},"content":{"rendered":"\n<p>While I was learning about <a href=\"https:\/\/argyle.ink\/anchor-interpolated-morphing\">Adam&#8217;s AIM technique<\/a> the other day, it occurred to me that a cool use case would be a <strong>photo gallery<\/strong>. See what AIM (Anchor-Interpolated Morph) can do is animate the entrance and exit of elements <em>from other elements.<\/em> As in, use those other elements as literal CSS anchors.<\/p>\n\n\n\n<p>In this case, the &#8220;thumbnails&#8221; in a photo gallery could be the anchors, and we&#8217;d animate in the larger versions of those photos <em>from<\/em> those thumbnail anchors. And back out to them too. <\/p>\n\n\n\n<p>It&#8217;s a kind of animation that not only looks cool, but is literally helpful, reminding you where an element came from and where it went. Imagine you&#8217;re proofing some images from a magazine photo shoot one by one, it would be a helpful reminder as you&#8217;re clicking through them which one you just looked at. <\/p>\n\n\n\n<p>Here&#8217;s the demo:<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_019cb5ec-807e-727a-b18a-5eb41b0fc901\" src=\"\/\/codepen.io\/editor\/anon\/embed\/019cb5ec-807e-727a-b18a-5eb41b0fc901?height=750&amp;theme-id=1&amp;slug-hash=019cb5ec-807e-727a-b18a-5eb41b0fc901&amp;default-tab=result\" height=\"750\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed 019cb5ec-807e-727a-b18a-5eb41b0fc901\" title=\"CodePen Embed 019cb5ec-807e-727a-b18a-5eb41b0fc901\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<details class=\"wp-block-details is-layout-flow wp-block-details-is-layout-flow\"><summary>Here&#8217;s a video in case you&#8217;re on a browser that doesn&#8217;t support this particular cocktail of technology.<\/summary>\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t<\/details>\n\n\n\n<h2 class=\"wp-block-heading\">HTML and CSS Setup (No JavaScript by Default)<\/h2>\n\n\n\n<p>A (rather amazing) bonus of all this is how you can make a photo-grid like this, with the &#8220;open larger&#8221; ability, <strong>without any JavaScript<\/strong>. It&#8217;s really as simple as this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">popovertarget<\/span>=<span class=\"hljs-string\">\"img-1\"<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n    <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/images.unsplash.com\/photo-1506905925346-21bda4d32df4?w=700&amp;h=400&amp;fit=crop&amp;auto=format\"<\/span>\n    <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"Mountain landscape\"<\/span>\n    <span class=\"hljs-attr\">loading<\/span>=<span class=\"hljs-string\">\"lazy\"<\/span>\n  \/&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n  <span class=\"hljs-attr\">popover<\/span>\n  <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"img-1\"<\/span>\n  <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/images.unsplash.com\/photo-1506905925346-21bda4d32df4?w=1800&amp;auto=format\"<\/span>\n  <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"Mountain landscape\"<\/span>\n  <span class=\"hljs-attr\">loading<\/span>=<span class=\"hljs-string\">\"lazy\"<\/span>\n\/&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>That&#8217;s a <code>&lt;button&gt;<\/code> that points to a <code>popover<\/code>, so the default behavior is to toggle the popover open and closed when clicked. The &#8220;popover&#8221; is: <em>the larger image!<\/em> Notice I can slap the <code>popover<\/code> attribute right on the <code>&lt;img&gt;<\/code> that comes right after the button. <\/p>\n\n\n\n<p>That accomplishes the functionality we&#8217;re after. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Three States<\/h2>\n\n\n\n<p>The deal with AIM (or any animation technique you&#8217;re dealing with that has entry and exit animations) is that there are really <strong>three states<\/strong>. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">State 1: Before Open \/ On Way In<\/h3>\n\n\n\n<p>We&#8217;ll need to specifically make entry styles. Like the styles before the element (the &#8220;popover&#8221; in our case) enters the page. This can be done with <code>@keyframes<\/code>, but I prefer the explicitness (and <a href=\"https:\/\/www.joshwcomeau.com\/css\/starting-style\/#aside-update-new-benefit-discovered\">interruptibility<\/a>) of <code>@starting-style<\/code>. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/* The large image *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-selector-attr\">&#91;popover]<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  ... \n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* Before Open \/ On Way In Styles *\/<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>  @starting-style {\n<\/span><\/mark><mark class='shcb-loc'><span>  }\n<\/span><\/mark><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>But there is more to deal with here, as this along isn&#8217;t going to cut it. We&#8217;re going to need to consider our open styles first, because these starting styles need to have at least the same specificity as those, otherwise it won&#8217;t work. So let&#8217;s do those next.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">State 2: Open<\/h3>\n\n\n\n<p>We can be really specific with open state styles by using <code>:popover-open<\/code>. We could change a class in JavaScript or something too, but why? This is literally what popovers are for. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/* The Large Image *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-selector-attr\">&#91;popover]<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  ... \n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* Open Styles *\/<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>  &amp;:popover-open {\n<\/span><\/mark><mark class='shcb-loc'><span>  }\n<\/span><\/mark><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* Before Open \/ On Way In Styles *\/<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>  <span class=\"hljs-keyword\">@starting-style<\/span> {\n<\/span><\/mark><mark class='shcb-loc'><span>    &amp;<span class=\"hljs-selector-pseudo\">:popover-open<\/span> {\n<\/span><\/mark><mark class='shcb-loc'><span>    }\n<\/span><\/mark><mark class='shcb-loc'><span>  }\n<\/span><\/mark><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that I&#8217;m putting the starting styles <em>after<\/em> the open styles. To me that&#8217;s a smidge unintuitive, but <code>@starting-style<\/code> doesn&#8217;t have any specificity on it&#8217;s own, and we need to make sure the styles &#8220;beat&#8221; the open styles. We can win here by using the same selector and putting them later in the source order. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">State 3: After Closed \/ On Way Out<\/h3>\n\n\n\n<p>Interestingly, these styles are the styles just directly on the element without any special extra selectors needed. But me, I kinda like putting them into a special selector just for the explicit-ness of it. <\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/* The Large Image *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-selector-attr\">&#91;popover]<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  ... \n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* Open Styles *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  &amp;:popover-open {\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* Before Open Styles *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">@starting-style<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    &amp;<span class=\"hljs-selector-pseudo\">:popover-open<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><mark class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* After Closed \/ On Way Out Styles *\/<\/span>\n<\/span><\/mark><mark class='shcb-loc'><span>  &amp;<span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:popover-open)<\/span> {\n<\/span><\/mark><mark class='shcb-loc'><span>  }\n<\/span><\/mark><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Make sure it actually animates<\/h3>\n\n\n\n<p>Big ol&#8217; caveat here! One style that changes when a popover opens and closes is that it&#8217;s <code>display<\/code> value changes from <code>none<\/code> to <code>block<\/code>. This is going to pretty much totally nuke these entry and exit styles. <\/p>\n\n\n\n<p>There is a little trick in CSS to ensure that, even though that <code>display<\/code> value is changing, our animations still work. See, <em>normally,<\/em> the <code>display<\/code> value changes <em>immediately<\/em>, but if we specifically give it a <code>transition-behavior: allow-discrete<\/code> then it will change perfectly in time for entry and exist animations to run. <\/p>\n\n\n\n<p>So let&#8217;s do that, and apply the actual transitions.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-code-table\"><span class='shcb-loc'><span><span class=\"hljs-comment\">\/* The Large Image *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-selector-attr\">&#91;popover]<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>  @media (<span class=\"hljs-attribute\">prefers-reduced-motion<\/span>: no-preference) {\n<\/span><\/span><span class='shcb-loc'><span>    transition:\n<\/span><\/span><mark class='shcb-loc'><span>      display <span class=\"hljs-number\">1s<\/span> allow-discrete,\n<\/span><\/mark><span class='shcb-loc'><span>      translate <span class=\"hljs-number\">1s<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* Open Styles *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  &amp;<span class=\"hljs-selector-pseudo\">:popover-open<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* Before Open Styles *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">@starting-style<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    &amp;<span class=\"hljs-selector-pseudo\">:popover-open<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attribute\">translate<\/span>: -<span class=\"hljs-number\">100px<\/span> <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-comment\">\/* After Closed \/ On Way Out Styles *\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  &amp;<span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:popover-open)<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attribute\">translate<\/span>: <span class=\"hljs-number\">100px<\/span> <span class=\"hljs-number\">0<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>The transitions are applied with a <code>@media (prefers-reduced-motion: no-preference)<\/code> media query. This just removes them. You could also just tone them down. Lots of control here. <\/li>\n\n\n\n<li>The exit styles are <em>different<\/em> than the entry styles. Fun! <\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">The AIM Part<\/h2>\n\n\n\n<p>Those styles above just set the stage for entry and exit styles on popovers. As written, they open a popover by sliding in from the left and sliding out to the right. But with AIM, we want to be animating <strong>from the specific coordinates of another element.<\/strong><em> <\/em>That&#8217;s the &#8220;anchor&#8221; part of AIM. <\/p>\n\n\n\n<p>To do AIM, we make the <strong>entry styles<\/strong><em> <\/em>based on the top\/left\/width\/height of the anchor (a photo thumbnail) and we make the <strong>open styles<\/strong> whatever else we wanna do (open it larger and centered).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"878\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-19-at-9.13.24-AM.png?resize=1024%2C878&#038;ssl=1\" alt=\"\" class=\"wp-image-9020\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-19-at-9.13.24-AM.png?resize=1024%2C878&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-19-at-9.13.24-AM.png?resize=300%2C257&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-19-at-9.13.24-AM.png?resize=768%2C659&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/03\/Screenshot-2026-03-19-at-9.13.24-AM.png?w=1182&amp;ssl=1 1182w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<p>That looks like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-attr\">&#91;popover]<\/span> {\n  ...\n\n  <span class=\"hljs-attribute\">position-anchor<\/span>: --\u2693\ufe0e-morph;\n\n  <span class=\"hljs-attribute\">--speed<\/span>: <span class=\"hljs-number\">0.5s<\/span>;\n  @media (<span class=\"hljs-attribute\">prefers-reduced-motion<\/span>: no-preference) {\n    transition:\n      display <span class=\"hljs-built_in\">var<\/span>(--speed) allow-discrete,\n      height <span class=\"hljs-built_in\">var<\/span>(--speed) ease,\n      width <span class=\"hljs-built_in\">var<\/span>(--speed) ease,\n      top <span class=\"hljs-built_in\">var<\/span>(--speed) ease,\n      left <span class=\"hljs-built_in\">var<\/span>(--speed) ease;\n  }\n\n  &amp;<span class=\"hljs-selector-pseudo\">:popover-open<\/span> {\n    <span class=\"hljs-attribute\">height<\/span>: auto; <span class=\"hljs-comment\">\/* or fit-content *\/<\/span>\n    <span class=\"hljs-attribute\">max-height<\/span>: <span class=\"hljs-number\">70<\/span>dvb;\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-number\">70<\/span>dvi;\n    <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-number\">15<\/span>dvi;\n    <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-number\">15<\/span>dvb;\n  }\n\n  <span class=\"hljs-keyword\">@starting-style<\/span> {\n    &amp;<span class=\"hljs-selector-pseudo\">:popover-open<\/span> {\n      <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-built_in\">anchor<\/span>(left);\n      <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-built_in\">anchor<\/span>(top);\n      <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-built_in\">anchor-size<\/span>(width);\n      <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">anchor-size<\/span>(height);\n    }\n  }\n\n  &amp;<span class=\"hljs-selector-pseudo\">:not(<\/span><span class=\"hljs-selector-pseudo\">:popover-open)<\/span> {\n    <span class=\"hljs-attribute\">left<\/span>: <span class=\"hljs-built_in\">anchor<\/span>(left);\n    <span class=\"hljs-attribute\">top<\/span>: <span class=\"hljs-built_in\">anchor<\/span>(top);\n    <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-built_in\">anchor-size<\/span>(width);\n    <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">anchor-size<\/span>(height);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note that our entry and exit styles are <em>the same<\/em> here, which is actually nice. It shows where a photo came from and where it goes back to. Here&#8217;s that final demo again.<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_019cb5ec-807e-727a-b18a-5eb41b0fc901\" src=\"\/\/codepen.io\/editor\/anon\/embed\/019cb5ec-807e-727a-b18a-5eb41b0fc901?height=750&amp;theme-id=1&amp;slug-hash=019cb5ec-807e-727a-b18a-5eb41b0fc901&amp;default-tab=result\" height=\"750\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed 019cb5ec-807e-727a-b18a-5eb41b0fc901\" title=\"CodePen Embed 019cb5ec-807e-727a-b18a-5eb41b0fc901\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Note that the animation starts before the larger image is necessarily all the way loaded, which is a smidge awkward. You could remove the lazy loading, or figure out some JavaScript that will delay the action until it&#8217;s loaded or something.<\/p>\n\n\n\n<p>And here&#8217;s an example where an AIM exit style goes to a totally different anchor!<\/p>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_019c9184-da1d-76bf-b87b-2cb41fdff184\" src=\"\/\/codepen.io\/editor\/anon\/embed\/019c9184-da1d-76bf-b87b-2cb41fdff184?height=750&amp;theme-id=1&amp;slug-hash=019c9184-da1d-76bf-b87b-2cb41fdff184&amp;default-tab=result\" height=\"750\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed 019c9184-da1d-76bf-b87b-2cb41fdff184\" title=\"CodePen Embed 019c9184-da1d-76bf-b87b-2cb41fdff184\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n","protected":false},"excerpt":{"rendered":"<p>An image gallery is a nice example of AIM, where the larger version of an image can &#8220;morph&#8221; out from the smaller one when opened, and back in when closed.<\/p>\n","protected":false},"author":1,"featured_media":8972,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[466,121,100,7,133],"class_list":["post-8857","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-aim","tag-anchor","tag-animation","tag-css","tag-images"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2026\/03\/aim-scaled.jpg?fit=2560%2C1429&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8857","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=8857"}],"version-history":[{"count":16,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8857\/revisions"}],"predecessor-version":[{"id":9023,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/8857\/revisions\/9023"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/8972"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=8857"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=8857"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=8857"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}