Shopify themes, liquid, logos, and UX
We're moving the community! Starting July 7, the current community will be read-only for approx. 2 weeks. You can browse content, but posting will be temporarily unavailable. Learn more
I need help adding transitions to my header drawer menu on mobile for dawn theme 15.2.0. I have changed the menu into an accordion that works just fine, however i could not get any transitions to work with it. It is important that the transitions work for both closing & opening the different menu items. This is my store url: https://washologi.com/
This is my header-drawer.liquid file code:
{% comment %}
Renders a header drawer menu for mobile and desktop.
Usage:
{% render 'header-drawer' %}
{% endcomment %}
<header-drawer data-breakpoint="{% if section.settings.menu_type_desktop == 'drawer' %}desktop{% else %}tablet{% endif %}">
<details id="Details-menu-drawer-container" class="menu-drawer-container">
<summary
class="header__icon header__icon--menu header__icon--summary link focus-inset"
aria-label="{{ 'sections.header.menu' | t }}"
>
<span>
{{- 'icon-hamburger.svg' | inline_asset_content -}}
{{- 'icon-close.svg' | inline_asset_content -}}
</span>
</summary>
<div id="menu-drawer" class="gradient menu-drawer motion-reduce color-{{ section.settings.menu_color_scheme }}">
<div class="menu-drawer__inner-container">
<div class="menu-drawer__navigation-container">
<nav class="menu-drawer__navigation">
<ul class="menu-drawer__menu has-submenu list-menu" role="list">
{%- for link in section.settings.menu.links -%}
<li>
{%- if link.links != blank -%}
<details id="Details-menu-drawer-menu-item-{{ forloop.index }}">
<summary
id="HeaderDrawer-{{ link.handle }}"
class="menu-drawer__menu-item list-menu__item link link--text focus-inset{% if link.child_active %} menu-drawer__menu-item--active{% endif %}"
role="button"
aria-expanded="false"
onclick="toggleAccordion(this)"
>
{{ link.title | escape }}
<span class="accordion-icon">+</span>
</summary>
<div
id="link-{{ link.handle | escape }}"
class="menu-drawer__submenu has-submenu gradient motion-reduce"
tabindex="-1"
>
<div class="menu-drawer__inner-submenu">
<button class="menu-drawer__close-button link link--text focus-inset" aria-expanded="true">
<span class="svg-wrapper">
{{- 'icon-arrow.svg' | inline_asset_content -}}
</span>
{{ link.title | escape }}
</button>
<ul class="menu-drawer__menu list-menu" role="list" tabindex="-1">
{%- for childlink in link.links -%}
<li>
{%- if childlink.links == blank -%}
<a
id="HeaderDrawer-{{ link.handle }}-{{ childlink.handle }}"
href="{{ childlink.url }}"
class="menu-drawer__menu-item link link--text list-menu__item focus-inset{% if childlink.current %} menu-drawer__menu-item--active{% endif %}"
{% if childlink.current %}
aria-current="page"
{% endif %}
>
{{ childlink.title | escape }}
{% render 'drawermenu-image-custom', link: childlink, menu_images_toggle: section.settings.drawer_menu_images_toggle, menu_images_size: section.settings.menu_images_size %}
</a>
{%- else -%}
<details id="Details-menu-drawer-{{ link.handle }}-{{ childlink.handle }}">
<summary
id="HeaderDrawer-{{ link.handle }}-{{ childlink.handle }}"
class="menu-drawer__menu-item link link--text list-menu__item focus-inset"
>
{{ childlink.title | escape }}
<span class="svg-wrapper">
{{- 'icon-arrow.svg' | inline_asset_content -}}
</span>
<span class="svg-wrapper">
{{- 'icon-caret.svg' | inline_asset_content -}}
</span>
</summary>
<div
id="childlink-{{ childlink.handle | escape }}"
class="menu-drawer__submenu has-submenu gradient motion-reduce"
>
<button
class="menu-drawer__close-button link link--text focus-inset"
aria-expanded="true"
>
<span class="svg-wrapper">
{{- 'icon-arrow.svg' | inline_asset_content -}}
</span>
{{ childlink.title | escape }}
</button>
<ul
class="menu-drawer__menu list-menu"
role="list"
tabindex="-1"
>
{%- for grandchildlink in childlink.links -%}
<li>
<a
id="HeaderDrawer-{{ link.handle }}-{{ childlink.handle }}-{{ grandchildlink.handle }}"
href="{{ grandchildlink.url }}"
class="menu-drawer__menu-item link link--text list-menu__item focus-inset{% if grandchildlink.current %} menu-drawer__menu-item--active{% endif %}"
{% if grandchildlink.current %}
aria-current="page"
{% endif %}
>
{{ grandchildlink.title | escape }}
</a>
</li>
{%- endfor -%}
</ul>
</div>
</details>
{%- endif -%}
</li>
{%- endfor -%}
</ul>
</div>
</div>
</details>
{%- else -%}
<a
id="HeaderDrawer-{{ link.handle }}"
href="{{ link.url }}"
class="menu-drawer__menu-item list-menu__item link link--text focus-inset{% if link.current %} menu-drawer__menu-item--active{% endif %}"
{% if link.current %}
aria-current="page"
{% endif %}
>
{{ link.title | escape }}
</a>
{%- endif -%}
</li>
{%- endfor -%}
</ul>
</nav>
<div class="menu-drawer__utility-links">
{%- if shop.customer_accounts_enabled -%}
<a
href="{%- if customer -%}{{ routes.account_url }}{%- else -%}{{ routes.account_login_url }}{%- endif -%}"
class="menu-drawer__account link focus-inset h5 medium-hide large-up-hide"
rel="nofollow"
>
{%- if section.settings.enable_customer_avatar -%}
<account-icon>
{%- if customer and customer.has_avatar? -%}
{{ customer | avatar }}
{%- else -%}
<span class="svg-wrapper">
{{- 'icon-account.svg' | inline_asset_content -}}
</span>
{%- endif -%}
</account-icon>
{%- else -%}
<span class="svg-wrapper">
{{- 'icon-account.svg' | inline_asset_content -}}
</span>
{%- endif -%}
{%- liquid
if customer
echo 'customer.account_fallback' | t
else
echo 'customer.log_in' | t
endif
-%}
</a>
{%- endif -%}
{%- if localization.available_countries or localization.available_languages -%}
<div class="menu-drawer__localization header-localization">
{%- if localization.available_countries and localization.available_countries.size > 1 -%}
<localization-form>
{%- form 'localization', id: 'HeaderCountryMobileForm', class: 'localization-form' -%}
<div>
<h2 class="visually-hidden" id="HeaderCountryMobileLabel">
{{ 'localization.country_label' | t }}
</h2>
{%- render 'country-localization', localPosition: 'HeaderCountryMobile' -%}
</div>
{%- endform -%}
</localization-form>
{% endif %}
{%- if localization.available_languages and localization.available_languages.size > 1 -%}
<localization-form>
{%- form 'localization', id: 'HeaderLanguageMobileForm', class: 'localization-form' -%}
<div>
<h2 class="visually-hidden" id="HeaderLanguageMobileLabel">
{{ 'localization.language_label' | t }}
</h2>
{%- render 'language-localization', localPosition: 'HeaderLanguageMobile' -%}
</div>
{%- endform -%}
</localization-form>
{%- endif -%}
</div>
{%- endif -%}
<ul class="list list-social list-unstyled" role="list">
{%- if settings.social_twitter_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_twitter_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-twitter.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.twitter' | t }}</span>
</a>
</li>
{%- endif -%}
{%- if settings.social_facebook_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_facebook_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-facebook.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.facebook' | t }}</span>
</a>
</li>
{%- endif -%}
{%- if settings.social_pinterest_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_pinterest_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-pinterest.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.pinterest' | t }}</span>
</a>
</li>
{%- endif -%}
{%- if settings.social_instagram_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_instagram_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-instagram.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.instagram' | t }}</span>
</a>
</li>
{%- endif -%}
{%- if settings.social_tiktok_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_tiktok_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-tiktok.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.tiktok' | t }}</span>
</a>
</li>
{%- endif -%}
{%- if settings.social_tumblr_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_tumblr_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-tumblr.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.tumblr' | t }}</span>
</a>
</li>
{%- endif -%}
{%- if settings.social_snapchat_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_snapchat_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-snapchat.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.snapchat' | t }}</span>
</a>
</li>
{%- endif -%}
{%- if settings.social_youtube_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_youtube_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-youtube.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.youtube' | t }}</span>
</a>
</li>
{%- endif -%}
{%- if settings.social_vimeo_link != blank -%}
<li class="list-social__item">
<a href="{{ settings.social_vimeo_link }}" class="list-social__link link">
<span class="svg-wrapper">
{{- 'icon-vimeo.svg' | inline_asset_content -}}
</span>
<span class="visually-hidden">{{ 'general.social.links.vimeo' | t }}</span>
</a>
</li>
{%- endif -%}
</ul>
</div>
</div>
</div>
</div>
</details>
</header-drawer>
<script>
function toggleAccordion(summaryElement) {
event.preventDefault();
const detailsElement = summaryElement.parentElement;
const isOpen = detailsElement.hasAttribute('open');
const icon = summaryElement.querySelector('.accordion-icon');
document.querySelectorAll('.menu-drawer details[open]').forEach((openDetail) => {
if (openDetail !== detailsElement) {
openDetail.removeAttribute('open');
openDetail.querySelector('.accordion-icon').textContent = '+';
}
});
if (isOpen) {
detailsElement.removeAttribute('open');
icon.textContent = '+';
} else {
detailsElement.setAttribute('open', '');
icon.textContent = '-';
}
}
</script>
<style>
@media (max-width: 989px){
.js .menu-drawer__navigation .submenu-open{
visibility: visible;
}
.js .menu-drawer__submenu{
position: static;
transform: none !important;
background-color: #FAFAFA;
}
.menu-drawer__close-button{
display: none;
}
.menu-drawer summary.menu-drawer__menu-item {
border-bottom: 1px solid;
}
.accordion-icon {
font-size: 2rem;
font-weight: bold;
position: absolute;
right: 0;
padding-right: 30px;
color: rgb(var(--color-foreground));
}
.menu-drawer__submenu .menu-drawer__menu-item {
padding-top: 1px;
padding-bottom: 1px;
}
.header-menu-image {
margin-left: auto;
}
}
</style>
You can see what kind of transitions i want if you take a look at my footer menu accordion. Thanks!
Add the following code right before the closing </style> tag in your file:
.menu-drawer details::details-content {
display: block;
margin-inline: 2rem;
block-size: 0;
overflow: hidden;
transition-property: block-size, content-visibility;
transition-duration: 0.3s;
transition-behavior: allow-discrete;
transition-timing-function: ease-out;
}
.menu-drawer details[open]::details-content {
block-size: auto;
block-size: calc-size(auto, size);
}
Hope this code is helpful to you! 😀
Thanks for the suggestion! It works perfectly when i go into mobile mode through the inspect tool on desktop, however when i try it out on my phone the transition does not seem to work on neither Safari nor Google.
Since the previous code i also updated the js now so that it does not close a submenu when another one is opened. This is the new js:
<script>
function toggleAccordion(summaryElement) {
event.preventDefault();
const detailsElement = summaryElement.parentElement;
const isOpen = detailsElement.hasAttribute('open');
const icon = summaryElement.querySelector('.accordion-icon');
// Toggle the current menu
if (isOpen) {
detailsElement.removeAttribute('open');
icon.textContent = '+';
} else {
detailsElement.setAttribute('open', '');
icon.textContent = '-';
}
}
</script>
Hi @ww04
I’m unable to test directly on a mobile device.
Please try updating your custom code to include dynamic height calculation for the detail-content.
In the below code, I added submenu to your custom code toggleAccordion. Let me know if you need further guidance!
function toggleAccordion(summaryElement) {
event.preventDefault();
const detailsElement = summaryElement.parentElement;
const isOpen = detailsElement.hasAttribute('open');
const icon = summaryElement.querySelector('.accordion-icon');
const submenu = detailsElement.querySelector(".menu-drawer__submenu")
if (isOpen) {
detailsElement.removeAttribute('open');
icon.textContent = '+';
submenu.style = `block-size: ${item.offsetHeight}px;`;
} else {
detailsElement.setAttribute('open', '');
icon.textContent = '-';
submenu.style = ``;
}
}
I understand that you cant preview it on mobile device, however could you provide me with an example of how you would do the dynamic height calculation? I have tried a couple of different things but have not gotten it to work.
Thanks!
@ww04 Hope this code is helpful to you! 😊
I use JavaScript to get the offsetHeight of the submenu and then set the style for the details element.
1. Update custom style
.menu-drawer details::details-content {
display: block;
margin-inline: 2rem;
block-size: 0;
overflow: hidden;
transition-property: block-size, content-visibility;
transition-duration: 0.3s;
transition-behavior: allow-discrete;
transition-timing-function: ease-out;
}
.menu-drawer details[open]::details-content {
block-size: var(--eco-custom-block-size);
}
2. Update custom toggleAccordion
function toggleAccordion(summaryElement) {
event.preventDefault();
const detailsElement = summaryElement.parentElement;
const isOpen = detailsElement.hasAttribute('open');
const icon = summaryElement.querySelector('.accordion-icon');
const submenu = document.querySelector(".menu-drawer__submenu");
detailsElement.style=`--eco-custom-block-size:${submenu.offsetHeight}px`;
if (isOpen) {
detailsElement.removeAttribute('open');
icon.textContent = '+';
} else {
detailsElement.setAttribute('open', '');
icon.textContent = '-';
}
}