Hello @dan ,
I thought at sometime there was a demo of a tree menu with a mobile modal. Is there a demo project that we can download?
Thanks!
Hello @dan ,
I thought at sometime there was a demo of a tree menu with a mobile modal. Is there a demo project that we can download?
Thanks!
I’ve been thinking this through.
I would like to create an off-canvas mobile menu from the tree menu, while keeping the tree menu working normally on desktop or larger screens. Can the tree menu function like the menu bar, with the same features, but as a vertical menu instead of horizontal? I don’t need a top bar, and ideally, I could choose where to place the hamburger icon.
extremely basic version no styling
Thank you for that! Unfortunately as you probably have guessed from your statement, this is close but not quite. It is not persistent on the desktop or larger screen sizes. I can see how to make it open by default but any activity then closes it, that isn’t good for website stickiness or navigation. I do however really appreciate your steeping in! ![]()
is this what you mean, even less work on styling, but persistent on larger screens and mobile on mobile
Can potential have the 2 menus on at once
Perfect, Thank you very very much for engaging with this! So I know, what did you change? It’s not obvious to me.
EDIT: I see, you have two trees. I will figure out another solution. Unfortunately that will be a big overhead hit by the time I have a sight with several hundred pages.
So far I have not figure out how to show Tier 1 (home), tier2 (category) and tier 3 (sub-category) in the Tree Menu and then tier 4 as a hub menu (related articles). I can manually curate the tier 4 menu. That probably won’t be a big issue because I have a tendency to publish an entire hub (5-10 articles) all at the same time anyway. But automation would be nice if done right with SEO and accessibility in mind.
the mobile menu is hidden at a certain screen size same for the side menu, they both use the same page titles. So once setup no need to change. The persistent menu is just sticky container with tree in it, modal for the mobile menu.
when they are hidden they are not in the page
Not sure is is possible without creating two menus at present
it was more of a challenge test for me at this point
The following is what I am trying to achieve. Ignore the design, it’s the function that I am after. Just copy and paste the text and save it as an html file then open in your browser.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Article Layout | Example Site</title>
<style>
body {
font-family: system-ui, sans-serif;
margin: 0;
background: #f9fafb;
color: #222;
}
/* Header */
.header {
background: #fff;
border-bottom: 1px solid #ddd;
position: sticky;
top: 0;
z-index: 100;
}
.header-container {
max-width: 1200px;
margin: auto;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.site-title {
color: #2563eb;
font-size: 1.25rem;
font-weight: bold;
}
.nav-desktop {
display: flex;
gap: 1.5rem;
}
.nav-desktop a {
color: #444;
text-decoration: none;
}
.nav-desktop a:hover, .nav-desktop a:focus {
color: #2563eb;
outline: none;
}
.menu-btn {
display: none;
border: 1px solid #ccc;
padding: 0.5rem 0.75rem;
border-radius: 6px;
background: #f0f0f0;
}
/* Layout */
.main-container {
display: flex;
flex-direction: column;
max-width: 1200px;
margin: 2rem auto;
padding: 0 1rem;
}
@media (min-width: 768px) {
.main-container {
flex-direction: row;
gap: 2rem;
}
}
/* Sidebar */
.sidebar-content {
list-style-type: none;
background: #fff;
border-right: 1px solid #eee;
position: fixed;
top: 0;
left: -100%;
width: 250px;
height: 100%;
padding: 1rem;
overflow-y: auto;
transition: left 0.3s ease;
z-index: 50;
}
.sidebar-content.active { left: 0; }
/* Accordion button styling */
.toggle {
display: flex;
align-items: center;
gap: 0.5rem;
border: none;
background: none;
font: inherit;
cursor: pointer;
padding: 0.25rem 0;
}
/* Icon animation */
.toggle .icon {
display: inline-block;
transition: transform 0.25s ease, color 0.25s ease;
width: 1em;
text-align: center;
}
/* Active/expanded icon */
.toggle.active .icon {
transform: rotate(180deg);
color: #2563eb;
}
.overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.4);
opacity: 0;
pointer-events: none;
transition: opacity 0.3s;
z-index: 40;
}
.overlay.active { opacity: 1; pointer-events: all; }
@media (min-width: 768px) {
.sidebar-content {
position: static;
left: 0;
height: auto;
border-right: 1px solid #eee;
width: 250px;
transition: none;
}
.overlay { display: none; }
}
.sidebar-mobile-menu {
display: flex;
flex-direction: column;
gap: 0.5rem;
border-bottom: 1px solid #ddd;
margin-bottom: 1rem;
padding: 5rem 0 1rem;
}
@media (min-width: 768px) {
.sidebar-mobile-menu { display: none; }
}
@media (max-width: 767px) {
.nav-desktop { display: none; }
.menu-btn { display: flex; align-items: center; gap: 0.25rem; }
}
/* Nested lists for accordion */
.sidebar-list .nested {
max-height: 0;
overflow: hidden;
padding-left: 1rem;
transition: max-height 0.28s ease;
}
.sidebar-list, .sidebar-list ul {
list-style: none;
padding-left: 0;
margin: 0;
}
/* Content */
.content {
flex: 1;
display: flex;
flex-direction: column;
}
@media (min-width: 1024px) {
.content {
flex-direction: row;
gap: 2rem;
}
}
.article {
flex: 1;
line-height: 1.6;
}
.article h1, .article h2, .article h3 { margin-top: 1.5rem; }
.article blockquote {
border-left: 4px solid #2563eb;
padding-left: 1rem;
color: #555;
font-style: italic;
}
/* Related Articles (sticky) */
.related {
background: #f0f2f5;
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
margin-top: 2rem;
position: sticky;
top: 6rem;
max-height: fit-content;
}
.related a {
display: block;
color: #333;
margin-bottom: 0.5rem;
text-decoration: none;
}
.related a:hover, .related a:focus { color: #2563eb; outline: none; }
/* Footer */
.footer {
background: #fff;
border-top: 1px solid #ddd;
margin-top: 4rem;
}
.footer-grid {
max-width: 1200px;
margin: auto;
padding: 2rem;
display: grid;
gap: 2rem;
}
.footer-grid ul li{
list-style: none;
}
@media (min-width: 768px) {
.footer-grid { grid-template-columns: repeat(3, 1fr); }
}
.footer h3 { margin-bottom: 0.5rem; }
.subscribe-form, .contact-form { display: flex; flex-direction: column; gap: 0.5rem; }
.subscribe-form input, .contact-form input, .contact-form textarea {
border: 1px solid #ccc;
border-radius: 6px;
padding: 0.5rem;
}
.subscribe-form button, .contact-form button {
background: #2563eb;
color: white;
border: none;
padding: 0.5rem;
border-radius: 6px;
cursor: pointer;
}
.legal ul, .socials ul {
list-style: none;
padding: 0;
margin: 1.5rem 0;
}
.legal li, .socials li {
margin-bottom: 0.5rem;
}
.socials ul {
list-style: none;
padding: 0;
margin: 0;
}
.socials li {
margin-bottom: 0.5rem;
}
.social-link {
display: inline-flex;
align-items: center;
gap: 0.25rem; /* space between icon and text */
color: #2563eb;
text-decoration: none;
font-weight: 600;
transition: color 0.25s;
}
.social-link svg {
display: block;
}
.social-link:hover,
.social-link:focus {
color: #1d4ed8;
outline: none;
}
.footer-bottom { text-align: center; padding: 1rem; border-top: 1px solid #eee; font-size: 0.875rem; }
</style>
</head>
<body>
<header class="header" role="banner">
<div class="header-container">
<h1 class="site-title"><a href="index.html">Simple360</a></h1>
<nav class="nav-desktop" role="navigation" aria-label="Primary navigation">
<a href="index.html">Home</a>
<a href="articles.html">Articles</a>
<a href="about.html">About</a>
<a href="contact.html">Contact</a>
</nav>
<button id="mobileSidebarToggle" class="menu-btn" aria-expanded="false" aria-controls="sidebarTree">☰ <span>Menu</span></button>
</div>
</header>
<div class="main-container">
<aside class="sidebar" role="complementary" aria-label="Site categories and navigation">
<div id="sidebarOverlay" class="overlay"></div>
<nav id="sidebarTree" class="sidebar-content" role="navigation" aria-label="Sidebar navigation">
<div class="sidebar-mobile-menu">
<a href="index.html">Home</a>
<a href="categories.html">Categories</a>
<a href="about.html">About</a>
<a href="contact.html">Contact</a>
</div>
<h2>Categories</h2>
<ul class="sidebar-list">
<li>
<button class="toggle" aria-expanded="false" aria-controls="tech-sublist">
<span class="icon">+</span>Technology
</button>
<ul id="tech-sublist" class="nested" role="menu" aria-label="Technology subcategories">
<li role="none"><a href="ai.html" class="nav-link" role="menuitem">AI & Machine Learning</a></li>
<li role="none"><a href="webdev.html" class="nav-link" role="menuitem">Web Development</a></li>
</ul>
</li>
<li>
<button class="toggle" aria-expanded="false" aria-controls="health-sublist">
<span class="icon">+</span>Health
</button>
<ul id="health-sublist" class="nested" role="menu" aria-label="Health subcategories">
<li role="none"><a href="nutrition.html" class="nav-link" role="menuitem">Nutrition</a></li>
<li role="none"><a href="fitness.html" class="nav-link" role="menuitem">Fitness</a></li>
</ul>
</li>
</ul>
</nav>
</aside>
<main class="content" role="main">
<article class="article">
<h1>The Influence of Modern Web Design on User Experience</h1>
<p>Good web design affects more than appearance; it shapes how people interact with information. Layouts that balance space, typography, and intuitive flow reduce friction and help visitors find what they need quickly. Accessibility and responsiveness remain essential, ensuring usability across devices.</p>
<h2>Typography and Readability</h2>
<p>Typography defines rhythm on the screen. A readable type system establishes hierarchy and guides attention. Body text should maintain adequate contrast and spacing for long reading sessions.</p>
<blockquote>“Design is not only what it looks like and feels like. Design is how it works.” — Steve Jobs</blockquote>
<h3>Elements of Balanced Layouts</h3>
<ul>
<li>Consistent spacing reinforces flow and alignment.</li>
<li>Moderate contrast enhances legibility without fatigue.</li>
<li>Meaningful iconography adds context without distraction.</li>
</ul>
<h2>Structured Interaction and Content</h2>
<p>Interactive design is strongest when it feels natural. Buttons, menus, and forms should respond clearly and immediately.</p>
</article>
<aside class="related" role="complementary" aria-label="Related articles">
<h2>Related Articles</h2>
<nav>
<a href="article1.html">📄 How AI Impacts Modern Life</a>
<a href="article2.html">📄 Top Web Development Trends</a>
<a href="article3.html">📄 Staying Healthy at Work</a>
<a href="article4.html">📄 Balancing Fitness and Routine</a>
</nav>
</aside>
</main>
</div>
<footer class="footer" role="contentinfo">
<div class="footer-grid">
<section>
<h2>Subscribe</h2>
<form class="subscribe-form">
<label for="subscribe-email" class="sr-only">Email address</label>
<input type="email" id="subscribe-email" placeholder="Your email" required />
<button type="submit">Subscribe</button>
</form>
</section>
<section>
<h2>Contact Us</h2>
<form class="contact-form">
<label for="contact-name" class="sr-only">Name</label>
<input type="text" id="contact-name" placeholder="Name" required />
<label for="contact-email" class="sr-only">Email</label>
<input type="email" id="contact-email" placeholder="Email" required />
<label for="contact-message" class="sr-only">Message</label>
<textarea id="contact-message" placeholder="Message" rows="3" required></textarea>
<button type="submit">Send</button>
</form>
</section>
<section>
<h2>Quick Links</h2>
<div class="legal">
<ul>
<li><a href="about.html">About</a></li>
<li><a href="privacy.html">Privacy Policy</a></li>
<li><a href="terms.html">Terms of Use</a></li>
</ul>
</div>
<div class="socials">
<ul>
<li>
<a href="https://twitter.com" target="_blank" rel="noopener noreferrer" aria-label="X (Twitter)" class="social-link">
<svg width="16" height="16" fill="currentColor" aria-hidden="true" viewBox="0 0 24 24">
<path d="M23 3a10.9 10.9 0 01-3.14 1.53A4.48 4.48 0 0022.4 1s-4 .67-6.2 2.19a4.48 4.48 0 00-7.86 4.1A12.94 12.94 0 013 2.1s-4 8 4 11.95c-2.88 0-4.28-1.96-4.28-1.96v.2c0 2.05 1.46 3.78 3.3 4.17a4.52 4.52 0 01-2 .08c.56 1.75 2.18 3.02 4.1 3.06A9 9 0 013 19.53a12.79 12.79 0 006.92 2.03c8.3 0 12.85-6.86 12.85-12.81 0-.2 0-.39-.01-.58A9.22 9.22 0 0023 3z"/>
</svg>
X
</a>
</li>
<li>
<a href="https://facebook.com" target="_blank" rel="noopener noreferrer" aria-label="Facebook" class="social-link">
<svg width="16" height="16" fill="currentColor" aria-hidden="true" viewBox="0 0 24 24">
<path d="M22 12c0-5.52-4.48-10-10-10S2 6.48 2 12c0 4.99 3.66 9.12 8.44 9.88v-6.99H7.9v-2.89h2.54V9.41c0-2.5 1.49-3.89 3.77-3.89 1.09 0 2.23.19 2.23.19v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56v1.87h2.78l-.44 2.89h-2.34v6.99C18.34 21.12 22 16.99 22 12z"/>
</svg>
Facebook
</a>
</li>
<li>
<a href="https://instagram.com" target="_blank" rel="noopener noreferrer" aria-label="Instagram" class="social-link">
<svg width="16" height="16" fill="currentColor" aria-hidden="true" viewBox="0 0 24 24">
<path d="M12 2.2c3.2 0 3.584.012 4.85.07 1.17.055 1.963.24 2.423.403a4.92 4.92 0 011.78 1.03c.51.51.83 1.05 1.03 1.78.163.46.348 1.254.403 2.423.058 1.266.07 1.65.07 4.85s-.012 3.584-.07 4.85c-.055 1.17-.24 1.963-.403 2.423a4.92 4.92 0 01-1.03 1.78c-.51.51-1.05.83-1.78 1.03-.46.163-1.254.348-2.423.403-1.266.058-1.65.07-4.85.07s-3.584-.012-4.85-.07c-1.17-.055-1.963-.24-2.423-.403a4.92 4.92 0 01-1.78-1.03 4.92 4.92 0 01-1.03-1.78c-.163-.46-.348-1.254-.403-2.423C2.212 15.584 2.2 15.2 2.2 12s.012-3.584.07-4.85c.055-1.17.24-1.963.403-2.423a4.92 4.92 0 011.03-1.78c.51-.51 1.05-.83 1.78-1.03.46-.163 1.254-.348 2.423-.403C8.416 2.212 8.8 2.2 12 2.2zm0-2.2C8.736 0 8.332.014 7.052.072 5.77.13 4.824.314 4.042.545 3.21.787 2.493 1.14 1.817 1.817.14 2.493.787 3.21.545 4.042.314 4.824.13 5.77.072 7.052.014 8.332 0 8.736 0 12s.014 3.668.072 4.948c.058 1.282.242 2.228.473 3.01.242.832.595 1.549 1.271 2.225.676.676 1.393 1.029 2.225 1.271.782.231 1.728.415 3.01.473C8.332 23.986 8.736 24 12 24s3.668-.014 4.948-.072c1.282-.058 2.228-.242 3.01-.473.832-.242 1.549-.595 2.225-1.271.676-.676 1.029-1.393 1.271-2.225.231-.782.415-1.728.473-3.01.058-1.28.072-1.684.072-4.948s-.014-3.668-.072-4.948c-.058-1.282-.242-2.228-.473-3.01-.242-.832-.595-1.549-1.271-2.225-.676-.676-1.393-1.029-2.225-1.271-.782-.231-1.728-.415-3.01-.473C15.668.014 15.264 0 12 0z"/>
<circle cx="12" cy="12" r="3.2"/>
<circle cx="18.4" cy="5.6" r="1.44"/>
</svg>
Instagram
</a>
</li>
</ul>
</div>
</section>
</div>
<div class="footer-bottom">© 2025 My Website. All rights reserved.</div>
</footer>
<script defer>
document.addEventListener('DOMContentLoaded', () => {
const toggles = document.querySelectorAll('.toggle');
const mobileBtn = document.getElementById('mobileSidebarToggle');
const sidebar = document.getElementById('sidebarTree');
const overlay = document.getElementById('sidebarOverlay');
const links = document.querySelectorAll('.nav-link, nav a');
const nestedLists = document.querySelectorAll('.nested');
nestedLists.forEach(n => { n.classList.remove('expanded'); n.style.maxHeight = '0px'; });
toggles.forEach(btn => {
const nested = btn.closest('li').querySelector('.nested');
const icon = btn.querySelector('.icon');
btn.addEventListener('click', () => {
const isOpen = nested.classList.contains('expanded');
nestedLists.forEach(other => {
if (other !== nested && other.classList.contains('expanded')) {
other.classList.remove('expanded');
other.style.maxHeight = '0px';
const otherBtn = other.parentElement.querySelector('.toggle');
otherBtn.setAttribute('aria-expanded', 'false');
otherBtn.querySelector('.icon').textContent = '+';
otherBtn.classList.remove('active');
}
});
if (!isOpen) {
nested.classList.add('expanded');
nested.style.maxHeight = nested.scrollHeight + 'px';
btn.setAttribute('aria-expanded', 'true');
icon.textContent = '−';
btn.classList.add('active');
} else {
nested.classList.remove('expanded');
nested.style.maxHeight = '0px';
btn.setAttribute('aria-expanded', 'false');
icon.textContent = '+';
btn.classList.remove('active');
}
});
});
const openSidebar = () => {
sidebar.classList.add('active');
overlay.classList.add('active');
mobileBtn.setAttribute('aria-expanded', 'true');
document.body.style.overflow = 'hidden';
};
const closeSidebar = () => {
sidebar.classList.remove('active');
overlay.classList.remove('active');
mobileBtn.setAttribute('aria-expanded', 'false');
document.body.style.overflow = '';
};
mobileBtn.addEventListener('click', () => {
if (window.innerWidth < 768) {
sidebar.classList.contains('active') ? closeSidebar() : openSidebar();
}
});
overlay.addEventListener('click', closeSidebar);
links.forEach(link => link.addEventListener('click', () => { if (window.innerWidth < 768) closeSidebar(); }));
window.addEventListener('resize', () => {
if (window.innerWidth >= 768) {
closeSidebar();
nestedLists.forEach(n => n.style.maxHeight = n.classList.contains('expanded') ? n.scrollHeight + 'px' : '0px');
}
});
const currentURL = window.location.pathname.split('/').pop();
links.forEach(link => {
if (link.getAttribute('href') === currentURL) {
link.style.color = '#2563eb';
link.style.fontWeight = '700';
}
});
});
</script>
</body>
</html>
just dropzones for side and top
Thanks again, Steve. ![]()
Please note that on smaller screens, the top navigation becomes part of the slide-in menu. Also, keep in mind that these are different pages. I’m thinking that in Elements, I’ll need to manually build this across all breakpoints and then save them as globals. I’m particular about this because internal linking is critical for SEO and overall page stickiness.
I’m building a training and education website. Visitors aren’t coming to buy or shop—they’re coming purely to learn. That’s not to say I won’t ever monetize the site, but for now, the focus is on voluntary learning within my topic area. This isn’t a brochure site.