Docs

One slot, on your page, in one paste.

You already sell sponsorships by hand. This replaces the part where you edit your HTML every time the sponsor changes, and keeps the look you already have.

Drop in the tag

One container where the slot goes, and the script once in the page. The container is yours: move it, size it, style it.

<div data-sb-slot="your_key"></div>
<script src="https://sponsorsdk.dogfooded.com/dfu/s.js" async></script>

You get the key when you create the slot. The script is 2 KB, sets no cookies, and measures nothing but its own visibility.

It already looks like your page

Every value in the slot is a CSS variable whose default inherits from your page: the typeface, the text color, and a border derived from that same color. You get this without configuring anything.

Your project hereOne sentence, one link.Sponsored

Layouts

A layout decides where the logo, the text and the label go. It does not decide colors or borders: that is theming, and the two compose.

Your project hereOne sentence, one link.Sponsored
<div data-sb-slot="your_key"></div>

Write it like a prop. It wins over whatever is set in your dashboard, so three slots on the same page can use three layouts.

<div data-sb-slot="your_key" data-sb-variant="stack"></div>

Whichever one you pick, we render the sponsored label ourselves. An impression is only counted when that label is readable, so a layout you did not build could never quietly cost you your earnings.

Skins

Three sets of tokens you can pick in one click from your dashboard. Card carries its own dark values, switched on the visitor's system preference.

Your project hereOne sentence, one link.Sponsored

Light and dark

A skin can carry two sets of values. The Card skin does: we serve both with the slot, and the script switches on the visitor's system preference, then switches again if it changes mid-session.

Your project hereOne sentence, one link.Sponsored

The page behind it does not change here. It is the slot that carries its own light and dark values, so a dark card works on a light page and the other way round.

Tokens

Set them from your own stylesheet, no dashboard needed. The slot follows without you overriding a single class.

[data-sb-slot] { --sb-radius: 14px; --sb-pad: 14px 16px; }

The logo is the one most people reach for first. Here is the same slot at three sizes, so you can judge them against each other rather than one at a time.

20 px
PolishAutonomous A/B testing for your landing page.Sponsored
28 px
PolishAutonomous A/B testing for your landing page.Sponsored
44 px
PolishAutonomous A/B testing for your landing page.Sponsored
[data-sb-slot] { --sb-logo: 44px; --sb-logo-radius: 10px; }
TokenDefaultEffect
--sb-bgtransparentslot background
--sb-colorinherittext color
--sb-bg-imagenonegradient or image behind it
--sb-border18% of your text colorborder color
--sb-border-w1pxborder width
--sb-radius8pxcorner radius
--sb-pad10px 12pxinner density
--sb-gap10pxspace between logo and text
--sb-logo28pxlogo size
--sb-logo-radius4pxlogo corner radius
--sb-title-colorinheritheadline color
--sb-title-weight600headline weight
--sb-body-opacity.75sentence opacity
--sb-body-size.92emsentence size
--sb-label-size10pxsponsored label size
--sb-label-opacity.6sponsored label opacity

A layout may change a default, never your value: the layout rules redeclare the same token with a different fallback, so anything you set explicitly still wins.

--sb-bg-image takes a gradient or an image, and stacks over --sb-bg. A gradient is a background image in CSS, not a color, which is why they are two tokens rather than one.

