Imported from FreeForCharity/FFC-IN-ffcadmin.org (
AGENTS.md). Install upstream withnpx skills add FreeForCharity/FFC-IN-ffcadmin.org. Copyright stays with the author.
AI Agent Instructions: FFC-IN-ffcadmin.org
Project: FFC-IN-ffcadmin.org -- a Free For Charity nonprofit website (ffcadmin.org)
Organization: Free For Charity provides free, professionally built websites for 501(c)(3) nonprofit organizations. Every repo in this organization serves that mission.
Tech Stack
| Layer | Technology |
|---|---|
| Framework | Next.js with App Router (see package.json for version) |
| Language | TypeScript (strict mode) |
| Styling | Tailwind CSS v4 (CSS-based config, no tailwind.config file) |
| Export | Static (output: 'export' in next.config.ts) |
| Hosting | GitHub Pages (custom domain + subpath fallback) |
| CI/CD | GitHub Actions |
| Testing | Jest + Testing Library, Playwright (E2E), jest-axe (accessibility) |
Core Commands
| Command | What It Does | Typical Duration |
|---|---|---|
pnpm install |
Install dependencies | ~17s |
pnpm run dev |
Start dev server | ~1s startup |
pnpm run format |
Run Prettier to format code | ~2s |
pnpm run lint |
Run ESLint | ~2s |
pnpm test |
Run Jest unit tests | ~5s |
pnpm run build |
Production static build | ~30s |
pnpm run test:e2e |
Run Playwright E2E tests | ~15s |
NEVER CANCEL long-running commands. Builds and E2E tests take time. Set your timeout to 180+ seconds and let them finish.
Development Workflow
All changes follow this process:
- Issue -- Work starts from a GitHub Issue
- Branch -- Create a feature branch from
main - Develop -- Make changes, commit frequently
- Pre-commit checklist (run in this order -- build before tests, the same ordering CI uses; CI additionally uses
format:check):pnpm run format-- Auto-fix formattingpnpm run lint-- Catch code quality issuespnpm run type-check-- Verify TypeScript types (CI runs this)pnpm run build-- Verify the static export succeedspnpm test-- Run unit tests (some tests check the build output, so build first)pnpm run test:e2e-- Run end-to-end tests
- PR -- Open a Pull Request, link to the issue with
Fixes #NNNorRefs #NNN - Merge -- Merge via merge queue (no direct commits to
main)
Project Architecture
src/
app/ # Next.js App Router -- pages and layouts
page.tsx # Home page
layout.tsx # Root layout
[route]/page.tsx # Additional routes (e.g., privacy-policy/)
components/ # Reusable UI components
data/ # Content modules (.ts) and JSON data files
lib/ # Utility functions and helpers
assetPath.ts # GitHub Pages asset path helper
public/ # Static assets (Images/, Svgs/, fonts, favicons)
next.config.ts # Next.js configuration
tsconfig.json # TypeScript configuration
Naming Conventions
ALL route folders MUST use kebab-case. This is an SEO best practice per Google Search Central. URLs like /about-us are preferred over /aboutUs or /about_us.
Examples:
src/app/about-us/page.tsx(correct)src/app/aboutUs/page.tsx(wrong)src/app/contact-form/page.tsx(correct)
Component files use PascalCase: HeroSection.tsx, DonateButton.tsx.
GitHub Pages & Asset Paths
These sites deploy to https://freeforcharity.github.io/FFC-IN-ffcadmin.org/ and optionally to a custom domain if one is configured for this repo.
Always use the assetPath() helper from src/lib/assetPath.ts for image and asset references:
import { assetPath } from '@/lib/assetPath';
// Correct -- works on both custom domain and GitHub Pages subpath
<img src={assetPath('/Images/hero.jpg')} alt="Hero" />
// Wrong -- breaks on GitHub Pages subpath
<img src="/Images/hero.jpg" alt="Hero" />
The NEXT_PUBLIC_BASE_PATH environment variable controls the basePath in next.config.ts. The build system handles this automatically; you should not hardcode paths.
Security
- NEVER expose API tokens or secrets in code, comments, or documentation
- NEVER hardcode secrets in any file
- In GitHub Actions workflows, ALWAYS use
${{ secrets.SECRET_NAME }}syntax - ALWAYS validate that secrets exist before using them in workflows
- NEVER echo or print secrets to logs
- For local development, use
.envfiles (excluded from git via.gitignore) - If a user provides a secret, DO NOT write it in any file. Instruct them to add it to GitHub Secrets or a local
.envfile.
Testing Strategy
| Type | Tool | Purpose |
|---|---|---|
| Unit | Jest + Testing Library | Component rendering, utility functions |
| Accessibility | jest-axe | WCAG compliance, ARIA validation |
| E2E | Playwright | Full page navigation, visual regression |
Accessibility target: WCAG AA compliance. The jest-axe integration catches common ARIA issues, color contrast violations, and missing landmarks.
Known Issues
- ESLint
imgwarnings: Some ESLint rules flag<img>tags in favor ofnext/image. For static exports,<img>withassetPath()is the correct approach. These warnings are expected. - Google Fonts: Font loading may fail on restricted networks or air-gapped environments. The site should degrade gracefully with system fonts.
- Static export limitations: Dynamic features like API routes, middleware, and ISR are not available. All pages must be statically renderable at build time.
Commit Message Format
Use Conventional Commits format: <type>: <description>
| Type | When to Use |
|---|---|
feat: |
New feature or page |
fix: |
Bug fix |
docs: |
Documentation only |
style: |
Formatting (no code change) |
refactor: |
Code restructuring (no behavior change) |
test: |
Adding or updating tests |
chore: |
Build config, dependencies, CI |
Example: feat: add volunteer signup form with validation
CI Pipeline
GitHub Actions enforces the following on every PR:
- Prettier --
pnpm run format:check(formatting must pass) - ESLint --
pnpm run lint(no errors allowed) - TypeScript --
pnpm run type-check(no type errors) - Build --
pnpm run build(static export must succeed) - Jest --
pnpm test(all unit tests must pass; some assert against the build output) - Playwright --
pnpm run test:e2e(E2E tests must pass) - CodeQL -- Static analysis and security scanning (separate workflow)
PRs cannot merge until all checks pass.
External links are checked weekly, not per PR. PR CI skips every external URL (flaky). linkinator-external.yml scans the built site on Mondays for 4xx/5xx, then scripts/check-soft-404.mjs re-reads every URL that answered 200 and fails on "not found" / "retired" wording or on a missing phrase from .link-expectations.json — a vendor that moves content often keeps serving 200 on the old URL. Add an expectation when a page's content matters, not just its existence.
Section-Specific Guides
Some sections have their own contribution patterns. Read the relevant guide before working in them.
| Section | Guide |
|---|---|
/legacy-wordpress-administration/ |
CONTRIBUTING-LEGACY-WP-ADMIN.md |
