Anonymous table box creation and Google Plus Mobile

13 Dec 2013

In Bug 928074 I thought Gecko had a bug with display: table-cell elements wrapped by <a> elements. In a nutshell, you couldn't click or tap anywhere in the full width of Google Plus Mobile's menu—it had to be exactly on top of the menu item text for anything to happen.

But I was wrong, turns out.

Daniel Holbert gets credit for finding the actual bug and kindly explaining it to me—I'm just writing it up so I can commit some of this to memory.

Per the CSS 2.1 spec, implementations should generate anonymous table elements around elements with CSS table model display values (if needed). In the case of the Google Plus menu, display:table-cell elements parented by an <a> like so:

<li>
  <a>
    <div>Home</div>
  </a>
</li>

The <div> requires an anonymous table-row box ("anon-tr") parent, and finally an anonymous table box to wrap that. There's two kinds of table boxes you can end up with: table and inline-table ("anon-it"), determined by the parent of "Home". In this case, "Home"'s parent is an <a>, which is by default an inline box. So you should end up with something like this:

<li>
  <a>
 ╔═══════anon-it═══════╗
 ║ ┌─────anon-tr─────┐ ║
 ║ │ <div>Home</div> │ ║
 ║ └─────────────────┘ ║
 ╚═════════════════════╝
  <a>
</li>

In Gecko, Presto, and IE, this is the (correct) result:

┌──────────────viewport────────────┐
│<li>                              │
│┌─────────────┐                   │
││<a>clkbl area│                   │
││  <div></div>│                   │
││</a>         │                   │
│└─────────────┘                   │
│<li>                              │
└──────────────────────────────────┘

Not very usable. In fact, the bug reporter assumed that it was broken becuase they failed to click exactly on the width of the link text.

WebKit and Blink browsers have bugs, it turns out, where they generate a table box, rather than an inline-table box so the <a> expands to the entire width of its parent <li> and is clickable.

┌────────────────viewport──────────┐
│<li>                              │
│┌────────────────────────────────┐│
││<a>      clickable area         ││
││  <div></div>                   ││
││</a>                            ││
│└────────────────────────────────┘│
│<li>                              │
└──────────────────────────────────┘

If you find yourself in a similar situation, the simplest fix is to add an explicit display: block to the wrapping <a> element and it will work as expected in all browsers.