Usage
div with display: contents. Its children are laid out by the nearest box-generating ancestor, as if Contents were not there. Use it only when an element must exist in the DOM without changing layout.
| Need | Use |
|---|---|
A ref to observe, measure or portal into, around children that a parent FlexBox or Grid must still lay out | Contents with ref |
A data-* hook (a test id, a tracking label, a mount marker) around children that the parent must still lay out | Contents with data-* |
| Group children with no element at all | A fragment |
| Children need gap, padding, alignment, size or paint | Box, FlexBox or Grid with props |
One child that is already a Box, FlexBox or Grid | Put the ref and data-* on that child |
| Semantics: a list, a landmark, a heading, a labelled group | A Box with as, or the shipped component |
FlexBox, and its gap applies between them.
Contents accepts children, ref and data-* attributes, nothing else. There is no className, style, regionClass, as, Box layout prop, role, aria-*, id, tabIndex or event handler. Each is declared never, so passing one is a type error even through a spread. Contents generates no box, so there is nothing to lay out or paint; regionClass stays on Box and FlexBox.
design-system/contents-layout-transparent fails when Contents:
children, key, ref or data-*, or a spread;ref nor a data-* attribute (use a fragment);Box, FlexBox, Grid or Contents (put the ref and data-* on that element).details and its items, as a direct child of one or as the wrapper of li, tr, td, option, summary or an item role (put the hook on the parent or on each item). Design-system hosts count by their import from @prepared911/ui, so ItemGroup, Table, TabsList, DataList, Menu and their items are covered under any local name.Button, Label and Text, where its div is invalid HTML.as or role is not built from string literals: the rule cannot prove that host is not a list, table or phrasing element.memo or lazy, cast, aliased or passed as a value, where the placement checks cannot follow it.pnpm check runs the rule on every app, remote and package: the root lint config covers the trees it lints, and pnpm lint:transparent-wrapper covers the trees that config ignores and every packages/ui source tree, including its Storybook, tests and tooling. It follows renamed imports, local aliases, namespace imports (including computed members, Reflect.get, aliases, destructuring, casts and optional chains), relative imports inside packages/ui/src, and local barrels up to four deep: export … from, export *, an import re-exported with export { … }, and a const alias of a host.
The rule cannot see inside a local component. A wrapper that renders a list or table host, paired with a wrapper that renders its items, passes lint: <MyList><Contents data-test-id="m"><MyRow /></Contents></MyList> where MyList renders ItemGroup and MyRow renders Item. Put the hook inside such a wrapper, on the host or on each item, rather than wrapping its items in Contents.
The rule also does not follow a whole namespace used as a value, such as { ...UI }, Object.values(UI) or Object.entries(UI). Reporting those would flag legitimate uses, such as the docs site's live-preview scope, which spreads every @prepared911/ui namespace. Read Contents by name, as UI.Contents or a direct import, so the placement checks apply.