TIL: CSS Windowboxing

Published Mar 16, 2024

Today I learned some new visual layout terms and a new CSS feature, so this is two thoughts for the price of one!

1. Words

  • Letterboxing is commonly used to describe the use of padding to fit a too-wide image onto a screen without cutting it (named after the shape of the mail slot present on some doors).
  • TIL that pillarboxing is the inverse where the image is too tall (citations needed, but apparently named after the shape of some UK-style mail collection boxes).
  • TIL that the combined application of both is windowboxing, which I don’t think has anything to do with mail.

I really want to believe the etymology for pillarboxing because it makes for a fun symmetry (duality?) between inbound/wide and outbound/tall!

Also, a fun fact I noticed while writing: “letter”, “pillar”, and “window” are all 6-character words.

I happened across these terms in a StackOverflow question that described my recent layout problem better than I could. And the answer worked perfectly!

2. CSS

So this was my specific layout problem:

  • I have a square grid of “pixels” that are actually <div> elements with background colors. They don’t contain any content, so they have no natural size.
  • I want this grid to display as large as possible on the screen while staying square.
  • I want to have space for a header and footer, but I don’t want to hard-code their sizes.
  • I want the grid centered on the axis that has more space.

All of my previous attempts (playing with flexbox attributes, wrapping successively more divs, more things I don’t remember) failed in at least one of these ways:

  1. The grid overflowed its containing <div> and made the page scroll.
  2. The grid didn’t fill the available space in portrait, landscape, or (somehow) both.
  3. The grid wasn’t centered within its containing box (I honestly can’t remember how I messed that up, but I did).
  4. The grid cells were not-square by exactly the pixels needed for the header and footer.

When I first ran into this, I was porting an app from a previous implementation (both using Yew), but I only noticed that the grid cells weren’t square after porting. I eventually found that past-me hadn’t gotten it right either, so I gave up and wrote myself a big apology comment for later.

But, today, I learned about container queries! Like MDN explains, @container queries are like the @media queries we’ve been using for responsive web design for a while, but now you get to choose the container instead of it being the whole browser viewport. Now that I can check whether the box I’m rendering into is tall or wide, I can choose to size my content to fill width or height.

“Put another box around it” is a viable strategy for basically any software problem, but in this case, I need two of them:

  1. The main layout div (that lives between the header and footer) that gets container-type: size to make it a queryable container.
  2. The windowboxing div that swaps between “fill height” and “fill width” based on the container element’s aspect ratio.

And then the content div sits inside of all of that as height: 100%; width 100%;, correctly unaware of which way it’s going to stretch.

Here’s the CSS I actually used for the windowboxing div:

.box-square {
    aspect-ratio: 1 / 1; /* Square aspect ratio: W / H */
    width: 100%;         /* Fill width by default      */

/* If the container is wider than it is tall, ... */
@container (min-aspect-ratio: 1 / 1) {
    .box-square {
        width: auto;  /* Let width vary again         */
        height: 100%; /* Fill height instead of width */

I’m not yet at the “good commit messages” stage of the project, but the commit is up if you want to see the whole diff.

And now my Tiles experiment renders correctly! Apparently for the very first time 😅