# Side Project Showcase — Free Astro Template

A standalone "side projects inventory" page for indie makers, engineers, and hobbyists. Ships with a TypeScript data file, client-side filters, status badges, stack chips, permalink anchors, and a JSON feed endpoint — all fully static.

![Preview](./preview.png)

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

---

## Features

- **TypeScript-typed project data** — edit `src/data/projects.ts`, get build-time type errors if you misspell a status
- **Status badge system** — Live, Archived, Sold, WIP, OSS — each WCAG AA verified
- **Client-side filter bar** — status chips, stack dropdown, text search, `/` keyboard shortcut — all with AND logic, no JS framework needed
- **Hybrid layout** — 2-3 featured cards at top, 3-col grid below, responsive to 1-col on mobile
- **Permalink anchors** — every project card has an `id` matching its slug for deep linking
- **JSON feed endpoint** — `/projects.json` for embedding your project list elsewhere
- **Dark mode** — system-preference toggle, persists to localStorage, no flash on load
- **Warm copper palette** — `#B5541C` accent on `#FAF7F2` cream, dark-mode-aware
- **Schema.org JSON-LD** — `Person` + `ItemList` of `SoftwareApplication` items
- **GitHub Pages workflow** — push to your repo, enable Pages, done

## Stack

- Astro 4 + Tailwind CSS 3
- `@fontsource/epilogue` (display + body weight 400–800)
- `@fontsource/jetbrains-mono` (year ranges, stack chips, metrics)
- `@astrojs/sitemap`
- Playwright + `@axe-core/playwright` + Lighthouse CI

---

## Quick start

```bash
# 1. Download from the live demo (recommended):
#    templates.allanninal.dev/side-project-showcase/
#    Click "Source (Astro)" to get the full source ZIP.

# 2. Unzip and init:
unzip side-project-showcase-source.zip -d my-projects && cd my-projects
git init && git add . && git commit -m "init from side-project-showcase template"
npm install

# 3. Develop:
npm run dev

# 4. Build:
npm run build        # outputs to dist/
npm run preview      # serve dist/ locally
```

---

## Deploy to GitHub Pages (3 steps)

1. Push your repo to GitHub.
2. Repo Settings → Pages → Source: **GitHub Actions**.
3. Push to `main`. The included `.github/workflows/pages.yml` builds and deploys automatically.

The workflow reads `GITHUB_REPOSITORY` to resolve `PUBLIC_BASE_PATH` and `PUBLIC_SITE_URL` — no manual edits needed.

### Custom domain

Set two **repository variables** (Settings → Secrets and variables → Actions → Variables):

```
PUBLIC_BASE_PATH = /
PUBLIC_SITE_URL  = https://your-custom-domain.com
```

### Alternate deploys

| Host | Steps |
|---|---|
| **Vercel** | Import repo → Framework: Astro → deploy. Set `PUBLIC_SITE_URL` and `PUBLIC_BASE_PATH=/` in env vars. |
| **Netlify** | Import repo → Build command: `npm run build` → Publish dir: `dist`. Same env vars. |
| **Cloudflare Pages** | Connect repo → Framework: Astro. Same env vars. |

---

## Customize

### Add / edit your projects

All project data is in `src/data/projects.ts`. Edit the `projects` array and the `PERSONA` object. TypeScript will catch any misspelled status values at build time.

```ts
// src/data/projects.ts
export const projects: Project[] = [
  {
    slug: 'my-project',       // used as the permalink anchor #my-project
    name: 'My Project',
    description: 'One-line description of what it does.',
    status: 'Live',           // 'Live' | 'Archived' | 'Sold' | 'WIP' | 'OSS'
    stack: ['Go', 'Postgres'],
    yearStart: 2024,
    yearEnd: 'ongoing',       // or a number: 2025
    url: 'https://myproject.dev',
    metric: '500 users',      // optional: shown in the card
    featured: true,           // optional: card appears in the top featured row
    icon: '🚀',              // optional: emoji shown in the card header
  },
  // ...
];
```

Mark 2-3 entries `featured: true` — they appear in the larger top row.

### Change your persona

```ts
export const PERSONA = {
  name: 'Your Name',
  title: 'Your city-based engineer',
  bio: 'Your primary tech stack',
  github: 'https://github.com/yourusername',
  twitter: 'https://x.com/yourusername',
  linkedin: 'https://www.linkedin.com/in/yourusername/',
  email: 'you@example.com',
  lastUpdated: 'May 2026',
};
```

### Change colors

Edit the CSS variables in `src/styles/global.css`:

```css
:root {
  --color-accent: #B5541C;       /* copper-rust — your primary brand color */
  --color-bg: #FAF7F2;           /* warm cream background */
  /* ... */
}
[data-theme="dark"] {
  --color-accent: #D4713A;       /* lighter in dark mode for contrast */
  /* ... */
}
```

### Change fonts

Edit `src/styles/global.css` (import lines) and `tailwind.config.mjs` (fontFamily):

```bash
npm install @fontsource/your-font
```

### Custom domain for social links

Edit `PERSONA.linkedin` in `src/data/projects.ts` and the contact links in `src/components/SiteFooter.astro`.

---

## Testing

```bash
npm run test:all        # build → Playwright → link check → Lighthouse CI
npm run test            # Playwright suite only
npm run test:a11y       # axe-core accessibility suite
npm run test:links      # broken link check
npm run test:lighthouse # Lighthouse CI (rebuilds without hub URL first)
```

### What each suite covers

| Suite | Coverage |
|---|---|
| `pages.spec.ts` | Every route renders with h1 + footer; projects.json returns valid data |
| `interactions.spec.ts` | Status chip filtering, stack dropdown, text search, `/` shortcut, clear filters, empty state, TemplateInfo bar, theme toggle |
| `responsive.spec.ts` | Every route × 4 viewports (375/768/1280/1920), sticky filter bar, 1-col grid on mobile |
| `theme.spec.ts` | Light + dark on every route, token values, persistence across reload |
| `a11y.spec.ts` | axe-core WCAG 2.1 AA on every route × both themes |
| `links.spec.ts` | No broken internal links, external links have `rel="noopener noreferrer"`, sitemap + robots.txt accessible |
| `template-info.spec.ts` | Download bar renders, all 4 hrefs correct, zero github.com links inside |

### Adding tests for a new page or interaction

1. Add the route to `ROUTES` in `tests/helpers.ts`.
2. Add a test block in `tests/interactions.spec.ts` for any new interactive element.
3. The responsive and a11y suites automatically cover new routes via the `ROUTES` array.

---

## Support / donations

This template is **free and MIT-licensed**. If it saved you time, [tip on Ko-fi](https://ko-fi.com/allanninal) — no signup, no subscription.

**Sponsor button:** The repo's GitHub Sponsor button is driven by `.github/FUNDING.yml`. Edit `ko_fi: allanninal` to your own handle, or delete the file to hide it.

**Footer Ko-fi link:** controlled by `PUBLIC_AUTHOR_DONATE_URL` in `.env`. Set blank to hide:

```bash
PUBLIC_AUTHOR_DONATE_URL=   # blank = hidden
```

**Your own donate button:** set `PUBLIC_USER_DONATE_URL=https://ko-fi.com/yourhandle` — a prominent donate button renders in the footer (any URL works).

---

## License

MIT. Use for personal, commercial, or client projects — no attribution required (though appreciated).

Built and maintained by [Allan Ninal](https://www.linkedin.com/in/allanninal/) as part of a 365-day free template library at [templates.allanninal.dev](https://templates.allanninal.dev).
