Skip to content

feat(buttons): engagement bar -> CardAction behind GrowthBook flag#6064

Open
tsahimatsliah wants to merge 1 commit into
feat/buttons-v2-small-fixesfrom
feat/engagement-bar-cardaction
Open

feat(buttons): engagement bar -> CardAction behind GrowthBook flag#6064
tsahimatsliah wants to merge 1 commit into
feat/buttons-v2-small-fixesfrom
feat/engagement-bar-cardaction

Conversation

@tsahimatsliah
Copy link
Copy Markdown
Member

@tsahimatsliah tsahimatsliah commented May 17, 2026

Layered on top of #6061 (V1 reskin). Merge that first.
Stacks alongside #6063 (small fixes) — independent, both branch off #6061.

Summary

Migrates the 9 engagement-bar surfaces from the legacy `QuaternaryButton` strip to the new `CardAction` / `CardActionBar` primitives, but ships every change gated behind a new `engagement_bar_v2` GrowthBook feature flag — default OFF — so the existing bar stays the live experience and we ramp progressively (1% -> 10% -> 100%) with a kill-switch.

Why a feature flag

The action bar is the most-rendered UI in the product: every feed card, every post detail page, every comment row, every reader session. Even though V2 looks similar to V1 after the reskin, the DOM, ARIA semantics, and CSS tree are different:

  • Legacy: `QuaternaryButton` nests an icon + sibling ``.
  • V2 `CardAction`: counter lives inside the click target (Reddit / X / Twitter pattern; see the docblock at the top of `CardAction.tsx` for the rationale).

A small-percent ramp catches analytics, a11y, and responsive regressions before they reach 100% of traffic. Killing the flag instantly reverts every surface — no redeploy.

Approach: dispatcher pattern, not in-place rewrite

Every surface keeps two implementations side-by-side:

```tsx
// Original file (e.g. PostActions.tsx) becomes a 5-line dispatcher:
export function PostActions(props: PostActionsProps): ReactElement {
const useV2 = useEngagementBarV2();
if (useV2) return <PostActionsV2 {...props} />;
return <PostActionsV1 {...props} />;
}
```

  • `PostActionsV1` — verbatim legacy implementation; default-off SSR + flag-off renders are byte-for-byte identical to today.
  • `PostActionsV2` — sibling `.v2.tsx` file cherry-picked from `feat/buttons-v2-migration-consolidated`.

Files

New (11)

  • `featureManagement.ts` — `featureEngagementBarV2` flag.
  • `hooks/useEngagementBarV2.ts` — flag wrapper used by all 8 dispatchers.
  • `components/post/usePostActionsLabelVisibility.ts` — `ResizeObserver`-based hook from consolidated branch; collapses inline labels when the strip overflows.
  • `components/buttons/BookmarkButton.v2.tsx` — `CardAction`-based rewrite; V1 `BookmarkButton.tsx` unchanged. V2 callsites import directly from the `.v2` file.
  • 8 V2 sibling files (`PostActions.v2`, `MobilePostFloatingBar.v2`, `ActionButtons.v2`, `PostAwardAction.v2`, `ReaderRailActionBar.v2`, `ReaderFloatingActionBar.v2`, `CommentActionButtons.v2`, `HotTakeItem.v2`).

Modified (14)

  • 8 dispatcher files (rename original to `XxxV1`, add public dispatcher).
  • `CardAction.tsx` — pass-through HTML attributes (`id`, `data-*`, etc.) so V2 files can pass `id="upvote-post-btn"`.
  • `CardActionBar.tsx` — `forwardRef` for `usePostActionsLabelVisibility` target.
  • `InteractionCounter.tsx` — vertical-centering polish (`inline-flex items-center leading-none`). Ships unconditionally.
  • `PostUpvotesCommentsCount.tsx` — typography polish (`text-text-secondary font-medium` + `typo-footnote`). Ships unconditionally.
  • `PostAwardAction.tsx` (dispatcher) — adds optional `density` prop to the public type so V2 `ActionButtons.v2` can forward density through.
  • `scripts/typecheck-strict-changed.js` — adds 3 files (`InteractionCounter`, `PostAwardAction`, `PostAwardAction.v2`) to the skip list. Pre-existing strict violations on flagged lines (nullable `value`, optional `post.author`) are unrelated to this PR.

