Contextual callouts — small paragraphs that sit beside primary text and offer secondary information to a reader — have long been a feature of books, magazines and other printed materials. I particularly enjoy coming across these small asides when reading, as they add texture and interest to the content but can be easily passed over if I have limited time to read whatever it is I’m reading.

I have been searching for a while now for a way to bring this to the web with pure CSS. The solutions in the past have typically been fairly messy, requiring annoying floating and clearing, or some pretty manual absolute positioning. That is changing now, thanks to CSS grid.

I can’t pretend to have found a complete solution to all of the problems related to these callouts, but the solution presented here will hopefully work in several situations.

Blog post using CSS grid to position contextual callouts beside relevant paragraphs.
What we’re building: a blog post with contextual callouts.

The Markup

Say I’m building a blog post template. I’ll need a header to contain the title and date, a section for share links, and then the primary blog content consisting of headings, paragraphs, images, and, yes, callouts. First let’s write some semantic markup:

<article class="blog-post">
    <header class="blog-post-header">
        <h1 class="blog-post-title">...</h1>
        <time datetime="..." class="blog-post-date">...</time>
    </header>
    <aside class="blog-post-share">...</aside>
    <p>...</p>
    <h2>...</h2>
    <p>...</p>
    <aside>
        <p>...</p>
    </aside>
    <p>...</p>
    <p>...</p>
    <aside>
        <p>...</p>
    </aside>
    <figure>
        <img src="..." alt="...">
        <figcaption>...</figcaption>
    </figure>
</article>

The article element contains the header, share links aside, and all post content as direct children. This will be important as we apply CSS grid styles to the post. Callouts are written as aside elements, perfect for content that is connected tangentially to the rest of the document, and appear in the document directly after the paragraph they are connected to.

Fallback Styling

First, we’ll apply some basic styles for browsers that don’t yet support CSS grid. We’ll add some margin, padding and set a max-width of 70 characters using the ch unit:

.blog-post {
    max-width: 70ch;
    margin: 3rem auto;
    padding: 1.5rem;
}

The Fun Part: Using CSS Grid!

Now, progressively enhance for browsers that do support CSS grid using an @supports query:

@supports(display: grid) {
    .blog-post {
        display: grid;
        grid-template-columns: repeat(12, 1fr);
        grid-column-gap: 2rem;
    }
}

Here I’m setting setting up the .blog-post article as a 12-column grid, each with a width of 1 fraction unit and a gap between each column of 2rem. Now it’s time to start placing content on the grid:

.blog-post-header {
    grid-column-start: 2;
    grid-column-end: span 3;
}

.blog-post p,
.blog-post h2 {
    grid-column-start: 5;
    grid-column-end: span 6;
}

.blog-post-share {
    grid-column-start: span 1;
    grid-column-end: -2;
}

Now, the post header, post paragraphs and headings, and share links all sit next to each other in a row. Pretty cool. The callouts are up next:

.blog-post aside {
    grid-column-start: 3;
    grid-column-end: 5;
}

The asides are now pulled to the left of the paragraph immediately preceding them, looking exactly like callouts. If you wanted more control over the positioning of these callouts (say you wanted one at the bottom of the row, rather than at the top), you could apply some targeted styling with the align-self property, but for this exercise, I’m assuming a more generic approach for content generated from a CMS.

CSS grid lines are visible using the grid inspector in Firefox's dev tools.
Viewing the grid in the Firefox dev tools.

And that’s pretty much all it takes to setup contextual callouts with CSS grid. No sweat.

Bonus: Full-Width Figures

As a bonus, here’s my styling for figures within a post, causing them to stretch the full width of the grid container, while the rest of the content is fit to a narrower width:

.blog-post figure {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    grid-column-gap: 2rem;
    grid-column-start: 1;
    grid-column-end: -1;
}

.blog-post figure img {
    grid-column-start: 1;
    grid-column-end: -1;
}

.blog-post figure figcaption {
    grid-column-start: 5;
    grid-column-end: span 6;
}

I set the figure up to span across all 12 columns of its parent grid, and I set up a nested grid within the figure with the same number of columns and spacing as the parent. This allows me to have the img element to span the full width, while the figcaption is aligned with the rest of the primary text in the blog post.

Using CSS grid to easily make an image full-width, while text remains at a smaller width.
Making an image full-width while the text remains inset.

Caveats

There is one primary caveat to the approach outlined here. Because row height is determined by the content within the row, it is possible that a caption that is longer than the paragraph to the right of it could elongate that row and create some odd blank space to the right.

Essentially, it is required that all callouts be of equal length or shorter than their preceding paragraphs.

A caveat of using CSS grid for callouts: callouts cannot be taller than their preceding paragraphs.
One gotcha: the callout can’t be taller than the paragraph preceding it.

Conclusion

That wraps up this one. Check out the live experiment here. I’d love to see what other people do with callouts!