feat(feed): pinned sponsored strip on the main feed [mock-up, do not merge] - #6490
Draft
tsahimatsliah wants to merge 21 commits into
Draft
feat(feed): pinned sponsored strip on the main feed [mock-up, do not merge]#6490tsahimatsliah wants to merge 21 commits into
tsahimatsliah wants to merge 21 commits into
Conversation
Five placements for a "presented by" slot plus a ten-logo partner wall, modelled on a live show's lower third but static: a broadcast bar covers nothing, a feed bar costs posts, and a marquee in the periphery of a reading surface is the disruption we are avoiding. Concepts (pinned rail, inline rail, feed band, card slot, side rail) are rendered over real ArticleGrid cards so they can be judged against actual card weight, and the Evaluation story records the geometry measured off those stories at 375px and 1440px. Logos are painted as currentColor silhouettes through a CSS mask rather than filtered images, so one asset works in both themes, and are sized by area rather than cap height so stubby marks stay legible next to 6:1 lockups. Two asset constraints this surfaced, shown in the LogoTreatment story: knockouts fill in under an alpha mask, and 31 of the 66 wordmarks in the advertiser library are raster images wrapped in an .svg. Nothing is wired into the new tab — this is a design evaluation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three changes to the sponsored strip concepts: The presenting slot now keeps its brand colour while the partner wall stays silhouetted — one coloured mark against a neutral row is the hierarchy, and colour is what the paid slot is buying. That only works with a logo whose inks clear both grounds, so the primary moves from Sentry (#362D59, 0.20 luminance, sinks into the dark theme) to Google, the most colourful of the few library wordmarks with no near-black or near-white ink. LogoTreatment now proves this rather than asserting it: each candidate renders on both grounds at once via the app's own .invert class, with Sentry and Notion kept as the failing cases. The partner wall is spread across the full run instead of packed left, matching the reference bar. The rails use justify-between over a gap-4 floor with the fade reserved by padding, so the last mark only meets the fade on genuine overflow; the band moves to an even ten-column grid that breaks to five by two on mobile. Spreading also lowered the all-ten threshold from ~980px to ~920px — measurements in the Evaluation story updated throughout. Storybook's Tailwind never scanned packages/extension, so utilities used only in the concept file — anything with a breakpoint variant especially — generated no CSS and the band silently rendered five columns at every width. Added the glob. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pinned rail's translucent, blurred ground read as an overlay floating over the feed, which is more presence than a sponsor row should take. It is now an opaque background-default with the same hairline top border. Logo cap heights go 13 -> 16 for the wall and 18 -> 21 for the presenting mark, and the wall moves from tertiary to secondary text contrast. Both rails keep their fixed h-12 / h-11: the tallest optical result at the new cap is 22px, so the marks grow inside the bar rather than growing the bar. The wider marks broke the band's equal-fraction columns — LaunchDarkly at 83px overflowed a 78px cell and collided with its neighbour — so its grid moves to `auto` columns with the grid's own justify-between handing out the slack, stepping 10 / 5 / 3 columns as it narrows. Verified free of overlap and spill at 375, 768 and 1440. Bigger logos cost reach: the minimum run goes 625px -> 746px and the all-ten threshold ~920px -> ~1040px, and the band is taller on mobile (123px -> 179px). Evaluation numbers updated, with the trade recorded so the cap can be argued back down. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Label copy goes from "Presented by" to "Made possible by" (the lockup component follows the copy: PresentedBy -> PrimaryLockup). NVIDIA is not in the advertiser logo library, so the mark is a local story fixture under storybook/public/mock-logos: the Wikimedia wordmark recoloured to the brand green. The recolour is the point — the stock lockup is black, which fails the both-grounds test the coloured slot has to pass, while #76B900 sits at 0.62 luminance and clears light and dark alike. It leads the LogoTreatment candidate list; Google stays as the alternative. Logos are no longer lazy-loaded. Eleven inline SVGs weigh nothing, the masked partner marks are CSS and never defer, and deferring the one <img> let the paid slot be the last thing on the page to appear — which is exactly how it surfaced, as a lead mark that never painted. The longer label plus NVIDIA's wordmark widen the lockup 164px -> 210px, pushing the all-ten threshold ~1040px -> ~1090px and leaving no partner clear of the fade at 375px. Evaluation updated, and the copy is now named as one of the dials that buys that room back. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The strip fixture is now NVIDIA's complete logo — eye mark, wordmark and (R) — rather than the wordmark alone, taken from the 656x120 Wikimedia lockup and recoloured to the brand green on the root so all three paths inherit it. The recolour still carries its weight: NVIDIA sets the wordmark in black or white to suit the background, which is two assets, and the black one vanishes on the dark feed. The all-green lockup is a standard NVIDIA presentation and holds at both grounds from one file. Labels drop out of all caps. "Made possible by" and "Also backing daily.dev" now read in sentence case, and the letter-spacing that existed to make caps legible goes with it. Both changes narrow the lockup 210px -> 190px, which pulls the all-ten threshold ~1090px -> ~1070px and puts a partner back on screen at 375px, where the caps label had left none. Evaluation updated and the label casing named as the dial that did it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A flat file cannot satisfy this slot. NVIDIA sets its symbol in green and its wordmark in black or white to suit the background — two assets, and whichever one ships is invisible on the other theme. The all-green compromise shipped previously was legible but not the brand's lockup. Sponsor gains an optional `Artwork`: inline artwork rendered in place of the logo image, for a lockup whose parts need different colour rules. The NVIDIA fixture keeps #76B900 on the symbol and puts the wordmark and (R) on `currentColor`, so it is black on the light feed and white on the dark one from one source. Paths are NVIDIA's own artwork; only the fills are ours, and the three path roles were identified by bounding box rather than by eye. The flat file stays as the silhouette source. The lead mark is also ranked above the wall: PRIMARY_CAP 21 -> 23 lands it at 18px against a 15px median partner, a 20% step rather than a tier. Threshold moves ~1070px -> ~1070px (the wider lockup is offset by the earlier sentence-case saving); measurements refreshed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The lead sponsor's mark is now a link to their site — target=_blank with rel=noopener noreferrer — and the only thing in the strip that answers a cursor, lifting 5% from its left edge on hover. The partner wall is inert: no buttons, no links, no hover, no focus stops. One unambiguous paid click target, no row of hover states competing with the posts around it, and nothing extra in the tab order of every session — which matters most for the pinned rail. Two more partners (dbt, Sonar), both clean vectors, bringing the wall to twelve. Twelve divides evenly into the band's columns, which move to 3 / 4 / 6 so every row stays full; ten would have left a ragged last row at every breakpoint. The wall's minimum run goes 746px -> 889px and the all-ten threshold becomes an all-twelve threshold at ~1220px, which is wider than most laptops — at twelve slots the rails are a large-desktop format, and the evaluation now says so. The band grows 56px -> 89px at 1440px; the card slot still fits one post card exactly. Verified free of overlap and spill at 375, 768 and 1440. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MOCK-UP FOR REVIEW — not for merge. Concept A of the sponsored strip put on the webapp feed so it can be seen on a preview deployment rather than only in Storybook. The component moves from the extension's newtab folder to shared/components/sponsors, since the webapp renders it now, and the NVIDIA lockup moves with it. Sponsor fixtures live in mockSponsors.ts and are imported by both the story and the feed wiring, so what is reviewed in Storybook is the same data the preview renders. The flat nvidia.svg is deleted: the lead mark is inline artwork and nothing referenced the file any more. SponsorRailPinned is now `fixed` rather than `sticky`. It has to pin regardless of what it is nested in, and a consumer cannot override `sticky` with a position utility of equal specificity. What is deliberately missing before this could ship: sponsors come from a hard-coded fixture rather than the ad service, there is no feature flag, no impression or click logging beyond the optional callback, and the partner logos are hot-linked from business.daily.dev. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…debar The rail was `fixed`, so it was positioned against the viewport: full window width, running underneath the left sidebar and clipping its own "Made possible by" label behind it. It is `sticky` again, in flow inside the layout's padded main — which is where the sidebar offset already lives. The bar now spans the feed column and nothing else, and picks up that offset across layout variants and sidebar states without having to know what either is, which is what makes the old and new layouts both work from one rule. `laptop:px-10` matches the feed column's own padding so the marks line up with the card grid rather than the gutter. Height drops 48px -> 40px. The tallest optical mark is 22px, so the bar still has 9px of air above and below. Also fixes a stale comment: the blur was removed two commits ago but the note claiming the bar is translucent survived, because that edit targeted the wrong comment style and silently matched nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Concept A's cost is now 40px of the feed column rather than 48px of the viewport, and the padding that aligns the bar with the card grid pushes the all-twelve threshold ~1220px -> ~1250px. Measured, not derived. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two changes to how the wall behaves. It reshuffles on every page load, so no advertiser is permanently first and — since the row trims from the end — none is permanently the one dropped on a narrow window. The lead slot never moves; it was paid for by position. The shuffle runs on mount rather than during render, so the server and client agree on the first paint instead of tripping a hydration mismatch. The rails now render only the marks that fit whole, instead of overflowing behind a fade. Mark widths are known from their ratios, so this needs no measurement beyond the row itself. Measured: 12 marks at 1440px, 11 at 1200, 9 at 1000, 7 at 800, 1 at 375 — an advertiser is either shown or not, never a half logo under a gradient. The fade and its padding are gone with the overflow they existed to hide. The row measures directly on mount rather than waiting for ResizeObserver's first callback, which is both more deterministic for the initial paint and necessary where that callback never arrives; RO still drives resizes, with a window-resize fallback where it is missing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
On the deployed feed the wall rendered empty: the row was measured before layout settled, came back 0, and 0 was taken to mean no mark fits. With no resize afterwards it stayed empty — a strip with a lead sponsor and no wall at all. A zero width now means "not measured yet" and renders the full wall, same as before the first measurement. The row is also re-measured once after the layout's 300ms padding transition, since the first reading can be of a width that is still on its way somewhere else when the sidebar opens or closes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The step-down counts are story geometry. On the feed the sidebar and column padding take their cut first, so the same viewport fits one fewer mark — 8 rather than 9 at 1000px, measured on the preview. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Browsers draw their URL preview in the bottom corners, over anything the page renders. A feed is almost entirely links, so a bar flush to the bottom edge spends most of its life with a tooltip across it — on a long URL, across the whole strip, including the paid slot. The bar now holds 28px clear of the edge as a rounded island with a border and a shadow, and the bubble lands in the gap. Moving it sideways would not have worked: Chrome relocates the bubble to the opposite corner when the cursor nears it, so there is no safe side, only a safe height. The offset is deliberately generous rather than exact — the bubble is browser chrome, invisible to the DOM, and it scales with page zoom. The ground stays opaque. `surface-float` is a translucent token, and cards scrolling through the marks costs more legibility than the float is worth. The gallery's concept-A override drops the border and radius it used to add by hand, which the component now owns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The floating island is reverted. It cleared the browser's URL tooltip but read as a widget bolted onto the product rather than part of it, which is most of what made the flush version work. The bar is flush again, and the tooltip is handled by what sits under it. A value rail stacks beneath the sponsor row, so the thing the bubble covers is ambient data rather than the row someone paid for — and each rail opens with its label on the left, which is exactly where the bubble lands, so what it eats is a word rather than a number. The reference format stacks the same way: sponsor lockup above, tickers below. Stacking also answers a question the strip could not answer before. A permanent bar carrying only advertising is rent; one carrying something the reader came for is a feature that happens to be sponsored. Ten rails to choose from, ordered ambient to personal — tag momentum, model rankings, hot posts, streak, leaderboard, squad pulse, live rooms, role pulse, release radar, poll. Each is mocked, and none needs new plumbing: trendingTags, userStreak, the leaderboard queries, live rooms, opportunities and poll posts are already in the app. The feed wiring now renders the dock with tag momentum, that being the closest thing daily.dev has to the reference's stock ticker. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A permanent bar has to justify being permanent every second it is on screen. These are ten answers, grouped by the argument they make. Get out of the way — Retract on read (leaves on scroll down, returns on scroll up, the pattern browsers use for their own chrome), Condense on read (the wall collapses to a count while moving; the paid mark never leaves), Hairline peek (a 6px seam that opens on approach), Idle reveal (minimal while anything moves, full when the reader stops). Become chrome — Status bar (dressed as an editor status bar, a shape developers read as tooling), Shortcut bar (carries the app's keyboard hints, so the row is useful before it is sold), Browser seam (reads as the window's bottom edge rather than the page's). Do a job — Progress rail (its top edge is the feed's scroll progress). Earn it by restraint — Broadcast credits (one partner at a time, crossfading, a mention rather than a wall), Colophon (no panel at all, low-contrast marks on the page that brighten on hover). No second row and no float: every one is a single flush bar. The strip's primitives are exported so the variants share optical sizing, rotation and fitting rather than reimplementing them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The bar is not restyled in any of these. It stays flush, full width, sticky, one row, opaque, no float — the version that already read as part of the product. What changes is geometry, ordering, or what the page hands the browser. Give it nothing to cover — Left gutter (content starts past the tooltip's reach), Right-anchored (lead mark at the far end, the wall's tail takes the corner, and the wall already reshuffles so it is a different mark each session), Sacrificial band (taller bar, content top-aligned, the bottom 26px are padding), Centred (both corners empty, which is the only static layout safe from the tooltip in either one). Make it smaller — Short href (the tooltip is exactly as wide as the URL inside it, so linking cards to /p/<id> shrinks it from most of the bar to a stub; a routing change with no layout cost at all), Narrow anchor (only the title is a link, so most of the pointer's time over a card draws no tooltip). Move only when it matters — Lift on hover, Slide on hover (contents move, bar does not), Adaptive (estimates the tooltip's width from the hovered href and moves only for long ones), Swap ends (lead mark and wall trade places, geometry untouched). The story ships a tooltip simulator: hover any card and a stand-in appears bottom-left with the real href at roughly the real size, and it jumps corners the way Chrome does. Each fix can be judged by hovering rather than by argument. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Measured on the live feed, 28 links on one screen, at the 12px UI font the tooltip uses: nav and tag links 33 chars 189px 13% of a 1440 screen post slugs 68 chars 393px 27% ad click-throughs 742 chars clipped 50% (Chrome's cap) The promoted cards are the worst offender by an order of magnitude: they link through a signed token, so the URL carries a whole JWT and the tooltip runs to the truncation limit every time. That re-ranks the fixes, and not in favour of the ones that looked neatest. A horizontal clearance has to be 400px to survive an ordinary post link and half the bar to survive a promoted one, at which point there is no bar left to protect. A vertical clearance is 26px whatever the URL says. Each variant now carries a `holds` verdict — any URL, post links only, or needs shorter URLs — and the overview leads with the measurement rather than the taxonomy. The testbed was also lying: it drove `commentsPermalink`, but cards build their href from the slug (see CardOverlay), so every bench ran against a 33-character URL. It now drives the slug, with a tracking length that reproduces the real worst case — verified at 670 chars, a 720px tooltip, 5 of 12 marks buried unfixed and 0 of 12 with the vertical fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The dock ships on the feed with Hot right now as the default rail, and the rail's label is now a dropdown that swaps it in place. Putting the control in the label is what makes this cost nothing. That word was already the throwaway element — it sits where the browser's URL tooltip lands, which is why the rail opens with it — so turning it into a trigger spends no space the row was not already spending, and moves nothing else on the strip. Eleven channels, including a new Your agents rail: what the reader's own agents are doing, which is closer to a build indicator than a ticker and the most personal of the set. The choice is kept in localStorage rather than on the account — it is a preference about one strip, it should survive a reload, and it is not worth a round trip or a migration. It is read after mount, not in a state initialiser, so the server and client agree on first paint. Verified: defaults to Hot right now, the menu lists all eleven, selecting Release radar swaps the row and stores `releases`, and the choice survives a reload. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Several rails carried three items and left most of the row empty on a wide feed, which read as a strip with nothing to say. Every channel is now stocked well past what fits: 16 tags, 10 models, 8 headlines, 12 releases, 8 squads, and six or seven lines each for streak, leaderboard, live rooms, roles, agents and the poll. Carrying more than fits is the point — the row should read as a ticker continuing past the edge rather than a list that happens to stop. That only works if the edge is deliberate, so the row now fades out over its last 40px. A hard clip cuts a word in half and reads as a bug. Measured at 1440: Release radar fills 958px of a 1252px row with all twelve visible; Hot right now, whose items are sentences, shows five and fades the rest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A design mock-up of a TBPN-style sponsored strip, put on the feed so it can be reviewed on a real page instead of only in Storybook. Sponsors are hard-coded placeholders. Nothing here has been sold.
Where to look
Extension / Sponsored Strip. Start with the Evaluation story; Gallery compares all five concepts, LogoTreatment shows the theme and asset constraints.What it is
One lead sponsor plus a twelve-logo partner wall, in a 48px rail fixed to the bottom edge.
currentColor— black on the light feed, white on the dark one — while the symbol holds#76B900. NVIDIA ships a black wordmark and a white one; a single flat file is invisible on one theme or the other.currentColor, spread withjustify-between, and completely inert — no links, no hover, no focus stops, so twelve logos cost nothing in the tab order of every session.Four other placements were evaluated
Concept A (this one) is the only option that guarantees a session-long impression, and the only one that never gives the pixels back. The full comparison is in the Evaluation story. The one number worth carrying into review:
Twelve marks need an 889px run, so the rail needs ~1220px of viewport before the last logo clears the fade — wider than most laptops. Below that it shows fewer sponsors than were sold, down to none at 375px. At twelve slots this is a large-desktop format. The dials are slot count, the 16px wall cap, and label length; the wrapping concepts (feed band, card slot) hold all twelve at every width instead.
Before this could ship
business.daily.dev.svg, which mask to a solid blockVerification
Storybook is verified directly — both themes, 375 / 768 / 1440, no overlap or spill, and the story renders the same fixture objects the feed wiring uses. The webapp wiring itself is unverified locally: the app holds paint until the API boot resolves, which needs a real session, so the preview deployment is the first place this is actually seen on the feed.
🤖 Generated with Claude Code
Preview domain
https://feat-feed-sponsored-strip.preview.app.daily.dev