This project demonstrates a high-end UI/UX Design + Custom Front-End Development execution. While many Elementor sites rely on standard widgets, I created this “Viewport-Locked” Immersive Slider using pure HTML5, CSS3, and Vanilla JavaScript to achieve a level of fluid motion typically only seen on bespoke, high-budget agency sites.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Travel Explorer - Africa Animated</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;700;800&display=swap" rel="stylesheet">
<style>
/* MANDATORY ELEMENTOR OVERRIDES */
html, body {
margin: 0 !important;
padding: 0 !important;
overflow: hidden;
font-family: 'Inter', sans-serif;
background: #000;
}
.elementor-html { margin: 0 !important; padding: 0 !important; }
/* HERO CONTAINER */
.hero-slider {
width: 100vw;
height: 100vh;
position: relative;
overflow: hidden;
display: flex;
align-items: center;
color: #fff;
}
/* DUAL LAYER BACKGROUND ANIMATION */
.bg-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1;
}
.bg-layer {
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background-size: cover;
background-position: center;
transition: opacity 1.2s cubic-bezier(0.4, 0, 0.2, 1), transform 1.5s ease-out;
opacity: 0;
transform: scale(1.1);
}
.bg-layer.active {
opacity: 1;
transform: scale(1);
}
.bg-layer::after {
content: '';
position: absolute;
top: 0; left: 0; width: 100%; height: 100%;
background: linear-gradient(to right, rgba(0,0,0,0.7) 0%, rgba(0,0,0,0.2) 50%, rgba(0,0,0,0.6) 100%);
}
/* NAVBAR */
.navbar {
position: absolute;
top: 0; left: 0; width: 100%;
padding: 40px 60px;
display: flex;
justify-content: space-between;
align-items: center;
z-index: 100;
box-sizing: border-box;
}
.logo-icon {
width: 40px; height: 40px;
background: #fff;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
display: flex; align-items: center; justify-content: center;
}
/* SIDE NAVIGATION ANIMATION */
.side-nav {
position: absolute;
left: 60px;
height: 200px;
width: 2px;
background: rgba(255,255,255,0.1);
z-index: 10;
}
.active-line {
position: absolute;
top: 0;
width: 2px;
height: 60px;
background: #fff;
transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);
}
.side-nav .dot {
position: absolute;
left: -11px;
width: 24px; height: 24px;
background: #fff;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
color: #000; font-size: 10px; font-weight: bold;
transition: transform 0.6s cubic-bezier(0.65, 0, 0.35, 1);
z-index: 11;
}
/* TEXT ANIMATION WRAPPERS */
.content-left {
position: relative;
z-index: 10;
margin-left: 120px;
max-width: 500px;
}
.anim-hide {
overflow: hidden;
display: block;
}
.content-left h1 {
font-size: 110px;
margin: 0;
line-height: 0.9;
font-weight: 800;
letter-spacing: -3px;
transform: translateY(100%);
transition: transform 0.8s cubic-bezier(0.2, 1, 0.3, 1);
}
.content-left p {
font-size: 15px;
line-height: 1.7;
margin: 25px 0 35px 0;
color: rgba(255,255,255,0.7);
transform: translateY(50px);
opacity: 0;
transition: transform 0.8s 0.2s cubic-bezier(0.2, 1, 0.3, 1), opacity 0.8s 0.2s;
}
.content-left.active h1 { transform: translateY(0); }
.content-left.active p { transform: translateY(0); opacity: 1; }
.btn-more {
background: #fff;
color: #000;
padding: 14px 30px;
border-radius: 4px;
text-transform: uppercase;
font-size: 11px;
font-weight: 800;
text-decoration: none;
display: inline-block;
transition: 0.3s;
}
/* SLIDER CARDS ANIMATION */
.slider-wrapper {
position: absolute;
right: 0;
width: 50%;
height: 450px;
z-index: 20;
overflow: hidden;
}
.card-track {
display: flex;
gap: 30px;
transition: transform 0.8s cubic-bezier(0.65, 0, 0.35, 1);
padding-left: 20px;
}
.card {
width: 260px;
height: 400px;
border-radius: 15px;
background-size: cover;
background-position: center;
flex-shrink: 0;
box-shadow: 0 30px 60px rgba(0,0,0,0.4);
transition: transform 0.5s ease, filter 0.5s ease;
filter: grayscale(0.4) brightness(0.8);
}
.card.active {
transform: scale(1.05);
filter: grayscale(0) brightness(1);
}
/* CONTROLS */
.controls {
position: absolute;
bottom: 60px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 20px;
z-index: 100;
}
.nav-btn {
width: 50px; height: 50px;
border-radius: 50%;
border: 1px solid rgba(255,255,255,0.3);
background: rgba(255,255,255,0.05);
backdrop-filter: blur(10px);
display: flex; align-items: center; justify-content: center;
cursor: pointer;
transition: 0.4s;
color: #fff;
}
.nav-btn:hover { background: #fff; color: #000; }
@media (max-width: 1024px) {
.slider-wrapper { display: none; }
.content-left h1 { font-size: 70px; }
.content-left { margin-left: 60px; }
}
</style>
</head>
<body>
<div class="hero-slider">
<div class="bg-container" id="bg-container">
<!-- Backgrounds added via JS -->
</div>
<nav class="navbar">
<div class="menu-btn" style="font-size:10px; letter-spacing:2px; display:flex; align-items:center; gap:10px;">
<svg width="20" height="12" viewBox="0 0 20 12" fill="none" stroke="currentColor" stroke-width="2"><path d="M0 1h20M0 6h20M0 11h20"/></svg>
<span>MENU</span>
</div>
<div class="logo-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="#000"><path d="M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z"/></svg>
</div>
<div style="font-size:10px; letter-spacing:2px;">LOGIN</div>
</nav>
<div class="side-nav">
<div class="active-line" id="side-line"></div>
<div class="dot" id="side-dot">1</div>
</div>
<div class="content-left active" id="text-content">
<span class="anim-hide"><h1 id="main-title">AFRICA</h1></span>
<p id="main-desc">Africa is the world's second-largest and second-most populous continent. Experience the cradle of humanity.</p>
<a href="#" class="btn-more">DISCOVER</a>
</div>
<div class="slider-wrapper">
<div class="card-track" id="card-track">
<!-- Cards added via JS -->
</div>
</div>
<div class="controls">
<div class="nav-btn" onclick="changeSlide(-1)">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M15 18l-6-6 6-6"/></svg>
</div>
<div class="nav-btn" onclick="changeSlide(1)">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M9 18l6-6-6-6" transform="rotate(180 12 12)"/></svg>
</div>
</div>
</div>
<script>
const slides = [
{
title: "AFRICA",
desc: "Explore the vast savannas and ancient heritage of the world's second-largest continent.",
bg: "https://images.unsplash.com/photo-1516026672322-bc52d61a55d5?q=80&w=1920"
},
{
title: "SAHARA",
desc: "The golden silence of the world's largest hot desert. An endless horizon of moving dunes.",
bg: "https://images.unsplash.com/photo-1509316785289-025f5b846b35?q=80&w=1920"
},
{
title: "SAFARI",
desc: "Witness the majesty of the Big Five in their natural habitat across the Serengeti plains.",
bg: "https://images.unsplash.com/photo-1547471080-7cc2caa01a7e?q=80&w=1920"
}
];
let current = 0;
const bgContainer = document.getElementById('bg-container');
const cardTrack = document.getElementById('card-track');
// Init Slider
function init() {
slides.forEach((slide, i) => {
// Create BG Layers
const bg = document.createElement('div');
bg.className = `bg-layer ${i === 0 ? 'active' : ''}`;
bg.style.backgroundImage = `url('${slide.bg}')`;
bgContainer.appendChild(bg);
// Create Cards
const card = document.createElement('div');
card.className = `card ${i === 0 ? 'active' : ''}`;
card.style.backgroundImage = `url('${slide.bg}')`;
cardTrack.appendChild(card);
});
}
function changeSlide(dir) {
current = (current + dir + slides.length) % slides.length;
// 1. Animate Background
const bgs = document.querySelectorAll('.bg-layer');
bgs.forEach((bg, i) => bg.classList.toggle('active', i === current));
// 2. Animate Text (Staggered)
const content = document.getElementById('text-content');
content.classList.remove('active');
setTimeout(() => {
document.getElementById('main-title').innerText = slides[current].title;
document.getElementById('main-desc').innerText = slides[current].desc;
content.classList.add('active');
}, 400);
// 3. Animate Card Track
const cards = document.querySelectorAll('.card');
cards.forEach((card, i) => card.classList.toggle('active', i === current));
cardTrack.style.transform = `translateX(-${current * 290}px)`;
// 4. Animate Side Nav
document.getElementById('side-line').style.transform = `translateY(${current * 70}px)`;
document.getElementById('side-dot').style.transform = `translateY(${current * 70}px)`;
document.getElementById('side-dot').innerText = current + 1;
}
init();
</script>
</body>
</html>