Skip to content

Add feed hero section with carousel, highlights, and ad placement - #6515

Open
tsahimatsliah wants to merge 18 commits into
mainfrom
claude/hero-carousel-layout-e5e06u
Open

Add feed hero section with carousel, highlights, and ad placement#6515
tsahimatsliah wants to merge 18 commits into
mainfrom
claude/hero-carousel-layout-e5e06u

Conversation

@tsahimatsliah

@tsahimatsliah tsahimatsliah commented Aug 24, 2026

Copy link
Copy Markdown
Member

Changes

Introduces a new hero section component for the feed that displays:

  • A carousel of featured posts (top 4 headlines) with navigation controls
  • A "Happening Now" highlights list alongside the carousel
  • An ad placement in the rail (when available and user is not Plus)
  • Responsive layout: two-column on laptop+, stacked on mobile/tablet
  • Support for multiple post types (article, share, collection, freeform) with type-specific card renderers

New Components

  • FeedHero: Container component that fetches headlines and featured posts, manages ad queries and event logging
  • FeedHeroSection: Layout wrapper combining carousel, highlights, and ad placement
  • FeedHeroCarousel: Post carousel with indicator dots and prev/next navigation
  • FeedHeroAd: Ad card optimized for the rail placement
  • FeedSectionToolbar: Reusable toolbar for feed section headers (search, filters, bookmarks)
  • PostTypeToListCard: Mapping of post types to list card components (mirrors existing PostTypeToWideCard)

Integration

  • Wired into MainFeedLayout for My Feed and Popular pages when featureFeedHero flag is enabled
  • Feed component now accepts disableHighlightCards prop to prevent duplicate highlights when the hero section displays them
  • Comprehensive Storybook stories with mocks covering all component states and responsive breakpoints

Feature Flag

  • featureFeedHero: Controls hero section visibility on main feed pages (defaults to disabled)

Manual Testing

  • Carousel navigation (dots, prev/next buttons) wraps correctly
  • Responsive layout verified at mobile (390px), tablet (768px), laptop (1024px), desktop (1440px)
  • Ad placement renders with/without image, tags, advertiser info
  • Highlights list scrolls independently when content exceeds rail height
  • Post type variants (article, share, collection, freeform) render correctly
  • Feature flag integration prevents duplicate highlights in feed grid

https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa

Preview domain

https://claude-hero-carousel-layout-e5e0.preview.app.daily.dev

claude added 5 commits August 23, 2026 20:58
Adds a hero section for the top of the feed, plus a Storybook page to
iterate on it:

- FeedHeroCarousel: pages through featured posts with the existing
  featured-wide cards, dot indicators and prev/next chips labelled with
  the neighbouring headlines.
- FeedHeroAd: a compact native/direct ad placement for the rail, built
  from the existing ad primitives (link, image, attribution, pixel,
  viewability).
- FeedHeroSection: carousel on the left, rail on the right with the ad
  slot, the Happening Now highlights card and an Explore all CTA.
- FeedSectionToolbar: section title with search, filter and bookmark
  actions for the row above the tag chips.

Nothing renders this yet — the layout lives in Storybook under
Features/Feed/Hero, composed with the existing ExploreChipsBar and feed
cards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
The rail placement led with a call-to-action button that dominated a
20rem card, even though the whole card is already a click target. It now
carries the same elements as the feed ad card and nothing else:
advertiser logo and disclosure, the ad title, matching tags, and the
cover image in its own column.

Also adds the Storybook coverage needed to review it: responsive
breakpoints rendered in per-width iframes, hero section states (no ad,
single post, short highlights list), one carousel case per post type,
ad placement variants, and toolbar variants.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
…obile

Follow-up on the hero section review:

- Drops the "Explore all" button; the highlights card already ends in
  "Read all".
- Flattens the rail: the ad slot and the highlights card lose their
  background and border, keeping only a hover tint on the ad.
- Replaces the two headline-labelled paging buttons with a position
  counter and two arrow buttons, right-aligned. The headline moves to a
  tooltip and stays in the button's aria-label.
