Skip to content
PolyFish CSS

A sandbox, and what components cannot do

Capability tables for six components, listing their deliberate limits beside their features.

· ~4 min read · 0.18.2 in the changelog

The component reference has capability tables now: for buttons, tabs, tables, cards, dialogs and navigation, what each one does sits beside what it deliberately does not, with the reason. It is an odd thing to publish. It is also the first thing I want to read before adopting somebody else's component.

The second column is the useful one. A card with a stretched link cannot hold a second clickable link — the stretched link covers it. Tabs cannot deep-link, because a radio input has no URL. Navigation does one level, because nested hover menus are unusable on touch. And dialogs are the single component that genuinely requires JavaScript, which the table says outright rather than leaving you to discover when one fails to open.

This release also added a live markup sandbox. It was removed in rc.5; the post on that release explains why.

Why the second column is the one that matters

Capability tables are unusual in framework documentation, and the reason they are unusual is that they are unflattering. A feature list is an argument for adoption; a limits list is an argument against it, written by the people who want you to adopt. We publish it anyway, because the alternative is that you discover the limit at the point where it costs most: after the component is in your page, styled, reviewed and shipped.

Every entry in that column is a constraint with a cause, not a missing feature waiting for a release. A stretched link covers its card, so a second link inside the card is unreachable — that is what "stretched" means, not an oversight. Radio-driven tabs have no URL because a radio input is not an address, so deep-linking to a tab needs a different mechanism, not a patch. These are the shape of the technique, and naming them is cheaper for everyone than an issue thread six months from now.

Update: one of those limits has since gone

The table said dialogs were the single component that genuinely required JavaScript. That is no longer true, and it is worth saying why it changed rather than quietly editing the row.

It required a script because opening a modal meant calling showModal(); there was no markup that could do it. The platform closed that gap: invoker commands let a button open a dialog declaratively, closedby handles dismissal, and the popover API covers menus. A dialog now opens from HTML alone, and the optional script has shrunk to the one thing markup still cannot express — turning radio tabs into a full ARIA tablist.

That rewrite had its own lesson. Our first attempt put role="tablist" on the tabs container, whose children are radios and panels, which ARIA forbids; the second inserted a <div> to hold the role and silently renumbered every div:nth-of-type() in the panel rules, so the wrong panel showed. The working version is an empty <span> that claims the labels with aria-owns. A capability table is only honest if the capabilities are retested when the platform moves.

What this post does not cover

  • Components the framework does not ship at all, such as a carousel.
  • The full ARIA authoring practices — the capability tables point at them rather than restating them.

Checked against popover API, invoker commands, ARIA forbids. Figures are measured from the build they describe.

What this doesn’t cover

A capability table is only as honest as its last review. This one described the platform as it stood at the time, and one of its limits has since gone. It says nothing about performance, nothing about what these components cost to render, and nothing about whether a screen reader finds them comprehensible — only whether the ARIA is correct.

Sources: MDN on the Popover API and the ARIA Authoring Practices tabs pattern. The capability table is reproduced from the component reference on this site.

Components Capabilities Dialogs Tabs ARIA Release CSS