Enhance UI components and improve user notifications
- Updated App.vue to include a toast notification system for new messages, enhancing user engagement. - Modified SplashScreen.vue to streamline the intro text display with improved typing effects. - Added Montserrat font styles in style.css for better typography across the application. - Improved controller navigation in useControllerNav.ts to support enhanced focus management and sound feedback. - Updated routing logic in index.ts to redirect authenticated users from the login page to the home page. - Enhanced the Login.vue view with transition effects for a smoother user experience during login and setup processes.
This commit is contained in:
+597
-115
@@ -1,18 +1,21 @@
|
||||
<template>
|
||||
<div class="min-h-screen flex relative dashboard-view">
|
||||
<!-- Background container with 3D perspective -->
|
||||
<div class="min-h-screen flex relative dashboard-view" :class="{ 'glass-throw-active': showZoomIn }">
|
||||
<!-- Background container with 3D perspective - full width to avoid letterboxing -->
|
||||
<div class="bg-perspective-container">
|
||||
<!-- Background - default -->
|
||||
<div
|
||||
<!-- Background - default (zoom animates on this layer, not container, to avoid letterboxing) -->
|
||||
<div
|
||||
ref="bgDefault"
|
||||
class="bg-layer"
|
||||
:class="{ 'bg-transitioning-out': showAltBackground || showWeb5Background || showNetworkBackground || showSettingsBackground || showMyAppsBackground || showAppStoreBackground || showCloudBackground || showHomeBackground }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${currentBackgroundImage})` }"
|
||||
/>
|
||||
class="bg-layer bg-fullwidth"
|
||||
:class="[
|
||||
{ 'bg-transitioning-out': showAltBackground || showWeb5Background || showNetworkBackground || showSettingsBackground || showMyAppsBackground || showAppStoreBackground || showCloudBackground || showHomeBackground },
|
||||
{ 'zoom-reveal-bg': showZoomIn }
|
||||
]"
|
||||
:style="{ backgroundImage: `url(/assets/img/${currentBackgroundImage})` }"
|
||||
/>
|
||||
<!-- Background - alternate for app details and Web5 -->
|
||||
<div
|
||||
ref="bgAlt"
|
||||
class="bg-layer"
|
||||
class="bg-layer bg-fullwidth"
|
||||
:class="{ 'bg-transitioning-in': showAltBackground || showWeb5Background || showNetworkBackground || showSettingsBackground || showMyAppsBackground || showAppStoreBackground || showCloudBackground || showHomeBackground }"
|
||||
:style="{ backgroundImage: `url(/assets/img/${altBackgroundImage})` }"
|
||||
/>
|
||||
@@ -31,8 +34,31 @@
|
||||
class="bg-glitch-scan"
|
||||
:class="{ 'glitch-active': isGlitching }"
|
||||
/>
|
||||
<!-- Continuous glitch/flash overlays - same as login, every 5s -->
|
||||
<div
|
||||
class="dashboard-glitch-layer dashboard-glitch-1 bg-fullwidth"
|
||||
:style="{ backgroundImage: `url(/assets/img/${currentBackgroundImage})` }"
|
||||
/>
|
||||
<div
|
||||
class="dashboard-glitch-layer dashboard-glitch-2 bg-fullwidth"
|
||||
:style="{ backgroundImage: `url(/assets/img/${currentBackgroundImage})` }"
|
||||
/>
|
||||
<div class="dashboard-glitch-scan" />
|
||||
</div>
|
||||
|
||||
<!-- Oomph accent - brief impact flash when dashboard loads -->
|
||||
<div
|
||||
v-if="showZoomIn"
|
||||
class="fixed inset-0 pointer-events-none z-[100] oomph-flash"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<!-- Reveal flashes and glitch overlay - enthralling entrance -->
|
||||
<div
|
||||
v-if="showZoomIn"
|
||||
class="fixed inset-0 pointer-events-none z-[99] reveal-flash-glitch"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
||||
<!-- Background overlay - 0.3 opacity default, 0.8 opacity for Web5, Network, and Settings -->
|
||||
<div
|
||||
class="fixed inset-0 transition-opacity duration-500 pointer-events-none"
|
||||
@@ -40,61 +66,70 @@
|
||||
style="z-index: -5;"
|
||||
/>
|
||||
|
||||
<!-- Sidebar - Desktop Only -->
|
||||
<aside class="hidden md:flex w-[256px] border-r border-glass-border shadow-glass-sm flex-shrink-0 relative flex-col" style="background: rgba(0, 0, 0, 0.25); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);">
|
||||
<div class="p-6 flex-1">
|
||||
<div class="flex items-center gap-3 mb-8">
|
||||
<AnimatedLogo />
|
||||
<div class="min-w-0 flex-1">
|
||||
<h2 class="text-lg font-semibold text-white truncate">{{ serverName }}</h2>
|
||||
<p class="text-xs text-white/60">v{{ version }}</p>
|
||||
<!-- Sidebar - Desktop Only, animates in at end with separate parts -->
|
||||
<aside
|
||||
data-controller-zone="sidebar"
|
||||
class="hidden md:flex w-[256px] flex-shrink-0 relative flex-col"
|
||||
:class="{ 'sidebar-animate': showZoomIn }"
|
||||
>
|
||||
<div class="sidebar-shell">
|
||||
<div class="sidebar-inner">
|
||||
<div class="sidebar-logo flex items-center gap-3 mb-8 p-6 pb-0">
|
||||
<AnimatedLogo />
|
||||
<div class="min-w-0 flex-1">
|
||||
<h2 class="text-lg font-semibold text-white truncate">{{ serverName }}</h2>
|
||||
<p class="text-xs text-white/60">v{{ version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="sidebar-nav space-y-2 p-6 pt-4">
|
||||
<RouterLink
|
||||
v-for="(item, idx) in desktopNavItems"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
class="sidebar-nav-item flex items-center gap-3 px-4 py-3 rounded-lg text-white/80 hover:bg-white/10 hover:text-white transition-colors"
|
||||
exact-active-class="nav-tab-active"
|
||||
:style="{ '--nav-stagger': idx }"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
v-for="(path, index) in getIconPath(item.icon)"
|
||||
:key="index"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
:d="path"
|
||||
/>
|
||||
</svg>
|
||||
<span>{{ item.label }}</span>
|
||||
</RouterLink>
|
||||
</nav>
|
||||
|
||||
<div class="sidebar-controller px-6 pb-2">
|
||||
<ControllerIndicator />
|
||||
</div>
|
||||
|
||||
<div class="sidebar-logout p-6">
|
||||
<button
|
||||
@click="handleLogout"
|
||||
class="sidebar-logout-btn w-full flex items-center gap-3 px-4 py-3 rounded-lg text-white/80 hover:bg-white/10 hover:text-white transition-colors"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
<span>Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="space-y-2">
|
||||
<RouterLink
|
||||
v-for="item in desktopNavItems"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
class="flex items-center gap-3 px-4 py-3 rounded-lg text-white/80 hover:bg-white/10 hover:text-white transition-colors"
|
||||
exact-active-class="nav-tab-active"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
v-for="(path, index) in getIconPath(item.icon)"
|
||||
:key="index"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
:d="path"
|
||||
/>
|
||||
</svg>
|
||||
<span>{{ item.label }}</span>
|
||||
</RouterLink>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Controller indicator - Desktop sidebar -->
|
||||
<div class="px-6 pb-2">
|
||||
<ControllerIndicator />
|
||||
</div>
|
||||
|
||||
<!-- User Section - Desktop Only -->
|
||||
<div class="p-6">
|
||||
<button
|
||||
@click="handleLogout"
|
||||
class="w-full flex items-center gap-3 px-4 py-3 rounded-lg text-white/80 hover:bg-white/10 hover:text-white transition-colors"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
<span>Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="flex-1 overflow-hidden relative pb-20 md:pb-0">
|
||||
<!-- Main Content (Xbox: Right goes here from sidebar) -->
|
||||
<main
|
||||
data-controller-zone="main"
|
||||
class="flex-1 overflow-hidden relative pb-20 md:pb-0 glass-piece"
|
||||
:class="{ 'glass-throw-main': showZoomIn }"
|
||||
>
|
||||
<!-- Connection Status Banner -->
|
||||
<div v-if="isOffline && !store.isReconnecting && store.isAuthenticated" class="path-option-card mx-6 mt-6 px-6 py-3 border-l-4 border-yellow-500">
|
||||
<div class="flex items-center gap-2 text-yellow-200">
|
||||
@@ -117,34 +152,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- New message toast (top right, glassmorphic) -->
|
||||
<Teleport to="body">
|
||||
<Transition name="toast">
|
||||
<div
|
||||
v-if="toastMessage.show"
|
||||
@click="messageToast.dismissToastAndOpenMessages"
|
||||
class="fixed top-20 right-4 left-4 z-[100] w-auto max-w-md cursor-pointer rounded-xl p-4 transition-all hover:border-white/30 hover:shadow-2xl md:top-6 md:right-6 md:left-auto md:max-w-md toast-glass"
|
||||
>
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-orange-500/20">
|
||||
<svg class="h-5 w-5 text-orange-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="min-w-0 flex-1">
|
||||
<p class="text-sm font-medium text-white">New message</p>
|
||||
<p class="mt-0.5 text-sm text-white/70 line-clamp-2">{{ toastMessage.text }}</p>
|
||||
<p class="mt-1 text-xs text-orange-400">Click to view</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
|
||||
<!-- Persistent Mobile Tabs for Apps/Marketplace -->
|
||||
<!-- Persistent Mobile Tabs for Apps/Marketplace - glass piece 3 -->
|
||||
<div
|
||||
v-if="showAppsTabs"
|
||||
class="md:hidden fixed top-0 left-0 right-0 z-40 px-4 pt-4 pb-2"
|
||||
class="md:hidden fixed top-0 left-0 right-0 z-40 px-4 pt-4 pb-2 glass-piece"
|
||||
:class="{ 'glass-throw-mobile-tabs': showZoomIn }"
|
||||
style="background: rgba(0, 0, 0, 0.25); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); transform: translateZ(0);"
|
||||
>
|
||||
<div class="glass-card p-2 rounded-lg flex gap-2 relative">
|
||||
@@ -182,10 +194,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Persistent Mobile Tabs for Network/Cloud -->
|
||||
<!-- Persistent Mobile Tabs for Network/Cloud - glass piece 4 -->
|
||||
<div
|
||||
v-if="showNetworkTabs"
|
||||
class="md:hidden fixed top-0 left-0 right-0 z-40 px-4 pt-4 pb-2"
|
||||
class="md:hidden fixed top-0 left-0 right-0 z-40 px-4 pt-4 pb-2 glass-piece"
|
||||
:class="{ 'glass-throw-mobile-tabs-2': showZoomIn }"
|
||||
style="background: rgba(0, 0, 0, 0.25); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); transform: translateZ(0);"
|
||||
:style="{ top: showAppsTabs ? '80px' : '0' }"
|
||||
>
|
||||
@@ -224,7 +237,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="perspective-container-wrapper" :class="{ 'pt-40': showAppsTabs && showNetworkTabs, 'pt-20': showAppsTabs !== showNetworkTabs }">
|
||||
<div class="perspective-container-wrapper glass-piece" :class="{ 'pt-40': showAppsTabs && showNetworkTabs, 'pt-20': showAppsTabs !== showNetworkTabs, 'glass-throw-content': showZoomIn && !isHomeRoute }">
|
||||
<div class="perspective-container">
|
||||
<RouterView v-slot="{ Component, route }">
|
||||
<Transition :name="getTransitionName(route)">
|
||||
@@ -246,11 +259,12 @@
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Mobile Bottom Tab Bar -->
|
||||
<!-- Mobile Bottom Tab Bar - glass piece 5 -->
|
||||
<nav
|
||||
ref="mobileTabBar"
|
||||
data-mobile-tab-bar
|
||||
class="md:hidden fixed bottom-0 left-0 right-0 border-t border-glass-border shadow-glass z-50"
|
||||
class="md:hidden fixed bottom-0 left-0 right-0 border-t border-glass-border shadow-glass z-50 glass-piece"
|
||||
:class="{ 'glass-throw-tabbar': showZoomIn }"
|
||||
style="background: rgba(0, 0, 0, 0.25); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);"
|
||||
>
|
||||
<div class="flex justify-around items-center px-2 py-3 relative">
|
||||
@@ -288,18 +302,21 @@
|
||||
import { computed, ref, watch, onMounted, onBeforeUnmount, nextTick } from 'vue'
|
||||
import { RouterLink, RouterView, useRouter, useRoute } from 'vue-router'
|
||||
import { useAppStore } from '../stores/app'
|
||||
import { useMessageToast } from '@/composables/useMessageToast'
|
||||
import { useLoginTransitionStore } from '../stores/loginTransition'
|
||||
import AnimatedLogo from '@/components/AnimatedLogo.vue'
|
||||
import ControllerIndicator from '@/components/ControllerIndicator.vue'
|
||||
import { playDashboardLoadOomph } from '@/composables/useLoginSounds'
|
||||
|
||||
const router = useRouter()
|
||||
const messageToast = useMessageToast()
|
||||
const toastMessage = messageToast.toastMessage
|
||||
const route = useRoute()
|
||||
const store = useAppStore()
|
||||
const loginTransition = useLoginTransitionStore()
|
||||
|
||||
const showZoomIn = ref(false)
|
||||
|
||||
// Background swap for app details, Web5, Network, Settings, My Apps, App Store, Cloud, and Home
|
||||
const showAltBackground = ref(false)
|
||||
const isHomeRoute = computed(() => route.path === '/dashboard' || route.path === '/dashboard/')
|
||||
const showHomeBackground = ref(route.path === '/dashboard' || route.path === '/dashboard/')
|
||||
const showWeb5Background = ref(route.path.includes('/dashboard/web5'))
|
||||
const showNetworkBackground = ref(route.path.includes('/dashboard/server'))
|
||||
@@ -528,11 +545,35 @@ function updateNetworkTabIndicator() {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.body.classList.add('dashboard-active')
|
||||
if (loginTransition.justLoggedIn) {
|
||||
playDashboardLoadOomph()
|
||||
showZoomIn.value = true
|
||||
loginTransition.setPendingWelcomeTyping(true)
|
||||
loginTransition.setJustLoggedIn(false)
|
||||
// Trigger glitches during background reveal (0.5s, 1.2s, 2s into 2.8s zoom)
|
||||
const triggerRevealGlitch = () => {
|
||||
isGlitching.value = true
|
||||
setTimeout(() => { isGlitching.value = false }, 380)
|
||||
}
|
||||
setTimeout(triggerRevealGlitch, 500)
|
||||
setTimeout(triggerRevealGlitch, 1200)
|
||||
setTimeout(triggerRevealGlitch, 2000)
|
||||
// Keep glass-throw active long enough for sidebar (last to animate, ~7.5s)
|
||||
setTimeout(() => {
|
||||
showZoomIn.value = false
|
||||
}, 8000)
|
||||
// Trigger welcome typing on Home after main content animation finishes
|
||||
setTimeout(() => {
|
||||
loginTransition.setStartWelcomeTyping(true)
|
||||
loginTransition.setPendingWelcomeTyping(false)
|
||||
}, 4000)
|
||||
}
|
||||
|
||||
updateTabBarHeight()
|
||||
messageToast.startPolling()
|
||||
updateAppsTabIndicator()
|
||||
updateNetworkTabIndicator()
|
||||
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
updateTabBarHeight()
|
||||
updateAppsTabIndicator()
|
||||
@@ -541,8 +582,8 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
document.body.classList.remove('dashboard-active')
|
||||
window.removeEventListener('resize', updateTabBarHeight)
|
||||
messageToast.stopPolling()
|
||||
})
|
||||
|
||||
// Watch route changes to update indicator position
|
||||
@@ -786,6 +827,362 @@ function getTransitionName(currentRoute: any) {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 2advanced-style: fluid, cinematic, layered motion */
|
||||
/* Strong easeInOut - smooth acceleration/deceleration, no bounce */
|
||||
|
||||
/* Background - zoom in from depth with motion blur */
|
||||
.zoom-reveal-bg {
|
||||
animation: zoom-reveal 2.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.zoom-reveal-bg {
|
||||
opacity: 0;
|
||||
transform: scale(0.15);
|
||||
filter: blur(24px);
|
||||
}
|
||||
|
||||
@keyframes zoom-reveal {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.15);
|
||||
filter: blur(24px);
|
||||
}
|
||||
35% {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.5);
|
||||
filter: blur(20px);
|
||||
}
|
||||
65% {
|
||||
opacity: 0.85;
|
||||
transform: scale(0.88);
|
||||
filter: blur(6px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2advanced-style glass assembly - fluid, layered, deliberate timing */
|
||||
.glass-throw-active {
|
||||
perspective: 1400px;
|
||||
}
|
||||
|
||||
.glass-piece {
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
/* Sidebar - animates in at end with separate parts (like cards) */
|
||||
.sidebar-shell {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
backdrop-filter: blur(18px);
|
||||
-webkit-backdrop-filter: blur(18px);
|
||||
border-right: 1px solid transparent;
|
||||
box-shadow: 4px 0 24px rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-animate .sidebar-shell {
|
||||
animation: sidebar-shell-fly 1.2s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
animation-delay: 5.2s;
|
||||
opacity: 0;
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
@keyframes sidebar-shell-fly {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-100%);
|
||||
border-color: transparent;
|
||||
}
|
||||
70% {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
border-color: transparent;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-inner {
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.sidebar-animate .sidebar-inner {
|
||||
animation: sidebar-inner-draw 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
animation-delay: 6.1s;
|
||||
}
|
||||
|
||||
@keyframes sidebar-inner-draw {
|
||||
0% {
|
||||
opacity: 0;
|
||||
clip-path: inset(0 100% 0 0);
|
||||
}
|
||||
20% { opacity: 1; }
|
||||
100% {
|
||||
opacity: 1;
|
||||
clip-path: inset(0 0 0 0);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-nav-item {
|
||||
opacity: 0;
|
||||
transform: translateX(-12px);
|
||||
}
|
||||
|
||||
.sidebar-animate .sidebar-nav-item {
|
||||
animation: sidebar-nav-item-in 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
animation-delay: calc(6.3s + var(--nav-stagger, 0) * 0.06s);
|
||||
}
|
||||
|
||||
@keyframes sidebar-nav-item-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-12px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-controller {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.sidebar-animate .sidebar-controller {
|
||||
animation: sidebar-fade-in 0.4s ease-out forwards;
|
||||
animation-delay: 6.9s;
|
||||
}
|
||||
|
||||
.sidebar-logout-btn {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.sidebar-animate .sidebar-logout-btn {
|
||||
animation: sidebar-logout-pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
||||
animation-delay: 7.1s;
|
||||
}
|
||||
|
||||
@keyframes sidebar-fade-in {
|
||||
0% { opacity: 0; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes sidebar-logout-pop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-logo {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
|
||||
.sidebar-animate .sidebar-logo {
|
||||
animation: sidebar-logo-in 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
animation-delay: 6.15s;
|
||||
}
|
||||
|
||||
@keyframes sidebar-logo-in {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* When not animating, show everything */
|
||||
aside:not(.sidebar-animate) .sidebar-shell {
|
||||
border-color: rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
aside:not(.sidebar-animate) .sidebar-inner,
|
||||
aside:not(.sidebar-animate) .sidebar-logo,
|
||||
aside:not(.sidebar-animate) .sidebar-nav-item,
|
||||
aside:not(.sidebar-animate) .sidebar-controller,
|
||||
aside:not(.sidebar-animate) .sidebar-logout-btn {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
animation: none;
|
||||
clip-path: none;
|
||||
}
|
||||
|
||||
/* Smooth easeInOut - no overshoot, refined deceleration */
|
||||
|
||||
.glass-throw-main {
|
||||
animation: glass-throw-main 1.6s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.15s forwards;
|
||||
opacity: 0;
|
||||
transform: translateX(20%) scale(0.2);
|
||||
filter: blur(14px);
|
||||
}
|
||||
|
||||
.glass-throw-content {
|
||||
animation: glass-throw-content 1.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.22s forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(12%) scale(0.25);
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.glass-throw-mobile-tabs {
|
||||
animation: glass-throw-top 1.3s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.08s forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(-90%) scale(0.28);
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.glass-throw-mobile-tabs-2 {
|
||||
animation: glass-throw-top 1.35s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.18s forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(-90%) scale(0.28);
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.glass-throw-tabbar {
|
||||
animation: glass-throw-bottom 1.4s cubic-bezier(0.25, 0.46, 0.45, 0.94) 0.2s forwards;
|
||||
opacity: 0;
|
||||
transform: translateY(85%) scale(0.25);
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
/* Motion blur peaks mid-transition, smooth settle */
|
||||
@keyframes glass-throw-sidebar {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(-100%) scale(0.25);
|
||||
filter: blur(12px);
|
||||
}
|
||||
45% {
|
||||
opacity: 0.9;
|
||||
transform: translateX(-15%) scale(0.85);
|
||||
filter: blur(8px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0) scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glass-throw-main {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateX(20%) scale(0.2);
|
||||
filter: blur(14px);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.85;
|
||||
transform: translateX(0) scale(0.9);
|
||||
filter: blur(6px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateX(0) scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glass-throw-content {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(12%) scale(0.25);
|
||||
filter: blur(10px);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.9;
|
||||
transform: translateY(0) scale(0.9);
|
||||
filter: blur(4px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glass-throw-top {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-90%) scale(0.28);
|
||||
filter: blur(10px);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.9;
|
||||
transform: translateY(0) scale(0.95);
|
||||
filter: blur(4px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes glass-throw-bottom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(85%) scale(0.25);
|
||||
filter: blur(10px);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.9;
|
||||
transform: translateY(0) scale(0.95);
|
||||
filter: blur(4px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
filter: blur(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Oomph accent - subtle 2advanced-style flash synced with boot thud */
|
||||
.oomph-flash {
|
||||
background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.08) 0%, transparent 65%);
|
||||
animation: oomph-flash 0.7s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
|
||||
}
|
||||
|
||||
@keyframes oomph-flash {
|
||||
0% { opacity: 0; }
|
||||
25% { opacity: 0.9; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
|
||||
/* Reveal flashes - enthralling entrance during zoom */
|
||||
.reveal-flash-glitch {
|
||||
background: radial-gradient(ellipse at center, rgba(255, 255, 255, 0.12) 0%, transparent 70%);
|
||||
animation: reveal-flash-sequence 2.8s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes reveal-flash-sequence {
|
||||
0% { opacity: 0; }
|
||||
12% { opacity: 0.6; }
|
||||
18% { opacity: 0; }
|
||||
42% { opacity: 0.4; }
|
||||
48% { opacity: 0; }
|
||||
70% { opacity: 0.35; }
|
||||
78% { opacity: 0; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
|
||||
/* Wrapper to contain perspective without clipping */
|
||||
.perspective-container-wrapper {
|
||||
position: relative;
|
||||
@@ -817,10 +1214,10 @@ function getTransitionName(currentRoute: any) {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
/* Forward transition: Current screen pulls forward, new screen emerges from back */
|
||||
/* Forward transition: 2advanced fluid depth */
|
||||
.depth-forward-enter-active.view-wrapper,
|
||||
.depth-forward-leave-active.view-wrapper {
|
||||
transition: all 0.45s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
transition: all 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.depth-forward-enter-from.view-wrapper {
|
||||
@@ -847,10 +1244,10 @@ function getTransitionName(currentRoute: any) {
|
||||
filter: blur(8px);
|
||||
}
|
||||
|
||||
/* Back transition: Current screen pulls back, previous screen comes forward */
|
||||
/* Back transition: 2advanced fluid depth */
|
||||
.depth-back-enter-active.view-wrapper,
|
||||
.depth-back-leave-active.view-wrapper {
|
||||
transition: all 0.45s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
transition: all 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
||||
}
|
||||
|
||||
.depth-back-enter-from.view-wrapper {
|
||||
@@ -877,22 +1274,22 @@ function getTransitionName(currentRoute: any) {
|
||||
filter: blur(4px);
|
||||
}
|
||||
|
||||
/* Enhanced effect with rotation for more console-like feel */
|
||||
/* Subtle 3D tilt - 2advanced layered depth */
|
||||
@media (min-width: 768px) {
|
||||
.depth-forward-enter-from.view-wrapper {
|
||||
transform: translateZ(-800px) scale(0.75) rotateX(8deg);
|
||||
transform: translateZ(-800px) scale(0.75) rotateX(5deg);
|
||||
}
|
||||
|
||||
.depth-forward-leave-to.view-wrapper {
|
||||
transform: translateZ(400px) scale(1.2) rotateX(-5deg);
|
||||
transform: translateZ(400px) scale(1.2) rotateX(-4deg);
|
||||
}
|
||||
|
||||
.depth-back-enter-from.view-wrapper {
|
||||
transform: translateZ(400px) scale(1.2) rotateX(-5deg);
|
||||
transform: translateZ(400px) scale(1.2) rotateX(-4deg);
|
||||
}
|
||||
|
||||
.depth-back-leave-to.view-wrapper {
|
||||
transform: translateZ(-800px) scale(0.75) rotateX(8deg);
|
||||
transform: translateZ(-800px) scale(0.75) rotateX(5deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1016,46 +1413,52 @@ function getTransitionName(currentRoute: any) {
|
||||
transform: translateY(30vh);
|
||||
}
|
||||
|
||||
/* Background 3D container */
|
||||
.bg-perspective-container {
|
||||
/* Background 3D container - full width, black fill during zoom to avoid letterboxing */
|
||||
.dashboard-view .bg-perspective-container {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: -10;
|
||||
perspective: 1000px;
|
||||
perspective-origin: 50% 50%;
|
||||
overflow: hidden;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
/* Background layers with 3D transitions */
|
||||
.bg-layer {
|
||||
/* Background layers with 3D transitions - full width like login */
|
||||
.dashboard-view .bg-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-size: cover !important;
|
||||
background-position: center center !important;
|
||||
background-repeat: no-repeat !important;
|
||||
transition: all 0.45s cubic-bezier(0.68, -0.55, 0.265, 1.55);
|
||||
transform-style: preserve-3d;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
/* Default state - bg-4 visible, bg-3 hidden back */
|
||||
.bg-layer:first-child {
|
||||
.dashboard-view .bg-layer:first-of-type {
|
||||
opacity: 1;
|
||||
transform: translateZ(0) scale(1);
|
||||
}
|
||||
|
||||
.bg-layer:nth-child(2) {
|
||||
.dashboard-view .bg-layer:nth-of-type(2) {
|
||||
opacity: 0;
|
||||
transform: translateZ(-200px) scale(0.9) rotateY(-15deg);
|
||||
}
|
||||
|
||||
/* Transitioning out - current background moves away with zoom */
|
||||
.bg-layer.bg-transitioning-out {
|
||||
.dashboard-view .bg-layer.bg-transitioning-out {
|
||||
opacity: 0;
|
||||
transform: translateZ(200px) scale(1.15) rotateY(15deg) !important;
|
||||
}
|
||||
|
||||
/* Transitioning in - new background comes forward with zoom */
|
||||
.bg-layer.bg-transitioning-in {
|
||||
.dashboard-view .bg-layer.bg-transitioning-in {
|
||||
opacity: 1;
|
||||
transform: translateZ(0) scale(1.05) rotateY(0deg) !important;
|
||||
}
|
||||
@@ -1166,5 +1569,84 @@ function getTransitionName(currentRoute: any) {
|
||||
95% { opacity: 0.45; }
|
||||
100% { opacity: 0; transform: translateX(120%); }
|
||||
}
|
||||
|
||||
/* Full width background - same as login */
|
||||
.dashboard-view .bg-fullwidth {
|
||||
min-width: 100%;
|
||||
width: 100%;
|
||||
background-size: cover !important;
|
||||
background-position: center center !important;
|
||||
}
|
||||
|
||||
/* Continuous glitch overlays - same as login, every 5s */
|
||||
.dashboard-glitch-layer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dashboard-glitch-1 {
|
||||
mix-blend-mode: screen;
|
||||
filter: hue-rotate(22deg) saturate(1.35);
|
||||
animation: dashboard-glitch-shift 5s steps(10, end) infinite;
|
||||
background-size: cover !important;
|
||||
background-position: center center !important;
|
||||
}
|
||||
|
||||
.dashboard-glitch-2 {
|
||||
mix-blend-mode: screen;
|
||||
filter: hue-rotate(-30deg) saturate(1.45);
|
||||
animation: dashboard-glitch-shift-2 5s steps(9, end) infinite;
|
||||
background-size: cover !important;
|
||||
background-position: center center !important;
|
||||
}
|
||||
|
||||
.dashboard-glitch-scan {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 6;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255,255,255,0.16), rgba(0,0,0,0) 60%),
|
||||
repeating-linear-gradient(180deg, rgba(255,255,255,0.05) 0 2px, rgba(0,0,0,0) 2px 4px),
|
||||
radial-gradient(ellipse at center, rgba(0,0,0,0) 40%, rgba(0,0,0,0.35) 100%);
|
||||
opacity: 0;
|
||||
animation: dashboard-glitch-scan 5s ease-out infinite;
|
||||
}
|
||||
|
||||
@keyframes dashboard-glitch-shift {
|
||||
0%, 82% { transform: translate(0,0); clip-path: inset(0% 0 0 0); opacity: 0; }
|
||||
82.1% { opacity: 0.28; }
|
||||
84% { transform: translate(6px,-2px); clip-path: inset(8% 0 70% 0); }
|
||||
86% { transform: translate(-5px,2px); clip-path: inset(42% 0 40% 0); }
|
||||
88% { transform: translate(3px,0); clip-path: inset(68% 0 10% 0); }
|
||||
91% { transform: translate(-4px,3px); clip-path: inset(18% 0 60% 0); }
|
||||
93% { transform: translate(5px,-3px); clip-path: inset(55% 0 20% 0); }
|
||||
95% { transform: translate(-3px,1px); clip-path: inset(10% 0 80% 0); }
|
||||
100% { transform: translate(0,0); clip-path: inset(0% 0 0 0); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes dashboard-glitch-shift-2 {
|
||||
0%, 82% { transform: translate(0,0); clip-path: inset(0% 0 0 0); opacity: 0; }
|
||||
82.1% { opacity: 0.24; }
|
||||
84% { transform: translate(-6px,2px); clip-path: inset(12% 0 65% 0); }
|
||||
86% { transform: translate(5px,-1px) skewX(0.6deg); clip-path: inset(36% 0 42% 0); }
|
||||
89% { transform: translate(-3px,2px); clip-path: inset(72% 0 8% 0); }
|
||||
92% { transform: translate(4px,-3px); clip-path: inset(22% 0 58% 0); }
|
||||
95% { transform: translate(-4px,1px); clip-path: inset(50% 0 26% 0); }
|
||||
100% { transform: translate(0,0); clip-path: inset(0% 0 0 0); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes dashboard-glitch-scan {
|
||||
0%, 82% { opacity: 0; transform: translateY(-20%); }
|
||||
84% { opacity: 0.5; }
|
||||
90% { opacity: 0.35; }
|
||||
100% { opacity: 0; transform: translateY(115%); }
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user