Skip to content

Commit b513eea

Browse files
committed
seo: enhance homepage noscript fallback with structured content and 45 internal links — improves crawlable body content for Google
1 parent 8e9b699 commit b513eea

2 files changed

Lines changed: 61 additions & 4 deletions

File tree

frontend/index.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,8 +155,6 @@
155155
</style>
156156
</head>
157157
<body class="antialiased h-screen overflow-hidden">
158-
<div id="root" class="h-full w-full"></div>
159-
160158
<!-- SEO-friendly noscript content for crawlers -->
161159
<noscript>
162160
<div class="noscript-content" style="background: #0f172a;">
@@ -208,7 +206,8 @@ <h2>Reference pages</h2>
208206
<p>Please enable JavaScript to use LiftShift.</p>
209207
</div>
210208
</noscript>
211-
209+
210+
<div id="root" class="h-full w-full"></div>
212211
<script type="module" src="/index.tsx"></script>
213212
</body>
214213
</html>

vite.config.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,64 @@ const servePublicIndexHtmlPlugin = () => {
9595
};
9696
};
9797

98+
const SEO_NOSCRIPT = `<noscript>
99+
<div class="noscript-content" style="background: #0f172a; max-width: 800px; margin: 0 auto; padding: 40px 20px; color: #e2e8f0; font-family: system-ui, sans-serif; line-height: 1.6;">
100+
<h1 style="font-size:2rem;margin-bottom:0.75rem;">LiftShift — Free &amp; Open Source Workout Analytics</h1>
101+
<p style="margin-bottom:1rem;color:#94a3b8;font-size:1.1rem;">Your workout app logs what you did. LiftShift tells you what it <em>means</em> — and what to do next. Free, open source, and private. No account needed.</p>
102+
103+
<h2 style="font-size:1.5rem;margin:2rem 0 0.75rem;">Why your logging app isn't enough</h2>
104+
<p style="margin-bottom:0.75rem;color:#94a3b8;">Hevy, Strong, and Lyfta are excellent workout loggers. They track sets, reps, and weight perfectly. But their built-in analytics stop at basic line charts and raw numbers. They don't tell you if you're plateauing, which muscles you're neglecting, or what to change next session.</p>
105+
<p style="margin-bottom:0.75rem;color:#94a3b8;">LiftShift reads your exported data from any of these apps and gives you the answers your logger won't. Every chart, every status label, every suggestion — all computed locally in your browser. Nothing leaves your device.</p>
106+
107+
<h2 style="font-size:1.5rem;margin:2rem 0 0.75rem;">What you get</h2>
108+
<p style="margin-bottom:0.75rem;color:#94a3b8;"><strong>Training insights</strong> — Interactive <a href="/metrics/muscle-heatmap/" style="color:#6ee7b7;">muscle heatmaps</a> with rolling 7-day windows and volume zone scoring. See exactly which muscles are getting enough work and which are being neglected. Drill down per exercise to understand why.</p>
109+
<p style="margin-bottom:0.75rem;color:#94a3b8;"><strong>Progress tracking</strong> — <a href="/metrics/personal-records/" style="color:#6ee7b7;">Smart PR detection</a> across three tiers: all-time Gold PRs, 2-month Silver PRs, and flagged Premature PRs that you couldn't sustain. PR drought alerts tell you when a lift has stalled.</p>
110+
<p style="margin-bottom:0.75rem;color:#94a3b8;"><strong>Plateau detection</strong> — Every exercise gets a status: Getting stronger, Plateauing, or Taking a dip. When you're stuck, LiftShift gives a specific, actionable suggestion — add a rep, bump the weight, or deload.</p>
111+
<p style="margin-bottom:0.75rem;color:#94a3b8;"><strong>Set-by-set coaching feedback</strong> — Open any past workout and LiftShift analyzes every set across 19 scenarios. Each set gets a badge (Normal Fatigue, Too Aggressive, Good Reset) with exact numbers and improvement suggestions.</p>
112+
<p style="margin-bottom:0.75rem;color:#94a3b8;"><strong>AI-ready exports</strong> — Export your entire training history as structured data. Built-in analysis modules cover junk volume audits, structural balance, joint health, program adherence, and more. Paste into any AI and ask anything. See the <a href="/ai/" style="color:#6ee7b7;">AI reference page</a> for details.</p>
113+
<p style="margin-bottom:0.75rem;color:#94a3b8;"><strong>Data tools</strong> — Calendar filtering rebuilds every chart for any date range. Compare training blocks in seconds. Combine data from multiple apps into one unified dashboard. Track your <a href="/metrics/training-volume/" style="color:#6ee7b7;">training volume</a> and <a href="/metrics/one-rep-max/" style="color:#6ee7b7;">1RM estimates</a> over time.</p>
114+
<p style="margin-bottom:0.75rem;color:#94a3b8;"><strong>Lifetime Progress</strong> — Every muscle gets a 9-tier journey from Seedling to Legend based on cumulative sets. See estimated time to your next milestone. GitHub-style yearly consistency heatmaps show your training frequency at a glance.</p>
115+
116+
<h2 style="font-size:1.5rem;margin:2rem 0 0.75rem;">How to get started</h2>
117+
<ol style="margin-left:1.5rem;margin-bottom:1rem;">
118+
<li style="margin-bottom:0.5rem;color:#94a3b8;">Choose your app — <a href="/supported-apps/hevy/" style="color:#6ee7b7;">Hevy</a> (API or CSV), <a href="/supported-apps/strong/" style="color:#6ee7b7;">Strong</a> (CSV), or <a href="/supported-apps/lyfta/" style="color:#6ee7b7;">Lyfta</a> (API or CSV) — and export your workout history.</li>
119+
<li style="margin-bottom:0.5rem;color:#94a3b8;">Open LiftShift and import your file. All processing happens locally in your browser.</li>
120+
<li style="margin-bottom:0.5rem;color:#94a3b8;">Explore your dashboards. Every feature is free. No account, no subscription, no data upload.</li>
121+
</ol>
122+
123+
<h2 style="font-size:1.5rem;margin:2rem 0 0.75rem;">Private by design</h2>
124+
<p style="margin-bottom:0.75rem;color:#94a3b8;">LiftShift is <strong>fully open source</strong> under the AGPL-3.0 license. <a href="https://github.com/aree6/LiftShift" style="color:#6ee7b7;">Source code on GitHub</a>. Every line is auditable. Your workout data never leaves your device — all analytics run locally in your browser. Nothing is uploaded to LiftShift servers. Read our <a href="/privacy/" style="color:#6ee7b7;">privacy policy</a> for the full breakdown.</p>
125+
126+
<h2 style="font-size:1.5rem;margin:2rem 0 0.75rem;">Learn more</h2>
127+
<ul style="margin-left:1.5rem;margin-bottom:1rem;">
128+
<li style="margin-bottom:0.5rem;color:#94a3b8;"><a href="/about/" style="color:#6ee7b7;">About LiftShift</a> — what it does, what it doesn't do, and what makes it different.</li>
129+
<li style="margin-bottom:0.5rem;color:#94a3b8;"><a href="/how-it-works/" style="color:#6ee7b7;">How it works</a> — detailed walkthrough of every feature with examples.</li>
130+
<li style="margin-bottom:0.5rem;color:#94a3b8;"><a href="/features/" style="color:#6ee7b7;">Full feature list</a> — everything LiftShift can do with your training data.</li>
131+
<li style="margin-bottom:0.5rem;color:#94a3b8;"><a href="/metrics/" style="color:#6ee7b7;">Metrics glossary</a> — plain-language definitions for volume, PRs, 1RM, and heatmaps.</li>
132+
<li style="margin-bottom:0.5rem;color:#94a3b8;"><a href="/supported-apps/" style="color:#6ee7b7;">Supported apps</a> — import guides for Hevy, Strong, and Lyfta.</li>
133+
<li style="margin-bottom:0.5rem;color:#94a3b8;"><a href="/hevy-vs-lyfta/" style="color:#6ee7b7;">Hevy vs Lyfta</a> — honest comparison of both workout trackers.</li>
134+
<li style="margin-bottom:0.5rem;color:#94a3b8;"><a href="/faq/" style="color:#6ee7b7;">FAQ</a> — quick answers to common questions.</li>
135+
<li style="margin-bottom:0.5rem;color:#94a3b8;"><a href="/ai/" style="color:#6ee7b7;">AI reference</a> — structured definition optimized for AI assistants and LLMs.</li>
136+
</ul>
137+
138+
<p style="margin-top:2rem;color:#6ee7b7;font-size:1.1rem;">Enable JavaScript to use LiftShift or <a href="/about/" style="color:#6ee7b7;">learn more about the project</a>.</p>
139+
</div>
140+
</noscript>`;
141+
142+
const injectSeoNoscriptPlugin = () => {
143+
return {
144+
name: 'inject-seo-noscript',
145+
apply: 'build' as const,
146+
closeBundle() {
147+
const indexPath = path.resolve(__dirname, 'dist', 'client', 'index.html');
148+
if (!fs.existsSync(indexPath)) return;
149+
let html = fs.readFileSync(indexPath, 'utf-8');
150+
html = html.replace('</body>', SEO_NOSCRIPT + '\n</body>');
151+
fs.writeFileSync(indexPath, html);
152+
},
153+
};
154+
};
155+
98156
export default defineConfig(({ mode }) => {
99157
const env = loadEnv(mode, '.', '');
100158
const backendUrl = stripTrailingApiPath(env.VITE_BACKEND_URL || 'http://localhost:5000');
@@ -120,7 +178,7 @@ export default defineConfig(({ mode }) => {
120178
},
121179
},
122180
},
123-
plugins: [serveFaviconIcoPlugin(), servePublicIndexHtmlPlugin(), tailwindcss(), react(), vike()],
181+
plugins: [serveFaviconIcoPlugin(), servePublicIndexHtmlPlugin(), injectSeoNoscriptPlugin(), tailwindcss(), react(), vike()],
124182
resolve: {
125183
alias: {
126184
'@': path.resolve(__dirname, 'frontend'),

0 commit comments

Comments
 (0)