feat(Item): add default/outline/muted surface variants#4277
Open
AKnassa wants to merge 1 commit into
Open
Conversation
Adds `variant` to Item, using Card's background and border tokens at Item's element radius (--radius-element, not Card's container radius): `default` paints the card background with a visible border, `outline` draws the border only, and `muted` paints the muted background with no border. The border is drawn inside the padding — its width subtracted from whichever density padding is active — so the total inset stays on the spacing scale. `variant` is opt-in. An Item without it renders exactly as before, since a standing border would be surprising in the lists and menus Item mostly appears in. Interaction states now composite over the variant surface instead of replacing it. The hover, active, highlighted and selected overlays moved from background-color to a background-image gradient layer — the technique already used by TreeListItem and AvatarGroupOverflow — leaving background-color to the variant. This is load-bearing, not cosmetic: --color-background-muted and --color-overlay-hover are the same value in light mode, so painting hover as a background color gave a `muted` Item no hover feedback at all and stripped the opaque surface off a `default` Item. Overlay precedence is unchanged, so a no-variant Item composites byte-identically to before. Item does not use container(), so Card's --container-padding-* inset technique was unavailable; each density instead publishes its padding as local custom properties that the bordered variants subtract from. The inset uses padding longhands because StyleX gives shorthands lower specificity, so longhands win regardless of rule order. Issue facebook#4179
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
github-actions
Bot
requested review from
cvkxx,
ernestt,
kentonquatman and
rubyycheung
July 24, 2026 01:01
AKnassa
marked this pull request as ready for review
July 24, 2026 01:07
AKnassa
requested review from
cixzhang,
ejhammond and
imdreamrunner
as code owners
July 24, 2026 01:07
Contributor
PR Analysis ReportModified ComponentsItem
Bundle Size Summary
Accessibility AuditStatus: 2 accessibility violation(s) found — 1 critical, 1 serious. Item - 2 issue(s)
Generated by PR Enrichment workflow | View full report |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Gives
Itemavariantprop with three looks —default(filled card background with a border),outline(border only), andmuted(soft grey background, no border). It's opt-in: anItemwithout the prop looks exactly like it does today.Refs #4179 — left open deliberately, since the issue is assigned and carries API questions this PR shouldn't close on its own.
Why
Today an
Itemis always transparent and only paints a background when you hover or select it. There's no way to give one a standing border or a filled surface the wayCardcan. This adds that, reusingCard's own colour and border tokens so the two stay visually consistent across themes — but atItem's smaller--radius-elementcorner radius, since anItemis an element, not a container.What changed
variantprop onItem—default,outline, ormuted. Leaving it off keeps the current transparent look, which is what lists and menus want.Itemis the same overall size as a borderless one.variantis reflected asdata-variantso themes can target each look.Item.doc.mjs(English, Chinese and dense), plus two new Storybook stories.The interesting bit
--color-background-mutedand--color-overlay-hoverare the same value in light mode (#0536590C). So the obvious implementation — having the variant paintbackground-color— means amutedItem gets no visible hover at all, and adefaultItem loses its solid card surface the moment you hover it.The fix: the variant owns
background-color, and the four interaction states (hover, pressed, highlighted, selected) moved to abackground-imagegradient layer, which CSS paints above the background colour. That's the same technique already used byTreeListItem,AvatarGroupOverflowandChatComposerDrawer.Because all four still write the same property in the same order, their precedence is unchanged — an
Itemwith no variant renders byte-identically to before. There's a test pinning exactly that.How to see it
Storybook → Core / Item → Variants and Variants (interactive). Hover the muted row: it visibly darkens. Before this change it wouldn't have






Testing
Itemtests, 14 of them new. They read the real compiled CSS out ofdocument.styleSheetsrather than just checking the prop arrived, so they assert actual declarations.packages/clitimeout flakes present onmainat the same commit; zero failures inpackages/core. Lint and typecheck clean.Open questions for review
Flagging these honestly rather than assuming — #4179 leaves them to the spec, and I've made a judgement call on each:
defaultbecomeItem's default? I kept the no-variant look transparent (the issue leans this way too), so nothing regresses in the many lists and menus usingItemtoday.defaultis opt-in.outlinehave? I used none — border only, fully transparent — so it composes over any surface. The alternative was--color-background-surface.