Skip to content

feat(post): bring the focus card to parity with the classic layout for the post_redesign experiment - #6658

Open
tsahimatsliah wants to merge 31 commits into
mainfrom
claude/post-modal-experiment-impact-824ab5
Open

tsahimatsliah wants to merge 31 commits into
mainfrom
claude/post-modal-experiment-impact-824ab5

Conversation

@tsahimatsliah

@tsahimatsliah tsahimatsliah commented Sep 15, 2026

Copy link
Copy Markdown
Member

What

Pre-flight for starting the post_redesign experiment: the treatment arm (focus card) now carries everything the control arm carries, on /posts and on the /articles ad template, so the arms differ in layout only.

Control vs treatment, measured page by page on laptop and phone: https://claude.ai/code/artifact/6f52e0e9-a0df-4856-8aa4-4c1464ecadbe

Changes

  • Signup banner rendered on the focus card for logged-out laptop visitors, with bottom clearance so the thread stays reachable (it was excluded from the redesign since feat(post): redesigned post page & modal reader (PostFocusCard) #6184).
  • Programmatic ads carried slot-for-slot. The page builds one ad set from its slot map and hands it to both layouts through a typed ads prop on PostFocusCard: leaderboard, in-content MPUs (TLDR and hosted body), above-comments MPU, comment MPUs, phone strip. Only article and video posts carry in-page units on /posts, as on classic; other types keep the strip alone. Before this the treatment arm had zero programmatic ads for logged-out visitors.
  • Rail units to the right of the column. Floating beside the centred column when the card's own box leaves room for them (measured, not a viewport query, so the sidebar counts); as a second column on laptops, with both page wrappers at 72rem so the column still clears 728px for the leaderboard; inline below laptop, matching the classic rail that stacks under the article from tablet up. The focus card pins the closing unit itself; rail elements carry no positioning, so the classic rail is untouched.
  • /articles follows the same flag and renders the focus card in the treatment arm with the template's own slot map and rules intact: light mode, no signup surfaces, noindex, hard-navigation guard, read_ads kill switch, no direct-sold widget. Summary segments render above a hosted body, as on the classic template.
  • Squad posts match the classic squad template: shared tweets render as embedded tweets; shared videos keep the embed and summary; neither carries a read CTA; no tags on any squad post; the sharer's commentary keeps its line breaks in body type.
  • AdAsComment renders in the focus card discussion for non-Plus readers when no programmatic comment units are present, as classic does.
  • Enrolment hygiene: usePostRedesign only evaluates the flag when the surface can render the redesign, so ?author / ?squad sessions are never enrolled. One evaluation site, shared by /posts, /articles and the modal.

Merged with main after Kueez replaced AdSense (#6664); same slot numbers, Prebid loader.

Not changed, by design (from the original focus-card PR)

  • Shared articles present the shared item as the article (title, cover, TLDR, Read post) where classic shows a link card.

Tests

  • PostPage.tsx: signup banner on the focus card; identical mounted ad-slot set on both layouts (article, video); collection keeps the strip alone; rail never pinned in flow; laptop rail column; snapshot icon in the last segment; ?author never evaluates the flag.
  • ReadArticlePage.tsx: both arms of /articles; identical mounted set for an article and for a collection (summary and body units).
  • PostFocusCard.spec.tsx: shared tweet, shared video, tags per template, commentary type.

Before starting the experiment (GrowthBook)

Logged-out visitors are in the audience by default (loggedIn: false). With this branch that is safe for revenue.

Pre-existing and unrelated: /articles renders dark in both arms on production although the template asks for light mode.

🤖 Generated with Claude Code

Preview domain

https://claude-post-modal-experiment-imp.preview.app.daily.dev

tsahimatsliah and others added 3 commits September 15, 2026 10:23
PhoneTopAdStrip renders in the layout banner, outside the page, and gated
itself on useOrganicAdsenseSlots() alone. In the post_redesign arm the page
never loads adsbygoogle (the focus card carries no slot markup), so for a
logged-out phone visitor the strip pinned a reserved-height empty box that
could neither fill nor collapse, and offset the auth banner below it.

The page's redesign decision now lives in usePostPageRedesign, and the
page's own getLayout renders the banner with the ISR post so the strip is
skipped whenever the focus card renders.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rge)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 15, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
daily-webapp Ready Ready Preview Sep 17, 2026 3:16pm UTC

Request Review

…evert before merge)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The laptop signup banner was excluded from the redesign arm since the
focus card landed, so logged-out visitors in treatment lost the page's
main conversion surface. Render it for both layouts; the focus card gets
bottom clearance because it ends in the thread, where the classic page
ends in the footer the fixed banner already covers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The post_redesign treatment dropped every AdSense unit for logged-out
visitors, so the experiment compared inventory as much as layout. The
focus card now takes the same five placements the classic layout renders
(top leaderboard, TLDR MPUs, MPU after the direct-sold widget, MPU above
the discussion, comment-thread MPUs) through one ads prop the page builds
once for both layouts, and the pinned phone strip returns to both. The
arms now differ in layout only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eriment-impact-824ab5

