Utilizing the transform origin for this animation poses a slight challenge as it can be confusing. To simplify things, I've created a snippet with the correct animation. Essentially, it relies on a single transform statement for all boxes, with the animation direction controlled by a CSS variable individually set on each box (or combined):
transform: translateX(calc(-100% * var(--xdir,1))) rotateZ(calc(90deg * var(--rdir,1)));
By organizing your boxes logically and implementing something like grid
, you can ensure they always appear well-balanced. Rather than physically moving the boxes (using left
or top
), utilize transforms as they impact only the visual appearance of your page, not its layout. This approach helps prevent unintended interactions between the boxes. Give this a try:
document.body.addEventListener('click', event => {
document.querySelector('main').classList.toggle('expanded');
});
main {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
main span {
/* Text box style */
grid-column: 1 / span 2;
grid-row: 1 / span 2;
margin: auto;
display: block;
}
main span.box {
/* Square box style */
margin: 0;
display: block;
width: 100px;
height: 100px;
background: red;
transition: transform .5s;
}
main span.box.tl,
main span.box.bl {
grid-column: 1;
margin-left: auto;
}
main span.box.tr,
main span.box.br {
grid-column: 2;
margin-right: auto;
--xdir: -1;
}
main span.box.tl,
main span.box.tr {
grid-row: 1;
margin-top: auto;
}
main span.box.bl,
main span.box.br {
grid-row: 2;
margin-bottom: auto;
}
main span.box.bl,
main span.box.tr {
background: blue;
}
main span.box.tr,
main span.box.bl {
--rdir: -1;
}
main.expanded span.box {
transform: translateX(calc(-100% * var(--xdir,1))) rotateZ(calc(90deg * var(--rdir,1)));
}
<main>
<span>Hi!</span>
<span class="box tl"></span>
<span class="box tr"></span>
<span class="box bl"></span>
<span class="box br"></span>
</main>
Alternatively, consider adding a looping animation:
main {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
}
main span {
/* Text box style */
grid-column: 1 / span 2;
grid-row: 1 / span 2;
margin: auto;
display: block;
}
main span.box {
/* Square box style */
margin: 0;
display: block;
width: 100px;
height: 100px;
background: red;
transition: transform .5s;
}
main span.box.tl,
main span.box.bl {
grid-column: 1;
margin-left: auto;
}
main span.box.tr,
main span.box.br {
grid-column: 2;
margin-right: auto;
--xdir: -1;
}
main span.box.tl,
main span.box.tr {
grid-row: 1;
margin-top: auto;
}
main span.box.bl,
main span.box.br {
grid-row: 2;
margin-bottom: auto;
}
main span.box.bl,
main span.box.tr {
background: blue;
}
main span.box.tr,
main span.box.bl {
--rdir: -1;
}
@keyframes flip {
40% { transform: none; }
80% { transform: var(--flip-transform); }
to { transform: var(--flip-transform); }
}
main span.box {
--flip-transform: translateX(calc(-100% * var(--xdir,1))) rotateZ(calc(90deg * var(--rdir,1)));
animation: flip 1s 1s alternate infinite;
animation-fill-mode: both;
}
<main>
<span>Hi!</span>
<span class="box tl"></span>
<span class="box tr"></span>
<span class="box bl"></span>
<span class="box br"></span>
</main>