- Renders the featured post through the feed's own list card below
  laptop, so the top of the mobile feed matches the rest of it. The
  post-type-to-list-card map moves out of FeedItemComponent into
  cards/common/listCards.ts so both callers share one map.
- Renames the section heading to "Recommended for you" in the story.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
The dots on the left already show which post is showing, so the counter
next to the arrows was a second readout of the same thing. Shortens the
section heading to "Recommended" in the story too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
Puts the hero section on the real feed so it can be reviewed with live
data. Off by default behind the `feed_hero` flag.

- FeedHero fetches the six current `majorHeadlines` for the Happening Now
  list, then loads the top four of those headlines' posts in full through
  `feedByIds` for the carousel, keeping the headline order.
- The rail ad comes from the feed ad placement, skipped for Plus, and
  logs impression, viewable impression and click.
- Upvote, downvote, bookmark and copy link run the same mutations the
  feed cards use; post clicks log with a feed origin.
- MainFeedLayout mounts it in the feed's own topContent slot on My feed
  and Popular, and tells the feed to stop inserting its Happening Now
  card so the headlines are not listed twice.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
@vercel

vercel Bot commented Aug 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
daily-webapp Error Error Aug 26, 2026 9:04am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored Aug 26, 2026 9:04am

Request Review

A preview deploy is a production build, so GrowthBook devtools can't
force `feed_hero` there and the section stays invisible for review.
`?feed_hero=1` turns it on for that browser and remembers it,
`?feed_hero=0` turns it back off.

To be removed once the flag is configured in GrowthBook — a URL that
opts someone into an experiment arm would skew the allocation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
Review feedback on the live feed:

- Caps the section at 80rem and centres it; on a wide monitor it ran the
  full feed width.
- The featured card's cover now fills its column, inset with its own
  corners like the feed cards, instead of being letterboxed over a
  blurred backdrop. Gated on `coverImage` so the in-feed wide cards keep
  the treatment they ship with.
- Rebuilds the ad slot at the size of the two promo cards it replaces: a
  banner cover, then advertiser, title and tags.
- Adds a `compact` mode to the highlights content for a surface with no
  card chrome: smaller heading, tighter rows, and a fade at the bottom of
  the scroll area so the last row isn't sliced by the pinned footer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
- The ad slot now runs advertiser, title, tags, then the cover, matching
  the order of the feed cards.
- The featured card's summary was clamped at three lines, leaving a gap
  above the action row on a card that is much taller than a feed card. It
  runs to six in the hero, behind `expandDescription` so the in-feed wide
  cards keep the shorter clamp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
The carousel now rotates on its own. The active indicator fills over the
slide's duration and the slide advances on that animation's `animationend`,
so pausing the fill pauses the rotation: hovering or focusing anywhere in
the hero freezes it mid-count and leaving resumes from the same point
instead of restarting. Reduced motion drops the animation, which also stops
the rotation. Only a change the reader asked for is announced, so an
automatic rotation doesn't talk over a screen reader every few seconds.

The paging arrows drop to the tertiary variant so they sit behind the
content rather than beside it.

In the rail, the ad, the highlight rows and "Read all" now share one text
edge on both sides. The ad bleeds past it and is clipped by the column, so
its hover surface reads as a full-width band like the rows above it, and
the footer link goes flat instead of carrying its own inset pill. The ad's
tracking pixel moves out of flow — it was a zero-size flex item still
claiming a full gap — which, with a tighter column gap, closes most of the
dead space between the ad and Happening Now. The hero and the rail get a
little more room between them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
…ing Now

The slide being replaced now stays mounted on top of the new one until its
fade finishes, so the two cross over instead of the card popping. Measured
in a browser, the pair never drops below full opacity mid-transition, and
the outgoing slide unmounts on its own `animationend` — hence a near-zero
duration under reduced motion rather than no animation at all, which would
leave it mounted forever.

The rail becomes one list: "Happening Now" heads the column and the ad is
its first row, sharing the headlines' row treatment with the cover on the
right at the cards' ratio. Reclaiming the space the stacked ad took brings
the list from three visible headlines to five.

