Skip to content

Commit d272a26

Browse files
committed
Apply skeleton loader on docs page
app/loading.tsx is picked up automatically by Next.js and shown while the async page.tsx (download metrics fetch) resolves. The skeleton mirrors the real page structure exactly - hero, highlight cards, overview panel, stats strip - all as shimmer blocks using the existing --border/--chip CSS variables so it respects light and dark mode.
1 parent 58a1e67 commit d272a26

3 files changed

Lines changed: 148 additions & 2 deletions

File tree

docs/app/globals.css

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ footer { text-align: center; padding: 24px 0; color: var(--muted); font-size: 0.
162162

163163
.feedbackForm { display: flex; flex-direction: column; gap: 24px; }
164164

165+
.feedbackFormWrap { display: flex; flex-direction: column; gap: 24px; }
165166
.feedbackTypeGroup { border: none; padding: 0; margin: 0; }
166167
.feedbackTypeCards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; margin-top: 8px; }
167168
.feedbackTypeCard {
@@ -320,6 +321,24 @@ main { max-width: 980px; margin: 24px auto; padding: 0 16px; }
320321

321322
@media (max-width: 900px) { .grid { grid-template-columns: 1fr; } }
322323

324+
/* Skeleton loader */
325+
@keyframes skShimmer {
326+
from { background-position: -400px 0; }
327+
to { background-position: 400px 0; }
328+
}
329+
.skEl {
330+
display: inline-block;
331+
border-radius: 6px;
332+
background: linear-gradient(90deg, var(--border) 25%, var(--chip) 50%, var(--border) 75%);
333+
background-size: 800px 100%;
334+
animation: skShimmer 1.4s ease-in-out infinite;
335+
}
336+
.homeSkeleton .homeActions { gap: 10px; }
337+
.homeSkeleton .homeHeroMeta { display: flex; flex-direction: column; gap: 8px; }
338+
.homeSkeleton .homeHeroCopy { display: flex; flex-direction: column; gap: 10px; }
339+
.homeSkeleton .homeCard { display: flex; flex-direction: column; gap: 8px; }
340+
.homeSkeleton .homeOverviewCopy { display: flex; flex-direction: column; }
341+
323342
/* Home landing page */
324343
.homePage {
325344
min-height: 100vh;
@@ -514,6 +533,30 @@ main { max-width: 980px; margin: 24px auto; padding: 0 16px; }
514533
line-height: 1.62;
515534
}
516535

536+
.homeStats {
537+
display: grid;
538+
grid-template-columns: repeat(3, 1fr);
539+
gap: 1px;
540+
background: var(--border);
541+
border: 1px solid var(--border);
542+
border-radius: 14px;
543+
overflow: hidden;
544+
width: min(100%, 1160px);
545+
margin: 48px auto 0;
546+
}
547+
.homeStatItem {
548+
display: flex;
549+
flex-direction: column;
550+
align-items: center;
551+
gap: 4px;
552+
padding: 28px 16px;
553+
background: var(--card);
554+
text-align: center;
555+
}
556+
.homeStatItem svg { color: var(--primary); font-size: 1.1rem; margin-bottom: 4px; }
557+
.homeStatItem strong { font-size: 2rem; font-weight: 700; letter-spacing: -0.02em; color: var(--text); }
558+
.homeStatItem span { font-size: 0.82rem; color: var(--muted); }
559+
517560
.homeOverview {
518561
width: min(100%, 1160px);
519562
margin: 12px auto 0;

docs/app/loading.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
export default function HomeLoading() {
2+
return (
3+
<div className="homePage homeSkeleton">
4+
<div className="homeThemeControl" />
5+
6+
<section className="homeHero">
7+
<div className="homeHeroCopy">
8+
<div className="homeHeroMeta">
9+
<span className="skEl" style={{ width: 160, height: 16 }} />
10+
<span className="skEl" style={{ width: 200, height: 14 }} />
11+
</div>
12+
<span className="skEl" style={{ width: '70%', height: 44, marginTop: 8 }} />
13+
<span className="skEl" style={{ width: '90%', height: 16, marginTop: 4 }} />
14+
<span className="skEl" style={{ width: '80%', height: 16 }} />
15+
<span className="skEl" style={{ width: '60%', height: 16 }} />
16+
<div className="homeActions">
17+
<span className="skEl" style={{ width: 160, height: 42, borderRadius: 8 }} />
18+
<span className="skEl" style={{ width: 120, height: 42, borderRadius: 8 }} />
19+
</div>
20+
</div>
21+
<div className="homeHeroVisual">
22+
<div className="homeHeroLogo">
23+
<span className="skEl" style={{ width: 126, height: 34 }} />
24+
</div>
25+
<div className="homeHeroPanel">
26+
{[1,2,3].map(i => (
27+
<div key={i} className="homePanelRow">
28+
<span className="skEl" style={{ width: 100, height: 14 }} />
29+
<span className="skEl" style={{ width: 48, height: 14 }} />
30+
</div>
31+
))}
32+
</div>
33+
</div>
34+
</section>
35+
36+
<section className="homeHighlights">
37+
{[1,2,3,4].map(i => (
38+
<article key={i} className="homeCard">
39+
<span className="skEl" style={{ width: 32, height: 32, borderRadius: 8 }} />
40+
<span className="skEl" style={{ width: '60%', height: 18, marginTop: 4 }} />
41+
<span className="skEl" style={{ width: '90%', height: 14 }} />
42+
<span className="skEl" style={{ width: '75%', height: 14 }} />
43+
</article>
44+
))}
45+
</section>
46+
47+
<section className="homeOverview">
48+
<div className="homeOverviewCopy">
49+
<span className="skEl" style={{ width: 220, height: 28 }} />
50+
<div style={{ display: 'flex', flexDirection: 'column', gap: 10, marginTop: 16 }}>
51+
{[1,2,3,4].map(i => <span key={i} className="skEl" style={{ width: `${[90,80,95,70][i-1]}%`, height: 14 }} />)}
52+
</div>
53+
</div>
54+
<div className="homeOverviewPanel">
55+
{[1,2,3,4].map(i => (
56+
<div key={i} className="homePanelRow">
57+
<span className="skEl" style={{ width: 110, height: 14 }} />
58+
<span className="skEl" style={{ width: 60, height: 14 }} />
59+
</div>
60+
))}
61+
<span className="skEl" style={{ width: '100%', height: 36, borderRadius: 8, marginTop: 8 }} />
62+
</div>
63+
</section>
64+
65+
<section className="homeStats">
66+
{[1,2,3].map(i => (
67+
<div key={i} className="homeStatItem">
68+
<span className="skEl" style={{ width: 24, height: 24, borderRadius: 6 }} />
69+
<span className="skEl" style={{ width: 100, height: 36, marginTop: 4 }} />
70+
<span className="skEl" style={{ width: 140, height: 14 }} />
71+
</div>
72+
))}
73+
</section>
74+
</div>
75+
)
76+
}

docs/app/page.tsx

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,17 @@
11
import Link from 'next/link'
2-
import { FiArrowRight, FiBookOpen, FiLayers, FiPackage, FiRefreshCw, FiTool } from 'react-icons/fi'
2+
import { FiArrowRight, FiBookOpen, FiDownload, FiLayers, FiPackage, FiRefreshCw, FiTool } from 'react-icons/fi'
33

44
import MetbitMark from './components/MetbitMark'
55
import ThemeToggle from './components/ThemeToggle'
66
import { formatDate, latestRelease, manifest, releaseHref } from '@/lib/versioned-docs'
7+
import { getDownloadMetrics } from '@/lib/download-metrics'
8+
9+
function fmt(n: number | null): string {
10+
if (n === null) return '—'
11+
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`
12+
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`
13+
return n.toLocaleString()
14+
}
715

816
export const metadata = {
917
title: 'metbit documentation',
@@ -33,7 +41,8 @@ const highlights = [
3341
},
3442
]
3543

36-
export default function HomePage() {
44+
export default async function HomePage() {
45+
const metrics = await getDownloadMetrics(latestRelease.tag)
3746
return (
3847
<div className="homePage">
3948
<div className="homeThemeControl" aria-label="Landing page theme control">
@@ -147,6 +156,24 @@ export default function HomePage() {
147156
</Link>
148157
</div>
149158
</section>
159+
160+
<section className="homeStats" aria-label="Download statistics">
161+
<div className="homeStatItem">
162+
<FiDownload aria-hidden />
163+
<strong>{fmt(metrics?.pypiAllTime ?? null)}</strong>
164+
<span>Total PyPI downloads</span>
165+
</div>
166+
<div className="homeStatItem">
167+
<FiDownload aria-hidden />
168+
<strong>{fmt(metrics?.pypiLastMonth ?? null)}</strong>
169+
<span>Downloads last month</span>
170+
</div>
171+
<div className="homeStatItem">
172+
<FiDownload aria-hidden />
173+
<strong>{fmt(metrics?.githubTotal ?? null)}</strong>
174+
<span>GitHub release downloads</span>
175+
</div>
176+
</section>
150177
</div>
151178
)
152179
}

0 commit comments

Comments
 (0)