Every repo you've starred on GitHub, on one searchable page.
Search across names, owners, descriptions and topics. Filter by language. Sort by when you starred it, how many stars the repo has, or name. The header shows whose stars these are; the footer shows where the data came from and how much GitHub API budget is left.
Both variables are optional β the page works with neither.
| Variable | What it does |
|---|---|
GITHUB_TOKEN | Raises GitHub's rate limit from 60 requests/hour to 5,000. Needs no scopes β this val only reads public data. |
GITHUB_USER | Whose stars to show. Defaults to rodrigoalma. |
π Add GITHUB_TOKEN here: https://www.val.town/x/rodrigoalcaraz/github-stars/environment-variables?key=GITHUB_TOKEN
π Add GITHUB_USER here: https://www.val.town/x/rodrigoalcaraz/github-stars/environment-variables?key=GITHUB_USER
You can also view someone else's stars without touching settings:
?user=torvalds. Add ?refresh=1 to bypass the cache.
The theme is a dark class on <html>, which is what Twind's class dark-mode
strategy keys off. Three pieces:
| Where | What it does |
|---|---|
frontend/root.tsx (inline bootstrap) | Switches Twind from its default media strategy to class, then applies the saved theme (or the OS preference) before first paint β that's what avoids a flash of light theme on a dark screen. |
frontend/lib/theme.ts | The logic: read, apply, toggle, persist to localStorage, and follow the OS while no explicit choice exists. |
frontend/components/ThemeToggle.tsx | The button. Reads its initial state off <html> so the icon always matches what the bootstrap applied. |
Precedence: a saved choice always wins. With nothing saved, the page follows the OS and updates live if the OS flips mid-session.
- The bootstrap is inline on purpose β a
<script type="module">is deferred, so it would paint light-on-dark first. This is the one place inline script is the right tool; the toggle's logic lives in a real, typed module. color-schemeis set alongside the class so the browser paints scrollbars, form controls, and the canvas before Twind's CSS exists.mode: "silent"in the Twind config becausedarkon<html>is not a Twind utility and would otherwise warn on every single page load.- The config call is guarded on
window.twindand tolerates bothinstallandsetup, so a blocked CDN can't take the theme down with it. If the toggle ever stops working, this is the line to check β with the config not applied,dark:falls back to themediastrategy and the page follows the OS instead. - Only color utilities carry
dark:variants. Twind enables the variant for all directives, but Tailwind's own dark mode only generates color-related ones β staying on colors keeps the classes portable.
Rendering mermaid diagram...
| Path | Type | Purpose |
|---|---|---|
index.ts | http | Hono app: serves the shell + assets, GET /api/stars |
github.ts | script | GitHub REST client, the Star shape, and the blob cache |
frontend/root.tsx | script | HTML shell (Hono JSX); stamps immutable asset URLs |
frontend/index.tsx | script | React entrypoint |
frontend/components/App.tsx | script | Data fetch, filtering, sorting, paging |
frontend/components/ProfileHeader.tsx | script | Avatar, name, bio, counts |
frontend/components/Filters.tsx | script | Search box, sort select, language chips |
frontend/components/RepoCard.tsx | script | One starred repo |
frontend/components/SetupNote.tsx | script | The amber box explaining the rate limit |
frontend/components/StarIcon.tsx | script | Inline SVG star |
frontend/lib/types.ts | script | Client-side mirror of the API shapes |
frontend/lib/format.ts | script | compact(), relative(), language colors |
frontend/lib/theme.ts | script | Theme read/apply/toggle + localStorage |
frontend/components/ThemeToggle.tsx | script | The dark-mode button |
frontend/favicon.svg | file | Icon |
- Two things protect the rate limit.
github.tscaches a snapshot in blob storage for 5 minutes, so repeat page views cost zero GitHub requests. When the budget really is gone,/api/starsanswers 429 with the ledger and the page rendersSetupNoteβ instructions for making a token β instead of a bare error. - Never answer 502 from
/api/stars. The CDN replaces an origin 502 with its own HTML error page, so the JSON body never arrives and the client dies onUnexpected token '<'. A missing user is 404, an exhausted limit is 429, a GitHub failure is 500.App.tsxparses the response body itself as a backstop. - The server flattens GitHub's payload.
github.tskeeps only the fields the page renders, so/api/starsstays small even for someone with 1,000 stars (we walk at most 10 pages of 100). frontend/lib/types.tsmirrors the server shapes by hand rather than importinggithub.tsβ a client module may only import things the browser can fetch, andgithub.tspulls instd/blob.- Client components use
className; the Hono JSX shell usesclass. - Styling is Twind (Tailwind utility classes, no build step). Twind's theme is
Tailwind v2 β no
950shades, no opacity modifiers likebg-black/5. Use explicit palette colors and inline styles for computed values (the language dots). - An unquoted object key containing a space silently blanks the whole page.
Vim script: "#199f4b"(no quotes) is accepted in the editor but transpiles toVim, script:in the emitted JavaScript, which throws at module load. The module graph dies before React mounts, so#rootstays empty and nothing reaches the val logs β no error, just a blank page. The fallback text inside#rootinroot.tsxexists so this shows up as visible text instead. If you ever see it, check the browser console, then check the transpiled output of the module that changed. data-statuson the loading/error/empty/count/footer elements,data-repoanddata-languageon each card,data-rate-limiton the footer's API line, anddata-setup="rate-limit"on the amber box β for Playwright.