The hero's cover grows to two thirds of the card, which leaves the text a
third — narrow enough that the headline needed its own size, so `coverImage`
and `expandDescription` collapse into one `hero` flag carrying the cover
crop, the headline size and the summary clamp together. That split only
holds where the text column can still hold a headline: below 1360px the
hero falls back to an even split, which on a 1024px laptop is the
difference between a full headline and a truncated one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
…eadline

The cover was taking two thirds of the hero and squeezing the headline, so
the wide cards gain a five-column span: the only ratio here where the text
needs more than one column, hence a text-span map alongside the image one.
Below 1360px the even split still stands.

The ad drops into the Happening Now list as the second row rather than the
first, so the freshest headline keeps the lead — and as the first row only
when there are no headlines to sit under. It now carries a headline's shape:
the copy on top, and the disclosure under it in the colour the rows give
their timestamps, verified as the same computed value in both themes rather
than by eye. The advertiser mark shrinks to the meta line's scale, the cover
narrows, and the spacing around both opens up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
… card

A five-line headline overran the hero's fixed-height card by about 7px, and
`overflow: hidden` took the difference out of the action row, leaving a
sliced strip of buttons along the bottom edge. The summary now gives way
instead: it is the only shrinkable child, so the headline keeps every line
and the actions keep their place.

The opt-in is explicit because `base.css` resets everything to
`flex-shrink: 0`, which is what let the overflow reach the actions rather
than being absorbed higher up.

Shrinking alone cut the last line mid-glyph, so the text block reserves a
line of bottom padding under a matching fade: at full height the gradient
covers only the padding and the last line stays solid, and once the block is
squeezed the padding goes first and the cut line fades out. The padding sits
on the block rather than the summary because `overflow: hidden` clips at the
padding edge, which would leak a seventh line past the six-line clamp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
The rail gains a full-size direct placement below Happening Now, carrying
the feed ad card's own design with its background and border removed so it
sits on the page rather than in a box. Its inset goes with the chrome: the
creative lines up with the headline rows above it instead of standing 16px
inside them.

The markup comes out of `AdGrid` into `AdCardContent` so both placements
render one design rather than two copies. `AdGrid` keeps the card shell and
the auto-rotation, which stays behind: the hero renders inside the feed's
`ActiveFeedContext`, so rotating here would refetch against the feed's own
ad cache and overwrite a slot the feed still owns.

Each slot holds its own query key so the two ask the ad server separately
and can come back with different creatives, and each logs its own
impression, click and viewability under its own slot name.

The rail now runs past the carousel rather than the two columns sharing one
height, so the headline list keeps all five rows. The cost is dead space
under the carousel, which grows with the card.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
…them

The placement takes a column of its own to the right of Happening Now, so
the section stays one row tall rather than the rail hanging past the
carousel and leaving a few hundred pixels of dead space beside it.

Three columns only fit from 1360px. Below that the ad would come out around
220px, narrow enough to push its "Remove" control off the end and to crowd
the headline list, so that width keeps the two-column rail and sits the
placement out; under `laptop` everything stacks and it returns at the end.

The third column also takes about 200px off the carousel, which is enough
that the 40/60 split stopped leaving the headline a readable column, so the
card goes back to an even split whenever the ad column is up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
The hero drops its own width. The feed container already caps this box at
the card grid's width, which tracks the reader's column count, so the fixed
cap was both too wide at three columns and too narrow at five; without it
the section measures exactly the grid beneath it.

The rail's hover surfaces were bleeding past their column and being clipped
by it, which squared off every corner they had. They now sit inside the
column with the padding and the radius the feed cards use, so the shape
under the cursor is a rounded card rather than a full-bleed band. The ad
card takes the same treatment, and with the chrome back its content keeps
the feed card's own inset — which retires the flush option added for the
version that had none.

More padding costs a row: the headline list shows four now rather than five.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FWv8BuKdrX1GtDnSobxFAa
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants