From 14031f73a342dc6b3549bb92bc5175461b1511b0 Mon Sep 17 00:00:00 2001 From: Kerboul Date: Mon, 9 Mar 2026 07:44:27 -0700 Subject: [PATCH] Initial commit --- app.js | 59 +++++++++ icon.svg | 11 ++ index.html | 89 +++++++++++++ manifest.json | 17 +++ package.json | 13 ++ style.css | 352 ++++++++++++++++++++++++++++++++++++++++++++++++++ sw.js | 48 +++++++ 7 files changed, 589 insertions(+) create mode 100644 app.js create mode 100644 icon.svg create mode 100644 index.html create mode 100644 manifest.json create mode 100644 package.json create mode 100644 style.css create mode 100644 sw.js diff --git a/app.js b/app.js new file mode 100644 index 0000000..8417fee --- /dev/null +++ b/app.js @@ -0,0 +1,59 @@ +// Register Service Worker for PWA +if ('serviceWorker' in navigator) { + window.addEventListener('load', () => { + navigator.serviceWorker.register('./sw.js') + .then((registration) => { + console.log('SW registered: ', registration); + }) + .catch((registrationError) => { + console.log('SW registration failed: ', registrationError); + }); + }); +} + +// PWA Install Prompt Logic +let deferredPrompt; +const installBtn = document.getElementById('installBtn'); + +window.addEventListener('beforeinstallprompt', (e) => { + // Prevent Chrome 67 and earlier from automatically showing the prompt + e.preventDefault(); + // Stash the event so it can be triggered later. + deferredPrompt = e; + // Update UI to notify the user they can add to home screen + if(installBtn) { + installBtn.hidden = false; + } +}); + +if(installBtn) { + installBtn.addEventListener('click', async () => { + if (deferredPrompt !== null) { + // Show the install prompt + deferredPrompt.prompt(); + // Wait for the user to respond to the prompt + const { outcome } = await deferredPrompt.userChoice; + if (outcome === 'accepted') { + console.log('User accepted the install prompt'); + } else { + console.log('User dismissed the install prompt'); + } + // We've used the prompt, and can't use it again, throw it away + deferredPrompt = null; + installBtn.hidden = true; + } + }); +} + +// Interactive element animation +const pulseElement = document.getElementById('pulseElement'); +if(pulseElement) { + pulseElement.addEventListener('click', () => { + pulseElement.style.transform = 'scale(0.9)'; + setTimeout(() => { + pulseElement.style.transform = 'scale(1)'; + const randomColor = Math.floor(Math.random()*16777215).toString(16); + pulseElement.style.background = `#${randomColor}`; + }, 150); + }); +} diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..f355c7c --- /dev/null +++ b/icon.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..ae87171 --- /dev/null +++ b/index.html @@ -0,0 +1,89 @@ + + + + + + + Amusement - Stylish PWA Demo + + + + + + + + + + + + + + + + +
+
+
+
+
+ +
+
+ + +
+ +
+
+
Next-Gen Experience
+

Stunning visuals,
Zero Compromises.

+

Experience a breathtaking Progressive Web App designed with modern aesthetics, glassmorphism, and seamless interactions.

