Skip to content

Commit a4f09d4

Browse files
feat(landing): rebuild home into an ecosystem-integrated experience (#23)
* feat(landing): rebuild home into an ecosystem-integrated experience Turn the single-hero landing page into a full developer/researcher funnel that surfaces the whole RocketPy ecosystem. Sections: sticky nav (text labels + persistent Simulate CTA + live GitHub star badge + mobile menu); reworked hero with `pip install rocketpy`, CTA cluster and micro social-proof; live stats band; four-class quickstart with syntax-highlighted, copyable code and an Open-in-Colab link; output showcase; auto-scrolling features carousel; validation + used-by wall; ecosystem cards (RocketSerializer / Infinity-API / App / Docs); R&D roadmap; community; how -to-cite with copyable BibTeX; final CTA; and an expanded footer with link columns. Live stats (GitHub stars/forks/contributors, recent PyPI downloads) are fetched at build time by scripts/fetch-stats.mjs and injected into dist, with static fallbacks and continue-on-error so a flaky API never breaks a deploy. A monthly cron redeploy keeps the numbers fresh. New css/home.css reuses the about.css component recipes so both pages read as one system; footer columns are additive so the About footer is unchanged. Accessibility: prefers-reduced-motion, skip link, focus rings and a noscript fallback. No new npm dependencies. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(format): include .mjs files in prettier glob Ensures scripts/fetch-stats.mjs is covered by `npm run format`, keeping the build script consistent with the rest of the repo (addresses Copilot review feedback on PR #23). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(stats): cap network requests with a timeout so a hung API can't stall deploy `continue-on-error` handles a failed stats fetch but not a hung one — a slow or stalled GitHub/PyPI endpoint would keep the deploy step waiting up to the job's default timeout. Add an 8s AbortSignal.timeout to both fetches (rejection falls through to the existing try/catch and committed defaults) and a 3-minute step-level timeout-minutes as a backstop. Addresses the blocking finding from the PR #23 review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(landing): refine copy, sync About nav, drop fabricated roadmap Landing-page content and consistency fixes from review feedback: - Hero: balance the tagline so "The sky is not the limit!" no longer orphans "limit!" on its own line (text-wrap: balance). - Quickstart: clarify that SolidMotor is one of several motor variants (Solid/Hybrid/Liquid/Generic); keep the four-class framing. - Stats band: replace "refreshed on every deploy" with a build-injected "Updated <Month Year>" label. - Remove the fabricated "frontier of flight simulation" R&D section (and its now-dead CSS) - the team is not working on those items. - Rename the web app to "Jarvis" in the ecosystem card and footer. - State that RocketPy is the official simulator of EuRoC (2022) and LASC (2025), supported by the team. About page now shares the landing's sticky header: - Extract the site-header/nav into a shared css/nav.css loaded by both pages (removed the duplicate block from home.css). - About uses the same nav (links back to landing sections), star badge, Simulate CTA, mobile toggle, skip link and #main target. - fetch-stats.mjs now injects the live star count into about.html too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a977dbd commit a4f09d4

13 files changed

Lines changed: 2803 additions & 204 deletions

File tree

.github/workflows/static.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ on:
99
# Allows you to run this workflow manually from the Actions tab
1010
workflow_dispatch:
1111

12+
# Monthly redeploy (1st of each month, 06:00 UTC) so the live stats injected
13+
# at build time (GitHub stars/forks/contributors, PyPI downloads) stay fresh
14+
# without needing a push. Scheduled runs always execute on the default branch.
15+
schedule:
16+
- cron: "0 6 1 * *"
17+
1218
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
1319
permissions:
1420
contents: read
@@ -40,6 +46,12 @@ jobs:
4046
run: npm ci
4147
- name: Build site
4248
run: npm run build
49+
- name: Refresh live stats
50+
run: npm run stats
51+
env:
52+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
53+
continue-on-error: true
54+
timeout-minutes: 3
4355
- name: Add .nojekyll
4456
run: touch dist/.nojekyll
4557
- name: Setup Pages

about.html

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
/>
4646

4747
<link href="./css/main.css" rel="stylesheet" />
48+
<link href="./css/nav.css" rel="stylesheet" />
4849
<link href="./css/footer.css" rel="stylesheet" />
4950
<link href="./css/about.css" rel="stylesheet" />
5051
<link href="./css/font-awesome/css/fontawesome.min.css" rel="stylesheet" />
@@ -53,45 +54,55 @@
5354
</head>
5455

5556
<body>
57+
<a href="#main" class="skip-link">Skip to content</a>
5658
<div class="page-wrapper">
57-
<header class="top-bar">
58-
<div class="brand-and-cta">
59-
<a class="brand-link" href="./index.html" aria-label="RocketPy home">
60-
<span class="brand-logo"></span>
61-
</a>
62-
</div>
63-
<nav class="social-nav panel-bg" aria-label="Main navigation">
64-
<a href="./about.html" class="icon-button" aria-label="About us"
65-
><i class="fa-solid fa-users"></i
66-
></a>
59+
<header class="site-header" id="top">
60+
<a class="brand-link" href="./index.html" aria-label="RocketPy home">
61+
<span class="brand-logo"></span>
62+
</a>
63+
<nav class="nav-links" id="navLinks" aria-label="Primary">
64+
<a class="nav-link" href="./index.html#features">Features</a>
65+
<a class="nav-link" href="./index.html#showcase">Showcase</a>
66+
<a class="nav-link" href="./index.html#ecosystem">Ecosystem</a>
6767
<a
68+
class="nav-link"
6869
href="https://docs.rocketpy.org/en/latest/index.html"
69-
class="icon-button"
70-
aria-label="Documentation"
71-
><i class="fa-solid fa-book"></i
72-
></a>
70+
>Docs</a
71+
>
72+
<a class="nav-link" href="./index.html#community">Community</a>
73+
<a class="nav-link" href="./about.html" aria-current="page">About</a>
74+
</nav>
75+
<div class="nav-actions">
7376
<a
77+
class="star-badge"
7478
href="https://github.com/RocketPy-Team/RocketPy"
75-
class="icon-button"
76-
aria-label="GitHub repository"
77-
><i class="fa-brands fa-github"></i
78-
></a>
79-
<a
80-
href="https://www.instagram.com/rocketpyteam"
81-
class="icon-button"
82-
aria-label="Instagram"
83-
><i class="fa-brands fa-instagram"></i
84-
></a>
79+
aria-label="Star RocketPy on GitHub"
80+
>
81+
<i class="fa-brands fa-github" aria-hidden="true"></i>
82+
<i class="fa-solid fa-star" aria-hidden="true"></i>
83+
<span data-stat="stars">1,021</span>
84+
</a>
8585
<a
86-
href="https://www.linkedin.com/company/rocketpy"
87-
class="icon-button"
88-
aria-label="LinkedIn"
89-
><i class="fa-brands fa-linkedin"></i
90-
></a>
91-
</nav>
86+
href="https://app.rocketpy.org/"
87+
class="simulate-button nav-cta"
88+
role="button"
89+
>
90+
<span class="simulate-icon"></span>
91+
<span>Simulate</span>
92+
</a>
93+
<button
94+
class="nav-toggle"
95+
type="button"
96+
aria-label="Toggle menu"
97+
aria-expanded="false"
98+
aria-controls="navLinks"
99+
>
100+
<i class="fa-solid fa-bars" aria-hidden="true"></i>
101+
</button>
102+
</div>
92103
</header>
93104

94-
<main class="main-content">
105+
<main class="main-content" id="main">
95106
<!-- ============================ HERO ============================ -->
96107
<section class="about-hero panel-bg">
97108
<p class="about-eyebrow">Since 2018 &middot; Open Source</p>
@@ -1055,6 +1066,22 @@ <h3 class="featured-name">${p.name}</h3>
10551066
renderFeatured("coreGrid", "core");
10561067
renderContributors();
10571068
renderStats();
1069+
1070+
// Mobile nav toggle — mirrors the landing page's shared site-header.
1071+
const header = document.querySelector(".site-header");
1072+
const navToggle = document.querySelector(".nav-toggle");
1073+
if (header && navToggle) {
1074+
navToggle.addEventListener("click", () => {
1075+
const open = header.classList.toggle("nav-open");
1076+
navToggle.setAttribute("aria-expanded", open ? "true" : "false");
1077+
});
1078+
header.querySelectorAll(".nav-link").forEach((link) =>
1079+
link.addEventListener("click", () => {
1080+
header.classList.remove("nav-open");
1081+
navToggle.setAttribute("aria-expanded", "false");
1082+
}),
1083+
);
1084+
}
10581085
</script>
10591086
</body>
10601087
</html>

css/footer.css

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,3 +191,142 @@
191191
margin-left: 0;
192192
}
193193
}
194+
195+
/* ==========================================================================
196+
EXPANDED SITE FOOTER (landing page) — additive; the legacy
197+
.footer-left/.footer-center/.footer-right rules above still drive the
198+
About page footer unchanged.
199+
========================================================================== */
200+
.site-footer {
201+
padding: clamp(2.5rem, 5vw, 3.5rem) clamp(1.25rem, 4vw, 3rem) 1.8rem;
202+
text-align: left;
203+
font:
204+
normal 16px "Open Sans",
205+
sans-serif;
206+
}
207+
208+
.footer-top {
209+
display: grid;
210+
grid-template-columns: minmax(220px, 1fr) 2.2fr;
211+
gap: clamp(2rem, 5vw, 4rem);
212+
max-width: 1240px;
213+
margin: 0 auto;
214+
}
215+
216+
.footer-brand .footer-logo {
217+
margin-bottom: 14px;
218+
}
219+
220+
.footer-tagline {
221+
font-family: "Open Sans", sans-serif;
222+
font-size: 0.9rem;
223+
line-height: 1.6;
224+
color: rgba(255, 255, 255, 0.7);
225+
max-width: 280px;
226+
margin: 0;
227+
}
228+
229+
.footer-social {
230+
display: flex;
231+
gap: 10px;
232+
margin-top: 18px;
233+
}
234+
235+
.footer-social a {
236+
width: 38px;
237+
height: 38px;
238+
border-radius: 8px;
239+
background: #33383b;
240+
color: #ffffff;
241+
display: inline-flex;
242+
align-items: center;
243+
justify-content: center;
244+
font-size: 18px;
245+
text-decoration: none;
246+
transition:
247+
background 0.2s ease,
248+
transform 0.2s ease;
249+
}
250+
251+
.footer-social a:hover,
252+
.footer-social a:focus-visible {
253+
background: #4a5568;
254+
transform: translateY(-2px);
255+
}
256+
257+
.footer-links-grid {
258+
display: grid;
259+
grid-template-columns: repeat(4, 1fr);
260+
gap: 24px;
261+
}
262+
263+
.footer-col h4 {
264+
font-family: "Rubik", sans-serif;
265+
font-size: 13px;
266+
font-weight: 700;
267+
letter-spacing: 0.08em;
268+
text-transform: uppercase;
269+
color: #ffffff;
270+
margin: 0 0 12px;
271+
}
272+
273+
.footer-col a {
274+
display: block;
275+
font-family: "Open Sans", sans-serif;
276+
font-size: 0.9rem;
277+
font-weight: 400;
278+
color: rgba(255, 255, 255, 0.68);
279+
text-decoration: none;
280+
padding: 6px 0;
281+
transition: color 0.2s ease;
282+
}
283+
284+
.footer-col a:hover,
285+
.footer-col a:focus-visible {
286+
color: #ffffff;
287+
}
288+
289+
.footer-bottom {
290+
max-width: 1240px;
291+
margin: clamp(2rem, 4vw, 2.6rem) auto 0;
292+
padding-top: 20px;
293+
border-top: 1px solid rgba(255, 255, 255, 0.1);
294+
display: flex;
295+
flex-wrap: wrap;
296+
justify-content: space-between;
297+
gap: 10px;
298+
}
299+
300+
.footer-bottom p {
301+
font-family: "Open Sans", sans-serif;
302+
font-size: 0.82rem;
303+
color: rgba(255, 255, 255, 0.6);
304+
margin: 0;
305+
}
306+
307+
.footer-bottom a {
308+
color: rgba(255, 255, 255, 0.8);
309+
text-decoration: none;
310+
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
311+
}
312+
313+
.footer-bottom a:hover {
314+
color: #ffffff;
315+
}
316+
317+
@media (max-width: 880px) {
318+
.footer-top {
319+
grid-template-columns: 1fr;
320+
}
321+
322+
.footer-bottom {
323+
justify-content: center;
324+
text-align: center;
325+
}
326+
}
327+
328+
@media (max-width: 620px) {
329+
.footer-links-grid {
330+
grid-template-columns: 1fr 1fr;
331+
}
332+
}

0 commit comments

Comments
 (0)