Skip to content

A queued alert

If two follows arrive at once and you draw them directly, the second wipes the first before it can be read. Widget.queue lines them up: each alert shows when the previous one ends.

manifest.json
{
"events": ["Twitch.Follow", "Kick.Follow", "Twitch.Sub"],
"strings": {
"es": { "follow": "¡Gracias por el follow, {name}!", "sub": "¡{name} se ha suscrito!" },
"en": { "follow": "Thanks for the follow, {name}!", "sub": "{name} just subscribed!" }
},
"menu": { "sections": [] }
}
index.html
<div id="banner"></div>
style.css
html,
body {
margin: 0;
background: transparent;
}
#banner {
position: absolute;
left: 50%;
top: 40px;
transform: translateX(-50%);
padding: 16px 32px;
font: 700 36px system-ui, sans-serif;
color: #ffffff;
background: #6d28d9;
opacity: 0;
transition: opacity 0.3s;
}
#banner.visible {
opacity: 1;
}
script.js
const banner = document.querySelector("#banner");
function thank(key) {
return ({ user }) => {
Widget.queue.push(() => {
Widget.dom.text(banner, Widget.t(key, { name: user }));
banner.classList.add("visible");
return () => banner.classList.remove("visible");
}, { duration: 6 });
};
}
Widget.on("Twitch.Follow", thank("follow"));
Widget.on("Kick.Follow", thank("follow"));
Widget.on("Twitch.Sub", thank("sub"));
  • The function you pass to push draws the alert. What it returns (here, a function that removes the visible class) runs when its duration ends: that is where you clean up everything you put on screen.
  • duration: 6 is how many seconds it stays visible. Without it, queue.durationSeconds from the manifest applies (5 by default).
  • The viewer’s name goes through Widget.t and Widget.dom.text, so it is never interpreted as HTML.
  • Let a sub go ahead of the follows: { duration: 6, priority: 1 }. With priority, the task runs before those with less.
  • Fold many follows into one: { group: "follow" }. When the alert starts, the pending ones of the group are folded into it, and the task receives how many in folded, so you can write “and 4 more”.
  • Respect the cut: if the task waits for something, check signal.aborted after every wait before drawing: Widget.queue.clear() aborts the running task.