teaching machines

Fitting by Rotating

July 31, 2017 by . Filed under public, www.

Last spring I was talking about media queries with some students, and we joked around about just rotating any structures that were too wide to fit in the viewport. As silly as this idea is, I wondered how easy it would be to do with just CSS.

First, here’s a div with some text that we will be putting through the gauntlet:

The problem I’m trying to solve is a bit ill-defined. That won’t stop us from exploring. Let’s first just try rotating when the viewport drops below a certain width. Here we force the viewport at 490 pixels, which triggers the media query, and apply a CSS rotation:

@media (max-width: 500px) {
  div {
    transform: rotate(-90deg);
  }
}

Note that the CSS standard bucks convention: a positive angle rotates clockwise. I want the “head” to be down—choosing to preserve the left-to-right reading—so I spin counterclockwise with a negative angle.

Unfortunately, the default pivot point for rotate is the center of the element. We’ll have an easier time thinking about this if we pivot around one of the corners instead. We can, by setting transform-origin:

@media (max-width: 500px) {
  div {
    transform: rotate(-90deg);
    transform-origin: top left;
  }
}

Thank goodness for that margin. Without it, I wouldn’t know where my text had gone. We succeeded in altering our pivot, but now we need to shift the whole thing down. This lands us in the mire of combining transforms, which are generally not commutative. The way I think about it is that the rightmost transform gets applied first, and the leftmost gets applied last. This is backward from how the transformation matrix is multiplied together.

Anyway, let’s first shift the whole element completely left of itself. And then rotate around that corner, which is now effectively the top right:

@media (max-width: 500px) {
  div {
    transform: rotate(-90deg) translate(-100%, 0);
    transform-origin: top left;
  }
}

I tried writing this so that shifting down was done on the Y axis, which seems more natural. To do this, I simply put the translate after the rotate (by putting it before—ugh):

@media (max-width: 500px) {
  div {
    transform: translate(0, 100%) rotate(-90deg);
    transform-origin: top left;
  }
}

But at this point, 100% refers to the height instead of the width and the shift amount is not correct. It’s the width of the element that we want to shift by.

The standalone page for the working solution is available here.