/* ========== PAGE TRANSITION ========== */
.w-page-transition {
position: fixed;
inset: 0;
pointer-events: none;
overflow: hidden;
z-index: -9999;
}
/* --- Dark layer --- */
.w-page-transition-dark {
position: absolute;
height: 100vh;
inset: 0;
background: var(--bg-dark, #000);
display: flex;
align-items: center;
justify-content: center;
gap: 20px;
opacity: 0;
transition: opacity 0.8s cubic-bezier(0.77, 0, 0.175, 1);
z-index: 1;
}
.w-page-transition-dark.active {
opacity: 1;
}
/* --- Dark - Logo wrapper --- */
.w-page-transition-dark__logo-wrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) translateY(50%);
display: flex;
flex-direction: row;
justify-content: center;
gap: 20px;
opacity: 0;
transition: transform 0.8s ease, opacity 0.8s ease;
z-index: 2;
}
.w-page-transition-dark__logo-wrapper.active {
transform: translate(-50%, -50%) translateY(0);
opacity: 1;
}
.w-page-transition-dark__text {
color: var(--text-title-contrast, #fff);
font-size: clamp(32px, 4vw, 100px);
}
.w-page-transition-dark__favicon {
opacity: 0;
width: 80px;
height: 80px;
}
/* --- Light layer --- */
.w-page-transition-light {
position: absolute;
height: 100vh;
inset: 0;
background: var(--bg-body, #fff);
display: flex;
align-items: center;
justify-content: center;
transform: translateY(100%);
transition: transform 1s cubic-bezier(0.77, 0, 0.175, 1);
z-index: 3;
}
.w-page-transition-light.active {
transform: translateY(0);
}
/* --- Light - Logo wrapper --- */
.w-page-transition-light__logo-wrapper {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) translateY(0);
display: flex;
flex-direction: row;
justify-content: center;
gap: 20px;
opacity: 0;
transition: opacity 0.6s ease 0.3s, transform 0.6s ease 0.3s;
z-index: 4;
}
.w-page-transition-light__logo-wrapper.active {
opacity: 1;
transform: translate(-50%, -50%) translateY(0);
}
.w-page-transition-light__text {
color: var(--text-title, #000);
font-size: clamp(32px, 4vw, 100px);
}
.w-page-transition-light__favicon {
width: 80px;
height: 80px;
opacity: 0;
transform: scale(0) rotate(-90deg);
transition: all 0.6s ease 0.3s;
}
.w-page-transition-light__favicon.appear {
opacity: 1;
transform: scale(1) rotate(0);
}
document.addEventListener("DOMContentLoaded", () => {
const dark = document.querySelector(".w-page-transition-dark");
const darkWrapper = document.querySelector(".w-page-transition-dark__logo-wrapper");
const light = document.querySelector(".w-page-transition-light");
const lightWrapper = document.querySelector(".w-page-transition-light__logo-wrapper");
const favicon = document.querySelector(".w-page-transition-light__favicon");
const pageTransition = document.querySelector(".w-page-transition");
if (!dark || !light || !pageTransition) return;
/** Helper delay */
function wait(ms) {
return new Promise((res) => setTimeout(res, ms));
}
/** PAGE LEAVE (trước khi chuyển trang) **/
function startLeave() {
pageTransition.style.zIndex = "9999";
dark.classList.add("active");
}
/** PAGE ENTER (hiệu ứng khi vào trang) **/
async function startEnter() {
// Luôn đảm bảo z-index cao khi bắt đầu
pageTransition.style.zIndex = "9999";
// Step 1: Dark layer fade in
dark.classList.add("active");
await wait(600);
// Step 2: Logo/text dark xuất hiện
darkWrapper.classList.add("active");
await wait(800);
// Step 3: Light layer trượt lên
light.classList.add("active");
lightWrapper.classList.add("active");
await wait(1000);
// Step 4: Logo favicon hiện ra
favicon.classList.add("appear");
await wait(1200);
// Step 5: Toàn bộ transition fade out nhanh
pageTransition.style.transition = "opacity 0.6s ease";
pageTransition.style.opacity = "0";
await wait(1200);
// Step 6: Reset trạng thái cho lần sau
dark.classList.remove("active");
darkWrapper.classList.remove("active");
light.classList.remove("active");
lightWrapper.classList.remove("active");
favicon.classList.remove("appear");
// Reset style sau khi đã ẩn hẳn
pageTransition.style.opacity = "";
pageTransition.style.transition = "";
// Đưa z-index xuống thấp để không che nội dung
pageTransition.style.zIndex = "-9999";
}
/** Run on initial load **/
startEnter();
/** Trigger transition khi click link **/
document.querySelectorAll("a[href]").forEach((link) => {
link.addEventListener("click", (e) => {
const url = link.href;
if (
url.includes("#") ||
url.startsWith("mailto:") ||
url.startsWith("tel:") ||
link.target === "_blank"
)
return;
e.preventDefault();
startLeave();
setTimeout(() => {
window.location.href = url;
}, 600);
});
});
});