Release Notes
Highlights and behavior changes per release. The full changelog ships as
CHANGELOG.md
in the package.
Pin the SDK exactly and bump deliberately:
npm install @elo-ads/web-sdk@0.6.1 --save-exact- Change: the card is set like a feed ad, as on iOS and Android.
<EloAd>draws a header row with a 36px image beside the title and the “Ad” disclosure on its own line under it, the description at full width on up to three lines, and the CTA button on a 44px row of its own at the trailing edge, or a chevron closing the header row. The card takes the height its copy needs. The strip is unchanged and still readsAd • Headline. See Ad Formats and Rendering Ads. - Change: the card needs a 192px slot. The server’s threshold for the card moved from 80px to 192px, so a
maxHeightfrom 72px to 191px gets the strip. A response that names no known format falls back the same way. - New Elo chooses what opens the ad when the creative has a CTA button. Elo sets per publisher and format whether the button is the ad’s only link or the whole ad is the link, with nothing to configure in code. The defaults keep 0.6.0’s behavior: the button alone on the strip, the whole ad on the card.
EloDebugAdOperationreports the choice astapSurface. - Fix A CTA button inside the ad’s own link is hidden from assistive technology. The link is named after the creative alone, on the card and the strip, as on iOS and Android.
- New Describe the slot, and the server chooses the format. Pass
maxHeight(and optionallymaxWidth), in CSS pixels, tosdk.fetchAds. The server returns the format that fits: the card needs 80px, the strip 72px, and a shorter slot gets no ad.<EloAd>renders whichever format the ad names, asad.presentation.format. LeavingmaxHeightunset keeps today’s card, so an integration that changes nothing sees nothing change. The strip is a single row for a slot beside the composer: a 40px mark, a one-line headline and description, and a trailing chevron or compact CTA button, 72px tall. When the creative carries a CTA, the button is the strip’s only link and the rest of the strip is inert, so a click near the composer that misses opens nothing. The container carrieselo-ad-cardorelo-ad-stripbesideelo-ad. See Ad Formats and Rendering Ads. - New An id and time for each chat message.
ChatMessagetakes optionalidandcreatedAt(aDate, or an ISO 8601 string with a time zone). Set them once, when the turn happens, and send the same values each time the turn is sent again. Elo then knows when each turn happened instead of only when the ad was requested, and theidmatches Search API calls that send it asX-Elo-Message-Id. A summary message takes the id and time of the turn it summarizes. A value the SDK can’t use is left out with a console warning, andfetchAdsnow sends onlyrole,content,id, andcreatedAtfor each message, not other fields on your message objects. - New The publisher’s CTA settings apply on the web. The ad now carries
presentation.cta_enabledandpresentation.cta_variant, which Elo sets per publisher and format.cta_enabled: falsehides the CTA button even when the creative sendscta_text. Theiconvariant adds an arrow after the label;icon_bounceandicon_shimmeradd the arrow and, after the ad has been at least half visible for one second, a short pop or shimmer that repeats every few seconds while it stays in view. The motion is off when the reader prefers reduced motion. On the strip,presentation.tap_insettakes up to 14px off each edge of the link on creatives without a CTA; the strip draws the same. - Change: the card is 80px tall, and its copy is one line. The headline, description, and CTA label end in an ellipsis instead of wrapping, as on iOS and Android, so the card never grows past the height the server chose it for. The hairline border is drawn inside the card as an inset shadow and no longer adds 2px; a
styleor class that setborderon.elo-adshould setbox-shadowinstead. - Change: ad requests no longer send
display_position. They senddimensionsinstead, and the server records the position from the format it chose:cardfor a card andbannerfor a strip, the same values the mobile card and strip report. Web showings in the unbounded chat slot staycard. - Breaking
sdk.getTrackingTotals()and theTrackingTotalstype are removed;sdk.setDebugObserver(observer, { capturePayloads })replaces them. The SDK now reports each event as it happens and keeps no counts of its own:initialized,ad_operation(the outcome of eachfetchAds, with the slot sent and the format and settings received), andtracking_attempted/tracking_completedfor render, impression, click, and tap. A page that wants totals counts the events. WithcapturePayloads, it also receives the ad request body with message content and context descriptions removed, the ad response, and the body each tap posts. It is the same debug observer as iOS and Android 0.6.0.TrackingEventnow includesclick. See the API Reference. - Fix A tap that opened nothing sends no click notice.
sdk.trackTapsent the demand source’s click notices for every tap. It now sends them only for a tap withopened: true. - Demand-source notices are sent from the reader’s browser. Some demand
sources ask for the notice that an ad was displayed or clicked to come from
the reader rather than from Elo’s servers, so their systems see the request
as the reader’s. An ad can now carry a
trackerslist, and<EloAd>sends each entry at the moment it names: display, viewable impression, or click, for pointer and keyboard activation alike. The URLs belong to the demand source and are sent as given, with nothing read back. Most ads carry none. For a layout you render yourself,trackRenderandtrackImpressionsend theirs as before, and the newsdk.trackClick(ad)sends the ones a click owes;sdk.trackTap(ad, tap)sends them too, so a layout already reporting taps needs no change. Each notice goes out once per showing. See Rendering Ads and the API Reference. - The SDK names itself to the ad server. Every request carries
X-Elo-SDK: EloWebSDK/0.6.0, which is how the ad server knows this build sends demand-source notices and stops sending them itself. Expect the header if you inspect or proxy the SDK’s requests.
- Tap analytics for Elo-served ads.
<EloAd>reports every click inside the card toPOST /v1/sdk/events: the region under the pointer (thumbnail, headline, description, CTA, chevron, or the card’s own padding), the position as a fraction of the card’s size, the card’s size, whether the click opened the destination, the time since the impression, and the click’s index within the showing. Coordinates are relative to the card, never the page or the screen. That is what makes a tap heatmap and a missed-call-to-action rate measurable. Nothing about how the card behaves changed: the click still navigates toad.click_urlin a new tab, and the ad server still counts it there. At most fifty taps are reported per showing, and a keyboard activation of the card link reports nothing, since it carries no position. If you render your own card,sdk.trackTap(ad, tap)does the same for your layout — see Rendering Ads and the API Reference. The iOS and Android SDKs ship the same reporting in their 0.5.0. getTrackingTotals()gained atapentry besiderenderandimpression. It counts the analytics events, not clicks — the click itself is a browser navigation the SDK never observes. Unlike the other two, a failed tap event isn’t retried: one lost tap is a lost sample, not a lost impression.- Ad requests now say which surface they’re for. The request carries
display_position: "card", so web showings group with the mobile card in reporting rather than landing in the ad server’sunknownbucket. The Web SDK renders one surface, so it reports it itself; there is no parameter to pass.
- An image-only creative no longer gets an invented description. A creative
with artwork but no description used to render the SDK’s own copy,
Click to learn more →. Call-to-action copy is backend-managed, so the SDK writes none of its own: such a creative now renders its headline and nothing beneath it, with the trailing chevron still signalling that the card is the link. iOS dropped the same fallback in its 0.4.0 release and Android never had one, so all three SDKs now agree. If you relied on the nudge, set a description on the creative or render the card yourself withtrackRender/trackImpression.
- New
sdk.setUserData(data): publishers who know their signed-in user can share age, gender, email, and phone with ad requests. Contact details travel over HTTPS and are SHA-256 hashed at the ad server before anything is stored, so you don’t hash them yourself and Elo never persists a plain email address or phone number. The fields are recorded for upcoming targeting features and have no effect on ad selection today. Memory-only likesetUserIdentifier: re-set per page load and clear on sign-out. The server discards user data entirely on requests flagged COPPA or TFUA, and on any request where GDPR applies. See the API Reference and Privacy & Consent. - Ads can carry a CTA button. A creative now carries an optional
cta_textfrom the demand source (e.g.Install,Join the Club), which<EloAd>renders as a filled capsule in the card’s trailing slot. The whole card stays the click target, so the button is an affordance rather than a second link. Style it with the new.elo-ad-ctaand.elo-ad-chevronhooks — see Rendering Ads. - The card no longer clips copy. Title, description, and CTA label each wrap
to as many lines as they need, so the card’s height follows the creative. A
creative with artwork but no description shows the same
Click to learn more →nudge the mobile cards use.
- New
sdk.trackRender(ad)/sdk.trackImpression(ad): render the ad yourself and still report it. Both are safe to call on every render pass — the one-render-one-impression-per-ad-opportunity limit now sits behind these methods rather than inside<EloAd>, so a hand-rolled card gets the same guarantee instead of having to hit the tracking URLs directly. Each resolvestrueonly when the ad server confirmed the ping.sdk.hasTrackedImpression(ad)tells a custom layout whether an opportunity has already impressed, so it can skip arming its own dwell timer. See Rendering Ads. Nothing changes for<EloAd>users. - New
ad.opportunity_id: the ad now carries its opportunity as well as its creative.opportunity_idis this showing — one per serve, and what the ad server keys render, impression, and click on;ad_idis the creative, stable across showings. Correlate delivery onopportunity_id, group byad_id. Additive: nothing was renamed. - New
sdk.getTrackingTotals():attempted,delivered,failed, andinFlightper event since the lastinit().failedsizes how many pings the ad server never confirmed. - An ad card no longer stays without its image when the same creative comes back in a later ad. A failed image load was remembered against the image, so in a chat where a fresh ad replaces the previous one without the card unmounting, one failure suppressed the thumbnail on every later ad serving that creative. It now applies only to the ad it was recorded for.
- Tracking before
init()no longer burns the ad’s one render or impression. It sent nothing — there was no visitor or session to attribute it to — but still marked the opportunity counted, so the real ping could never follow. The SDK warns and leaves the budget unspent instead. init()clears the tracking state. Re-initializing already started a new session, but the render/impression bookkeeping from the old one survived, where it could suppress an ad shown under the new session. Ads fetched before the re-init keep their pinned bid-time identity.
- New
sdk.setUserIdentifier(identifier): sites with a sign-in can send their own user ID asvisitor_idin place of the SDK’s anonymous ID, so delivery, frequency capping, and reporting follow the user rather than the browser. Memory-only; re-set per page load and clear on sign-out. See the API Reference. - The whole card is clickable. Clicks landing in the gaps between image, title, and description previously did nothing and recorded no click.
- A creative that wins more than once now records a render and an impression every time it’s shown — tracking dedup is keyed on the ad opportunity, not the creative.
- Render and impression delivery is confirmed: a ping only counts on a 2xx response, and a failed ping retries the next time the ad mounts or becomes viewable.
- An ad image that fails to load hides the thumbnail instead of showing a
broken tile, and the
X-Elo-Stateheader encodes UTF-8 safely for non-Latin-1 identifiers.
Initial release as @growl/web-sdk: init() with visitor/session identity and
IAB consent reading (TCF, GPP, COPPA/TFUA), fetchAds() against the Elo ad
server, and the React card with automatic render and viewable-impression
tracking (≥50% visible for 1 second).