{"id":395,"date":"2024-04-12T18:00:00","date_gmt":"2024-04-12T18:00:00","guid":{"rendered":"https:\/\/kerrprogroup.com\/?p=395"},"modified":"2025-03-19T12:20:50","modified_gmt":"2025-03-19T12:20:50","slug":"sliding-3d-image-frames-in-css","status":"publish","type":"post","link":"https:\/\/kerrprogroup.com\/index.php\/2024\/04\/12\/sliding-3d-image-frames-in-css\/","title":{"rendered":"Sliding 3D Image Frames In CSS"},"content":{"rendered":"

Sliding 3D Image Frames In CSS<\/title><\/p>\n<article>\n<header>\n<h1>Sliding 3D Image Frames In CSS<\/h1>\n<address>Temani Afif<\/address>\n<p> 2024-04-12T18:00:00+00:00<br \/>\n 2025-03-19T12:04:52+00:00<br \/>\n <\/header>\n<p><a href=\"https:\/\/www.smashingmagazine.com\/2023\/09\/revealing-images-css-mask-animations\/\">In a previous article<\/a>, we played with CSS masks to create cool hover effects where the main challenge was to rely only on the <code><img class=\"lazyload\"><\/code> tag as our markup. In this article, pick up where we left off by \u201crevealing\u201d the image from behind a sliding door sort of thing \u2014 like opening up a box and finding a photograph in it.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"LYaPPPo\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Image gift box (hover to reveal)](https:\/\/codepen.io\/smashingmag\/pen\/LYaPPPo) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/LYaPPPo\">Image gift box (hover to reveal)<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Pretty neat, right? You might think this is an easy thing to pull off. All we really need is an overlay above the image that we translate, and, boom, we\u2019re done, right?<\/p>\n<p>That\u2019s true. But if you check the code, you won\u2019t find any additional elements in the markup other than the exact same <code><img class=\"lazyload\"><\/code> tag we used last time. Plus, we cannot even use pseudo-elements to make this work. This is what makes such an effect a bit more challenging.<\/p>\n<p>Don\u2019t look at the code right now. Let\u2019s build it together by breaking the demo into isolated little CSS tricks.<\/p>\n<h2 id=\"the-image-and-sliding-overlay\">The Image And Sliding Overlay<\/h2>\n<p>You would be correct in thinking it\u2019s impossible to add an overlay to an image without an extra element. Instead, we are going to fake it and create the illusion of an overlay.<\/p>\n<p>Let\u2019s start with the following code:<\/p>\n<pre><code class=\"language-css\">img {\n --s: 200px; \/* the image size *\/\n \n width: var(--s);\n box-sizing: border-box;\n padding-right: var(--s);\n background: #8A9B0F;\n transition: 1s;\n}\nimg:hover {\n padding: 0;\n}\n<\/code><\/pre>\n<p>We have defined the <code>width<\/code> as a CSS variable (<code>--s<\/code>) and repurposed it to apply padding along the right side of the element. Combined with <code>box-sizing: border-box<\/code>, this will make the size of the content box equal to <code>0<\/code>. In other words, we don\u2019t see the image, but we see the background color since it covers the padding area.<\/p>\n<p>On hover, let\u2019s make the padding equal to <code>0<\/code>:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"jOJrqXo\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Padding animation to reveal the image](https:\/\/codepen.io\/smashingmag\/pen\/jOJrqXo) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/jOJrqXo\">Padding animation to reveal the image<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Nothing surprising, right? By <em>decreasing<\/em> the padding, we <em>increase<\/em> the size of the content box and it slowly reveals the image. We\u2019re basically squishing it vertically and allowing to widen back into place on hover.<\/p>\n<p>Let\u2019s add two more properties to the mix:<\/p>\n<pre><code class=\"language-css\">img {\n object-fit: cover;\n object-position: left;\n}\n<\/code><\/pre>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"oNVLLdM\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Adding object-* properties](https:\/\/codepen.io\/smashingmag\/pen\/oNVLLdM) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/oNVLLdM\">Adding object-* properties<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p><em>Tada!<\/em> The effect looks much better now, and we have an overlay reveal animation even if, in reality, the overlay you see is the background, which is behind the image! The illusion is perfect.<\/p>\n<p>Why does it behave like that? The logic is explained nicely over at <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/CSS\/object-fit#values\">MDN<\/a>:<\/p>\n<blockquote><p>\u201cThe replaced content is sized to maintain its aspect ratio while filling the element\u2019s entire content box. If the object\u2019s aspect ratio does not match the aspect ratio of its box, then the object will be clipped to fit.\u201d<\/p><\/blockquote>\n<p>In other words, the image will maintain its ratio while filling the content box. As a result, the image does not get distorted by the padding as we saw in the first demo \u2014 instead, it is clipped. Then, <code>object-position: left<\/code> aligns the position of the image to the left so it doesn\u2019t move while the size of the content box increases as a result of the decreased padding on hover.<\/p>\n<p>If we change the position to <code>right<\/code>, you get a different effect:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"xxBOOJW\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Using object-position: right](https:\/\/codepen.io\/smashingmag\/pen\/xxBOOJW) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/xxBOOJW\">Using object-position: right<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Instead of an overlay animation, we have a kind of sliding effect where the image enters from the left. This is directly related to another cool CSS trick that I used <a href=\"https:\/\/www.smashingmagazine.com\/2023\/10\/re-creating-pop-out-hover-effect-modern-css-part2\/\">in a previous article<\/a> to create a \u201cpop-out\u201d hover effect:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"VwqWRyj\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Fancy Pop Out Reveal hover effect!](https:\/\/codepen.io\/smashingmag\/pen\/VwqWRyj) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/VwqWRyj\">Fancy Pop Out Reveal hover effect!<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>For this article, we are going to rely on the first effect, where the image remains fixed. Here is a demo with all the sliding variations:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"GRYEZrr\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [A reveal hover effect with one element](https:\/\/codepen.io\/smashingmag\/pen\/GRYEZrr) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/GRYEZrr\">A reveal hover effect with one element<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>You will notice that it\u2019s pretty easy to switch between the different variations by toggling a couple of values in the CSS.<\/p>\n<h2 id=\"sliding-the-overlay-outside-the-image\">Sliding The Overlay Outside The Image<\/h2>\n<p>Now that we have our overlay, let\u2019s try to slide it outside of the image. Instead of decreasing its size like we did previously, we want it to maintain its size and move it.<\/p>\n<p>For this, let\u2019s use a <code>box-shadow<\/code> animation:<\/p>\n<pre><code class=\"language-css\">img {\n --s: 200px; \/* the image size *\/\n\n box-shadow: 0 0 #8A9B0F; \n}\nimg:hover {\n box-shadow: var(--s) 0 #8A9B0F;\n}\n<\/code><\/pre>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"KKEMvNq\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Adding box-shadow animation](https:\/\/codepen.io\/smashingmag\/pen\/KKEMvNq) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/KKEMvNq\">Adding box-shadow animation<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Cool, right? We have an overlay above our image that slides over to reveal the image \u2014 without using any extra elements in the markup or pseudo-elements in the styles!<\/p>\n<p>We can do the same effect using a <code>clip-path<\/code> animation as well.<\/p>\n<pre><code class=\"language-css\">img {\n --s: 200px; \/* the image size *\/\n\n box-shadow: 0 0 0 999px #8A9B0F; \n clip-path: inset(0 0 0 0);\n}\nimg:hover {\n clip-path: inset(0 -100% 0 0);\n}\n<\/code><\/pre>\n<p>We define a <code>box-shadow<\/code> as having a widespread radius, but we won\u2019t actually see it because it\u2019s clipped. On hover, though, we update the <code>inset()<\/code> value to reveal the <code>box-shadow<\/code> on the right side of the image.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"YzgWxxK\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Using clip-path instead of box-shadow](https:\/\/codepen.io\/smashingmag\/pen\/YzgWxxK) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/YzgWxxK\">Using clip-path instead of box-shadow<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Using the same technique, we can slide the overlay in whatever direction we want. Can you figure out how? Give it a shot by forking the Pen above and changing directions as an exercise before we move to the next part of our work.<\/p>\n<h2 id=\"adding-borders\">Adding Borders<\/h2>\n<p>Borders can help create space around the image and get it close to a square box shape. Don\u2019t forget that we want to create a 3D box in the end. But let\u2019s see what happens when we add borders.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"YzgWrvQ\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Adding border](https:\/\/codepen.io\/smashingmag\/pen\/YzgWrvQ) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/YzgWrvQ\">Adding border<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Hmm, not good. The border sits above the overlay, and the image isn\u2019t a perfect square, at least initially. Even if that seems glitchy at first, it\u2019s a logical outcome since the border is painted above the background, and its thickness adds up to the element\u2019s total size.<\/p>\n<p>What we need to do is adjust the padding to account for the border\u2019s size. Then, let\u2019s make the border transparent so that we can see the background color behind it.<\/p>\n<pre><code class=\"language-css\">img {\n --s: 200px; \/* the image size *\/\n --b: 10px; \/* border width *\/\n --c: #8A9B0F;\n \n width: var(--s);\n aspect-ratio: 1;\n box-sizing: border-box;\n padding-top: calc(var(--s) - 2*var(--b));\n border: var(--b) solid #0000;\n box-shadow: 0 0 0 999px var(--c); \n background: var(--c);\n clip-path: inset(0);\n object-fit: cover;\n object-position: bottom;\n}\nimg:hover {\n padding: 0;\n clip-path: inset(-100% 0 0);\n}\n<\/code><\/pre>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"mdoEBQX\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Fixing the border issue](https:\/\/codepen.io\/smashingmag\/pen\/mdoEBQX) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/mdoEBQX\">Fixing the border issue<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>This looks a lot better. It would be even better if we were to use a different color for the border area. Let\u2019s consider using <em>multiple<\/em> backgrounds.<\/p>\n<pre><code class=\"language-css\">img {\n --c: #8A9B0F;\n --_c: color-mix(in srgb, var(--c), #fff 25%);\n \n background:\n linear-gradient(var(--_c) 0 0) no-repeat\n 0 0 \/ 100% 100%,\n var(--c);\n background-origin: border-box;\n box-shadow: 0 0 0 999px var(--_c);\n \/* same as previous *\/\n}\nimg:hover {\n background-size: 100% 0%;\n \/* same as previous *\/\n}\n<\/code><\/pre>\n<p>First off, note that we\u2019ve added the <code>color-mix()<\/code> function that allows us to define a new color variation from the original color value (<code>--c:<\/code> <code>#8A9B0F<\/code>) by mixing it with white to get a brighter shade. Then, we use that new color to create a gradient above the element\u2019s background color, which is declared right after the gradient. The same color is also used for the <code>box-shadow<\/code>.<\/p>\n<p>The idea is to decrease the size of the gradient the same way we do with the padding so that the <code>background-color<\/code> behind the gradient is revealed.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"rNRLJpB\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Adding gradient animation](https:\/\/codepen.io\/smashingmag\/pen\/rNRLJpB) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/rNRLJpB\">Adding gradient animation<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>That\u2019s really nice! But did you catch the subtle visual issue? If you look closely, you can notice that the overlay is slightly out of alignment with the border.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/sliding-3d-image-frames-css\/1-border-overlay-intersect.png\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"312\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Capturing the overlay mid-slide and highlighting where the border and overlay intersect\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/sliding-3d-image-frames-css\/1-border-overlay-intersect.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/sliding-3d-image-frames-css\/1-border-overlay-intersect.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>This is because the padding has a transition that goes from <code>s - 2*b<\/code> to <code>0<\/code>. Meanwhile, the background transitions from <code>100%<\/code> (equivalent to <code>--s<\/code>) to <code>0<\/code>. There\u2019s a difference equal to <code>2*b<\/code>. The background covers the entire area, while the padding covers less of it. We need to account for this.<\/p>\n<p>Ideally, the padding transition would take less time to complete and have a small delay at the beginning to sync things up, but finding the correct timing won\u2019t be an easy task. Instead, let\u2019s increase the padding transition\u2019s range to make it equal to the background.<\/p>\n<pre><code class=\"language-css\">img {\n --h: calc(var(--s) - var(--b));\n padding-top: min(var(--h), var(--s) - 2*var(--b));\n transition: --h 1s linear;\n}\nimg:hover {\n --h: calc(-1 * var(--b));\n}\n<\/code><\/pre>\n<p>The new variable, <code>--h<\/code>, transitions from <code>s - b<\/code> to <code>-b<\/code> on hover, so we have the needed range since the difference is equal to <code>--s<\/code>, making it equal to the <code>background<\/code> and <code>clip-path<\/code> transitions.<\/p>\n<p>The trick is the <code>min()<\/code> function. When <code>--h<\/code> transitions from <code>s - b<\/code> to <code>s - 2*b<\/code>, the padding is equal to <code>s - 2*b<\/code>. No padding changes during that brief transition. Then, when <code>--h<\/code> reaches <code>0<\/code> and transitions from <code>0<\/code> to <code>-b<\/code>, the padding remains equal to <code>0<\/code> since, by default, it cannot be a negative value.<\/p>\n<p>It would be more intuitive to use <code>clamp()<\/code> instead:<\/p>\n<pre><code class=\"language-css\">padding-top: clamp(0px, var(--h), var(--s) - 2*var(--b));\n<\/code><\/pre>\n<p>That said, we don\u2019t need to specify the lower parameter since padding cannot be negative and will, by default, be clamped to <code>0<\/code> if you give it a negative value.<\/p>\n<p>We are getting much closer to the final result!<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"ExMgZbW\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Fixing the padding issue](https:\/\/codepen.io\/smashingmag\/pen\/ExMgZbW) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/ExMgZbW\">Fixing the padding issue<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Worth noting that we need to use <code>@property<\/code> to be able to apply a transition to the <code>--h<\/code> variable. The transition won\u2019t work in Firefox at the time of this writing.<\/p>\n<h2 id=\"the-3d-effect\">The 3D Effect<\/h2>\n<p>The last step is to add a touch of 3D to the effect. To better understand how we\u2019re going to approach this, let\u2019s temporarily remove the <code>box-shadow<\/code>, <code>clip-path<\/code>, and the <code>linear-gradient()<\/code> with the image in its revealed state.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"QWoKpyE\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [The revealed image with border](https:\/\/codepen.io\/smashingmag\/pen\/QWoKpyE) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/QWoKpyE\">The revealed image with border<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>We\u2019ll take three steps to create the 3D effect I have mapped out in the following figure.<\/p>\n<figure class=\"\n \n break-out article__image\n \n \n \"><\/p>\n<p> <a href=\"https:\/\/files.smashing.media\/articles\/sliding-3d-image-frames-css\/2-image-element-four-stages-3d-box.png\"><\/p>\n<p> <img decoding=\"async\" loading=\"lazy\" width=\"800\" height=\"217\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"The image element in four stages, starting with its initial state and a full 3D box in green for the final state.\" class=\"lazyload\" data-src=\"https:\/\/res.cloudinary.com\/indysigner\/image\/fetch\/f_auto,q_80\/w_400\/https:\/\/files.smashing.media\/articles\/sliding-3d-image-frames-css\/2-image-element-four-stages-3d-box.png\"><\/p>\n<p> <\/a><figcaption class=\"op-vertical-bottom\">\n (<a href=\"https:\/\/files.smashing.media\/articles\/sliding-3d-image-frames-css\/2-image-element-four-stages-3d-box.png\">Large preview<\/a>)<br \/>\n <\/figcaption><\/figure>\n<p>First, we increase the border\u2019s thickness on the left and bottom sides of the image:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">img {\n --b: 10px; \/* the image border *\/\n --d: 30px; \/* the depth *\/\n\n border: solid #0000;\n border-width: var(--b) var(--b) calc(var(--b) + var(--d)) calc(var(--b) + var(--d));\n}\n<\/code><\/pre>\n<\/div>\n<p>Second, we add a <code>conic-gradient()<\/code> on the background to create darker colors around the box:<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">background: \n conic-gradient(at left var(--d) bottom var(--d),\n #0000 25%,#0008 0 62.5%,#0004 0) \n var(--c);\n<\/code><\/pre>\n<\/div>\n<p>Notice the semi-transparent black color values (e.g., <code>#0008<\/code> and <code>#0004<\/code>). The slight bit of transparency blends with the colors behind it to create the illusion of a dark variation of the main color since the gradient is placed above the background color.<\/p>\n<p>And lastly, we apply a <code>clip-path<\/code> to cut out the corners that establish the 3D box.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">clip-path: polygon(var(--d) 0, 100% 0, 100% calc(100% - var(--d)), calc(100% - var(--d)) 100%, 0 100%, 0 var(--d));\n<\/code><\/pre>\n<\/div>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"JjzRWXZ\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [The image within a 3D box](https:\/\/codepen.io\/smashingmag\/pen\/JjzRWXZ) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/JjzRWXZ\">The image within a 3D box<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Now that we see and understand how the 3D effect is built let\u2019s put back the things we removed earlier, starting with the padding:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"ExMgWXR\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Putting back the padding animation](https:\/\/codepen.io\/smashingmag\/pen\/ExMgWXR) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/ExMgWXR\">Putting back the padding animation<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>It works fine. But note how we\u2019ve introduced the depth (<code>--d<\/code>) to the formula. That\u2019s because the bottom border is no longer equal to <code>b<\/code> but <code>b + d<\/code>.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">--h: calc(var(--s) - var(--b) - var(--d));\npadding-top: min(var(--h),var(--s) - 2*var(--b) - var(--d));\n<\/code><\/pre>\n<\/div>\n<p>Let\u2019s do the same thing with the linear gradient. We need to decrease its size so it covers the same area as it did before we introduced the depth so that it doesn\u2019t overlap with the conic gradient:<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"VwRKpzN\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Putting back the gradient animation](https:\/\/codepen.io\/smashingmag\/pen\/VwRKpzN) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/VwRKpzN\">Putting back the gradient animation<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>We are getting closer! The last piece we need to add back in from earlier is the <code>clip-path<\/code> transition that is combined with the <code>box-shadow<\/code>. We cannot reuse the same code we used before since we changed the <code>clip-path<\/code> value to create the 3D box shape. But we can still transition it to get the sliding result we want.<\/p>\n<p>The idea is to have two points at the top that move up and down to reveal and hide the <code>box-shadow<\/code> while the other points remain fixed. Here is a small video to illustrate the movement of the points.<\/p>\n<figure class=\"video-embed-container\">\n<div class=\"video-embed-container--wrapper\"><\/div>\n<\/figure>\n<p>See that? We have five fixed points. The two at the top move to increase the area of the polygon and reveal the box shadow.<\/p>\n<div class=\"break-out\">\n<pre><code class=\"language-css\">img {\n clip-path: polygon(\n var(--d) 0, \/* --> var(--d) calc(-1*(var(--s) - var(--d))) *\/\n 100% 0, \/* --> 100% calc(-1*(var(--s) - var(--d))) *\/\n \n \/* the fixed points *\/ \n 100% calc(100% - var(--d)), \/* 1 *\/\n calc(100% - var(--d)) 100%, \/* 2 *\/\n 0 100%, \/* 3 *\/\n 0 var(--d), \/* 4 *\/\n var(--d) 0); \/* 5 *\/\n}\n<\/code><\/pre>\n<\/div>\n<p>And we\u2019re done! We\u2019re left with a nice 3D frame around the image element with a cover that slides up and down on hover. And we did it with zero extra markup or reaching for pseudo-elements!<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"GRejXMK\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [3D image with reveal effect](https:\/\/codepen.io\/smashingmag\/pen\/GRejXMK) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/GRejXMK\">3D image with reveal effect<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>And here is the first demo I shared at the start of this article, showing the two sliding variations.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"LYaPPPo\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Image gift box (hover to reveal)](https:\/\/codepen.io\/smashingmag\/pen\/LYaPPPo) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/LYaPPPo\">Image gift box (hover to reveal)<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>This last demo is an optimized version of what we did together. I have written most of the formulas using the variable <code>--h<\/code> so that I only update one value on hover. It also includes another variation. Can you reverse-engineer it and see how its code differs from the one we did together?<\/p>\n<h2 id=\"one-more-3d-example\">One More 3D Example<\/h2>\n<p>Want another fancy effect that uses 3D effects and sliding overlays? Here\u2019s one I put together using a different 3D perspective where the overlay splits open rather than sliding from one side to the other.<\/p>\n<figure class=\"break-out\">\n<p data-height=\"480\" data-theme-id=\"light\" data-slug-hash=\"yLwBVGQ\" data-user=\"t_afif\" data-default-tab=\"result\" class=\"codepen\">See the Pen [Image gift box II (hover to reveal)](https:\/\/codepen.io\/smashingmag\/pen\/yLwBVGQ) by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/p><figcaption>See the Pen <a href=\"https:\/\/codepen.io\/smashingmag\/pen\/yLwBVGQ\">Image gift box II (hover to reveal)<\/a> by <a href=\"https:\/\/codepen.io\/t_afif\">Temani Afif<\/a>.<\/figcaption><\/figure>\n<p>Your homework is to dissect the code. It may look complex, but if you trace the steps we completed for the original demo, I think you\u2019ll find that it\u2019s not a terribly different approach. The sliding effect still combines the <code>padding<\/code>, the <code>object-*<\/code> properties, and <code>clip-path<\/code> but with different values to produce this new effect.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>I hope you enjoyed this little 3D image experiment and the fancy effect we applied to it. I know that adding an extra element (i.e., a parent <code><\/p>\n<div><\/div>\n<p><\/code> as a wrapper) to the markup would have made the effect a lot easier to achieve, as would pseudo-elements and translations. But we are here for the challenge and learning opportunity, right?<\/p>\n<p>Limiting the HTML to only a single element allows us to push the limits of CSS to discover new techniques that can save us time and bytes, especially in those situations where you might not have direct access to modify HTML, like when you\u2019re working in a CMS template. Don\u2019t look at this as an over-complicated exercise. It\u2019s an exercise that challenges us to leverage the power and flexibility of CSS.<\/p>\n<div class=\"signature\">\n <img decoding=\"async\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" alt=\"Smashing Editorial\" width=\"35\" height=\"46\" loading=\"lazy\" class=\"lazyload\" data-src=\"https:\/\/www.smashingmagazine.com\/images\/logo\/logo--red.png\"><br \/>\n <span>(gg, yk)<\/span>\n<\/div>\n<\/article>\n","protected":false},"excerpt":{"rendered":"<p>Sliding 3D Image Frames In CSS Sliding 3D Image Frames In CSS Temani Afif 2024-04-12T18:00:00+00:00 2025-03-19T12:04:52+00:00 In a previous article, we played with CSS masks to create cool hover effects where the main challenge was to rely only on the tag as our markup. In this article, pick up where we left off by \u201crevealing\u201d the image from behind a sliding door sort of thing \u2014 like opening up a…<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[11],"tags":[],"_links":{"self":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/395"}],"collection":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/comments?post=395"}],"version-history":[{"count":1,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/395\/revisions"}],"predecessor-version":[{"id":396,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/posts\/395\/revisions\/396"}],"wp:attachment":[{"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/media?parent=395"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/categories?post=395"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kerrprogroup.com\/index.php\/wp-json\/wp\/v2\/tags?post=395"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}