diff --git a/assets/views/base.html b/assets/views/base.html
index 64700fc..017ab6b 100644
--- a/assets/views/base.html
+++ b/assets/views/base.html
@@ -29,14 +29,29 @@
if (currentTheme() === 'system') applyTheme('system');
});
// Mark the active top-nav link via aria-current (styled with Tailwind).
+ //
+ // Pages that live under a category but not at its URL (a product detail
+ // page) declare it via ; without that the
+ // category sidebar would show nothing selected the moment you open a
+ // product. `window.activeCategory` is read by the sidebar's accordion
+ // groups, which are hx-preserve'd and so initialise only once — the
+ // `nav-category` event is what reaches them on later navigations.
function markActiveNav() {
var path = location.pathname;
+ var main = document.querySelector('main[data-active-category]');
+ var cat = main ? main.getAttribute('data-active-category') : '';
+ var catPath = cat ? '/category/' + cat : null;
+ window.activeCategory = cat;
document.querySelectorAll('a[data-nav]').forEach(function (a) {
var h = a.getAttribute('data-nav');
- var on = h === path || (h !== '/' && path.indexOf(h) === 0);
+ // A declared category wins outright: on /shop/{slug} the prefix rule
+ // would otherwise light up "all products" alongside the category.
+ var on = h === path || h === catPath
+ || (!catPath && h !== '/' && path.indexOf(h) === 0);
if (on) a.setAttribute('aria-current', 'page');
else a.removeAttribute('aria-current');
});
+ if (cat) window.dispatchEvent(new CustomEvent('nav-category', { detail: cat }));
}
document.addEventListener('DOMContentLoaded', markActiveNav);
document.addEventListener('htmx:afterSwap', markActiveNav);
@@ -250,7 +265,7 @@
{% endif %}
-
+
{% block content %}{% endblock content %}
diff --git a/assets/views/shop/_sidebar.html b/assets/views/shop/_sidebar.html
index 7fcb9b2..9d3ccbf 100644
--- a/assets/views/shop/_sidebar.html
+++ b/assets/views/shop/_sidebar.html
@@ -3,8 +3,10 @@
categories, each `{ name, slug, children: [{ name, slug }] }`. A category
with children is expandable (accordion); one without is a plain link.
Active state is set client-side by markActiveNav() via data-nav +
- aria-current; groups auto-expand when the current page is the category or
- one of its subcategories.
+ aria-current; groups auto-expand when the current page is the category, one
+ of its subcategories, or a product inside it (which markActiveNav announces
+ as window.activeCategory + the `nav-category` event, since this partial is
+ hx-preserve'd and therefore only ever initialises once).
Adapted from the vendored Penguin UI component
penguinui-components/sidebar/sidebar-with-collapsible-menus.html: Penguin's
@@ -27,8 +29,10 @@
{% for group in category_groups %}
{% if group.children | length > 0 %}
-