+ +
+ + +
+
+ +
+
+
+
+ +
+
+
+
+
+
+
+ 99% + Performance +
+
+ PWA + Native Feel +
+
+
+ Tap to Interact +
+
+
+
+
+
+ + + + diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..b02da6f --- /dev/null +++ b/manifest.json @@ -0,0 +1,17 @@ +{ + "name": "Amusement Dashboard", + "short_name": "Amusement", + "description": "A stunning visual demo and Progressive Web App template", + "start_url": "/index.html", + "display": "standalone", + "background_color": "#0f172a", + "theme_color": "#0f172a", + "icons": [ + { + "src": "./icon.svg", + "sizes": "192x192 512x512", + "type": "image/svg+xml", + "purpose": "any maskable" + } + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..87e3be2 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "amusement-pwa-template", + "version": "1.0.0", + "description": "Stylish PWA Demo", + "scripts": { + "dev": "vite", + "build": "vite build", + "serve": "vite preview" + }, + "devDependencies": { + "vite": "^5.0.0" + } +} diff --git a/style.css b/style.css new file mode 100644 index 0000000..6cf19bd --- /dev/null +++ b/style.css @@ -0,0 +1,352 @@ +:root { + --bg-dark: #0f172a; + --text-main: #f8fafc; + --text-muted: #94a3b8; + --primary-gradient: linear-gradient(135deg, #6366f1 0%, #a855f7 50%, #ec4899 100%); + --glass-bg: rgba(255, 255, 255, 0.03); + --glass-border: rgba(255, 255, 255, 0.08); + --glass-highlight: rgba(255, 255, 255, 0.15); + --font-sans: 'Inter', sans-serif; + --font-display: 'Outfit', sans-serif; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: var(--font-sans); + background-color: var(--bg-dark); + color: var(--text-main); + min-height: 100vh; + overflow-x: hidden; + line-height: 1.5; +} + +/* Background Effects */ +.background-effects { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + z-index: -1; + overflow: hidden; +} + +.blob { + position: absolute; + filter: blur(80px); + border-radius: 50%; + opacity: 0.6; + animation: float 20s infinite ease-in-out; +} + +.blob-1 { + top: -10%; left: -10%; + width: 50vw; height: 50vw; + background: radial-gradient(circle, #6366f1, transparent 70%); + animation-delay: 0s; +} + +.blob-2 { + bottom: -20%; right: -10%; + width: 60vw; height: 60vw; + background: radial-gradient(circle, #ec4899, transparent 70%); + animation-delay: -5s; +} + +.blob-3 { + top: 30%; left: 40%; + width: 40vw; height: 40vw; + background: radial-gradient(circle, #a855f7, transparent 70%); + animation-delay: -10s; + opacity: 0.4; +} + +@keyframes float { + 0%, 100% { transform: translate(0, 0) scale(1); } + 33% { transform: translate(5%, 10%) scale(1.1); } + 66% { transform: translate(-5%, 5%) scale(0.9); } +} + +/* Layout */ +.app-container { + max-width: 1400px; + margin: 0 auto; + padding: 2rem; + display: flex; + flex-direction: column; + min-height: 100vh; +} + +/* Header Glassmorphism */ +.glass-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1rem 2rem; + background: var(--glass-bg); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid var(--glass-border); + border-radius: 24px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); +} + +.logo { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.logo h1 { + font-family: var(--font-display); + font-size: 1.5rem; + font-weight: 800; + letter-spacing: -0.5px; +} + +.logo-icon { + font-size: 1.5rem; +} + +nav { + display: flex; + gap: 1rem; +} + +.nav-btn { + background: transparent; + border: none; + color: var(--text-muted); + font-family: var(--font-sans); + font-weight: 500; + font-size: 1rem; + cursor: pointer; + padding: 0.5rem 1rem; + border-radius: 12px; + transition: all 0.3s ease; +} + +.nav-btn:hover { + color: var(--text-main); + background: var(--glass-highlight); +} + +.nav-btn.active { + color: var(--text-main); +} + +.nav-btn.primary { + background: var(--text-main); + color: var(--bg-dark); + font-weight: 600; +} +.nav-btn.primary:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(255, 255, 255, 0.2); +} + +/* Hero Section */ +.hero-section { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + align-items: center; + gap: 4rem; + margin-top: 4rem; +} + +.badge { + display: inline-block; + padding: 0.5rem 1rem; + background: rgba(99, 102, 241, 0.1); + border: 1px solid rgba(99, 102, 241, 0.3); + color: #818cf8; + border-radius: 100px; + font-size: 0.875rem; + font-weight: 600; + margin-bottom: 1.5rem; + letter-spacing: 0.5px; + text-transform: uppercase; +} + +.display-text { + font-family: var(--font-display); + font-size: 4.5rem; + line-height: 1.1; + font-weight: 800; + margin-bottom: 1.5rem; + letter-spacing: -1px; +} + +.gradient-text { + background: var(--primary-gradient); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.subtitle { + font-size: 1.25rem; + color: var(--text-muted); + max-width: 500px; + margin-bottom: 2.5rem; +} + +.action-group { + display: flex; + gap: 1rem; +} + +.btn { + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; + padding: 1rem 2rem; + border-radius: 16px; + font-family: var(--font-sans); + font-weight: 600; + font-size: 1.125rem; + cursor: pointer; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + border: none; +} + +.btn-primary { + background: var(--primary-gradient); + color: white; + box-shadow: 0 10px 30px rgba(99, 102, 241, 0.4); +} + +.btn-primary:hover { + transform: translateY(-3px) scale(1.02); + box-shadow: 0 15px 40px rgba(99, 102, 241, 0.5); +} + +.btn-secondary { + background: var(--glass-bg); + border: 1px solid var(--glass-border); + color: var(--text-main); + backdrop-filter: blur(10px); +} + +.btn-secondary:hover { + background: var(--glass-highlight); + transform: translateY(-3px); +} + +/* Glass Card right side */ +.glass-card { + background: var(--glass-bg); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid var(--glass-border); + border-radius: 32px; + padding: 2rem; + box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5); + position: relative; + overflow: hidden; + transform: perspective(1000px) rotateY(-5deg) rotateX(5deg); + transition: transform 0.5s ease; +} + +.glass-card:hover { + transform: perspective(1000px) rotateY(0deg) rotateX(0deg); +} + +.glass-card::before { + content: ''; + position: absolute; + top: 0; left: -100%; + width: 50%; height: 100%; + background: linear-gradient(to right, transparent, rgba(255,255,255,0.1), transparent); + transform: skewX(-20deg); + animation: shine 6s infinite; +} + +@keyframes shine { + 0% { left: -100%; } + 20% { left: 200%; } + 100% { left: 200%; } +} + +.card-header .dots { + display: flex; + gap: 8px; + margin-bottom: 2rem; +} + +.dots span { + width: 12px; height: 12px; + border-radius: 50%; +} +.dots span:nth-child(1) { background: #ef4444; } +.dots span:nth-child(2) { background: #eab308; } +.dots span:nth-child(3) { background: #22c55e; } + +.skeleton-line { + height: 24px; + background: rgba(255, 255, 255, 0.05); + border-radius: 12px; + margin-bottom: 1rem; +} +.skeleton-line.full { width: 100%; } +.skeleton-line.medium { width: 70%; margin-bottom: 2rem; } + +.stats-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 1rem; + margin-bottom: 2rem; +} + +.stat-box { + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(255, 255, 255, 0.05); + border-radius: 16px; + padding: 1.5rem; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.stat-value { + font-family: var(--font-display); + font-size: 2rem; + font-weight: 800; + color: var(--text-main); +} +.stat-label { + font-size: 0.875rem; + color: var(--text-muted); +} + +.interactive-element { + background: var(--primary-gradient); + padding: 1rem; + border-radius: 16px; + text-align: center; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + user-select: none; +} +.interactive-element:active { + transform: scale(0.95); +} + +/* Responsive */ +@media (max-width: 1024px) { + .hero-section { + grid-template-columns: 1fr; + text-align: center; + } + .display-text { font-size: 3.5rem; } + .subtitle { margin: 0 auto 2.5rem; } + .action-group { justify-content: center; } + .glass-card { margin-top: 2rem; transform: none; } + .glass-card:hover { transform: translateY(-5px); } + nav { display: none; } +} diff --git a/sw.js b/sw.js new file mode 100644 index 0000000..93da150 --- /dev/null +++ b/sw.js @@ -0,0 +1,48 @@ +const CACHE_NAME = 'amusement-pwa-cache-v1'; +const urlsToCache = [ + './', + './index.html', + './style.css', + './app.js', + './manifest.json', + './icon.svg', + 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;800&family=Outfit:wght@400;600;800&display=swap' +]; + +self.addEventListener('install', event => { + event.waitUntil( + caches.open(CACHE_NAME) + .then(cache => { + return cache.addAll(urlsToCache); + }) + ); +}); + +self.addEventListener('fetch', event => { + event.respondWith( + caches.match(event.request) + .then(response => { + // Cache hit - return response + if (response) { + return response; + } + return fetch(event.request); + } + ) + ); +}); + +self.addEventListener('activate', event => { + const cacheWhitelist = [CACHE_NAME]; + event.waitUntil( + caches.keys().then(cacheNames => { + return Promise.all( + cacheNames.map(cacheName => { + if (cacheWhitelist.indexOf(cacheName) === -1) { + return caches.delete(cacheName); + } + }) + ); + }) + ); +});