← back to Dw Theme Hamburger
snippets/recently-viewed.liquid
194 lines
{% comment %}
Recently Viewed Products — Tracks product views in localStorage,
renders a product grid. Works on product pages, homepage, and collection pages.
{% endcomment %}
<div
id="dw-recently-viewed"
class="recently-viewed-section"
data-section-id="{{ section.id }}"
data-max-products="{{ section.settings.max_products }}"
data-current-handle="{{ product.handle | default: '' }}"
style="display:none;max-width:1200px;margin:0 auto;padding:32px 20px 48px;"
>
<h2 style="font-family:-apple-system,system-ui,sans-serif;font-size:22px;font-weight:600;text-align:center;margin:0 0 8px;color:#1a1a1a;letter-spacing:0.01em;">Recently Viewed</h2>
<p style="text-align:center;color:#888;font-size:13px;margin:0 0 28px;">Products you've browsed</p>
<div id="dw-rv-grid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:20px;"></div>
</div>
<style>
.rv-card {
text-decoration:none;
color:inherit;
display:block;
border-radius:6px;
overflow:hidden;
background:#fff;
border:1px solid #f0ece6;
transition:box-shadow 0.2s,transform 0.15s;
}
.rv-card:hover {
box-shadow:0 4px 16px rgba(0,0,0,0.08);
transform:translateY(-2px);
}
.rv-card-img {
width:100%;
aspect-ratio:1/1;
object-fit:cover;
display:block;
background:#f5f5f0;
}
.rv-card-info {
padding:12px 14px;
font-family:-apple-system,system-ui,sans-serif;
}
.rv-card-vendor {
font-size:11px;
font-weight:500;
color:#999;
text-transform:uppercase;
letter-spacing:0.04em;
margin:0 0 3px;
}
.rv-card-title {
font-size:13px;
font-weight:500;
color:#333;
margin:0 0 6px;
line-height:1.3;
display:-webkit-box;
-webkit-line-clamp:2;
-webkit-box-orient:vertical;
overflow:hidden;
}
.rv-card-price {
font-size:13px;
font-weight:600;
color:#111;
}
/* Smaller thumbnails on index/non-product pages */
.recently-viewed-section.rv-index #dw-rv-grid {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)) !important;
gap: 12px !important;
}
.recently-viewed-section.rv-index .rv-card-img {
aspect-ratio: 1/1;
}
.recently-viewed-section.rv-index .rv-card-info {
padding: 8px 10px;
}
.recently-viewed-section.rv-index .rv-card-vendor {
font-size: 9px;
}
.recently-viewed-section.rv-index .rv-card-title {
font-size: 11px;
-webkit-line-clamp: 1;
}
.recently-viewed-section.rv-index .rv-card-price {
font-size: 11px;
}
@media(max-width:768px) {
#dw-rv-grid {
grid-template-columns:repeat(2,1fr) !important;
gap:12px !important;
}
.rv-card-info { padding:8px 10px; }
.rv-card-title { font-size:12px; }
}
</style>
<script>
(function() {
var STORAGE_KEY = 'dw_recently_viewed';
var MAX_STORED = 20;
var container = document.getElementById('dw-recently-viewed');
var grid = document.getElementById('dw-rv-grid');
if (!container || !grid) return;
var maxShow = 6; // 2 rows of 3 (Steve 2026-07-13) — fixed at 6 regardless of the max_products setting
var currentHandle = container.dataset.currentHandle || '';
// === TRACK current product ===
if (currentHandle && currentHandle.length > 0) {
try {
var stored = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
// Remove if already exists (move to front)
stored = stored.filter(function(h) { return h !== currentHandle; });
// Add to front
stored.unshift(currentHandle);
// Trim to max
if (stored.length > MAX_STORED) stored = stored.slice(0, MAX_STORED);
localStorage.setItem(STORAGE_KEY, JSON.stringify(stored));
} catch(e) {}
}
// === RENDER recently viewed ===
try {
var handles = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
// Exclude current product
handles = handles.filter(function(h) { return h !== currentHandle; });
// Limit display
handles = handles.slice(0, maxShow);
if (handles.length < 1) return; // Don't show unless 2+ products
container.style.display = 'block';
// Add rv-index class on non-product pages for smaller thumbnails
if (!currentHandle) container.classList.add('rv-index');
var loaded = 0;
var total = handles.length;
var cards = {};
handles.forEach(function(handle, index) {
fetch('/products/' + handle + '.js')
.then(function(r) { return r.ok ? r.json() : null; })
.then(function(product) {
loaded++;
if (!product) { checkDone(); return; }
// Skip products with no image (prevents blank cards)
if (!product.featured_image) { checkDone(); return; }
var img = product.featured_image || '';
if (img && !img.startsWith('http')) img = 'https:' + img;
// Resize to 400px for performance
if (img) img = img.replace(/(\.[^.]+)$/, '_400x$1');
// Price removed from recently viewed cards
var vendor = product.vendor || '';
var title = product.title || '';
// Remove vendor from title if it's appended
if (vendor && title.includes(' | ' + vendor)) {
title = title.split(' | ' + vendor)[0];
}
var card = document.createElement('a');
card.href = '/products/' + handle;
card.className = 'rv-card';
card.innerHTML =
'<img class="rv-card-img" src="' + img + '" alt="' + title.replace(/"/g, '') + '" loading="lazy">' +
'<div class="rv-card-info">' +
'<p class="rv-card-vendor">' + vendor + '</p>' +
'<p class="rv-card-title">' + title + '</p>' +
'</div>';
cards[index] = card;
checkDone();
})
.catch(function() { loaded++; checkDone(); });
});
function checkDone() {
if (loaded < total) return;
// Insert in order
for (var i = 0; i < total; i++) {
if (cards[i]) grid.appendChild(cards[i]);
}
}
} catch(e) {}
})();
</script>