# Conflicts:
#	packages/webapp/pages/posts/[id]/index.tsx
A squad post sharing a tweet reached the focus card as if the tweet were
an article: tweet text as the H1, its media as a cover thumbnail, the AI
summary as a TLDR. The classic layout renders the same post as an
embedded tweet card, so the focus card now does too, keeping the sharer's
commentary above it and the read CTA below.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
No read CTA and no tags under a shared tweet: the tweet card is the whole
post, and the classic layout never shows tags on share posts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…cus card

The classic layout only threads the organic units through PostContent, so
squad and collection posts carry the pinned phone strip alone. The focus
card took the whole set for every eligible type, which gave a collection
four units where the control arm has one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The classic rail MPU sat in the sidebar; on the focus card it was inline
under the direct-sold widget. From 92rem the unit now sits in the gutter
to the right of the centred column, sticky under the header, and the
column stays exactly where it is. Narrower viewports keep it inline.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…edesign

The /articles template kept the classic layout regardless of the arm, so a
visitor landing there and one landing on /posts saw different designs in
the treatment. It now renders the same focus card behind the same flag,
with the template's own rules intact: light mode, no signup surfaces,
noindex, the hard-navigation guard and the read slot map.

The single-column design has no widget rail, so of the three rail units
only slot 11 has a home, beside the column on wide viewports; slots 12 and
19 stay with the classic template. The direct-sold widget stays off, as it
is on the classic template.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rail prop takes the classic rail's units in order: the two MPUs in
flow and the 300x600 tower pinning last, exactly as they sit at the end of
the classic widget column. The organic post page keeps its single sticky
unit. Below the rail breakpoint only the first unit has an inline home.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
With the rail hanging off an independently centred column the pair read
off-balance. The column and rail now centre together, so the column shifts
left by half the rail's width only when a rail renders, and the page
wrappers widen at the rail breakpoint to hold both.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… read rail on phones

A shared video carries no read CTA and no tags on the classic share
layout; the focus card now matches, keeping the embed and summary. The
arbitrage rail units gain the classic template's hideOnPhone so the phone
density budget stays at the strip, one in-content unit and the
above-comments MPU.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The focus card set a sharer's commentary as bold title text with the
line breaks kept, so a long post read as a wall of headline. The classic
share layout renders the composer's own markdown from titleHtml; the
focus card now does the same, with the plain title as the fallback.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
titleHtml collapses the author's line breaks into one paragraph, so
rendering it lost the structure they typed. The raw commentary keeps the
breaks; it now renders with them in body type rather than as a title.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tsahimatsliah and others added 4 commits September 17, 2026 12:10
… flag (revert before merge)"

This reverts commit b53c6aa.
…efore merge)"

The flag default returns to the control value; GrowthBook owns the ramp.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
With ads splitting the TLDR the snapshot icon sat on its own line under
the segments; it now trails the last segment's line, as it does on the
plain TLDR.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tsahimatsliah tsahimatsliah changed the title fix(post): drop the phone ad strip under the focus card redesign feat(post): bring the focus card to parity with the classic layout for the post_redesign experiment Sep 17, 2026
The classic squad template (share, freeform, welcome) never lists tags;
only the article, video and collection templates do. The focus card
followed the article template for every type.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Scoped to *.preview.app.daily.dev so tests and production are untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ries, keep the body unit on phones

Rail units no longer carry positioning of their own: the classic rail
received the focus card's sticky classes through the shared element and
pinned mid-rail, and the focus card's inline fallback pinned in the
column. The focus card now pins its last rail unit itself.

Video summaries split into segments on the focus card as they do on the
classic layout, and on /articles the first body unit keeps its phone
placement when the card renders a body instead of a summary.

