Add interactive homepage preview & styles

Replace the WIP homepage with a new interactive home-hero: updated copy, CTA labels, a stats area (total levels, #1 entry, last slot), a featured-list preview, spotlight and info panels, and a small Discord text tweak. Add CSS to style the new homepage components, featured cards, stat cards, spotlight layout and responsive rules for smaller screens. Update app.js to load and render list data on the index page: sort items, populate stats, render top 3 featured entries (with a fallback message), and hook into live updates via bindLiveUpdates/onLiveUpdate so the homepage stays current.
This commit is contained in:
2026-03-31 19:48:11 -05:00
parent b39c334fdf
commit 5acf920143
3 changed files with 158 additions and 33 deletions
+49
View File
@@ -137,6 +137,55 @@
}).catch(()=>null);
}
if(page==='index'){
const totalEl = qs('hero-total-levels');
const topEl = qs('hero-top-entry');
const lastEl = qs('hero-last-slot');
const featuredListEl = qs('featured-list');
function renderFeatured(items){
if(!featuredListEl) return;
const rankedItems = items.slice().sort((a,b)=>(Number(a.position)||0)-(Number(b.position)||0));
const featured = rankedItems.slice(0, 3);
if(!featured.length){
featuredListEl.innerHTML = `
<article class="featured-card">
<span class="featured-rank">#--</span>
<strong>No list data found</strong>
<p>The homepage preview could not load any FEDL entries yet.</p>
</article>
`;
return;
}
featuredListEl.innerHTML = featured.map(item=>`
<article class="featured-card">
<span class="featured-rank">#${escapeHtml(item.position || '--')}</span>
<strong>${escapeHtml(item.title || 'Untitled')}</strong>
<p>${item.url ? 'Video link is ready from the list page.' : 'This entry does not have a linked video yet.'}</p>
</article>
`).join('');
}
function renderHome(items){
const rankedItems = items.slice().sort((a,b)=>(Number(a.position)||0)-(Number(b.position)||0));
const firstItem = rankedItems[0];
const lastItem = rankedItems[rankedItems.length - 1];
if(totalEl) totalEl.textContent = String(rankedItems.length || 0);
if(topEl) topEl.textContent = firstItem ? firstItem.title : 'Unavailable';
if(lastEl) lastEl.textContent = lastItem && lastItem.position ? `#${lastItem.position}` : '--';
renderFeatured(rankedItems);
}
loadItems().then(renderHome).catch(()=>{
renderHome([]);
});
bindLiveUpdates();
onLiveUpdate(renderHome);
}
if(page==='roulette'){
const spinBtn = qs('roulette-spin');
const statusEl = qs('roulette-status');
+76 -32
View File
@@ -17,48 +17,92 @@
</nav>
</header>
<main>
<section class="wip-hero">
<div class="wip-copy">
<p class="hero-kicker">Under Construction</p>
<h2>Were building something way better than a blank page.</h2>
<p class="hero-text">The FEDL site is currently being rebuilt. The fresh version is on the way with cleaner pages, sharper browsing, and more polished Geometry Dash chaos.</p>
<section class="home-hero">
<div class="home-copy panel">
<p class="hero-kicker">FEDL Hub</p>
<h2>Track the list, learn the rules, and jump straight into the chaos.</h2>
<p class="hero-text">FEDL is your quick home base for browsing ranked demons, checking submission rules, spinning a roulette challenge, and keeping up with the list without digging through a bunch of scattered pages.</p>
<div class="hero-actions">
<a class="btn" href="lists.html">Browse Lists</a>
<a class="btn ghost-btn" href="rules.html">Read Rules</a>
<a class="btn ghost-btn" href="roulette.html">Try Roulette</a>
<a class="btn" href="lists.html">Open The List</a>
<a class="btn ghost-btn" href="rules.html">Submission Rules</a>
<a class="btn ghost-btn" href="roulette.html">Spin Roulette</a>
</div>
<div class="hero-stats" aria-label="FEDL overview">
<article class="stat-card">
<strong id="hero-total-levels">--</strong>
<span>Ranked levels loaded</span>
</article>
<article class="stat-card">
<strong id="hero-top-entry">--</strong>
<span>Current #1 entry</span>
</article>
<article class="stat-card">
<strong id="hero-last-slot">--</strong>
<span>Current last ranked slot</span>
</article>
</div>
</div>
<div class="wip-stage" aria-hidden="true">
<div class="construction-card">
<span class="construction-pill">WIP</span>
<div class="construction-grid">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<div class="progress-shell">
<div class="progress-bar"></div>
</div>
<p>Polishing pages, tuning visuals, and making room for more demon-fueled nonsense.</p>
<aside class="hero-stage panel">
<p class="stage-label">Live Snapshot</p>
<h3>Featured list picks</h3>
<p class="muted">A fast preview of what is waiting inside the list page right now.</p>
<div class="featured-list" id="featured-list">
<article class="featured-card">
<span class="featured-rank">#--</span>
<strong>Loading list data...</strong>
<p>Pulling the latest entries for the homepage preview.</p>
</article>
</div>
</div>
</aside>
</section>
<section class="wip-status-grid">
<article class="panel status-panel">
<p class="status-label">Current Status</p>
<h3>Major redesign in progress</h3>
<p class="muted">The homepage is temporarily acting as a progress screen while the rest of the site gets cleaned up.</p>
<section class="home-section-grid">
<article class="panel info-panel">
<p class="status-label">Browse Faster</p>
<h3>The list page is built for quick checking</h3>
<p class="muted">Search by title, jump between rank ranges, and open completion videos without leaving the site.</p>
<a class="text-link" href="lists.html">Go to lists</a>
</article>
<article class="panel status-panel">
<p class="status-label">What Still Works</p>
<h3>Lists, rules, and roulette</h3>
<p class="muted">You can still jump into the core sections while the extra pages are being finished.</p>
<article class="panel info-panel">
<p class="status-label">Stay Legit</p>
<h3>The rules page explains what submissions need</h3>
<p class="muted">Use it to review raw footage requirements, mod rules, skip policies, and approval expectations before you submit.</p>
<a class="text-link" href="rules.html">Read the rules</a>
</article>
<article class="panel info-panel">
<p class="status-label">Need A Challenge</p>
<h3>Roulette gives you a random demon instantly</h3>
<p class="muted">Great for practice sessions, challenge runs, or picking something painful when you cannot decide.</p>
<a class="text-link" href="roulette.html">Try roulette</a>
</article>
</section>
<section class="panel spotlight-panel">
<div class="spotlight-copy">
<p class="status-label">Start Here</p>
<h2>What you can do on FEDL</h2>
<p class="spotlight-text">This site is meant to be simple: find ranked demons, watch linked runs, check moderation expectations, and get back to playing without unnecessary clutter.</p>
</div>
<div class="spotlight-grid">
<article class="spotlight-card">
<h3>Lists</h3>
<p>Browse the ranking, filter by placement ranges, and watch linked videos from each entry.</p>
</article>
<article class="spotlight-card">
<h3>Rules</h3>
<p>Check completion requirements, accepted tools, and what moderators expect from a valid run.</p>
</article>
<article class="spotlight-card">
<h3>Roulette</h3>
<p>Pull a random demon with rank and level ID details for your next roulette attempt.</p>
</article>
</div>
</section>
<section class="panel discord-panel">
@@ -73,7 +117,7 @@
</div>
</div>
<p class="discord-text">Join the FEDL Discord to ask questions about rules, talk about levels, share progress, report issues, and get help from moderators and other players.</p>
<p class="discord-text">Join the FEDL Discord to ask about rules, talk about placements, share progress, report problems, and get help from moderators and other players.</p>
<div class="discord-highlights">
<span>Rule help</span>
+33 -1
View File
@@ -40,6 +40,37 @@ table.levels-table tr:hover td{background:rgba(255,255,255,0.01)}
.admin-actions input[type=text]{min-width:220px;padding:10px 12px;border-radius:10px;border:1px solid rgba(255,255,255,0.08);background:rgba(255,255,255,0.03);color:var(--text)}
.admin-table td:last-child{text-align:center}
.hero-kicker{margin:0 0 12px 0;text-transform:uppercase;letter-spacing:.18em;font-size:.75rem;font-weight:800;color:var(--accent-warm)}
.home-hero{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(300px,.85fr);gap:22px;align-items:stretch;min-height:calc(100vh - 132px)}
.home-copy,.hero-stage{position:relative;overflow:hidden}
.home-copy{display:flex;flex-direction:column;justify-content:center;padding:34px;border-radius:28px;background:linear-gradient(145deg,rgba(255,184,77,0.12),rgba(7,19,38,0.96) 42%),radial-gradient(circle at top left,rgba(255,255,255,0.08),transparent 35%);border:1px solid rgba(255,184,77,0.16)}
.home-copy::before{content:"";position:absolute;inset:auto -80px -90px auto;width:240px;height:240px;border-radius:50%;background:rgba(92,197,255,0.12);filter:blur(16px)}
.home-copy h2{margin:0 0 14px 0;font-size:clamp(2.6rem,6vw,5.3rem);max-width:12ch;position:relative;z-index:1}
.hero-text{max-width:60ch;margin:0;position:relative;z-index:1;color:#dce8f5;line-height:1.75;font-size:1.02rem}
.hero-actions{display:flex;flex-wrap:wrap;gap:12px;margin-top:28px;position:relative;z-index:1}
.hero-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px;margin-top:28px;position:relative;z-index:1}
.stat-card{padding:16px 18px;border-radius:18px;background:rgba(255,255,255,0.04);border:1px solid rgba(255,255,255,0.08);backdrop-filter:blur(10px)}
.stat-card strong{display:block;margin-bottom:8px;font-size:clamp(1.4rem,3vw,2rem);color:#fff}
.stat-card span{display:block;color:#c7d7ea;line-height:1.45;font-size:.94rem}
.hero-stage{padding:28px;border-radius:28px;background:linear-gradient(180deg,rgba(255,255,255,0.07),rgba(255,255,255,0.03));border:1px solid rgba(255,255,255,0.08)}
.stage-label{margin:0 0 8px 0;font-size:.76rem;letter-spacing:.16em;text-transform:uppercase;color:var(--accent);font-weight:800}
.hero-stage h3{margin:0 0 8px 0;font-size:2rem}
.featured-list{display:grid;gap:12px;margin-top:22px}
.featured-card{padding:18px;border-radius:18px;background:rgba(8,18,32,0.74);border:1px solid rgba(255,255,255,0.08)}
.featured-rank{display:inline-flex;align-items:center;justify-content:center;min-width:58px;padding:7px 12px;border-radius:999px;background:rgba(92,197,255,0.14);color:var(--accent);font-size:.82rem;font-weight:800}
.featured-card strong{display:block;margin:14px 0 8px 0;font-size:1.1rem}
.featured-card p{margin:0;color:#ccdbeb;line-height:1.55}
.home-section-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;margin-top:18px}
.info-panel{padding:24px;border-radius:22px;background:linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02));border:1px solid rgba(255,255,255,0.06)}
.info-panel h3{margin:0 0 10px 0;font-size:1.45rem}
.text-link{display:inline-block;margin-top:18px;color:var(--accent);font-weight:700;text-decoration:none}
.text-link:hover{color:#a9e5ff}
.spotlight-panel{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);gap:24px;align-items:start;margin-top:18px;padding:28px;border-radius:28px;background:linear-gradient(135deg,rgba(92,197,255,0.12),rgba(7,19,38,0.95));border:1px solid rgba(92,197,255,0.16)}
.spotlight-copy h2{margin:0 0 12px 0;font-size:2.1rem}
.spotlight-text{margin:0;color:#dce8f5;line-height:1.7}
.spotlight-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:14px}
.spotlight-card{padding:18px;border-radius:18px;background:rgba(255,255,255,0.05);border:1px solid rgba(255,255,255,0.08)}
.spotlight-card h3{margin:0 0 10px 0;font-size:1.08rem}
.spotlight-card p{margin:0;color:#dce8f5;line-height:1.6}
.wip-hero{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr);gap:22px;align-items:stretch;min-height:calc(100vh - 132px)}
.wip-copy,.wip-stage{position:relative}
.wip-copy{display:flex;flex-direction:column;justify-content:center;padding:32px;border-radius:26px;background:linear-gradient(145deg,rgba(255,184,77,0.1),rgba(7,19,38,0.96) 40%),radial-gradient(circle at top left,rgba(255,255,255,0.08),transparent 36%);border:1px solid rgba(255,184,77,0.16);overflow:hidden}
@@ -150,6 +181,7 @@ linear-gradient(180deg,rgba(8,18,32,0.98),rgba(7,19,38,0.94))}
.status-card{display:block;padding:18px;border-radius:14px;background:linear-gradient(180deg,rgba(255,255,255,0.05),rgba(255,255,255,0.02));border:1px solid rgba(255,255,255,0.06);text-decoration:none;color:var(--text)}
.status-card strong{display:block;margin-bottom:6px;font-size:1.65rem}
.status-card span{display:block;color:var(--muted);line-height:1.5}
@media(max-width:900px){.layout{flex-direction:column}.sidebar{width:100%}.video-modal iframe{height:320px}.roulette-head{flex-direction:column;align-items:flex-start}.discord-panel{flex-direction:column;align-items:flex-start;padding:22px 20px}.discord-panel h2{font-size:1.55rem}.discord-actions{width:100%;justify-content:flex-start}.rules-view header{padding:16px 18px}.rules-view header h1{font-size:1.55rem}.rules-page{padding:24px 20px}.rules-page h2{font-size:1.65rem}}
@media(max-width:900px){.layout{flex-direction:column}.sidebar{width:100%}.video-modal iframe{height:320px}.home-hero{grid-template-columns:1fr;min-height:auto}.home-copy,.hero-stage{padding:24px 22px}.hero-stats,.home-section-grid,.spotlight-grid{grid-template-columns:1fr}.spotlight-panel{grid-template-columns:1fr;padding:24px 22px}.roulette-head{flex-direction:column;align-items:flex-start}.discord-panel{flex-direction:column;align-items:flex-start;padding:22px 20px}.discord-panel h2{font-size:1.55rem}.discord-actions{width:100%;justify-content:flex-start}.rules-view header{padding:16px 18px}.rules-view header h1{font-size:1.55rem}.rules-page{padding:24px 20px}.rules-page h2{font-size:1.65rem}}
@media(max-width:900px){.error-grid{grid-template-columns:1fr}.error-copy,.error-side{padding:24px 20px}.error-copy h2{font-size:2.5rem}}
@media(max-width:900px){.admin-toolbar{flex-direction:column;align-items:stretch}.admin-actions{flex-direction:column;align-items:stretch}.admin-actions input[type=text]{min-width:0;width:100%}}
@media(max-width:640px){header{flex-direction:column;align-items:flex-start;gap:12px}nav{display:flex;flex-wrap:wrap;gap:10px}nav a{margin-left:0}.home-copy h2{max-width:none}.hero-actions{flex-direction:column;align-items:stretch}.hero-actions .btn{width:100%;text-align:center}}