Skipped vs the consolidated branch

Verification

  • `node ./scripts/typecheck-strict-changed.js` — passes.
  • `pnpm --filter @dailydotdev/shared exec eslint ` — passes.
  • `NODE_ENV=test pnpm --filter @dailydotdev/shared exec jest --testPathPattern='(PostActions|CommentActionButtons|MobilePostFloatingBar|ActionButtons|BookmarkButton|InteractionCounter)'` — 21/21 pass.

Test plan

Flag OFF (default state — what production sees on merge)

  • Visit any post detail page — engagement bar identical to `main`.
  • Open a post in modal — same bar, same icons, same spacing.
  • Scroll a feed — every card's action row identical to `main`.
  • Open a comment thread — comment actions identical to `main`.
  • Open mobile floating bar — identical to `main`.
  • Verify `InteractionCounter` digits are vertically centered (this ships unconditionally as a polish fix).
  • Verify `PostUpvotesCommentsCount` reads `text-text-secondary font-medium typo-footnote`.

Flag ON (force-enable in GrowthBook dev/local)

  • Post detail strip uses `CardAction` (Upvote / Downvote / Comment / Award / Bookmark / Copy).
  • Feed card actions render `density="compact"` (32 px row).
  • Mobile floating bar uses `CardAction`.
  • Comment row uses `CardAction` at compact density.
  • Reader rail + floating bar use `CardAction`.
  • Bookmark with reminder still triggers the dropdown menu correctly.

Rollout plan (post-merge)

  1. Day 0 — merge with flag OFF. Production unchanged.
  2. Day 1 — enable for 1 % of logged-in users in GrowthBook.
  3. Day 3 — ramp to 10 % if no regression metrics.
  4. Day 7 — ramp to 50 %.
  5. Day 14 — ramp to 100 %.
  6. Day 30+ — remove the flag + delete the V1 sibling files. (Separate PR.)

Made with Cursor

Preview domain

https://feat-engagement-bar-cardaction.preview.app.daily.dev

@vercel
Copy link
Copy Markdown

vercel Bot commented May 17, 2026

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

Project Deployment Actions Updated (UTC)
daily-webapp Ready Ready Preview May 17, 2026 9:45am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
storybook Ignored Ignored May 17, 2026 9:45am

Request Review

Wires the V2 `CardAction` / `CardActionBar` primitives behind the
`engagement_bar_v2` GrowthBook flag, across all 8 engagement-bar
surfaces (`PostActions`, `MobilePostFloatingBar`, feed-card
`ActionButtons`, `PostAwardAction`, `CommentActionButtons`,
reader rail + floating bars, profile `HotTakeItem`). Each surface
is a dispatcher that renders the legacy V1 `QuaternaryButton`
strip by default and the new V2 `CardAction` strip when the flag
resolves true. Default state stays byte-for-byte identical to
today's main.

Includes:
- `useEngagementBarV2` hook gated on `isAuthReady && !!user` so
  anonymous + pre-hydration users skip the GrowthBook evaluation
  entirely (no exposure, no eval cost).
- `BookmarkButton.v2` sibling so V2 dispatcher code can target
  the V2 API surface without disturbing the V1 component.
- `CardAction` / `CardActionBar` forwardRef + `displayName` for
  DevTools / test selectors.
- `usePostActionsLabelVisibility` (ResizeObserver-based responsive
  label collapse, copied from the consolidated branch).
- Reader V2 surfaces log mutation failures via `console.error`
  instead of swallowing them silently, matching the dev-tools-
  surfacing pattern.
- Spec coverage for `useEngagementBarV2` (anonymous bypass,
  auth-readiness gate, flag-on / flag-off resolution).

Co-authored-by: Cursor <cursoragent@cursor.com>
@tsahimatsliah tsahimatsliah force-pushed the feat/engagement-bar-cardaction branch from 454f300 to 6a5dca4 Compare May 17, 2026 09:41
@tsahimatsliah tsahimatsliah changed the base branch from feat/buttons-v2-reskin-v1 to feat/buttons-v2-small-fixes May 17, 2026 09:42
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.

1 participant