-
Notifications
You must be signed in to change notification settings - Fork 9
feat(website): lighter docs (GIFs → WebP) + contributors page #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
| --- | ||
| type GhContributor = { | ||
| login: string; | ||
| avatar_url: string; | ||
| html_url: string; | ||
| contributions: number; | ||
| type: string; | ||
| }; | ||
|
|
||
| let contributors: GhContributor[] = []; | ||
|
|
||
| try { | ||
| const res = await fetch( | ||
| 'https://api.github.com/repos/multivmlabs/aeo.js/contributors?per_page=100', | ||
| { headers: { 'User-Agent': 'aeojs-docs-build', Accept: 'application/vnd.github+json' } } | ||
| ); | ||
| if (res.ok) { | ||
| const data = (await res.json()) as GhContributor[]; | ||
| contributors = data | ||
| .filter((c) => c.type === 'User') | ||
| .sort((a, b) => b.contributions - a.contributions); | ||
| } | ||
| } catch { | ||
| // Silently skip the strip on fetch failure — homepage stays clean. | ||
| } | ||
|
|
||
| const top = contributors.slice(0, 8); | ||
| const remaining = Math.max(0, contributors.length - top.length); | ||
| --- | ||
|
|
||
| {contributors.length > 0 && ( | ||
| <section class="contrib-strip"> | ||
| <h2 class="contrib-strip-title">Built by</h2> | ||
| <div class="contrib-strip-row"> | ||
| {top.map((c) => ( | ||
| <a | ||
| href={c.html_url} | ||
| target="_blank" | ||
| rel="noopener" | ||
| class="contrib-strip-avatar" | ||
| title={`${c.login} — ${c.contributions} commit${c.contributions === 1 ? '' : 's'}`} | ||
| > | ||
| <img | ||
| src={`${c.avatar_url}&s=80`} | ||
| alt={c.login} | ||
| width="40" | ||
| height="40" | ||
| loading="lazy" | ||
| decoding="async" | ||
| /> | ||
| </a> | ||
| ))} | ||
| {remaining > 0 && ( | ||
| <a href="/contributors/" class="contrib-strip-more" aria-label={`${remaining} more contributors`}> | ||
| +{remaining} | ||
| </a> | ||
| )} | ||
| </div> | ||
| <a href="/contributors/" class="contrib-strip-link"> | ||
| View all {contributors.length} contributor{contributors.length === 1 ? '' : 's'} → | ||
| </a> | ||
| </section> | ||
| )} | ||
|
|
||
| <style> | ||
| .contrib-strip { | ||
| margin: 4rem auto 2rem; | ||
| text-align: center; | ||
| max-width: 60rem; | ||
| } | ||
| .contrib-strip-title { | ||
| font-size: 0.85rem; | ||
| font-weight: 500; | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.08em; | ||
| color: rgba(255, 255, 255, 0.4); | ||
| margin-bottom: 1rem; | ||
| } | ||
| .contrib-strip-row { | ||
| display: flex; | ||
| flex-wrap: wrap; | ||
| justify-content: center; | ||
| gap: 0.5rem; | ||
| margin-bottom: 1rem; | ||
| } | ||
| .contrib-strip-avatar, | ||
| .contrib-strip-more { | ||
| display: inline-flex; | ||
| align-items: center; | ||
| justify-content: center; | ||
| width: 40px; | ||
| height: 40px; | ||
| border-radius: 50%; | ||
| overflow: hidden; | ||
| background: rgba(255, 255, 255, 0.05); | ||
| border: 1px solid rgba(255, 255, 255, 0.08); | ||
| transition: border-color 0.15s ease, transform 0.15s ease; | ||
| text-decoration: none; | ||
| } | ||
| .contrib-strip-avatar:hover, | ||
| .contrib-strip-more:hover { | ||
| border-color: rgba(255, 255, 255, 0.3); | ||
| transform: translateY(-1px); | ||
| } | ||
| .contrib-strip-avatar img { | ||
| width: 100%; | ||
| height: 100%; | ||
| display: block; | ||
| object-fit: cover; | ||
| } | ||
| .contrib-strip-more { | ||
| font-size: 0.75rem; | ||
| font-weight: 500; | ||
| color: rgba(255, 255, 255, 0.7); | ||
| } | ||
| .contrib-strip-link { | ||
| display: inline-block; | ||
| font-size: 0.85rem; | ||
| color: rgba(255, 255, 255, 0.5); | ||
| text-decoration: none; | ||
| transition: color 0.15s ease; | ||
| } | ||
| .contrib-strip-link:hover { | ||
| color: #fff; | ||
| } | ||
| </style> |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,149 @@ | ||||||||||||
| --- | ||||||||||||
| import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; | ||||||||||||
|
|
||||||||||||
| type GhContributor = { | ||||||||||||
| login: string; | ||||||||||||
| avatar_url: string; | ||||||||||||
| html_url: string; | ||||||||||||
| contributions: number; | ||||||||||||
| type: string; | ||||||||||||
| }; | ||||||||||||
|
|
||||||||||||
| let contributors: GhContributor[] = []; | ||||||||||||
| let fetchError = false; | ||||||||||||
|
|
||||||||||||
| try { | ||||||||||||
| const res = await fetch( | ||||||||||||
| 'https://api.github.com/repos/multivmlabs/aeo.js/contributors?per_page=100', | ||||||||||||
| { headers: { 'User-Agent': 'aeojs-docs-build', Accept: 'application/vnd.github+json' } } | ||||||||||||
| ); | ||||||||||||
| if (res.ok) { | ||||||||||||
| const data = (await res.json()) as GhContributor[]; | ||||||||||||
| contributors = data | ||||||||||||
| .filter((c) => c.type === 'User') | ||||||||||||
| .sort((a, b) => b.contributions - a.contributions); | ||||||||||||
| } else { | ||||||||||||
| fetchError = true; | ||||||||||||
| } | ||||||||||||
| } catch { | ||||||||||||
| fetchError = true; | ||||||||||||
| } | ||||||||||||
| --- | ||||||||||||
|
|
||||||||||||
| <StarlightPage | ||||||||||||
| frontmatter={{ | ||||||||||||
| title: 'Contributors', | ||||||||||||
| description: 'The people who make aeo.js possible.', | ||||||||||||
| tableOfContents: false, | ||||||||||||
| }} | ||||||||||||
| > | ||||||||||||
| {fetchError ? ( | ||||||||||||
| <p class="contrib-fallback"> | ||||||||||||
| Couldn't load contributors right now. View the full list on | ||||||||||||
| <a href="https://github.com/multivmlabs/aeo.js/graphs/contributors" target="_blank" rel="noopener"> | ||||||||||||
| GitHub | ||||||||||||
| </a> | ||||||||||||
| . | ||||||||||||
| </p> | ||||||||||||
| ) : ( | ||||||||||||
| <> | ||||||||||||
| <p class="contrib-intro"> | ||||||||||||
| {contributors.length} contributor{contributors.length === 1 ? '' : 's'} have shaped aeo.js. Thank you. 🙏 | ||||||||||||
| </p> | ||||||||||||
|
Comment on lines
+50
to
+52
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Two edge-case rendering issues in the success branch's intro text at Extended reasoning...1. Subject-verb disagreement (line 51) The success branch renders: {contributors.length} contributor{contributors.length === 1 ? '' : 's'} have shaped aeo.js. Thank you. 🙏The noun is conditionally pluralized via the ternary, but the verb
Concatenated output: "1 contributor have shaped aeo.js. Thank you. 🙏" — a real subject-verb agreement error in user-facing copy. Counts of 0 ("0 contributors have") and 2+ render correctly. The fix mirrors the existing pluralization: {contributors.length} contributor{contributors.length === 1 ? '' : 's'} {contributors.length === 1 ? 'has' : 'have'} shaped aeo.js.For 2. Empty-success state (lines 17–28, 50–82)
The same path is reachable if every entry is filtered out by Addressing the refutation: the refuting verifier correctly notes the build doesn't crash, the trigger is brief/rare for an established repo, and the next deploy fixes it. That's why this is filed as nit, not normal — the page still renders and the fetchError fallback covers the meaningful 4xx/5xx cases. But the fix is one line and the file is being added in this PR, so it's worth catching now rather than after a confusing user report. The fix is to widen the fallback condition: {fetchError || contributors.length === 0 ? (
<p class="contrib-fallback">…GitHub fallback…</p>
) : (
…existing success markup…
)}Severity: nit — both issues are low-impact copy/UX polish on a docs page, not functional bugs. They co-locate in the same intro line, so a single edit addresses both. |
||||||||||||
| <ul class="contrib-grid"> | ||||||||||||
| {contributors.map((c) => ( | ||||||||||||
| <li class="contrib-card"> | ||||||||||||
|
Comment on lines
+52
to
+55
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
All
Suggested change
Prompt To Fix With AIThis is a comment left during a code review.
Path: website/src/pages/contributors.astro
Line: 52-55
Comment:
**`rel="noopener"` should also include `noreferrer`**
All `target="_blank"` anchors use only `rel="noopener"`. Adding `noreferrer` is the recommended practice — it also suppresses the `Referer` header and independently implies `noopener` in all browsers, so it's strictly safer without any downside. The same applies to the contributor card links (`href={c.html_url}`) and the CTA link.
```suggestion
<a href="https://github.com/multivmlabs/aeo.js/graphs/contributors" target="_blank" rel="noopener noreferrer">
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||
| <a href={c.html_url} target="_blank" rel="noopener" class="contrib-link"> | ||||||||||||
| <img | ||||||||||||
| src={`${c.avatar_url}&s=120`} | ||||||||||||
| alt={`${c.login} avatar`} | ||||||||||||
| width="60" | ||||||||||||
| height="60" | ||||||||||||
| loading="lazy" | ||||||||||||
| decoding="async" | ||||||||||||
| class="contrib-avatar" | ||||||||||||
| /> | ||||||||||||
| <span class="contrib-name">{c.login}</span> | ||||||||||||
| <span class="contrib-count"> | ||||||||||||
| {c.contributions} commit{c.contributions === 1 ? '' : 's'} | ||||||||||||
| </span> | ||||||||||||
| </a> | ||||||||||||
| </li> | ||||||||||||
| ))} | ||||||||||||
| </ul> | ||||||||||||
| <p class="contrib-cta"> | ||||||||||||
| Want to join them? | ||||||||||||
| <a href="https://github.com/multivmlabs/aeo.js" target="_blank" rel="noopener"> | ||||||||||||
| Open a PR | ||||||||||||
| </a> | ||||||||||||
| . | ||||||||||||
| </p> | ||||||||||||
| </> | ||||||||||||
| )} | ||||||||||||
| </StarlightPage> | ||||||||||||
|
|
||||||||||||
| <style> | ||||||||||||
| .contrib-intro, | ||||||||||||
| .contrib-cta, | ||||||||||||
| .contrib-fallback { | ||||||||||||
| text-align: center; | ||||||||||||
| color: rgba(255, 255, 255, 0.6); | ||||||||||||
| font-size: 0.95rem; | ||||||||||||
| margin: 1.5rem 0; | ||||||||||||
| } | ||||||||||||
| .contrib-intro a, | ||||||||||||
| .contrib-cta a, | ||||||||||||
| .contrib-fallback a { | ||||||||||||
| color: #fff; | ||||||||||||
| text-decoration: underline; | ||||||||||||
| text-underline-offset: 0.2em; | ||||||||||||
| } | ||||||||||||
| .contrib-grid { | ||||||||||||
| list-style: none; | ||||||||||||
| padding: 0; | ||||||||||||
| margin: 2rem 0; | ||||||||||||
| display: grid; | ||||||||||||
| grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); | ||||||||||||
| gap: 0.75rem; | ||||||||||||
| } | ||||||||||||
| .contrib-card { | ||||||||||||
| margin: 0; | ||||||||||||
| } | ||||||||||||
| .contrib-link { | ||||||||||||
| display: flex; | ||||||||||||
| flex-direction: column; | ||||||||||||
| align-items: center; | ||||||||||||
| gap: 0.5rem; | ||||||||||||
| padding: 1rem 0.75rem; | ||||||||||||
| border-radius: 12px; | ||||||||||||
| background: rgba(255, 255, 255, 0.03); | ||||||||||||
| border: 1px solid rgba(255, 255, 255, 0.08); | ||||||||||||
| color: inherit; | ||||||||||||
| text-decoration: none; | ||||||||||||
| transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease; | ||||||||||||
| } | ||||||||||||
| .contrib-link:hover { | ||||||||||||
| border-color: rgba(255, 255, 255, 0.2); | ||||||||||||
| background: rgba(255, 255, 255, 0.06); | ||||||||||||
| transform: translateY(-1px); | ||||||||||||
| } | ||||||||||||
| .contrib-avatar { | ||||||||||||
|
Comment on lines
+98
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
All color values are hardcoded in dark-mode terms ( Prompt To Fix With AIThis is a comment left during a code review.
Path: website/src/pages/contributors.astro
Line: 98-130
Comment:
**Hardcoded dark-mode colors break light theme**
All color values are hardcoded in dark-mode terms (`#fff`, `rgba(255, 255, 255, 0.6)`, `rgba(255, 255, 255, 0.03/0.08/0.2/0.06)`). Starlight ships with a built-in light/dark theme toggle; on the light theme, white text on an off-white card background would be nearly invisible. Prefer CSS custom properties from Starlight's design tokens (e.g. `var(--sl-color-text)`, `var(--sl-color-bg-sidebar)`) so the cards adapt automatically.
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||
| width: 60px; | ||||||||||||
| height: 60px; | ||||||||||||
| border-radius: 50%; | ||||||||||||
| display: block; | ||||||||||||
| } | ||||||||||||
| .contrib-name { | ||||||||||||
| font-size: 0.85rem; | ||||||||||||
| font-weight: 500; | ||||||||||||
| color: #fff; | ||||||||||||
| overflow: hidden; | ||||||||||||
| text-overflow: ellipsis; | ||||||||||||
| white-space: nowrap; | ||||||||||||
| max-width: 100%; | ||||||||||||
| } | ||||||||||||
| .contrib-count { | ||||||||||||
| font-size: 0.75rem; | ||||||||||||
| color: rgba(255, 255, 255, 0.5); | ||||||||||||
| } | ||||||||||||
| </style> | ||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The contributors page fetches only a single page (
per_page=100) and never follows pagination links, so once the repository exceeds 100 human contributors this page will silently omit everyone after the first page and present an incomplete list. Because this route is meant to represent project contributors, the data becomes inaccurate in normal growth scenarios unless additional pages are fetched.Useful? React with 👍 / 👎.