Repository navigation
[MAR-3033] Anchor extended product card CTAs to the bottom of the card - #175
Conversation
Carousel cards are sized to the tallest card, but Card's built-in content wrapper dropped that height, so CTAs on shorter cards sat partway down the card. Replace the outer Card with a Surface, which passes the card height through to its content, and add a weighted spacer so the CTA row sits at the bottom. Content taller than the card still scrolls. Add a UI test checking that CTAs line up with the card's bottom padding when content is short.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
cdhoffmann
left a comment
There was a problem hiding this comment.
The Spacer(weight(1f)) at L281 is placed after the text Column closes, but the price block is nested inside that column at L246 — so only the CTAs anchor. Prices still misalign across cards with different description lengths.
Figma treats pricing as a bottom element: "Content elements reside in a standard top stack, pushing functional pricing and CTAs to the bottom."
Suggest hoisting the price Column out as a sibling after the spacer:
Column(verticalScroll)
imageBox
Column { title; subtitle }
Spacer(weight(1f))
priceColumn // moved out
Row { ctas }
Spacer(contentPaddingBottom)
That also covers cards with a price but no CTA, which get no anchoring today since the Row at L288 is skipped.
iOS anchors price + CTA together as one block — worth keeping the two structurally identical.
Move the price column out of the text column so it sits after the weighted spacer, keeping the price and CTAs together at the bottom of the card. Add tests for: - the price anchoring to the bottom when there are no CTAs - the price anchoring to the bottom when there is no "was" price - prices and CTAs lining up across cards with different subtitles - CTAs lining up when only one card has a "was" price
cdhoffmann
left a comment
There was a problem hiding this comment.
Price and CTAs anchor together now — this matches iOS, where the spacer sits between the title/subtitle block and the price block for the same reason.
Nice catch re-applying start/end = style.contentPadding on the hoisted price Column; that padding was previously inherited from the text column and would have been easy to drop.
Tests cover the no-CTA and one-card-has-was-price cases. 👍
Description
Anchors the CTA buttons of the extended (
productDetail) product card to the bottom of the card, so CTAs line up across cards with different amounts of content in the carousel.ExtendedProductCard.ktCardwith aSurface, which passes the card's height through to its content. Shape, background, border, shadow and click handling are unchanged.Spacerabove it, so the CTAs sit at the bottom of the card. When content is taller than the card, the spacer shrinks to nothing and the card scrolls as before.ExtendedProductCardTest.ktextendedProductCard_ctaButtons_anchorToCardBottom_whenContentIsShort, which checks that both CTAs end at the card's bottom padding when content is short.Behavior notes:
--product-card-min-heightnow shows its blank space above the CTAs instead of below them.ProductCard(actionButtonstyle) is unchanged.Related Issue
MAR-3033
Motivation and Context
In the carousel, every extended card is set to the tallest card's height. The outer Material3
Cardwrapped its content in a way that dropped that height, so on shorter cards the CTA row sat right after the content, partway down the card. As a result, CTAs didn't line up across cards.How Has This Been Tested?
ExtendedProductCardTest,ProductCarouselTest,RecommendationCardsTest,ProductCardTest) on a Pixel 8 API 35 emulator: all 48 tests passed, including the new one.Screenshots (if appropriate):
Types of changes
Checklist: