From b443277b886900c266b04d1956f45b1c4fbb8077 Mon Sep 17 00:00:00 2001 From: Dylan Audius Date: Fri, 15 May 2026 14:59:36 -0700 Subject: [PATCH] fix: mobile contest cards too narrow in Explore carousel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CardList's horizontal slot was hard-coded to 172px wide, which is the right size for thumbnail-style cards (tracks, playlists) but cropped the redesigned contest card — host row, two-line title, and entries pill all got squeezed, and titles clipped mid-word. ContestCard had previously papered over this with `minWidth: 250`, which let the card render at 250px but kept the slot at 172px, so adjacent cards visually overlapped. - Added a `horizontalCardWidth` prop to CardList and removed the hard-coded width from the slot style, defaulting to the previous 172px so existing horizontal carousels (tracks, playlists, etc.) are unchanged. - FeaturedRemixContests now passes `windowWidth - 48`, matching the visual width of the cards on the dedicated `/contests` screen and leaving a small peek of the next card to invite horizontal swipe. - Dropped the now-redundant `minWidth: 250` workaround on ContestCard. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../components/contest-card/ContestCard.tsx | 6 +----- .../mobile/src/components/core/CardList.tsx | 20 ++++++++++++++++--- .../components/FeaturedRemixContests.tsx | 10 ++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/packages/mobile/src/components/contest-card/ContestCard.tsx b/packages/mobile/src/components/contest-card/ContestCard.tsx index 0cb40254c4d..3474a0128eb 100644 --- a/packages/mobile/src/components/contest-card/ContestCard.tsx +++ b/packages/mobile/src/components/contest-card/ContestCard.tsx @@ -298,11 +298,7 @@ export const ContestCard = (props: ContestCardProps) => { onPress={handlePress} border='default' shadow='mid' - // Floor the card at ~250px so the artist name + badges row in the - // header doesn't clip on the narrowest carousels (the mobile - // explore "Contests" rail was sized to whatever CardList default - // — the QA pass flagged the cards as too skinny). - style={{ overflow: 'hidden', borderRadius: 14, minWidth: 250 }} + style={{ overflow: 'hidden', borderRadius: 14 }} {...other} > {/* Cover banner */} diff --git a/packages/mobile/src/components/core/CardList.tsx b/packages/mobile/src/components/core/CardList.tsx index 53b18255ca6..4464e3c0e38 100644 --- a/packages/mobile/src/components/core/CardList.tsx +++ b/packages/mobile/src/components/core/CardList.tsx @@ -25,6 +25,11 @@ export type CardListProps = Omit, 'data'> & { // Use carousel spacing to override the parent's margins // e.g. make carousel start and end at edge of the screen carouselSpacing?: number + + // Override the per-item slot width in horizontal mode. Defaults to + // `spacing(43)` (172px), which is right for small thumbnail-style cards + // (tracks, playlists) but too narrow for the redesigned contest card. + horizontalCardWidth?: number | `${number}%` } export type LoadingCard = { _loading: true } @@ -60,12 +65,13 @@ const useStyles = makeStyles(({ spacing }) => ({ flexGrow: 0 }, cardHorizontal: { - width: spacing(43), paddingRight: spacing(3), paddingBottom: spacing(3) } })) +const DEFAULT_HORIZONTAL_CARD_WIDTH = 172 + export function CardList(props: CardListProps) { const { renderItem, @@ -78,6 +84,7 @@ export function CardList(props: CardListProps) { totalCount, horizontal: isHorizontal = false, carouselSpacing = 0, + horizontalCardWidth = DEFAULT_HORIZONTAL_CARD_WIDTH, ...other } = props @@ -109,7 +116,13 @@ export function CardList(props: CardListProps) { ) return ( - + {itemElement} ) @@ -119,7 +132,8 @@ export function CardList(props: CardListProps) { renderItem, styles.card, styles.cardHorizontal, - isHorizontal + isHorizontal, + horizontalCardWidth ] ) diff --git a/packages/mobile/src/screens/explore-screen/components/FeaturedRemixContests.tsx b/packages/mobile/src/screens/explore-screen/components/FeaturedRemixContests.tsx index c4ebae34fbf..273ac08c2e0 100644 --- a/packages/mobile/src/screens/explore-screen/components/FeaturedRemixContests.tsx +++ b/packages/mobile/src/screens/explore-screen/components/FeaturedRemixContests.tsx @@ -2,6 +2,7 @@ import React from 'react' import { useAllRemixContests } from '@audius/common/api' import { exploreMessages as messages } from '@audius/common/messages' +import { useWindowDimensions } from 'react-native' import { useTheme } from '@audius/harmony-native' import { ContestCard } from 'app/components/contest-card' @@ -12,8 +13,16 @@ import { useDeferredElement } from '../../../hooks/useDeferredElement' import { ExploreSection } from './ExploreSection' +// Carousel slot width for contest cards. The dedicated /contests screen +// renders cards at roughly full screen width minus its horizontal margins; +// the Explore carousel matches that visual width while leaving a small +// peek of the next card to invite horizontal swipe. +const CONTEST_CARD_PEEK = 48 + export const FeaturedRemixContests = () => { const { spacing } = useTheme() + const { width: windowWidth } = useWindowDimensions() + const contestCardWidth = windowWidth - CONTEST_CARD_PEEK const { InViewWrapper, inView } = useDeferredElement() const { data: allContestTrackIds, isPending: isAllContestsPending } = @@ -27,6 +36,7 @@ export const FeaturedRemixContests = () => { renderItem={({ item }) => } horizontal carouselSpacing={spacing.l} + horizontalCardWidth={contestCardWidth} isLoading={isAllContestsPending} LoadingCardComponent={TrackCardSkeleton} />