Building a Dynamic Grid Morphing Navigation with HTML5, CSS3, and Fetch API
Grid morphing navigation delivers visually engaging transitions between interactive menu states by combining semantic markup, modern CSS transforms, and asynhcronous content loading. Key implementation pillars include grid-based posisioning for menu cells, CSS transitions/transforms for scaling/shifting effects, and Fetch API for seamless content updates.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic Morphing Grid Nav</title>
<style>
:root {
--nav-blue: #0F52BA;
--nav-green: #2ECC71;
--nav-purple: #9B59B6;
--nav-orange: #F39C12;
--nav-pink: #E91E63;
--nav-teal: #00BCD4;
--nav-gray: #34495E;
--transition-fast: 250ms cubic-bezier(0.4, 0, 0.2, 1);
--transition-medium: 450ms cubic-bezier(0.4, 0, 0.2, 1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Roboto, sans-serif;
}
body {
background-color: #F5F7FA;
min-height: 100vh;
padding: 2rem 1rem;
}
.site-wrapper {
max-width: 1200px;
margin: 0 auto;
}
.page-header {
text-align: center;
margin-bottom: 3rem;
color: #2C3E50;
}
.morph-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 1.2rem;
margin-bottom: 3rem;
}
.grid-cell {
position: relative;
height: 140px;
border-radius: 12px;
overflow: hidden;
cursor: pointer;
transition: all var(--transition-medium);
z-index: 1;
}
.grid-cell.active {
transform: scale(1.15);
box-shadow: 0 18px 40px rgba(0,0,0,0.2);
z-index: 10;
}
.cell-inner {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
padding: 1rem;
text-align: center;
transition: all var(--transition-fast);
}
.cell-icon {
font-size: 2.8rem;
margin-bottom: 0.8rem;
transition: transform var(--transition-medium);
}
.cell-label {
font-weight: 600;
font-size: 1rem;
opacity: 0;
transform: translateY(15px);
transition: all var(--transition-fast);
}
.grid-cell:hover .cell-icon {
transform: translateY(-18px) scale(0.85);
}
.grid-cell:hover .cell-label {
opacity: 1;
transform: translateY(0);
}
.content-panel {
background-color: white;
border-radius: 16px;
padding: 2.5rem;
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
opacity: 1;
transform: translateY(0);
transition: all var(--transition-medium);
}
.content-panel.fade-out {
opacity: 0;
transform: translateY(20px);
}
.content-panel h2 {
color: var(--nav-blue);
margin-bottom: 1rem;
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
</head>
<body>
<div class="site-wrapper">
<header class="page-header">
<h1>Morphing Grid Navigation</h1>
<p>Interactive, animating navigation with smooth content updates</p>
</header>
<nav id="morph-nav" class="morph-grid"></nav>
<section id="content-display" class="content-panel">
<h2>Welcome</h2>
<p>Select a tile from the morphing grid above to view dynamic content. Hover over tiles to see subtle animation previews.</p>
</section>
</div>
<script>
const navTiles = [
{ id: 'home', icon: 'fa-house', label: 'Dashboard', color: 'var(--nav-blue)', source: 'data/dashboard.json' },
{ id: 'about', icon: 'fa-building', label: 'Company', color: 'var(--nav-green)', source: 'data/company.json' },
{ id: 'services', icon: 'fa-tools', label: 'Solutions', color: 'var(--nav-purple)', source: 'data/solutions.json' },
{ id: 'portfolio', icon: 'fa-images', label: 'Showcase', color: 'var(--nav-orange)', source: 'data/showcase.json' },
{ id: 'blog', icon: 'fa-newspaper', label: 'Updates', color: 'var(--nav-pink)', source: 'data/updates.json' },
{ id: 'contact', icon: 'fa-headset', label: 'Support', color: 'var(--nav-teal)', source: 'data/support.json' },
{ id: 'analytics', icon: 'fa-chart-line', label: 'Metrics', color: 'var(--nav-gray)', source: 'data/metrics.json' },
{ id: 'security', icon: 'fa-lock', label: 'Privacy', color: '#1ABC9C', source: 'data/privacy.json' }
];
let selectedTile = null;
function renderNavigation() {
const navContainer = document.getElementById('morph-nav');
navTiles.forEach(tile => {
const tileEl = document.createElement('div');
tileEl.className = 'grid-cell';
tileEl.style.backgroundColor = tile.color;
tileEl.dataset.tileId = tile.id;
tileEl.dataset.contentSource = tile.source;
tileEl.innerHTML = `
<div class="cell-inner">
<i class="cell-icon fa-solid ${tile.icon}"></i>
<span class="cell-label">${tile.label}</span>
</div>
`;
tileEl.addEventListener('click', () => activateTile(tileEl));
navContainer.appendChild(tileEl);
});
}
function activateTile(tileEl) {
if (selectedTile) {
selectedTile.classList.remove('active');
}
tileEl.classList.add('active');
selectedTile = tileEl;
updateContent(tileEl.dataset.contentSource);
}
async function updateContent(sourceUrl) {
const panel = document.getElementById('content-display');
panel.classList.add('fade-out');
try {
const response = await fetch(sourceUrl);
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
const data = await response.json();
setTimeout(() => {
panel.innerHTML = `<h2>${data.title}</h2><p>${data.description}</p>`;
panel.classList.remove('fade-out');
}, 250);
} catch (error) {
setTimeout(() => {
panel.innerHTML = `<h2 style="color:#E74C3C">Content Unavailable</h2><p>${error.message}</p>`;
panel.classList.remove('fade-out');
}, 250);
}
}
document.addEventListener('DOMContentLoaded', renderNavigation);
</script>
</body>
</html>
Key Technical Components
- Semantic HTML5 Structure: Uses
<header>,<nav>, and<section>for accessibility and clarity, with grid container holding dynamically generated navigation cells. - CSS3 Grid & Animations: Implements responisve grid layout with
auto-fitandminmaxfor adaptive tile sizing,transformfor scaling/shifting hover/active states, andtransitionwith cubic-bezier easing for smooth movement. - Fetch API for Asynchronous Loading: Replaces XMLHttpRequest with modern Fetch API for simpler promise-based content fetching, including error handling and content panel fade transitions during updates.
Create sample JSON files in a data/ directory (e.g., dashboard.json) to test content loading:
{
"title": "Dashboard Overview",
"description": "Track key performance indicators, recent activities, and quick access shortcuts from your personalized dashboard."
}