feat: enhance home page with animated particles, spotlight effects, and interactive elements for improved visual engagement and user experience

This commit is contained in:
2025-12-04 02:20:25 -05:00
parent d5528772b0
commit 6d4cf8836c

View File

@@ -126,6 +126,54 @@
pointer-events: none;
}
/* Animated floating particles */
.hero-particles {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
pointer-events: none;
}
.hero-particle {
position: absolute;
width: 10px;
height: 10px;
background: rgba(255, 255, 255, 0.15);
border-radius: 50%;
animation: float-particle linear infinite;
}
.hero-particle:nth-child(1) { left: 10%; animation-duration: 20s; animation-delay: 0s; width: 8px; height: 8px; }
.hero-particle:nth-child(2) { left: 20%; animation-duration: 25s; animation-delay: -5s; width: 12px; height: 12px; }
.hero-particle:nth-child(3) { left: 30%; animation-duration: 18s; animation-delay: -2s; width: 6px; height: 6px; }
.hero-particle:nth-child(4) { left: 40%; animation-duration: 22s; animation-delay: -7s; width: 10px; height: 10px; }
.hero-particle:nth-child(5) { left: 50%; animation-duration: 28s; animation-delay: -3s; width: 14px; height: 14px; }
.hero-particle:nth-child(6) { left: 60%; animation-duration: 19s; animation-delay: -9s; width: 8px; height: 8px; }
.hero-particle:nth-child(7) { left: 70%; animation-duration: 24s; animation-delay: -4s; width: 11px; height: 11px; }
.hero-particle:nth-child(8) { left: 80%; animation-duration: 21s; animation-delay: -6s; width: 7px; height: 7px; }
.hero-particle:nth-child(9) { left: 90%; animation-duration: 26s; animation-delay: -8s; width: 9px; height: 9px; }
.hero-particle:nth-child(10) { left: 15%; animation-duration: 23s; animation-delay: -1s; width: 13px; height: 13px; }
@keyframes float-particle {
0% {
transform: translateY(100vh) rotate(0deg);
opacity: 0;
}
10% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
transform: translateY(-100vh) rotate(720deg);
opacity: 0;
}
}
.hero-content {
position: relative;
z-index: 1;
@@ -180,6 +228,30 @@
animation: title-fade-in 0.8s ease-out;
}
.hero-title-highlight {
display: inline-block;
position: relative;
}
.hero-title-highlight::after {
content: '';
position: absolute;
bottom: 0.1em;
left: 0;
right: 0;
height: 0.15em;
background: linear-gradient(90deg, rgba(255,255,255,0.8), rgba(255,255,255,0.4));
border-radius: 2px;
transform: scaleX(0);
animation: underline-reveal 0.8s ease-out 0.5s forwards;
}
@keyframes underline-reveal {
to {
transform: scaleX(1);
}
}
@keyframes title-fade-in {
from {
opacity: 0;
@@ -358,6 +430,94 @@
.status-value {
font-size: 1.25rem;
font-weight: 700;
display: flex;
align-items: center;
gap: 0.5rem;
}
.status-live-dot {
width: 8px;
height: 8px;
background: #10b981;
border-radius: 50%;
animation: live-pulse 2s ease-in-out infinite;
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);
}
@keyframes live-pulse {
0%, 100% {
box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.7);
}
50% {
box-shadow: 0 0 0 8px rgba(16, 185, 129, 0);
}
}
.uptime-bar {
width: 40px;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
position: relative;
overflow: hidden;
}
.uptime-bar::after {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 99.9%;
background: linear-gradient(90deg, #10b981, #34d399);
border-radius: 2px;
animation: uptime-fill 2s ease-out forwards;
}
@keyframes uptime-fill {
from { width: 0; }
to { width: 99.9%; }
}
.support-indicator {
width: 8px;
height: 8px;
background: #06b6d4;
border-radius: 50%;
position: relative;
}
.support-indicator::before,
.support-indicator::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
border: 1px solid #06b6d4;
animation: support-wave 2s ease-out infinite;
}
.support-indicator::before {
animation-delay: 0s;
}
.support-indicator::after {
animation-delay: 1s;
}
@keyframes support-wave {
0% {
width: 8px;
height: 8px;
opacity: 1;
}
100% {
width: 24px;
height: 24px;
opacity: 0;
}
}
/* Features Grid */
@@ -603,6 +763,92 @@
color: var(--color-text-base);
}
/* Mini Network Chart */
.mini-chart {
display: flex;
align-items: flex-end;
gap: 2px;
height: 24px;
margin-top: var(--spacing-sm);
}
.mini-chart-bar {
flex: 1;
background: linear-gradient(to top, var(--color-primary), var(--color-primary-light));
border-radius: 2px 2px 0 0;
transition: height 0.3s ease;
min-height: 4px;
animation: chart-grow 0.8s ease-out forwards;
transform-origin: bottom;
}
@keyframes chart-grow {
from {
transform: scaleY(0);
}
to {
transform: scaleY(1);
}
}
.mini-chart-bar:nth-child(1) { animation-delay: 0.1s; }
.mini-chart-bar:nth-child(2) { animation-delay: 0.15s; }
.mini-chart-bar:nth-child(3) { animation-delay: 0.2s; }
.mini-chart-bar:nth-child(4) { animation-delay: 0.25s; }
.mini-chart-bar:nth-child(5) { animation-delay: 0.3s; }
.mini-chart-bar:nth-child(6) { animation-delay: 0.35s; }
.mini-chart-bar:nth-child(7) { animation-delay: 0.4s; }
.mini-chart-bar:nth-child(8) { animation-delay: 0.45s; }
/* Activity Indicator */
.activity-dots {
display: flex;
gap: 4px;
align-items: center;
}
.activity-dot {
width: 6px;
height: 6px;
background: var(--color-success);
border-radius: 50%;
animation: activity-blink 1.5s ease-in-out infinite;
}
.activity-dot:nth-child(1) { animation-delay: 0s; }
.activity-dot:nth-child(2) { animation-delay: 0.3s; }
.activity-dot:nth-child(3) { animation-delay: 0.6s; }
@keyframes activity-blink {
0%, 100% { opacity: 0.3; transform: scale(0.8); }
50% { opacity: 1; transform: scale(1); }
}
/* Connection Line Animation */
.connection-line {
position: relative;
height: 2px;
background: var(--color-border);
margin: var(--spacing-sm) 0;
overflow: hidden;
}
.connection-line::after {
content: '';
position: absolute;
top: 0;
left: -30%;
width: 30%;
height: 100%;
background: linear-gradient(90deg, transparent, var(--color-primary), transparent);
animation: connection-flow 2s linear infinite;
}
@keyframes connection-flow {
0% { left: -30%; }
100% { left: 100%; }
}
.progress-bar {
height: 10px;
background: var(--color-bg-tertiary);
@@ -1528,6 +1774,121 @@
height: 44px;
}
}
/* Tooltip System */
[data-tooltip] {
position: relative;
cursor: help;
}
[data-tooltip]::before,
[data-tooltip]::after {
position: absolute;
opacity: 0;
visibility: hidden;
transition: var(--transition-smooth);
z-index: 100;
pointer-events: none;
}
[data-tooltip]::before {
content: attr(data-tooltip);
bottom: calc(100% + 10px);
left: 50%;
transform: translateX(-50%) translateY(10px);
padding: 0.5rem 0.75rem;
background: var(--color-text-base);
color: var(--color-bg-base);
font-size: 0.8125rem;
font-weight: 500;
white-space: nowrap;
border-radius: var(--radius-sm);
box-shadow: var(--shadow-lg);
}
[data-tooltip]::after {
content: '';
bottom: calc(100% + 4px);
left: 50%;
transform: translateX(-50%);
border: 6px solid transparent;
border-top-color: var(--color-text-base);
}
[data-tooltip]:hover::before,
[data-tooltip]:hover::after {
opacity: 1;
visibility: visible;
}
[data-tooltip]:hover::before {
transform: translateX(-50%) translateY(0);
}
/* Glow Ring Animation for Important Elements */
.glow-ring {
position: relative;
}
.glow-ring::before {
content: '';
position: absolute;
inset: -3px;
background: linear-gradient(45deg, var(--color-primary), var(--color-primary-light), var(--color-info), var(--color-primary));
background-size: 400% 400%;
border-radius: inherit;
opacity: 0;
z-index: -1;
animation: glow-rotate 3s ease infinite;
transition: opacity 0.3s ease;
}
.glow-ring:hover::before {
opacity: 1;
}
@keyframes glow-rotate {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* Typing Cursor Effect */
.typing-cursor::after {
content: '|';
animation: blink-cursor 1s step-end infinite;
color: white;
margin-left: 2px;
}
@keyframes blink-cursor {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
/* Spotlight Effect on Cards */
.spotlight-card {
position: relative;
overflow: hidden;
}
.spotlight-card::before {
content: '';
position: absolute;
top: var(--y, 50%);
left: var(--x, 50%);
width: 200px;
height: 200px;
background: radial-gradient(circle, rgba(59, 130, 246, 0.15) 0%, transparent 70%);
transform: translate(-50%, -50%);
pointer-events: none;
opacity: 0;
transition: opacity 0.3s ease;
}
.spotlight-card:hover::before {
opacity: 1;
}
</style>
<script>
@@ -1896,15 +2257,178 @@
sectionObserver.observe(section);
});
// Spotlight effect for cards
document.querySelectorAll('.feature-card, .node-card, .stat-card').forEach(card => {
card.classList.add('spotlight-card');
card.addEventListener('mousemove', function(e) {
const rect = this.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
this.style.setProperty('--x', x + 'px');
this.style.setProperty('--y', y + 'px');
});
});
// Add glow ring effect to important buttons
document.querySelectorAll('.hero-cta-button-primary, .plan-cta').forEach(btn => {
btn.classList.add('glow-ring');
});
// Magnetic button effect
document.querySelectorAll('.hero-cta-button, .plan-cta').forEach(button => {
button.addEventListener('mousemove', function(e) {
const rect = this.getBoundingClientRect();
const x = e.clientX - rect.left - rect.width / 2;
const y = e.clientY - rect.top - rect.height / 2;
this.style.transform = `translate(${x * 0.15}px, ${y * 0.15}px)`;
});
button.addEventListener('mouseleave', function() {
this.style.transform = '';
});
});
// Text scramble effect for stat values
class TextScramble {
constructor(el) {
this.el = el;
this.chars = '!<>-_\\/[]{}—=+*^?#________';
this.update = this.update.bind(this);
}
setText(newText) {
const oldText = this.el.innerText;
const length = Math.max(oldText.length, newText.length);
const promise = new Promise((resolve) => this.resolve = resolve);
this.queue = [];
for (let i = 0; i < length; i++) {
const from = oldText[i] || '';
const to = newText[i] || '';
const start = Math.floor(Math.random() * 40);
const end = start + Math.floor(Math.random() * 40);
this.queue.push({ from, to, start, end });
}
cancelAnimationFrame(this.frameRequest);
this.frame = 0;
this.update();
return promise;
}
update() {
let output = '';
let complete = 0;
for (let i = 0, n = this.queue.length; i < n; i++) {
let { from, to, start, end, char } = this.queue[i];
if (this.frame >= end) {
complete++;
output += to;
} else if (this.frame >= start) {
if (!char || Math.random() < 0.28) {
char = this.chars[Math.floor(Math.random() * this.chars.length)];
this.queue[i].char = char;
}
output += `<span style="color: var(--color-primary-light)">${char}</span>`;
} else {
output += from;
}
}
this.el.innerHTML = output;
if (complete === this.queue.length) {
this.resolve();
} else {
this.frameRequest = requestAnimationFrame(this.update);
this.frame++;
}
}
}
// Smooth number animation with easing
function animateValue(element, start, end, duration, suffix = '') {
let startTimestamp = null;
const step = (timestamp) => {
if (!startTimestamp) startTimestamp = timestamp;
const progress = Math.min((timestamp - startTimestamp) / duration, 1);
const easeProgress = 1 - Math.pow(1 - progress, 4); // easeOutQuart
const current = Math.floor(easeProgress * (end - start) + start);
element.textContent = current.toLocaleString() + suffix;
if (progress < 1) {
window.requestAnimationFrame(step);
}
};
window.requestAnimationFrame(step);
}
// Dynamic theme detection and color adjustment
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');
function updateTheme() {
document.documentElement.classList.toggle('dark-mode', prefersDark.matches);
}
prefersDark.addEventListener('change', updateTheme);
updateTheme();
// Add keyboard navigation
let focusableElements = document.querySelectorAll('a, button, input, [tabindex]:not([tabindex="-1"])');
document.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
document.body.classList.add('keyboard-navigation');
}
});
document.addEventListener('mousedown', () => {
document.body.classList.remove('keyboard-navigation');
});
// Performance: Use passive event listeners where possible
document.addEventListener('touchstart', () => {}, { passive: true });
document.addEventListener('touchmove', () => {}, { passive: true });
// Add intersection ratio-based animations
const ratioObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
const ratio = entry.intersectionRatio;
entry.target.style.setProperty('--visibility', ratio);
});
}, {
threshold: Array.from({ length: 20 }, (_, i) => i / 20)
});
document.querySelectorAll('.feature-card, .stat-card').forEach(el => {
ratioObserver.observe(el);
});
console.log('✨ Homepage interactive features loaded successfully');
console.log('📊 Total animations:', document.querySelectorAll('[class*="fade-in"]').length);
console.log('🎯 Interactive elements:', document.querySelectorAll('.feature-card, .node-card, .stat-card, .plan-card').length);
console.log('🎨 Spotlight cards:', document.querySelectorAll('.spotlight-card').length);
console.log('💫 Glow ring elements:', document.querySelectorAll('.glow-ring').length);
})();
</script>
<div id="home">
<!-- Hero Section -->
<header class="hero">
<!-- Animated Particles -->
<div class="hero-particles">
<div class="hero-particle"></div>
<div class="hero-particle"></div>
<div class="hero-particle"></div>
<div class="hero-particle"></div>
<div class="hero-particle"></div>
<div class="hero-particle"></div>
<div class="hero-particle"></div>
<div class="hero-particle"></div>
<div class="hero-particle"></div>
<div class="hero-particle"></div>
</div>
<div class="home-container">
<div class="hero-content fade-in">
<div class="hero-badge">
@@ -1915,7 +2439,7 @@
</div>
<h1 class="hero-title">
Experience Powerful Hosting
Experience <span class="hero-title-highlight">Powerful</span> Hosting
</h1>
<p class="hero-subtitle">
@@ -1940,7 +2464,7 @@
</div>
<div class="hero-status-widget fade-in-delay-1">
<div class="status-item">
<div class="status-item" data-tooltip="All systems operating normally">
<div class="status-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<circle cx="12" cy="12" r="10"/>
@@ -1949,11 +2473,14 @@
</div>
<div class="status-text">
<div class="status-label">System Status</div>
<div class="status-value">All Operational</div>
<div class="status-value">
<span class="status-live-dot"></span>
All Operational
</div>
</div>
</div>
<div class="status-item">
<div class="status-item" data-tooltip="Physical servers in cluster">
<div class="status-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<rect x="2" y="3" width="20" height="14" rx="2"/>
@@ -1963,11 +2490,11 @@
</div>
<div class="status-text">
<div class="status-label">Active Nodes</div>
<div class="status-value">{{ \App\Models\Server::count() }}</div>
<div class="status-value stat-value" data-count="{{ \App\Models\Server::count() }}">{{ \App\Models\Server::count() }}</div>
</div>
</div>
<div class="status-item">
<div class="status-item" data-tooltip="30-day rolling average">
<div class="status-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
@@ -1975,11 +2502,14 @@
</div>
<div class="status-text">
<div class="status-label">Uptime</div>
<div class="status-value">99.9%</div>
<div class="status-value">
<span class="uptime-bar"></span>
99.9%
</div>
</div>
</div>
<div class="status-item">
<div class="status-item" data-tooltip="Expert assistance around the clock">
<div class="status-icon">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
@@ -1990,7 +2520,10 @@
</div>
<div class="status-text">
<div class="status-label">Support</div>
<div class="status-value">24/7</div>
<div class="status-value">
<span class="support-indicator"></span>
24/7
</div>
</div>
</div>
</div>