@gravity-ai/embed
v0.1.1
Published
Drop-in ad rendering widget — one script tag, no React required
Readme
@gravity-ai/embed
Drop-in ad rendering widget — one <script> tag, no React required. ~4KB gzipped.
Publishers load a single JS file and get fully rendered, themed, viewability-tracked ads with zero framework dependencies.
Quick Start
<script src="https://cdn.jsdelivr.net/npm/@gravity-ai/embed/dist/gravity-ad.js"></script>
<div data-gravity-ad
data-api-key="pub_xxxxx"
data-placement="below_response"
data-placement-id="my-slot"
data-variant="card"
data-query="I need running shoes">
</div>That's it. The widget auto-initialises on page load, calls the Gravity API, renders the ad, and fires an impression when the ad enters the viewport.
Pin a version for production (@gravity-ai/[email protected]), or bundle it yourself:
npm install @gravity-ai/embedVariants
Six built-in templates optimised for AI surfaces:
| Variant | Best for |
|---------|----------|
| card (default) | Sidebar widgets, standalone placements |
| inline | Horizontal banner-style, between content |
| minimal | Minimal left-border style, blends into text content |
| bubble | Chat-style bubble, conversational UIs |
| suggestion | Pill-style "you might also want to try", below input bars |
| text-link | Inline hyperlink within paragraph text |
Theming
Override CSS custom properties on the container (or any ancestor) to match your platform:
[data-gravity-ad] {
--gravity-bg: #1a1a2e;
--gravity-text: #eaeaea;
--gravity-text-muted: #a0a0b0;
--gravity-accent: #7c6ff7;
--gravity-border: #3a3a4e;
--gravity-radius: 12px;
--gravity-font: "IBM Plex Sans", sans-serif;
}Data Attributes
| Attribute | Required | Description |
|-----------|----------|-------------|
| data-gravity-ad | Yes | Marks the element as a Gravity ad slot |
| data-api-key | Yes | Publisher API key |
| data-placement | Yes | Placement position (e.g. below_response) |
| data-placement-id | Yes | Unique slot ID for tracking |
| data-query | Yes | User's query for contextual matching |
| data-variant | No | Template variant (default: card) |
| data-session-id | No | Session ID for consistent bucketing |
| data-user-id | No | User ID for personalisation |
| data-api-url | No | Custom API endpoint |
| data-show-label | No | Show "Sponsored" label (default: true) |
| data-label-text | No | Custom label text (default: Sponsored) |
| data-theme | No | auto (default) matches the page's light/dark surface; light/dark pin the palette. Publisher-defined --gravity-* variables always win |
| data-test-ad | No | Use test ads (true/false; omit for server default) |
| data-relevancy | No | Minimum relevancy threshold (0-1) |
Programmatic API
For SPAs or dynamic content, mount ads programmatically:
GravityAd.mount({
container: document.getElementById('my-ad'),
apiKey: 'pub_xxxxx',
placement: 'below_response',
placementId: 'my-slot',
query: 'I need running shoes',
variant: 'card',
onAd: (ad) => console.log('Ad loaded:', ad),
});Variant auto-selection
The widget resolves which template to use in this priority order:
data-variantattribute on the element (explicit publisher choice)composition.rendererKeyfrom the ad response (experiment arm)ad.renderer_keyfrom the ad response (placement default)ad.variantfrom the ad response- Falls back to
card
This means A/B tests that swap renderer_key via experiments work automatically — the widget renders whatever template the engine assigns.
Impression Tracking
Impressions fire on viewability (50% of the ad visible in the viewport via IntersectionObserver), not on API response. This matches IAB viewability standards and prevents billing for ads that are never seen.
SPA Support
The widget uses a MutationObserver to detect dynamically added [data-gravity-ad] elements. In a SPA, just append the element to the DOM and the widget handles the rest — no manual mount() call needed.
