Designing Better Tables: Using Popovers for Extra Data
Mads Stoumann

Mads Stoumann @madsstoumann

About: I'm a tech director, web developer, graphic designer, musician, blogger, comicbook-geek, LEGO-collector, food lover … as well as husband and father!

Location:
Copenhagen, Denmark
Joined:
Nov 16, 2020

Designing Better Tables: Using Popovers for Extra Data

Publish Date: Nov 3 '24
49 10

When working with large data tables, displaying all available information at once can often make the table overwhelming and hard to read. As a result, it’s common to hide secondary information and use a design pattern to reveal more details only when needed.

Data table design is central to good user experience (UX), especially when handling high-volume or complex data. According to articles like Why does table design UX matter? and Design better data tables, there’s a consensus on three main ways to manage these hidden details:

  • Expandable Rows – Expands table rows inline to reveal additional details.
  • Modal Views – Opens a modal dialog with more in-depth information.
  • Quick View (Sidebar) – Displays extra details in a sidebar that can slide in from the edge.

In this article, I’ll show you how to set up these three options flexibly, so you can switch between them without needing to rewrite the table’s markup.

For this, we’ll use popovers, which are ideal for easily attaching information directly to specific rows.


Setting Up the Basic Table

To start, we’ll use the same markup and styles as in A Guide to Styling Tables:

Basic Table

In the last cell, after the text content, we’ve added a simple button (the icon is from tabler), that will trigger a popover:

<button type="button" popovertarget="p1"></button>
Enter fullscreen mode Exit fullscreen mode

Next, we add the popover somewhere in the document to display the additional content when triggered:

<div id="p1" popover>
  ... content ...   
</div>
Enter fullscreen mode Exit fullscreen mode

Styling the Popover

For the popover styling, we’ll use the template from Adam Argyle’s Steal this popover code. This template includes smooth transitions for the popover’s appearance and disappearance:

.selector {
  &, &::backdrop {
    transition: 
      display .5s allow-discrete, 
      overlay .5s allow-discrete, 
      inset .5s,
      scale .5s,
      opacity .5s;
    opacity: 0;
  }

  &::backdrop {
    background: #0002;
  }

  &:popover-open {
    opacity: 1;
    &::backdrop {
      opacity: 0.5;
    }
  }

  @starting-style {
    &:popover-open,
    &:popover-open::backdrop {
      opacity: 0;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Modal View

For the modal layout, we’ll add a --modal modifier class to define the modal’s size and center it on the screen:

&.--modal {
  block-size: calc(100dvh - 2em);
  inline-size: calc(100dvw - 2em);
  inset-block-start: 1em;
  inset-inline: 1em;
  transform-origin: 50% 50%;

  @starting-style {
    &:popover-open{
      scale: 0;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Modal


Quick View (Sidebar)

To create a quick view sidebar, we'll add a new modifier class, --inline-start, to the popover. This sidebar slides in from the side of the viewport and can be closed with a smooth animation. Here’s the setup:

&.--inline-start {
  --width: clamp(220px, 33vw, 350px);
  block-size: calc(100dvh - 2em);
  inline-size: var(--width);
  inset-block-start: 1em;
  inset-inline: 1em;

  @starting-style {
    &:popover-open {
      inset-inline: calc(0px - var(--width));
    }
  }
  &[popover]:not(:popover-open) { 
    inset-inline: calc(0px - var(--ui-width));
  }
}
Enter fullscreen mode Exit fullscreen mode

With this setup, the popover slides in from the left. The line &[popover]:not(:popover-open) ensures that it slides back out when closed.

Inline Start

We can apply similar adjustments to create slide-in effects from the right or from above and below:

Inline End (--inline-end)

Inline End

Block End (--block-end)

Block End


Expandable Rows

And finally, let’s look at expandable rows. Unlike modals and sidebars, expandable rows integrate directly within the table layout. Traditionally, this approach involves a hidden <tr> with a <td> spanning all columns, and a JavaScript function to toggle its visibility.

But can we reuse the existing popover to expand rows inline? Not directly — but we can fake it by positioning the popover to appear just below the row, creating a similar effect.


First, we attach the popover to the bottom left of the table row by setting anchor-name on the <tr> and applying the following CSS:

.selector {
  inset-block: auto anchor(bottom);
  inset-inline: anchor(start);
}
Enter fullscreen mode Exit fullscreen mode

Next, we need two features that, unfortunately, are currently Chrome-only:

.selector {
  interpolate-size: allow-keywords;
  width: anchor-size(width);
}
Enter fullscreen mode Exit fullscreen mode

The interpolate-size property allows the popover to animate smoothly to its final height, while anchor-size lets it match the row’s width (or height, using anchor-size(height)).

Inline Expand

So far, so good — but the expanded row covers the next rows.

We can fix this by adding the popover height to the padding-block-end of the table cells within the expanded row:

Expandable

Cool. The illusion is complete!

To animate the height, we'll add a few extra declarations to the CSS:

.--expandable {
  transition: height .25s;
  @starting-style {
    &:popover-open{
      height: 0;
    }
  }
  &[popover]:not(:popover-open) { 
    height: 0;
  }
  &:popover-open {
    height: max-content;
  }
}
Enter fullscreen mode Exit fullscreen mode

Automating the Padding Adjustment

To avoid manually updating padding, we can use a script that dynamically updates the padding based on the popover’s height:

document
  .querySelectorAll(".selector")
  .forEach((popover) => {
    const trigger = document.querySelector(
      `[popovertarget="${popover.id}"]`
    );
    const row = trigger.closest("tr");

    row.style.anchorName = `--${popover.id}`;
    popover.style.positionAnchor = `--${popover.id}`;

    const initialPadding = parseFloat(
      getComputedStyle(row.cells[0]).paddingBlockEnd
    );

    popover.addEventListener("toggle", (event) => {
      const rowHeight =
        initialPadding +
        (event.newState === "open"
          ? popover.offsetHeight
          : 0);
      row.style.setProperty("--row", `${rowHeight}px`);
    });
  });
Enter fullscreen mode Exit fullscreen mode

This script listens for the popover’s toggle event, dynamically updating a custom property, --row, to match the popover height when open.


Demo

You can view a demo at browser.style/ui/table-expand, where you’ll also find the original table demo styles.

Comments 10 total

  • Mince
    MinceNov 3, 2024

    This is great

  • Ivan
    IvanNov 3, 2024

    nice one!

  • Babatunde Lawal
    Babatunde LawalNov 3, 2024

    Nice. I am looking for a paid internship job as a front-end developer. I am eager to learn and contribute to the team.

  • Reuven Hozias
    Reuven HoziasNov 4, 2024

    Looks great, thanks!

  • plutonium239
    plutonium239Nov 4, 2024

    I unknowingly used the modal view pattern in my app personal-dictionary.pages.dev because that's what felt intuitive to me lol. Thinking back now, the three styles convey different meanings based on their context, so modals do make sense for my app.

    • Mads Stoumann
      Mads StoumannNov 5, 2024

      With that amount of information, a modal makes sense!

  • Phila Hulaz
    Phila HulazNov 5, 2024

    Thanks for learn to create a table in applebees javascript.

Add comment