Free
Announcement
Shelfline Announcement
A announcement block extracted from the Shelfline template, available in Tailwind CSS and Bootstrap.
0
96
A announcement block extracted from the Shelfline template, available in Tailwind CSS and Bootstrap.
<!DOCTYPE html>
<html lang="en">
<head>
<script>
window.FontAwesomeConfig = { autoReplaceSvg: 'nest' };
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Shelfline Announcement | Tailwind CSS UI Block</title>
<meta name="description" content="A announcement block extracted from the Shelfline template, available in Tailwind CSS and Bootstrap.">
<meta name="keywords" content="Shelfline, Tailwind CSS template, HTML website template, ecommerce template, online marketplace, multi-category store, electronics shop, deals countdown, responsive ecommerce design">
<meta name="author" content="Let's templates">
<link rel="canonical" href="">
<meta property="og:type" content="product">
<meta property="og:site_name" content="Let's templates">
<meta property="og:locale" content="en_US">
<meta property="og:url" content="">
<meta property="og:title" content="Shelfline | Tailwind CSS Marketplace Ecommerce Template">
<meta property="og:description" content="A fresh, green Tailwind CSS marketplace template for multi-category online stores selling electronics, gaming, fashion, books and beauty, with deal countdowns, category pages, product pages, and a cart.">
<meta property="og:image" content="">
<meta property="og:image:alt" content="Shelfline template preview">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:url" content="">
<meta name="twitter:title" content="Shelfline | Tailwind CSS Marketplace Ecommerce Template">
<meta name="twitter:description" content="A fresh, green Tailwind CSS marketplace template for multi-category online stores selling electronics, gaming, fashion, books and beauty, with deal countdowns, category pages, product pages, and a cart.">
<meta name="twitter:image" content="">
<meta name="twitter:image:alt" content="Shelfline template preview">
<script src="https://cdn.tailwindcss.com/3.4.17"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;0,400;0,700;0,900;1,700;1,900&display=swap" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
forest: '#115e3b',
leaf: '#22c55e',
mint: '#dcfce7',
smoke: '#f4f4f4',
deal: '#ef3b3b'
},
fontFamily: {
sans: ['Lato', 'ui-sans-serif', 'system-ui', 'sans-serif']
}
}
}
};
</script>
<style>
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: 180ms ease-out both page-fade-out; }
::view-transition-new(root) { animation: 220ms ease-in both page-fade-in; }
@keyframes page-fade-out { to { opacity: 0; } }
@keyframes page-fade-in { from { opacity: 0; } }
html { scroll-behavior: smooth; }
[hidden] { display: none !important; }
.no-scrollbar { scrollbar-width: none; }
.no-scrollbar::-webkit-scrollbar { display: none; }
.btn-leaf { background: radial-gradient(circle at 35% 30%, #86efac, #22c55e 60%, #16a34a); }
.marquee-track { animation: marquee 30s linear infinite; }
.marquee:hover .marquee-track { animation-play-state: paused; }
@keyframes marquee { to { transform: translateX(-50%); } }
[data-hero-slide] { transition: opacity .6s ease; }
[data-trend] [data-trend-card] { flex: 1 1 0; min-width: 0; }
[data-trend] [data-trend-card][data-expanded="true"] { flex-grow: 2.2; }
[data-trend] [data-trend-info], [data-trend] [data-trend-buy] { opacity: 0; transition: opacity .3s ease; }
[data-trend] [data-trend-card][data-expanded="true"] [data-trend-info], [data-trend] [data-trend-card][data-expanded="true"] [data-trend-buy] { opacity: 1; }
[data-trend] [data-trend-card][data-expanded="true"] [data-trend-go] { display: none; }
[data-trend] [data-trend-card]:not([data-expanded="true"]) [data-trend-buy] { display: none; }
@media (max-width: 767px) {
[data-trend] { overflow-x: auto; scroll-snap-type: x mandatory; }
[data-trend] [data-trend-card] { flex: 0 0 70%; scroll-snap-align: start; }
[data-trend] [data-trend-info], [data-trend] [data-trend-buy] { opacity: 1; }
}
.reveal-ready [data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity .6s ease, transform .6s ease; transition-delay: var(--reveal-delay, 0ms); }
.reveal-ready [data-reveal].is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
.marquee-track { animation: none; }
[data-hero-slide] { transition: none; }
::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}
</style>
</head>
<body class="bg-white font-sans text-neutral-900 antialiased overflow-x-hidden">
<%
categories = [
["electronics", "Electronics"], ["gaming", "Video Games"], ["movies-tv", "Movies & TV"], ["toys-collection", "Toys & Collection"],
["books", "Books"], ["fashion", "Fashion"], ["winter", "Winter"], ["health-beauty", "Health & Beauty"], ["luxury-item", "Luxury Item"]
]
products = [
{ slug: "pulse-portable-speaker", name: "Pulse Portable Speaker", category: "electronics", price: 89, was: 119, image: "photo-1547052178-7f2c5a20c332", alt: "Grey fabric portable speaker with a wrist strap on a white surface" },
{ slug: "aura-mini-smart-speaker", name: "Aura Mini Smart Speaker", category: "electronics", price: 99, was: 129, image: "photo-1586078875290-c22eb791ad5d", alt: "Compact white smart speaker on a pale background" },
{ slug: "halo-studio-headphones", name: "Halo Studio Headphones", category: "electronics", price: 149, was: 199, image: "photo-1700087151960-178ea946e608", alt: "White and black over-ear headphones on a peach background" },
{ slug: "nova-x-ultra-phone", name: "Nova X Ultra 12GB+256GB", category: "electronics", price: 699, was: 849, image: "photo-1727941035071-910fd07135bb", alt: "Dark smartphone lying face down showing its camera bar" },
{ slug: "drift-wireless-earbuds", name: "Drift Wireless Earbuds", category: "electronics", price: 59, was: 79, image: "photo-1757168120889-4317e57a4849", alt: "Pair of black wireless earbuds on a yellow background" },
{ slug: "tempo-over-ear-headphones", name: "Tempo Over-Ear Headphones", category: "electronics", price: 119, was: 159, image: "photo-1779914944094-18c5724dad27", alt: "Black over-ear headphones resting on piano keys against yellow" },
{ slug: "neon-wireless-controller", name: "Neon Wireless Controller", category: "gaming", price: 64, was: 85, image: "photo-1612287230202-1ff1d85d1bdf", alt: "Wireless game controller lit by cyan and magenta light" },
{ slug: "raid-gaming-headset", name: "Raid Gaming Headset", category: "gaming", price: 79, was: 105, image: "photo-1610041321327-b794c052db27", alt: "Black and red wired gaming headset on a desk" },
{ slug: "keyline-mechanical-keyboard", name: "Keyline Mechanical Keyboard", category: "gaming", price: 109, was: 139, image: "photo-1618384887929-16ec33fab9ef", alt: "Compact black mechanical keyboard with orange accent keys on marble" },
{ slug: "lumen-home-projector", name: "Lumen Home Projector", category: "movies-tv", price: 329, was: 419, image: "photo-1535016120720-40c646be5580", alt: "Home projector beaming light in a dark red room" },
{ slug: "movie-night-popcorn-kit", name: "Movie Night Popcorn Kit", category: "movies-tv", price: 24, was: 32, image: "photo-1691480213129-106b2c7d1ee8", alt: "Striped yellow and white bucket overflowing with popcorn" },
{ slug: "cuddle-teddy-bear", name: "Cuddle Teddy Bear", category: "toys-collection", price: 29, was: 39, image: "photo-1556012018-50c5c0da73bf", alt: "Soft brown teddy bear with a red bow" },
{ slug: "brick-builder-1000", name: "Brick Builder 1000", category: "toys-collection", price: 45, was: 60, image: "photo-1587654780291-39c9404d746b", alt: "Pile of colourful plastic building bricks" },
{ slug: "classics-hardcover-set", name: "Classics Hardcover Set", category: "books", price: 54, was: 72, image: "photo-1520467795206-62e33627e6ce", alt: "Stack of colourful hardcover books" },
{ slug: "weekend-reads-bundle", name: "Weekend Reads Bundle", category: "books", price: 32, was: 42, image: "photo-1602396111763-06556dd0063a", alt: "Stack of pale paperback books on a white surface" },
{ slug: "everyday-cotton-tee", name: "Everyday Cotton Tee", category: "fashion", price: 22, was: 30, image: "photo-1523381294911-8d3cead13475", alt: "Row of sage green t-shirts on wooden hangers" },
{ slug: "mint-canvas-sneakers", name: "Mint Canvas Sneakers", category: "fashion", price: 58, was: 75, image: "photo-1564518125914-014ebd5f0346", alt: "Pair of mint canvas low-top sneakers on white" },
{ slug: "cloud-fleece-hoodie", name: "Cloud Fleece Hoodie", category: "winter", price: 48, was: 64, image: "photo-1620799140188-3b2a02fd9a77", alt: "White fleece hoodie hanging beside light blue jeans" },
{ slug: "quilted-puffer-jacket", name: "Quilted Puffer Jacket", category: "winter", price: 139, was: 185, image: "photo-1614031679232-0dae776a72ee", alt: "Man in a black quilted puffer jacket against a concrete wall" },
{ slug: "chunky-knit-beanie", name: "Chunky Knit Beanie", category: "winter", price: 19, was: 26, image: "photo-1544967919-44c1ef2f9e7a", alt: "Grey chunky knit beanie with knitting needles and dried leaves" },
{ slug: "calming-face-ampoule", name: "Calming Face Ampoule", category: "health-beauty", price: 36, was: 48, image: "photo-1713768704571-6aeb0d0e5105", alt: "Amber dropper bottle of face ampoule on an orange background" },
{ slug: "golden-cleansing-oil", name: "Golden Cleansing Oil", category: "health-beauty", price: 28, was: 37, image: "photo-1741896135512-084b251887f7", alt: "Clear bottle of golden cleansing oil with a dropper on pink" },
{ slug: "midnight-steel-watch", name: "Midnight Steel Watch", category: "luxury-item", price: 420, was: 560, image: "photo-1636639818651-d97365346a5c", alt: "Steel wristwatch with a deep blue dial on black" },
{ slug: "heritage-leather-satchel", name: "Heritage Leather Satchel", category: "luxury-item", price: 210, was: 280, image: "photo-1605733513597-a8f8341084e6", alt: "Grey leather satchel with gold buckles on a lilac surface" },
{ slug: "rose-crystal-perfume", name: "Rose Crystal Perfume", category: "luxury-item", price: 96, was: 128, image: "photo-1543422655-ac1c6ca993ed", alt: "Faceted glass perfume bottle resting on blush silk" }
]
photo = ->(id, width = 700) { "https://images.unsplash.com/#{id}?auto=format&fit=crop&w=#{width}&q=80" }
find = ->(slug) { products.find { |product| product[:slug] == slug } }
nav_links = [["Home", "index.html"], ["Movies & TV", "movies-tv.html"], ["Toys & Collection", "toys-collection.html"], ["Books", "books.html"], ["Fashion", "fashion.html"], ["Electronics", "electronics.html"], ["Winter", "winter.html"], ["Health & Beauty", "health-beauty.html"], ["Luxury Item", "luxury-item.html"], ["Blogs", "blogs.html"]]
slides = [
{ eyebrow: "Deal of the week", title: "Fresh drops<br>winter savings", text: "Rich sound, deep bass and", off: "30%", tail: "off. Your new favourite soundtrack starts now.", product: "halo-studio-headphones", href: "halo-studio-headphones.html" },
{ eyebrow: "Just landed", title: "Play longer<br>play louder", text: "Low-latency control with", off: "25%", tail: "off every gaming accessory this week.", product: "neon-wireless-controller", href: "gaming.html" },
{ eyebrow: "Gift edit", title: "Timeless gifts<br>for less", text: "Steel watches and leather goods at", off: "25%", tail: "off while stock lasts.", product: "midnight-steel-watch", href: "luxury-item.html" }
]
tiles = [
["Shoes", "fashion.html", "photo-1564518125914-014ebd5f0346", "Mint canvas sneakers", "#8fdcc8"],
["Toys & Collection", "toys-collection.html", "photo-1556012018-50c5c0da73bf", "Brown teddy bear with a red bow", "#aebcf0"],
["Movies & TV", "movies-tv.html", "photo-1691480213129-106b2c7d1ee8", "Striped bucket of popcorn", "#f7aba3"],
["Fashion", "fashion.html", "photo-1523381294911-8d3cead13475", "Green t-shirts on hangers", "#e0da8e"],
["Electronics", "electronics.html", "photo-1700087151960-178ea946e608", "White over-ear headphones", "#e4b3c8"],
["Winter", "winter.html", "photo-1620799140188-3b2a02fd9a77", "White fleece hoodie", "#9ed4b1"],
["Books", "books.html", "photo-1520467795206-62e33627e6ce", "Stack of hardcover books", "#f5cf94"],
["Health & Beauty", "health-beauty.html", "photo-1713768704571-6aeb0d0e5105", "Amber serum bottle", "#c9b8f0"]
]
for_you = %w[pulse-portable-speaker aura-mini-smart-speaker lumen-home-projector halo-studio-headphones nova-x-ultra-phone keyline-mechanical-keyboard cuddle-teddy-bear raid-gaming-headset].map { |slug| find.call(slug) }
trending = [
{ name: "Linen Band Collar Shirt", category: "fashion", price: 60, image: "photo-1627686011747-74adda3d2343", alt: "Man in an off-white linen shirt outdoors", href: "fashion.html" },
{ name: "Shearling Flight Jacket", category: "winter", price: 120, image: "photo-1627637454030-5ddd536e06e5", alt: "Man in a brown shearling flight jacket against a concrete wall", href: "winter.html" },
{ name: "Washed Overshirt", category: "fashion", price: 66, image: "photo-1719418271955-79273259772d", alt: "Man in a grey overshirt against a white wall", href: "fashion.html" },
{ name: "Quilted Puffer Jacket", category: "winter", price: 139, image: "photo-1614031679232-0dae776a72ee", alt: "Man in a black quilted puffer jacket against a concrete wall", href: "quilted-puffer-jacket.html" },
{ name: "Pleated Work Trousers", category: "fashion", price: 72, image: "photo-1540924782561-3fc182603b86", alt: "Man in stone pleated trousers and braces", href: "fashion.html" },
{ name: "Mint Canvas Sneakers", category: "fashion", price: 58, image: "photo-1564518125914-014ebd5f0346", alt: "Pair of mint canvas low-top sneakers", href: "mint-canvas-sneakers.html" },
{ name: "Halo Studio Headphones", category: "electronics", price: 149, image: "photo-1700087151960-178ea946e608", alt: "White and black over-ear headphones", href: "halo-studio-headphones.html" },
{ name: "Calming Face Ampoule", category: "health-beauty", price: 36, image: "photo-1713768704571-6aeb0d0e5105", alt: "Amber dropper bottle of face ampoule", href: "calming-face-ampoule.html" }
]
trend_tabs = [["fashion", "Fashion"], ["winter", "Winter"], ["electronics", "Electronic"], ["health-beauty", "Health & Beauty"]]
brands = [["Voltra", "fa-bolt", "#f97316"], ["Orbix", "fa-circle-nodes", "#111827"], ["Pinepeak", "fa-mountain", "#312e81"], ["Lumio", "fa-lightbulb", "#111827"], ["Kestra", "fa-feather", "#dc2626"], ["Norda", "fa-compass", "#0f766e"], ["Wavely", "fa-wave-square", "#2563eb"], ["Cubik", "fa-cube", "#65a30d"], ["Fernly", "fa-leaf", "#15803d"], ["Tallo", "fa-star", "#ca8a04"]]
gaming = %w[neon-wireless-controller raid-gaming-headset keyline-mechanical-keyboard drift-wireless-earbuds].map { |slug| find.call(slug) }
popular_tabs = [["all", "All Product"], ["electronics", "Electronic"], ["gaming", "Video Games"], ["books", "Books"], ["toys-collection", "Kids & Baby"], ["luxury-item", "Luxury"]]
popular = %w[drift-wireless-earbuds tempo-over-ear-headphones classics-hardcover-set brick-builder-1000 midnight-steel-watch neon-wireless-controller weekend-reads-bundle heritage-leather-satchel rose-crystal-perfume raid-gaming-headset].map { |slug| find.call(slug) }
deals = [["aura-mini-smart-speaker", "25%"], ["nova-x-ultra-phone", "30%"], ["lumen-home-projector", "25%"]].map { |slug, off| [find.call(slug), off] }
posts = [
{ slug: "future-of-online-shopping", title: "Where online shopping goes next", text: "How smarter search, better photos and honest reviews are changing the way we fill a basket.", image: "photo-1760565030283-d199530b41bc", alt: "Person holding colourful shopping bags with a thumbs up" },
{ slug: "shopping-on-the-go", title: "Shopping on the go is the norm", text: "Most orders now start on a phone. Here is how we made every step faster on a small screen.", image: "photo-1625669981647-dc396c990a6d", alt: "Man holding a black smartphone" },
{ slug: "inside-a-great-product-page", title: "Inside a product page that sells", text: "Clear photos, real sizing and fast delivery dates: the details that turn browsing into buying.", image: "photo-1605733513597-a8f8341084e6", alt: "Grey leather satchel with gold buckles on a lilac surface" }
]
faqs = [
["How fast is delivery?", "Orders placed before 3pm ship the same day. Standard delivery takes 2–4 working days and express arrives next day in most cities."],
["Can I return an item?", "Yes. Most items can be returned within 30 days in their original condition. Start a return from your account and print the free label."],
["Which payment methods do you accept?", "All major cards, digital wallets and pay-later options at checkout. Every payment is encrypted end to end."],
["Do members get extra discounts?", "Members save up to 50% during exclusive monthly events and get free express delivery on every order."],
["Can I track my order?", "You will get a tracking link by email and text as soon as your parcel leaves our warehouse."]
]
for_you_grid = { id: "for-you", title: "Picked for you", items: for_you, tabs: nil, link: "electronics.html", link_label: "See all products" }
popular_grid = { id: "most-popular", title: "Most popular", items: popular, tabs: popular_tabs, link: "electronics.html", link_label: "See all products" }
%>
<!-- block:start announcement -->
<div id="announcement" class="bg-forest text-[13px] text-white">
<div class="mx-auto flex max-w-[1440px] items-center justify-center gap-6 px-5 py-2 md:justify-between md:px-[60px]">
<nav class="hidden gap-7 md:flex" aria-label="Company">
<a href="contact.html" class="hover:text-mint">About</a>
<a href="contact.html#privacy" class="hover:text-mint">Privacy Policy</a>
<a href="contact.html#terms" class="hover:text-mint">Terms & Conditions</a>
</nav>
<p>Members save up to 50% all month long</p>
<a href="tel:+14805550103" class="hidden items-center gap-2 hover:text-mint md:inline-flex"><i class="fa-solid fa-phone text-[11px]"></i> (480) 555-0103</a>
</div>
</div>
<!-- block:end announcement -->
<script>
document.querySelectorAll("[data-menu-toggle]").forEach((button) => {
const menu = document.getElementById(button.getAttribute("aria-controls"));
if (!menu) return;
button.addEventListener("click", () => {
const open = button.getAttribute("aria-expanded") === "true";
button.setAttribute("aria-expanded", String(!open));
menu.classList.toggle("hidden", open);
});
});
(() => {
const read = () => { try { return Number(localStorage.getItem("shelfline-cart") ?? 2); } catch (e) { return 2; } };
const write = (value) => { try { localStorage.setItem("shelfline-cart", String(value)); } catch (e) {} };
const paint = () => document.querySelectorAll("[data-cart-count]").forEach((el) => { el.textContent = read(); });
window.shelflineCart = { read, write, paint };
paint();
document.addEventListener("click", (event) => {
const add = event.target.closest("[data-add-cart]");
if (add) {
write(read() + 1);
paint();
add.animate([{ transform: "scale(1)" }, { transform: "scale(1.15)" }, { transform: "scale(1)" }], { duration: 300 });
}
const wish = event.target.closest("[data-wish]");
if (wish) {
const saved = wish.getAttribute("aria-pressed") !== "true";
wish.setAttribute("aria-pressed", String(saved));
const icon = wish.querySelector("[data-wish-icon]");
if (icon) { icon.classList.toggle("fa-solid", saved); icon.classList.toggle("fa-regular", !saved); }
}
});
})();
document.querySelectorAll("[data-hero]").forEach((hero) => {
const slides = [...hero.querySelectorAll("[data-hero-slide]")];
const dots = [...hero.querySelectorAll("[data-hero-dot]")];
let index = 0;
let timer;
const show = (next) => {
index = (next + slides.length) % slides.length;
slides.forEach((slide, i) => {
slide.classList.toggle("opacity-0", i !== index);
slide.classList.toggle("pointer-events-none", i !== index);
slide.setAttribute("aria-hidden", String(i !== index));
});
dots.forEach((dot, i) => dot.setAttribute("aria-current", String(i === index)));
};
const start = () => {
if (navigator.webdriver || matchMedia("(prefers-reduced-motion: reduce)").matches) return;
clearInterval(timer);
timer = setInterval(() => show(index + 1), 6000);
};
dots.forEach((dot, i) => dot.addEventListener("click", () => { show(i); start(); }));
hero.addEventListener("mouseenter", () => clearInterval(timer));
hero.addEventListener("mouseleave", start);
start();
});
document.querySelectorAll("[data-scroller]").forEach((scroller) => {
const track = scroller.querySelector("[data-scroll-track]");
const step = () => (track.firstElementChild?.offsetWidth || 200) + 18;
scroller.querySelector("[data-scroll-prev]")?.addEventListener("click", () => track.scrollBy({ left: -step() * 2 }));
scroller.querySelector("[data-scroll-next]")?.addEventListener("click", () => {
if (track.scrollLeft >= track.scrollWidth - track.clientWidth - 4) return track.scrollTo({ left: 0 });
track.scrollBy({ left: step() * 2 });
});
});
document.querySelectorAll("[data-trend-section]").forEach((section) => {
const cards = [...section.querySelectorAll("[data-trend-card]")];
const tabs = [...section.querySelectorAll("[data-trend-tab]")];
const expand = (card) => cards.forEach((other) => { other.dataset.expanded = String(other === card); });
const filter = (value) => {
tabs.forEach((tab) => tab.setAttribute("aria-selected", String(tab.dataset.trendTab === value)));
const visible = cards.filter((card) => card.dataset.category === value || (value === "fashion" && card.dataset.category === "winter"));
const shown = (visible.length >= 2 ? visible : cards).slice(0, 5);
cards.forEach((card) => { card.hidden = !shown.includes(card); });
expand(shown[Math.min(1, shown.length - 1)]);
};
tabs.forEach((tab) => tab.addEventListener("click", () => filter(tab.dataset.trendTab)));
cards.forEach((card) => {
card.addEventListener("mouseenter", () => { if (matchMedia("(hover: hover)").matches) expand(card); });
card.addEventListener("focusin", () => expand(card));
});
filter(tabs[0]?.dataset.trendTab || "fashion");
});
document.querySelectorAll("[data-grid]").forEach((grid) => {
const tabs = [...grid.querySelectorAll("[data-grid-tab]")];
if (!tabs.length) return;
const items = [...grid.querySelectorAll("[data-grid-item]")];
tabs.forEach((tab) => tab.addEventListener("click", () => {
const value = tab.dataset.gridTab;
tabs.forEach((other) => other.setAttribute("aria-selected", String(other === tab)));
let shown = 0;
items.forEach((item) => {
const match = value === "all" || item.dataset.category === value;
item.hidden = !(match && shown < 8);
if (match && shown < 8) shown += 1;
});
}));
});
document.querySelectorAll("[data-countdown]").forEach((box) => {
if (navigator.webdriver) return;
const end = Date.now() + Number(box.dataset.hours || 48) * 3600 * 1000;
const units = { days: 86400, hours: 3600, mins: 60, secs: 1 };
const tick = () => {
let left = Math.max(0, Math.floor((end - Date.now()) / 1000));
Object.entries(units).forEach(([key, size]) => {
const el = box.querySelector(`[data-unit="${key}"]`);
if (el) el.textContent = String(Math.floor(left / size)).padStart(2, "0");
left %= size;
});
};
tick();
setInterval(tick, 1000);
});
document.querySelectorAll("[data-accordion]").forEach((accordion) => {
const items = [...accordion.querySelectorAll("[data-accordion-item]")];
items.forEach((item) => {
const button = item.querySelector("[data-accordion-button]");
const panel = document.getElementById(button.getAttribute("aria-controls"));
button.addEventListener("click", () => {
const open = button.getAttribute("aria-expanded") !== "true";
items.forEach((other) => {
const otherButton = other.querySelector("[data-accordion-button]");
const isTarget = other === item && open;
otherButton.setAttribute("aria-expanded", String(isTarget));
other.setAttribute("aria-expanded", String(isTarget));
document.getElementById(otherButton.getAttribute("aria-controls")).hidden = !isTarget;
});
});
});
});
document.querySelectorAll("[data-newsletter]").forEach((form) => {
form.addEventListener("submit", (event) => {
event.preventDefault();
const note = form.parentElement.querySelector("[data-newsletter-note]");
if (note) note.hidden = false;
form.reset();
});
});
(() => {
const items = document.querySelectorAll("[data-reveal]");
const skip = navigator.webdriver || !("IntersectionObserver" in window) || matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!items.length || skip) return;
document.documentElement.classList.add("reveal-ready");
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
entry.target.classList.add("is-visible");
observer.unobserve(entry.target);
});
}, { rootMargin: "0px 0px -10% 0px" });
items.forEach((item) => observer.observe(item));
})();
</script>
</body>
</html>
Premium
E-commerce
Unlock the entire library of premium templates and blocks. Renews automatically, cancel anytime.
Tailwind CSS · HTML download