The preview-host review override resolves after mount so the server and
the first client render agree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tsahimatsliah and others added 2 commits September 17, 2026 15:04
Between the laptop breakpoint and the floating-rail threshold the column
and rail now centre together as one block, the column narrowing to fit,
so laptops keep every rail unit instead of the single inline fallback.
The wide-screen and below-laptop placements are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@idoshamun idoshamun left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

The treatment arm is brought up to the control arm's ad set slot-for-slot on /posts (verified: carriesInPageAds matches what PostContent vs SquadPostContent/CollectionPostContent actually forward on classic, and useOrganicAdSlots()'s canRender defaults to true, so the control path is untouched). The share/tweet/commentary parity work and the tests around it look right.

Three things I'd want fixed before this starts an experiment, plus a few smaller ones. Details are inline.

Blocking

  1. useIsPreviewHost forces the treatment on every preview host and is self-declared as "revert before merge".
  2. /articles treatment column measures 624px below 92rem, under the 745px the leaderboard needs, so slot 2 resolves to 320x100 where control gets 728x90.
  3. post_redesign is still evaluated (i.e. enrolled) for ?author / ?squad sessions that can only ever render the classic layout.

Non-blocking, not attached to a diff line

  • Two doc blocks are now contradicted by this change and will mislead the next reader: useReadAdSlots.ts still says "the post page passes false in the focus-card redesign arm", and the /articles page header still says "Lives on its own route so /posts/[id] and the focus-card redesign are untouched". Same for the PR description's "Not changed, by design: Tags render on squad posts" — showTags now fixes exactly that.
  • The focus card's discussion panel never renders AdAsComment, which classic shows to non-Plus readers when commentAds is absent. Irrelevant while the audience is loggedIn: false, but it is a monetisation difference the moment logged-in users enter the experiment — worth stating as a known gap rather than discovering it in the readout.

Verification

  • Root AGENTS.md + flag/experiment rules read
  • Control arm traced: flag off leaves /posts and /articles markup and slot sets unchanged
  • Slot parity cross-checked against ReadPostContent, BasePostContent, SquadPostContent, slots.ts, resolveAdSizes
  • Not run: package tests / strict typecheck (not executed here, so not claimed)
  • Not verified: rendered widths at 1366px, 1440px and 1472–1560px with the sidebar expanded and collapsed

Reviewed by AI.

Comment thread packages/shared/src/hooks/post/usePostRedesign.ts Outdated
Comment thread packages/webapp/pages/articles/[id].tsx Outdated
Comment thread packages/webapp/components/post/usePostPageRedesign.ts Outdated
Comment thread packages/shared/src/components/post/focus/PostFocusCard.tsx Outdated
Comment thread packages/webapp/pages/articles/[id].tsx
- Drop the preview-host override: the flag alone decides the arm.
- Gate flag evaluation on the surface being able to render the redesign,
  so ?author and ?squad sessions are never enrolled; one evaluation site
  in usePostRedesign, which the page composes with its own rule.
- Key the floating rail off the space the card's own box leaves to the
  right of the column, measured with a ResizeObserver, instead of a
  viewport query that cannot see the sidebar or the wrapper.
- Widen both page wrappers to 72rem so the laptop column clears 728px and
  the leaderboard is requested at its booked size, as on classic.
- Render every rail unit inline below laptop, matching the classic rail
  that stacks under the article from tablet up.
- Render the summary above a hosted body when the ad template asks for
  segments, so collections keep their TLDR units.
- Render AdAsComment in the focus card discussion for non-Plus readers
  when no programmatic comment units are present, as classic does.
- Drop the dead canRender parameter and the stale docblocks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tsahimatsliah

Copy link
Copy Markdown
Member Author

Thanks for the review. Everything inline is addressed in acee305 and the threads are resolved with the specifics. On the summary items without a diff line:

  • Stale docblocks: useReadAdSlots.ts no longer mentions the redesign arm (the canRender parameter it described had no caller left and is gone); the /articles header now says the template follows post_redesign; the PR description's tags line is corrected.
  • AdAsComment: rendered in the focus card discussion for non-Plus readers when no programmatic comment units are present, the same rule as PostEngagements, so the arms match for logged-in members too.

Still to confirm on the preview once this build is up: requested leaderboard sizes at 1366 and 1440, and the 1472 to 1560 band with the sidebar expanded and collapsed. Will post the numbers here.

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.

2 participants