News

AdSlot

An advertisement's place kept at its size so the story never jumps: standard sizes by name, another on phones, a clear label, a placeholder, sticky in a rail.

#news#layout#ads#media#no-js

Demo

Preview

The story runs here, in the wider column. The rectangle beside it is sticky: beside a story taller than itself, it stays in view as the page scrolls.

An empty slot without a placeholder above still keeps its 320 by 100 pixels, and has no label.

Get it

It needs theme.qwe, as every component does; or take the whole kit, quillo-ui.qwe. Each component brings its own styles to the pages that show it.

$ curl -O https://quillolang.org/components/ui/theme.qwe$ curl -O https://quillolang.org/components/ui/ad_slot.qwe
main.qwe
import ui/theme.qwe use theme_cssuse ui/ad_slot.qweroute GET / = <html><head><style>{theme_css}</style></head><body class="ui-root">...</body></html>

Example

The code of the preview above.

demos/ad_slot.qwe
// The paper's own ad for the demo, drawn here: no network, no one else's brand.let ad_slot_demo_house = "data:image/svg+xml," + url_encode(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 250"><rect width="300" height="250" fill="#14213d"/><circle cx="250" cy="40" r="70" fill="#fca311" opacity=".9"/><text x="24" y="120" fill="#fff" font-family="Georgia, serif" font-size="30" font-weight="700">The Ledger</text><text x="24" y="152" fill="#e5e5e5" font-family="Arial, sans-serif" font-size="15">Every story, every morning.</text><rect x="24" y="182" width="150" height="38" rx="19" fill="#fca311"/><text x="99" y="206" fill="#14213d" font-family="Arial, sans-serif" font-size="15" font-weight="700" text-anchor="middle">$1 a week</text></svg>`)view AdSlotDemo() =    <div.demo-stack style="gap: 28px">        <AdSlot id="ad-slot-demo-top" size="leaderboard" mobile="mobile_banner" slot="home-top"/>        <div style="display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 24px; align-items: start">            <div style="display: grid; gap: 12px">                <p class="demo-note" style="font-size: 15px">The story runs here, in the wider column. The rectangle beside it is sticky:                    beside a story taller than itself, it stays in view as the page scrolls.</p>                <AdSlot id="ad-slot-demo-blank" size="mobile_large" placeholder={false} label="" align="start" slot="in-article-2"/>                <p class="demo-note">An empty slot without a placeholder above still keeps its 320 by 100 pixels, and has no label.</p>            </div>            <AdSlot id="ad-slot-demo-rail" size="rectangle" sticky={true} slot="article-rail">                <a href="#subscribe"><img src={ad_slot_demo_house} alt="The Ledger: every story, every morning. $1 a week." width="300" height="250"/></a>            </AdSlot>        </div>    </div>

Source

