Masonry

A column layout for items of uneven height: each item goes into the shortest column rather than onto the next row.

Accessibility

Masonry is a layout primitive: it adds no roles, no keyboard behavior and no names. What it does add is a gap between what a reader sees and what a reader hears.

What Masonry renders

A root <div>, and one <div> per item inside it. That is the whole DOM. Masonry sets no role, no ARIA attribute and no tabIndex on anything it renders, and it installs no keyboard handler.
It is a layout primitive, the way Grid and Flex are: it positions boxes and paints nothing. Every role, every name and every focusable control in the wall comes from what itemRender returns, and the semantics of the set are yours to add.
  • If the items are a list, say so around the Masonry: role="list" on a wrapper with role="listitem" on each card. Masonry will not do it, and a wall of anonymous divs announces as nothing.
  • Give the region a heading or an aria-label so a reader arriving in it knows what the wall is. antuikit forwards a ref and passes props through, so an attribute you set lands on that root div.
  • The item divs are positioned with position: absolute and an inline top, and the root carries an inline measured height. Neither is a semantic, but both mean the layout cannot be re-flowed by CSS alone.

Visual order is not DOM order

This is the one that matters, and no prop fixes it. The DOM keeps your items array order. The screen shows whatever the shortest-column rule produced. A keyboard and a screen reader follow the DOM, and a sighted reader follows the screen.
  • The two orders diverge as soon as the heights do. Placement is greedy: item by item, each one goes to the column with the smallest running height at that moment. So item 4 can sit visually above item 3, and tabbing moves to a card the eye has already passed or not yet reached.
  • There is no row-by-row mode to turn on. If the sequence is meaningful, use a different layout. WCAG 1.3.2 Meaningful Sequence is about the order content is presented in, and Masonry does not preserve one.
  • The per-item column field pins one entry to a column index. It controls where a card lands, not where it sits in the tab order, so it can line the two up for a few important items but not for a whole wall.
  • Sorting is still honored for anything read one item at a time: newest first stays newest first in the DOM, in the tab order and to a screen reader. It is only the two-dimensional scan that goes ragged, which is why Masonry is safe for a gallery and wrong for a ranking.

Reflow at 320px

WCAG 1.4.10 asks for content that works at 320 CSS pixels wide without two-dimensional scrolling. Masonry does not get there on its own, because the column count is a prop rather than something the layout negotiates.
  • Name xs in the responsive object. When no breakpoint matches, antd falls back to columns.xs and then to 1. One column at xs is the right answer, so name it rather than rely on the fallback.
  • A fixed columns number does not collapse at a narrow width: three columns at 320px means three columns roughly 100px wide, and the content inside them overflows or wraps to illegibility rather than the wall re-flowing.
  • The same applies to zoom. A reader at 400% is at an effective 320px viewport, so test the narrow case by zooming rather than only by resizing.
  • Because the root carries a measured inline height, a card whose content grows at a narrow width (a long word, a wrapped heading) is only accounted for once the re-measure runs. It arrives, but not in the same frame.

Focus is never managed

Masonry moves no focus, traps none and restores none. If a card holds a link or a button, that control is the only focusable thing in the wall, and it behaves exactly as it would outside one.
  • Removing an item is the case to check. A card that held focus takes it to the document body as it fades out. Move focus to the next card or the list heading before you remove it.
  • Because every item is absolutely positioned inside a fixed-height root, the browser's scroll-into-view on focus is working from coordinates rather than from flow. Keep a visible focus ring: a card that is scrolled to but not outlined is hard to find in a ragged wall.
  • Do not make the whole card a single click target with a nested link inside it. One visual card with two tab stops is confusing anywhere, and worse here, where the reading order already does not match the visual one.

Re-layout moves things under the reader

Masonry re-measures and re-places whenever the item list changes, the column count changes, the container resizes, or an image inside it fires load or error. Each of those moves cards that a reader may already be pointing at.
  • The image case is the common one. An unloaded image measures at whatever its box is now, so the first packing is computed from the wrong heights and corrected once the bytes land. Give the image an aspect ratio wherever you know it and the correction never happens.
  • The correction is real motion for someone using a screen magnifier or reading at high zoom: the card under the cursor slides to a different column. It is not announced, because no live region is involved and the DOM order did not change.
  • fresh trades stability for accuracy: it watches every item for resize and recomputes on each one. Without it, positions stay put while you scroll, which is usually the kinder behavior. Turn it on for the case it fixes (an item that grows in place and overlaps the one below it), not as a default.
  • Items fade in on mount and out on removal. Honor a reduced-motion request by turning motion off in the theme rather than per component: token: { motion: false } on a ThemeScope, in both modes. The layout still re-places, it just stops animating the change.