You know how you can get cardboard boxes that come totally flat? You fold ‘em up and tape ‘em to make them into a useful box. Then when it’s time to recycle them, you cut them back apart to flatten them. Recently, someone reached out to me about essentially this concept as a 3D animation and I thought it would make an interesting tutorial to do it entirely in CSS, so here we are! Show How might that animation look? How could we create that packing timeline? Could the sizing be flexible? Let’s make a pure CSS package toggle. Here’s what we’re working towards. Tap to pack and unpack the cardboard box. CodePen Embed Fallback Where to start?Where do you even start with something like this? It’s best to plan ahead. We know we’re going to have a template for our package. And that will need folding up in three dimensions. If working with 3D in CSS is new to you, I recommend this article to get you started. If you’re familiar with 3D CSS, it might be tempting to construct a cuboid and go from there. But, that’s going to pose some problems. We need to consider how a package goes from 2D to 3D. Let’s start by creating a template. We need to plan ahead with our markup and think about how we want our packing animation to work. Let’s start with some HTML.
Mixins are a good ideaThere’s quite a bit happening there. It’s a lot of 6s. I often like to use Pug for generating markup so I can split things up into reusable blocks. For example, every side will have two flaps. We can create a Pug mixin for the sides and use attributes to apply a modifier class name to make all that markup a lot easier to write.
We’re using two mixins. One creates the flaps for each side of the box. The other creates the sides of the box. Notice in the 7 mixin we are making use of 8. That is where children of mixin usage get rendered which is particularly useful, as we need to nest some of the sides to make our lives easier later.Our generated markup:
Nesting the sidesNesting the sides makes it easier to fold up our package. Much like each side has two flaps. The children of a side can inherit the sides’ transform and then apply their own. If we started with a cuboid, it would be hard to leverage this. Check out this demo that flips between nested and non-nested elements to see the difference in action. CodePen Embed Fallback Each box has a 9 set to the bottom right corner with 0. Checking the “Transform” toggle rotates each box 1. But, see how the behavior of that 2 changes if we nest the elements.We’re flipping between the two versions of markup but not changing anything else. Nested:
Not nested:
Transforming all the thingsAfter applying some styles to our HTML, we have our package template. CodePen Embed Fallback The styles specify the different colors and position the sides to the package. Each side gets a position that’s relative to the “main” side. (You’ll see why all that nesting is useful in a moment.) There are some things to be aware of. Much like working with cuboids, we are using 3, 4, and 5 variables for sizing. This will make it easier to change our package sizing down the line.
Why define sizing like this? We are using a unit-less default sizing of 6, an idea I picked up from Lea Verou’s 2016 CSS ConfAsia talk (starting at 52:44). Using custom properties as “data” instead of “values,” we are free to do what we want with them using 7. Additionally, JavaScript doesn’t have to care about value units and we can change to pixels, a percentage, etc., without having to make changes elsewhere. You could refactor this into a coefficient in the 8, but it could also quickly become overkill.The flaps for each side also need a size ever so smaller than the sides they are a part of. This is so we can see a slight gap as we would in real life. Also, the flaps on two sides need to sit a little lower. This is so that when we fold them up, we don’t get 9 fighting between them.
We’re also starting to consider the 9 for the individual pieces. A top flap will rotate from its bottom edge and a bottom flap will rotate from its top edge.We can use a pseudo-element for the tab on that right side. We are using 1 to get that desired shape.
Let’s start working with our template on a 3D plane. We can start by rotating the 2 on the X and Y axis.
CodePen Embed Fallback Folding upWe’re ready to start folding up our template! Our template will fold up based on a custom property, 3. If the value is 4, then we can fold up the template. For example, let’s fold some of the sides and the pseudo-element tab.
CodePen Embed Fallback Or, we could write a rule for all sides that aren’t the “main” one. 0And that would cover all the sides. CodePen Embed Fallback Remember when I said the nested sides allow us to inherit a parent’s transform? If we update our demo so we can change the value of 3, we can see how the value affects the transforms. Try sliding the 3 value between 4 and 8 and you’ll see exactly what I mean.CodePen Embed Fallback Now that we have a way to toggle the folding state of our template, we can start working on some motion. Our previous demo flips between the two states. We can make use of 9 for that. The quickest way? Add a 9 to the 2 of every child in the 2. 1CodePen Embed Fallback Multi-step transitions!But we don’t fold the template all up in one go — in real life, there’s a sequence to it where we’d fold up one side and its flap first then move on to the next, and so on. Scoped custom properties are perfect for this. 2Here we are saying that, for each 9, use a 4 of 5 multiplied by 6. The 6 value won’t change but each element can define which “step” it is in the sequence. And then we can be explicit about the order in which things happen. 3Consider the following demo for a better idea of how this works. Change the slider values to update the order in which things happen. Can you change which car wins? CodePen Embed Fallback That same technique is key for what we are going to for. We could even introduce an 8 that adds a slight pause to everything for even more realism. 4CodePen Embed Fallback If we look back at our package, we can take this further and apply a “step” to all the elements that are going to 2. It’s quite verbose but it does the job. Alternatively, you could inline these values in the markup. 5CodePen Embed Fallback But, it doesn’t feel very realistic. Maybe we oughta flip the box, tooIf I were folding up the box in real life, I’d likely flip the box up before folding in the top flaps. So how might we do that? Well, those with an eager eye might have noticed the 0 element. We are going to use this to slide the package. Then we’re going to rotate the package on the x-axis. This will create the impression of flipping the package onto its side. 6Adjusting the 5 declarations accordingly gives us something like this.CodePen Embed Fallback Unfolding the boxIf you flip between the folded and not folded states, you’ll notice that the unfold doesn’t look right. The unfolding sequence should be the exact reverse of the folding sequence. We could flip the 5 based on 3 and the number of steps. Our latest step is 4. We can update our 9 to this: 7That is quite the mouthful of 6 to reverse the 4. But, it works! We must remind ourselves to keep that 8 value up to date though!CodePen Embed Fallback We do have another option. As we continue down the “pure CSS” route, we will eventually make use of the checkbox hack to toggling between the folding states. We could have two sets of defined “steps” where one set is active when our checkbox gets checked. It’s certainly a more verbose solution. But, it does give us more finite control. 8Sizing and centeringBefore we ditch the use of 9 in our demo, let’s have a play with the size of our package. We want to check that our package remains centered while folding and flipping. In this demo, the package has a larger 3 and the 2 has a dashed border.CodePen Embed Fallback We may as well tweak our 2 to better center the package while we’re at it: 9This gives us reliable centering in the scene. It all comes down to preference though! CodePen Embed Fallback Adding in the checkbox hackNow let’s get 3 out of the way and make this “pure” CSS. For this, we need to introduce a bunch of controls in the HTML. We are going to use a checkbox for folding and unfolding our package. Then we’re going to use a 4 button to pick a package size. 0In the final demo, we will hide the inputs and make use of the label elements. For now, though, let’s leave them all visible. The trick is to use the sibling combinator ( 5) when certain controls get 6. We can then set custom property values on the 2. 1And here is the demo with that working! CodePen Embed Fallback Final polishNow we’re in a place to make things look “pretty” and add some extra touches. Let’s start by hiding all the inputs. 2We can style the sizing options as rounded buttons: 3We want to be able to tap anywhere to toggle between folding and unfolding our package. So our 8 and 9 labels will take up the full screen. Wondering why we have two labels? It’s a little trick. If we use a 4 and scale up the appropriate label, we can hide both labels while the package transitions. This is how we combat spam tapping (even though it won’t stop a user hitting the space bar on a keyboard). 4Check out this demo to see where we’ve added 1 to both 8 and 9. Neither label is visible during the transition.CodePen Embed Fallback We’ve got complete functionality! But, our package is a little underwhelming at the moment. Let’s add extra details to make things more “box”-like with things like parcel tape and packing labels. CodePen Embed Fallback Little details like this are only limited by our imagination! We can use our 3 custom property to affect anything. For example, the 5 is transitioning the 6 transform: 5The thing to remember is that whenever we add a new feature that affects the sequence, we need to update our steps. Not only the 5 values, but also the 8 value.That’s it!That’s how you make a pure CSS 3D package toggle. Are you going to drop this into your website? Unlikely! But, it’s fun to see how you might achieve these things with CSS. Custom properties are so powerful. |