ui/ad_slot.qwe
// AdSlot: a place for an advertisement in a news page, kept at its size before the// ad comes, so the story does not jump when it does: the standard sizes by name// (a 728 by 90 leaderboard, a 300 by 250 rectangle, ...), another size where the// screen is narrow, "Advertisement" over it so readers can tell it from the news,// and a quiet placeholder while it is empty. It can stay in view in a side column.////   <AdSlot size="leaderboard" mobile="mobile_banner" slot="home-top"/>//   <AdSlot size="rectangle" sticky={true} slot="article-rail"><iframe src="..." title="Advertisement"></iframe></AdSlot>//// size: "leaderboard" (728×90, default), "billboard" (970×250), "rectangle"// (300×250), "halfpage" (300×600), "skyscraper" (160×600), "mobile_banner"// (320×50) or "mobile_large" (320×100); width and height (pixels) give another.// mobile: the size to use under 760px wide (a name as above), when the first is too// wide there. slot: the ad server's name for the place, put in data-slot (and the// element's id is `id`). The ad itself is its children (an <iframe>, an <ins>, an// <img> in a link); without it, the slot shows a faint placeholder with its size,// or nothing if placeholder={false}, keeping the space either way. label: the word// over it ("Advertisement"; "" for none, screen readers still hear it). sticky: it// stays in view as the page scrolls, in a column taller than itself (top: 16px).// align: "center" (default) or "start".import ui/theme.qwe use ui_clslet ad_slot_css = css`.ui-adslot { display: grid; justify-items: center; gap: 4px; min-width: 0; margin: 0; color: var(--ui-muted); font: 11px/1.3 var(--ui-font); }.ui-adslot-start { justify-items: start; }.ui-adslot-sticky { position: sticky; top: 16px; align-self: start; }.ui-adslot-label { letter-spacing: .1em; text-transform: uppercase; }.ui-adslot-box { position: relative; display: grid; place-items: center; width: min(var(--w), 100%); height: var(--h); overflow: hidden;  background: var(--ui-subtle); }.ui-adslot-box > :is(iframe, img, ins, a, div) { display: block; width: 100%; height: 100%; border: 0; }.ui-adslot-box > a > img { display: block; width: 100%; height: 100%; object-fit: cover; }.ui-adslot-empty { border: 1px dashed var(--ui-border); background: repeating-linear-gradient(-45deg, transparent 0 8px,  color-mix(in srgb, var(--ui-border) 45%, transparent) 8px 9px), var(--ui-subtle); }.ui-adslot-blank { border: 0; background: transparent; }.ui-adslot-size { padding: 2px 8px; border-radius: 999px; background: var(--ui-surface); color: var(--ui-muted); font-size: 12px;  font-variant-numeric: tabular-nums; }@media (max-width: 760px) {  .ui-adslot-mobile .ui-adslot-box { width: min(var(--mw), 100%); height: var(--mh); }  .ui-adslot-mobile .ui-adslot-wide { display: none; }}@media (min-width: 761px) { .ui-adslot-narrow { display: none; } }`// A size's width and height: "rectangle" is [300, 250].fn ad_slot_size(name: str?) -> list = match name {    "billboard" => [970, 250]    "rectangle" => [300, 250]    "halfpage" => [300, 600]    "skyscraper" => [160, 600]    "mobile_banner" => [320, 50]    "mobile_large" => [320, 100]    _ => [728, 90]}view AdSlot(size: str = "leaderboard", width: int? = null, height: int? = null, mobile: str? = null, slot: str? = null,            label: str = "Advertisement", placeholder: bool = true, sticky: bool = false, align: str = "center", id: str? = null,            class: str? = null, children = null) {    style global ad_slot_css    let named = ad_slot_size(size)    let w = width ?? named[0]    let h = height ?? named[1]    let m = mobile != null ? ad_slot_size(mobile) : null    // No children is null on some Quillo versions and empty HTML on others: both are empty.    let empty = children == null || str(children) == ""    <aside class={ui_cls(["ui-adslot", m != null && "ui-adslot-mobile", sticky && "ui-adslot-sticky", align == "start" && "ui-adslot-start", class])}           id={id} aria-label={label != "" ? label : "Advertisement"} data-slot={slot} data-size={`${w}x${h}`}           style={`--w: ${w}px; --h: ${h}px${m != null ? `; --mw: ${m[0]}px; --mh: ${m[1]}px` : ""}`}>        {label != "" && <span.ui-adslot-label aria-hidden="true">{label}</span>}        <div class={ui_cls(["ui-adslot-box", empty && (placeholder ? "ui-adslot-empty" : "ui-adslot-blank")])}>            {empty                ? placeholder && [                    <span class="ui-adslot-size ui-adslot-wide" aria-hidden="true">{w} × {h}</span>,                    m != null && <span class="ui-adslot-size ui-adslot-narrow" aria-hidden="true">{m[0]} × {m[1]}</span>,                  ]                : children}        </div>    </aside>}
Thread
68Useful? Like it, or add to its thread below.

Thread 0 posts

On AdSlot: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.