[data-sb-slot] {
  --sb-bg: #12101c;
  --sb-color: #ece9f5;
  --sb-bg-image: linear-gradient(120deg, #6d3ff211, transparent 60%);
}

/* Une image de fond, en https et entre guillemets. */
[data-sb-slot] { --sb-bg-image: url("https://cdn.example.com/texture.png") }

This is the one token allowed to use parentheses, so it is validated on its own terms: balanced parentheses, an allowlist of CSS functions, and any url() restricted to https in quotes. Everything else stays on the strict rule.

Your own CSS

The class names are stable, and our stylesheet sits at deliberately low specificity. You override it from yours without a single !important.

[data-sb-slot] .sbu {
  border: none;
  background: #fafafa;
  font-family: "My Typeface", serif;
}
[data-sb-slot] .sbu-title { color: #b4530a; }
[data-sb-slot] .sbu-label { opacity: .45; }
ClassElement
.sbuthe link wrapping everything
.sbu--row, .sbu--stack…the layout, on the same element
.sbu-logothe sponsor's image
.sbu-textthe headline + sentence block
.sbu-titlethe headline
.sbu-bodythe sentence
.sbu-labelthe sponsored label

Your own markup

You already have a sponsor card that matches your site. Keep it as it is, mark the parts to fill, and we stop building anything.

<div data-sb-slot="your_key">
  <a data-sb="cta" class="my-card">
    <img data-sb="logo" alt="" />
    <span>
      <strong data-sb="title"></strong>
      <em data-sb="body"></em>
    </span>
    <small data-sb="sponsored"></small>
  </a>
</div>
AttributeWhat we put in it
data-sb="logo"the src if it is an <img>, otherwise a background
data-sb="title"the sponsor's headline
data-sb="body"their sentence
data-sb="cta"the link, if the element is an <a>
data-sb="sponsored"the label, if you leave the element empty

Every anchor is optional, the label included: it only decides where the label lands in your card. Leave it out and we place it ourselves, or keep the mention you already wrote.

If you do not already have a card you care about, take a layout instead.

A grid of sponsors

One container per slot, in your own grid. Each is a separate slot with its own key and its own sponsor, so booking one never moves what runs in another.

PolishAutonomous A/B testing for your landing page.Sponsored
CoaxalManual prompt-injection testing for AI agents.Sponsored
DogfoodedA studio shipping its own small products.Sponsored
<div class="sponsors">
  <div data-sb-slot="key_1" data-sb-variant="stack"></div>
  <div data-sb-slot="key_2" data-sb-variant="stack"></div>
  <div data-sb-slot="key_3" data-sb-variant="stack"></div>
</div>

<style>
  .sponsors { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px }
  @media (max-width: 640px) { .sponsors { grid-template-columns: 1fr } }
</style>

Rotation

Put several keys on one container and give it an interval in seconds. The slot moves from one sponsor to the next.

PolishAutonomous A/B testing for your landing page.Sponsored
<div data-sb-slot="key_1,key_2,key_3" data-sb-rotate="1"></div>

Each key stays a distinct slot. Rotation shares a position, it does not turn one slot into ten.

Each one is measured on its own, so a slot shown one turn in three reports the impressions it actually got, not a third of the page.

The interval has a floor of 4 seconds.

An impression needs 1 continuous second on screen. Rotating faster than that would mean no slide ever reaches the threshold: a perfect looking carousel, counting nothing. Ask for less and we hold the floor.

Animations

Rotation uses a cross fade by default. Add data-sb-anim to change it, on a rotating slot or on a static one you want to appear rather than pop in.

PolishAutonomous A/B testing for your landing page.Sponsored
<div data-sb-slot="key_1,key_2" data-sb-anim="dissolve" data-sb-rotate="8"></div>

Nothing moves unless you ask: without the attribute the slot appears the way it always has. And a visitor who asked their system for less motion gets none of it, the content still changes.

A ranked list

For a directory, a ladder reads better than a grid. Position one is worth more than position five, and you price them accordingly.

PolishAutonomous A/B testing for your landing page.Sponsored
CoaxalManual prompt-injection testing for AI agents.Sponsored
DogfoodedA studio shipping its own small products.Sponsored
<ol class="featured">
  <li><div data-sb-slot="rank_1" data-sb-variant="list"></div></li>
  <li><div data-sb-slot="rank_2" data-sb-variant="list"></div></li>
  <li><div data-sb-slot="rank_3" data-sb-variant="list"></div></li>
</ol>

Create the whole ladder in one go from your dashboard: pick a count and we number the slots for you.

What counts as an impression

You style the slot however you like, down to supplying your own markup. In exchange, an impression is only counted when it was genuinely seen. That is the whole deal, and the only thing we hold.

Squeeze the slot and watch the verdict change. The dimensions below are measured on the element you are looking at, against the same thresholds the script applies on your own page.

Your project hereOne sentence, one link.Sponsored
This earns nothing (needs 120 × 24)no label (needs ≥ 9px, readable)

A slot shrunk to two pixels or hidden does not get you a warning and does not get you removed. It simply earns nothing. We never argue with your CSS, we decline to count.

Thresholds

ConditionThreshold
Visible surface50% of pixels
Continuous duration1 second
Rendered sizeat least 120 × 24 px
Readable label≥ 9 px, contrast ≥ 3:1

Contrast is measured against the real background behind the label, with its opacity folded in, not against what the CSS claims.

A request stopped by an ad blocker costs you volume and never costs anyone integrity. The script reports that it failed to start, so a blocked page reads as blocked rather than as an audience that did not look.

Serve it from your own domain

Optional, and recommended. One rewrite rule and the slot becomes same-origin.

// next.config.js
export default {
  async rewrites() {
    return [{
      source: "/dfu/:path*",
      destination: "https://sponsorsdk.dogfooded.com/dfu/:path*",
    }];
  },
  skipTrailingSlashRedirect: true,
};
// vercel.json
{
  "rewrites": [
    {
      "source": "/dfu/:path*",
      "destination": "https://sponsorsdk.dogfooded.com/dfu/:path*"
    }
  ]
}

Latency is the small reason. The real one is that it makes your blocked-load figure trustworthy. Served from a third-party domain, the signal that says the script never started falls under the same filter rule as the script it was meant to report on, so a blocked page looks exactly like a page nobody looked at.

Everything works without it, with a less reliable blocked-load measurement.