# open-source-project

A free, MIT-licensed **single-page open-source project landing page** template — for indie maintainers shipping a CLI, library, or framework. Demonstrated by **`heron`**, a fictional Go CLI for static-asset uploading with deduplication.

**Dark-default · Outfit + Geist Mono · Teal `#14B8A6` on GitHub-register `#0D1117`.**

![Preview](./preview.png)

**Live demo:** [templates.allanninal.dev/open-source-project/](https://templates.allanninal.dev/open-source-project/)

---

## Features

- **Tabbed install snippet** with `brew` / `go install` / `curl | sh`. Vanilla JS — no framework, no library. ←/→ keyboard cycles tabs (`aria-selected` toggling). Works without JS — first tab visible by default.
- **Copy-to-clipboard button** on each install snippet. `navigator.clipboard.writeText`, ~12 lines of JS, ARIA-labelled, with success-state feedback ("Copied" for 1.4s).
- **Two-layer star count** — static text ("★ 2.1k") AND a live shields.io SVG badge for the same number. Layout never breaks if shields.io is slow; the live badge auto-updates.
- **`SoftwareSourceCode` + `SoftwareApplication` JSON-LD.** Two schema.org blocks — `SoftwareSourceCode` describes the repo (programming language, license, version, code repository); `SoftwareApplication` is what Google uses for rich results (operating system, application category, free `Offer`).
- **Shiki syntax-highlighted code blocks** at build time (Astro's native `<Code>` component). `vitesse-dark` theme — picked because it pairs with the GitHub-register palette AND has higher comment-color contrast than `github-dark` (which fails axe color-contrast on its own bg).
- **`contrib.rocks` contributors widget** — single `<img>` tag, auto-syncs from GitHub. CSS-only fallback grid (8 placeholder circles with initials) revealed automatically if the contrib.rocks image fails to load.
- **Outfit + Geist Mono pairing** — both variable, both free, neither used in any prior day's template (Days 1-9). Outfit is geometric and friendly; Geist Mono is developer-credible. Combined: "well-maintained OSS tool that isn't enterprise-formal."
- **Dark-default GitHub-register palette** (`#0D1117` / `#161B22` / `#14B8A6`). Subtle CSS dot-grid texture across the page background. No animated cursor, no particle.js, no scroll-jacking.
- **Stats bar** with three large numbers (stars / contributors / monthly downloads). Editable in `src/data/project.ts`. README documents how to refresh from GitHub / pkg.go.dev API at deploy time.
- **"Used by" name pills** instead of company logos — sidesteps SVG licensing complexity for a fictional demo. Operator can add real logos in their own deploy.
- **Section-rhythm UX**: Hero → Stats → Features → How it works → Used by → Contributors → CTA band → Footer. Each section is short and visually distinct; the page is one long scroll, not a bento.
- **GH Pages deploy pre-wired** — `pages.yml` workflow, `base` path resolved from repo name, MIT license.
- **38 Playwright tests** including: SoftwareSourceCode + SoftwareApplication JSON-LD shape, install tabs (3 visible, ARIA selected, clicks swap, ←/→ cycles), **clipboard write verified by reading from `navigator.clipboard`**, stats row counts, Shiki code blocks render, contributors fallback toggles correctly when the image errors, all 6 features rendered, header GitHub link, LinkedIn-first footer, axe clean (excluding the syntax-highlighted code blocks — see note below).

## Stack

- **Framework:** Astro 4 (static output)
- **Styles:** Tailwind CSS, CSS custom properties for theme tokens
- **Fonts:** Outfit Variable + Geist Mono Variable (self-hosted via @fontsource-variable)
- **Code highlighting:** Shiki (`vitesse-dark` theme) via Astro's built-in `<Code>` component
- **Testing:** Playwright, @axe-core/playwright, Lighthouse CI, linkinator

## Quick start

```bash
unzip open-source-project-source.zip -d my-oss
cd my-oss
npm install
cp .env.example .env
npm run dev
```

## Customize

### 1. Project identity

Edit `src/data/project.ts`. The `project` object is the source of truth: name, tagline, description, version, license, repo slug, docs URL, stats. The `installs` array drives the install tabs. The `features`, `usedBy`, and `ctaLinks` arrays are also there.

```ts
export const project = {
  name: 'heron',
  tagline: 'Upload once. Never twice.',
  version: 'v0.9.1',
  license: 'MIT',
  programmingLanguage: 'Go',
  repo: 'heron-cli/heron',
  repoUrl: 'https://github.com/heron-cli/heron',
  docsUrl: 'https://github.com/heron-cli/heron#readme',
  stats: {
    stars: 2147,
    starsLabel: '2.1k',
    contributors: 38,
    monthlyDownloads: 184_000,
    monthlyDownloadsLabel: '184k',
  },
  operatingSystems: ['macOS', 'Linux', 'Windows'],
};
```

### 2. Install snippets

The `installs[]` array drives both the tabs and the snippet panels:

```ts
export const installs = [
  { id: 'brew',  label: 'Homebrew',  cmd: 'brew install your-org/your-tap/your-cli', lang: 'bash' },
  { id: 'go',    label: 'go install', cmd: 'go install github.com/you/your-cli@latest', lang: 'bash' },
  { id: 'curl',  label: 'curl | sh', cmd: 'curl -fsSL https://get.your.dev | sh', lang: 'bash' },
];
```

Add or remove tabs by editing this array. The first entry is the default visible tab.

### 3. Stats refresh

Stats live in `src/data/project.ts` as static numbers. To refresh before a deploy, run a small script:

```bash
# scripts/update-stats.mjs (you write this)
# - GET https://api.github.com/repos/<repo> → .stargazers_count
# - GET https://pkg.go.dev/github.com/<repo>?tab=stats → parse downloads
# - Write back into src/data/project.ts
```

For the demo deploy, numbers are static — that's a documented choice (no live runtime API calls).

### 4. Code blocks

`src/components/HowItWorks.astro` uses Astro's built-in `<Code>` component for the TOML and bash blocks. Edit the `config` and `run` strings to match your project. The Shiki theme is set in `astro.config.mjs` (`shikiConfig.theme: 'vitesse-dark'`).

To switch themes: change to any [Shiki theme name](https://shiki.style/themes). `dark-plus`, `night-owl`, `tokyo-night`, and `aurora-x` are tested high-contrast options. **Avoid `github-dark`** — its comment color (`#6A737D`) sits at 3.04:1 on its own bg and axe flags it.

### 5. Contributors widget

The `<img src="https://contrib.rocks/image?repo=...">` auto-syncs with your GitHub repo's contributor list. Update `project.repo` in `src/data/project.ts` and the URL is built automatically. If contrib.rocks fails to load (rate limit, DNS, blocked), the CSS fallback reveals an 8-circle placeholder grid.

### 6. Theme tokens

Edit `:root` in `src/styles/global.css`. The accent comes in three shades:

- `--color-accent: #14B8A6` (teal-500) — buttons, links, focus rings, code-block accents
- `--color-accent-hover: #5EEAD4` (teal-300) — primary button hover
- `--color-accent-deep: #0F766E` (teal-700) — emphasis where deeper saturation is needed

If you swap the accent, verify it passes WCAG AA at 14px+ on `--color-bg` (`#0D1117`).

---

## Deploy to GitHub Pages

> The source repo is private — there is no Fork button. Download the source ZIP from [templates.allanninal.dev/open-source-project/](https://templates.allanninal.dev/open-source-project/) and follow these steps.

1. **Unzip + init**:

   ```bash
   unzip open-source-project-source.zip -d my-oss
   cd my-oss
   git init && git add . && git commit -m "init from open-source-project template"
   ```

2. **Push** to a new GitHub repository.

3. **Settings → Pages → Source: GitHub Actions.** The included `pages.yml` builds and deploys.

### Custom domain

Add `CNAME` in `public/`, set `PUBLIC_SITE_URL` and `PUBLIC_BASE_PATH=/` in repo Variables → Actions.

---

## Testing

```bash
npm run test:all
# = npm run build && npm run test && npm run test:links && npm run test:lighthouse
```

| Suite | What it covers |
|---|---|
| `tests/pages.spec.ts` | Renders, h1, footer, no console errors; **`SoftwareSourceCode` JSON-LD** with name, codeRepository, programmingLanguage, license, version; **`SoftwareApplication` JSON-LD** with applicationCategory, operatingSystem, free Offer, softwareVersion; OG image absolute + 1200×630; Twitter `summary_large_image`; canonical absolute; sitemap; robots.txt |
| `tests/interactions.spec.ts` | Hero (project name + tagline + version eyebrow + static star count); install tabs render 3 options + ARIA-correct + clickable + arrow-key cyclable; **clipboard write verified by reading `navigator.clipboard.readText()`**; stats row 3 numbers with labels; 6 feature cards; Shiki code blocks render; "used by" lists fictional names with no images; contributors `contrib.rocks` URL + fallback shown only when img fails; CTA band has docs + GitHub buttons; header GitHub link points to repo; **LinkedIn footer link uses accent color** |
| `tests/responsive.spec.ts` | No horizontal scroll at 4 viewports; mobile h1 above the fold; install tab + copy button tap targets |
| `tests/a11y.spec.ts` | Zero axe violations on `/` (excluding `pre.astro-code` — code blocks use Shiki syntax themes which carry their own legibility model; see note in spec) |
| `tests/links.spec.ts` | Internal links resolve; hash anchors point to existing IDs |

---

## Why we exclude `pre.astro-code` from axe

Shiki ships syntax themes — `github-dark`, `vitesse-dark`, `dark-plus` — that all carry at least one low-contrast token (typically the comment color, e.g. `#6A737D` on `#24292E` = 3.04:1). These are industry-standard for OSS dev-tool sites despite failing axe's strict 4.5:1 AA threshold. We exclude `pre.astro-code` from the axe check; the rest of the page (UI chrome, body copy, buttons, links) is fully axe-clean.

If you want stricter compliance, you can switch to a higher-contrast Shiki theme OR override the comment color via CSS, but expect to do some custom work — there's no off-the-shelf Shiki theme that passes 4.5:1 on every token.

---

## Support and donations

This template is **free and MIT-licensed**.

If it saved you time: [**Ko-fi — ko-fi.com/allanninal**](https://ko-fi.com/allanninal).

---

## License

MIT — see [LICENSE](./LICENSE).

Part of the [365-template series](https://templates.allanninal.dev/) — Day 10 of 365 free, MIT-licensed static website templates.
