Skip to main content

Solved: Tricky Floating Image Alignment

By Tyler Sticka

Published on June 29th, 2023


If you want to pair an image alongside some text content in CSS, you have a few options.

You can float the image left or right. Any text that extends past the image will naturally flow beneath, which is neat. But if the text is shorter in height than the image, you get an Oklahoma Panhandle effect with one big corner of negative space.

An image of a smiling puppy floating alongside some text. The text is much shorter than the height of the image, and the empty space is highlighted in red.

Instead, you might set a containing element’s display to grid or flex, arranging the image and text in their own columns. This gives you much finer control of alignment, so you can vertically center the text content until it overflows. Unfortunately, that overflow will never flow beneath the image, which makes it less space efficient.

The same image as before, but with more text content alongside. Because this image is arranged using a grid, there is now empty space highlighted in the bottom right corner.

Ideally, we’d combine these two alignments: The overflow behavior of float, with the alignment behavior of grid or flex. Surely that would require some newfangled CSS involving container query units or scroll-driven animation wizardry?

The first example, but with the shorter text content centered vertically within its container.
The second example, but with the longer text content wrapping around the bottom of the image.

It turns out, we can pull this off using a clever combination of properties that browsers have supported for a long time.

Here’s a demo of the solution in action. You can edit the content directly (thanks to contenteditable), or use the buttons to shorten or lengthen the text content.

This is the relevant HTML:

<div class="media">
  <div class="media__inner">
    <img class="media__object" src="…" alt="…" width="…" height="…">
    <div class="media__content"></div>
</div>Code language: HTML, XML (xml)

Which we style with this CSS:

.media {
  display: grid;

.media__object {
  float: right;
  margin-left: 1em;

.media__content {
  position: relative;
  top: 50%;
  transform: translateY(-50%);
}Code language: CSS (css)

(You may want to replace physical direction properties and keywords above with equivalent logical properties, depending on your project and audience.)

This bonafide CSS trick involves the following elements:

  1. .media: The outer wrapping element, which establishes a grid. This clears floats, establishes a new stacking context and, most crucially, resolves the container height for relative positioning (more on that later).
  2. .media__inner: The inner wrapping element. Grid items can’t float, which is why we have to nest our floating image and adjacent content two elements deep. The class name is optional since this has no styles of its own.
  3. .media__object: The image, floating to the right.
  4. .media__content: The content, which flows around the image. We offset its position to achieve the alignment effect.

The .media__content class has two different positioning rules applied to it:

  1. A combination of position: relative and top: 50% shifts the content down by half the height of the parent element.
  2. translateY(-50%) shifts the content back up by half its own height.
Step 1: The content shifts down by half the parent height using top.
Step 2: The content shifts up by half its own height using transform.

When the text content is shorter than the image, it will also be shorter than the container. By extension, the offset that uses translateY will be smaller than the offset using top, resulting in the content centering itself vertically.

But when the text content is tall enough to flow around the image, then the containing element will have stretched to match its size. This means the offsets will be equivalent, canceling each other out.

For this to work, top: 50% needs to be relative to the parent element, including its children (floating or otherwise). I tried a bunch of techniques for creating new stacking contexts, and setting the display to grid or flex were the only ones that seemed to work for this scenario. I chose grid since it’s very well-supported and requires no additional styles.

This technique doesn’t play well with the shape-outside property when the content is shorter. (That’s a bummer for circular images.) It’s also a shame that the grid wrapper is required.

I think it’s pretty neat that in this era of rapid development of CSS standards, there are still novel combinations of long-stable properties to discover.

Huge thanks to Vesa Piittinen for suggesting the original solution and granting me permission to write about it. Thanks also to Miriam Suzanne for confirming when I was barking up the wrong tree, to Nicole Sullivan for inventing the media object pattern I used in this demo, and to Buster for being a very good boy.


Christopher Kirk-Nielsen said:

Pretty simple, yet very powerful, thanks for sharing this cool trick! 🙂

One minor improvement: if you are using an interactive element for the object (say a link to a larger photo of lil’ Buster there!), it will be layered under the content element and thus cannot be clicked (though, even if it were just the image, or a block of text, you’d likely want to make it accessible for clicks and selection). A dash of extra positioning keeps the effect intact and makes the object reachable again:

.media__object {
  position: relative;
  z-index: 1;
Code language: CSS (css)

Roma Komarov said:

Really nice trick!

I knew of all the behaviors involved, but never did the connection to use it this way, basically using the definite size provided by the stretching of the flex/grid item and the way the flex/grid contains the float.

Just for fun decided to find the corresponding places in the specs that define (pun not intended) this definite size:

Leave a Comment

Please be kind, courteous and constructive. You may use simple HTML or Markdown in your comments. All fields are required.