Welcome! This is the UC Open Source Program Office (OSPO) Education website built with Astro and React.
This website helps learners explore open-source learning materials through six main pathways:
- Getting Started with Open Source
- Contributing to a Project
- Maintaining & Sustaining Software
- Building Inclusive Communities
- Understanding Licensing & Compliance
- Strategic Practices & Career Development
keystatic.config.ts # Keystatic CMS configuration (Git-backed editing)
src/
βββ components/
β βββ Card.astro # Basic card component
β βββ Folder.astro # Deprecated folder component
β βββ LessonFilter.tsx # React component for filtering lessons
β βββ PathwayCard.astro # Pathway card component
β βββ StackedPathways.jsx # Interactive stacked pathways (NEW)
βββ content/
β βββ config.ts # Astro Content Collections schema
β βββ lessons/ # Lesson data files (edited via Keystatic)
βββ layouts/
β βββ BaseLayout.astro # Main layout with header/footer
βββ lib/
β βββ lessons.ts # Loads lessons from Astro content collection (primary)
β βββ getSheetData.ts # Fetches from Google Sheets (optional migration source)
βββ pages/
β βββ index.astro # Homepage with stacked pathways
β βββ lessons.astro # Filterable lessons library
β βββ pathways/
β βββ index.astro # All pathways page
β βββ [id].astro # Individual pathway pages
βββ types/
β βββ lesson.ts # TypeScript types for lessons
βββ public/
βββ styles.css # Global styles with UC branding
--uc-blue: #1295D8 /* Primary brand color */
--uc-gold: #FFB511 /* Primary accent color */
--uc-dark-blue: #005581 /* Secondary blue */
--uc-light-blue: #72CDF4 /* Secondary light blue */
--uc-dark-gray: #1E1E1E /* Background */
--uc-med-gray: #2A2A2A /* Card backgrounds */- Primary: Helvetica Neue, Arial (fallback)
- Recommended: Kievit (if available)
- Node.js 18+ installed
- npm or yarn
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
# Preview production build
npm run previewThe site will be available at http://localhost:4321/education
Lessons are stored as files in the repo and edited via Keystatic:
- Keystatic admin UI (local dev only):
http://127.0.0.1:4321/keystatic - Content location:
src/content/lessons/*.json - Schema:
src/content/config.ts - Keystatic config:
keystatic.config.ts - Optional:
getSheetData.tscan fetch from Google Sheets for one-time migration
- Canonical slug is the lesson filename (Astro
entry.id), e.g.src/content/lessons/building-community.json->building-community. - Keep a mirrored
slugfield in each JSON file for compatibility with UI/tooling. - The in-file
slugmust match the filename slug exactly. dependsOninternal references must target this canonical filename slug.- In Keystatic,
Entry Slug (filename)is the internal file/path slug control;slugis the mirrored JSON field.
src/content/
βββ config.ts
βββ lessons/
βββ building-community.json
βββ leadership-and-governance.json
βββ example.json
βββ example-beginner.json
βββ example-advanced.json
Essential fields to display:
name- Lesson titleslug- Must match the JSON filename (canonical slug is still filename)description- Lesson descriptionurl- Link to the lessonlearnerCategory- Which pathway the lesson belongs to (can be left Unassigned)educationalLevel- Beginner/Intermediate/Advancedoss_role- OSS role (Contributor, Maintainer, etc.)subTopic- Grouping within a pathwaykeepStatus- Filter to only show lessons marked "keep" or "keepCandidate"
- Run the site locally:
npm run dev - Open Keystatic:
http://127.0.0.1:4321/keystatic - Edit existing lessons or create new ones under the Lessons collection
- Ensure slug keys are synced to filenames:
npm run sync:lesson-slugs - Validate lesson quality locally:
npm run validate:lessons - Commit the changed files in
src/content/lessons/
Run:
npm run validate:lessonsThe validator checks all lessons but only blocks CI for lesson files changed in the PR.
Blocking rules for changed lessons:
- Required fields must be non-empty strings:
name,slug,description,url slugmust match filename slug and be uniquedependsOnmust contain valid referenceseducationalLevelmust beBeginner,Intermediate, orAdvancedlearnerCategorymust be empty (Unassigned) or match a pathway category- Lesson
urlmust be valid and reachable (2xx/3xx after HEAD/GET checks)
Warning-only rules:
keywordsexists but is empty- Transient URL/network failures (timeouts, temporary connection failures)
- Out-of-scope issues in lesson files not changed by the PR
CI behavior:
- PR checks compute changed lesson files and run validation in delta mode
- A sticky PR comment is updated with validation summary and top issues
- PR fails only when blocking issues are found for changed lesson files
Troubleshooting URL checks:
- Retry if a site had a temporary outage
- Verify the lesson URL is public and reachable without authentication
- If a URL is permanently moved, update the lesson to the final destination
Keystatic supports GitHub-backed edits (creates commits/PRs). To enable this locally:
- Create a GitHub OAuth App for this repo/org
- Add callback URLs:
http://127.0.0.1:4321/api/keystatic/github/oauth/callback
- Create a
.envfile (copy from.env.example) and set:PUBLIC_KEYSTATIC_STORAGE=githubKEYSTATIC_GITHUB_CLIENT_ID=...KEYSTATIC_GITHUB_CLIENT_SECRET=...KEYSTATIC_SECRET=...(random long string)
If you donβt need GitHub auth locally, leave PUBLIC_KEYSTATIC_STORAGE unset and Keystatic will use local storage.
If you need to import the current Google Sheets data into files once:
- Run:
npm run migrate:lessons - This writes
src/content/lessons/*.json - After that, Google Sheets changes will not automatically update the site anymore.
- Stacked, interactive pathway cards
- Matches Figma design
- Uses
StackedPathways.jsxcomponent
- Filterable table view
- Filters: Search, OSS Role, Skill Level, Pathway
- Uses
LessonFilter.tsxReact component
- Dynamic routes for each pathway
- Lessons grouped by subTopic
- Displays metadata badges
β Homepage with stacked pathway navigation β 6 pathway pages with lessons β Filterable lessons library β UC official branding (colors & style) β Google Sheets CSV integration β Responsive design β Accessibility basics
-
Lesson Detail Pages
- Create individual lesson pages
- Show full metadata
- Add "View External Lesson" button
-
Feedback System
- Add feedback form on lesson pages
- Use GitHub Issues API to collect feedback
- Example: https://github.com/carpentries-incubator/proposals/issues/new
-
Roadmap View
- Create visual pathway roadmaps
- Show learning progression
- Step-by-step guided pathways
-
Enhanced Filtering
- Add domain-specific tags
- Lifecycle status filters
- Save filter preferences
-
Search Enhancement
- Implement better search (Algolia/Fuse.js)
- Search across keywords, descriptions
- Highlight search results
-
Progress Tracking (Phase 3)
- Save user progress through pathways
- Mark lessons as completed
- Use localStorage or optional user accounts
- Add more TypeScript types
- Write component tests
- Add ESLint configuration
- Improve accessibility (ARIA labels, keyboard nav)
- Add loading states for data fetching
- Error handling for failed CSV fetches
# Validate lesson metadata and quality rules
npm run validate:lessons
# Run Astro check for type errors
npx astro check
# Build to test for errors
npm run build
# Check built internal links (after build)
npm run check:linksCurrent setup:
- Platform: GitHub Pages
- Base URL:
/education - Site:
https://UC-OSPO-Network.github.io/education
To deploy:
npm run build
# Push to main branch - GitHub Actions will deploy.astrofiles support HTML + JavaScript- Use
---fences for TypeScript/JavaScript code - Components can be Astro (
.astro) or React (.jsx) - Use
client:loaddirective for interactive React components
This project uses strict TypeScript checks via Astroβs strict tsconfig.
- Prefer explicit interfaces for data structures (e.g. CSV rows, props)
- Avoid
any; use indexed signatures when working with CSV-style data - All React components should be typed using
.tsx - Run
npx astro checkbefore submitting changes
Strict typing helps catch errors early and improves maintainability.
import { getActiveLessons } from '../lib/lessons';
// In Astro component
const activeLessons = await getActiveLessons();- Create file in
src/pages/ - Import BaseLayout
- Add navigation link in
BaseLayout.astro
- Global styles:
public/styles.css - Component styles:
<style>tags in.astrofiles - Inline styles: Use CSS variables for consistency
Issue: Keystatic page not loading
- Solution: Ensure
@keystatic/astrois inastro.config.mjsand visithttp://127.0.0.1:4321/keystatic
Issue: GitHub sign-in fails (redirect mismatch)
- Solution: Confirm your OAuth callback URL is exactly
http://127.0.0.1:4321/api/keystatic/github/oauth/callback
Issue: CSV data not loading (migration)
- Solution: Check the published CSV URL in
getSheetData.ts
Issue: React component not interactive
- Solution: Add
client:loaddirective to component
Issue: Build fails
- Solution: Run
astro checkto find TypeScript errors
Questions? Contact:
- Tim Dennis (OSPO lead)
- Check GitHub Issues
- Review Figma design files
Built with β€οΈ by UC OSPO student developers