Examples
Countdown · FAQ. Tabs over sections are in Example: tabs. Each example shows
its constants and init; put them in the starter in place of its ID line and init.
Countdown
Counts down to a date and stops at zero. Reads: data-date (optional).
const ID = "__ID__";
const END = ""; // the end, as an ISO date with the owner's UTC offset; empty = a week from now
const LABEL = "Sale starts in";
const UNITS = [["days", 864e5], ["hours", 36e5], ["minutes", 6e4], ["seconds", 1e3]];
function init(el) {
const raw = el.dataset.date || END;
const end = raw ? new Date(raw).getTime() : Date.now() + 7 * 864e5;
if (Number.isNaN(end)) throw new Error(`bad date "${raw}"`);
const units = UNITS.map(([u]) => `<span class="sb-unit"><b data-unit="${u}">00</b><small>${u}</small></span>`);
el.innerHTML = `<p class="sb-label">${LABEL}</p><div class="sb-countdown" role="timer">${units.join('<span class="sb-divider" aria-hidden="true"></span>')}</div>`;
const cells = UNITS.map(([u]) => el.querySelector(`[data-unit="${u}"]`));
const tick = () => {
let ms = Math.max(0, end - Date.now());
if (!ms) clearInterval(timer);
UNITS.forEach(([, size], i) => {
cells[i].textContent = String(Math.floor(ms / size)).padStart(2, "0");
ms %= size;
});
};
const timer = setInterval(tick, 1000);
tick();
}
[data-seamble-id="__ID__"] { text-align: center; min-height: 5.5em; }
[data-seamble-id="__ID__"] .sb-countdown { display: inline-flex; align-items: center; gap: var(--sb-gap, 16px); }
[data-seamble-id="__ID__"] .sb-unit { display: inline-flex; flex-direction: column; align-items: center; }
[data-seamble-id="__ID__"] .sb-unit b { font-family: var(--heading-font-font-family); font-size: var(--sb-number-size, 2.6em); line-height: 1; font-variant-numeric: tabular-nums; color: var(--sb-number-color, var(--headingLargeColor, currentColor)); }
[data-seamble-id="__ID__"] .sb-unit small { font-size: 0.75em; opacity: 0.7; text-transform: capitalize; }
[data-seamble-id="__ID__"] .sb-divider { width: 2px; height: 1.6em; background: currentColor; opacity: 0.2; }
@media (max-width: 767px) { [data-seamble-id="__ID__"] .sb-unit b { font-size: var(--sb-number-size-mobile, 1.8em); } }
FAQ
Questions live in a constant, so a new question is an edit, not a re-paste. Reads: no attributes.
const ID = "__ID__";
const FAQ = [
["Do you ship abroad?", "Yes, to most countries. Rates show at checkout."],
["Can I return an order?", "Within 30 days, unused, with the receipt."],
];
function init(el) {
el.innerHTML = FAQ.map(() => `<details class="sb-item"><summary></summary><div class="sb-answer"></div></details>`).join("");
el.querySelectorAll(".sb-item").forEach((d, i) => {
d.querySelector("summary").textContent = FAQ[i][0];
d.querySelector(".sb-answer").textContent = FAQ[i][1];
});
}
[data-seamble-id="__ID__"] .sb-item { border-bottom: 1px solid color-mix(in srgb, currentColor 20%, transparent); }
[data-seamble-id="__ID__"] summary { cursor: pointer; padding: 16px 0; font-family: var(--heading-font-font-family); color: var(--sb-question-color, var(--headingSmallColor, currentColor)); }
[data-seamble-id="__ID__"] summary:focus-visible { outline: 2px solid currentColor; }
[data-seamble-id="__ID__"] .sb-answer { padding: 0 0 16px; }