A learning project for Diversitech Practicum 2026 students.
The goal: practice parallel Git workflows — branching, conflict resolution, and merging — before starting the real client project.
A simple jokes website where each student adds her own joke on a separate Git branch, then the team merges everything together.
The challenge: Cards on the homepage must be ordered alphabetically by last name (Hebrew), without using sort — this deliberately creates merge conflicts that must be resolved manually.
npm install
npm run devOpen http://localhost:5173 in your browser.
src/
├── students/
│ ├── index.ts ← student registry (manual alphabetical order)
│ ├── [student-name]/
│ │ ├── Card.tsx ← personal card component
│ │ ├── JokePage.tsx ← full joke page component
│ │ └── styles.module.css ← personal styles (generic class names only)
│ └── ...
├── pages/
│ ├── HomePage.tsx ← home page — card grid
│ ├── JokePage.tsx ← router page for a specific joke
│ ├── InstructionsPage.tsx ← project instructions
│ └── BranchesPage.tsx ← visual branch graph
├── generated/
│ └── branches.ts ← auto-generated by gen-branches
├── types.ts ← shared TypeScript interfaces
└── App.tsx ← routes
scripts/
└── gen-branches.cjs ← script that generates the branch graph data
Detailed instructions are also available inside the app — click "📋 איך מוסיפים בדיחה?"
git checkout team/[team-name]/sub/[subteam-name]
git pull
git checkout -b team/[team-name]/sub/[subteam-name]/[your-name]src/students/[your-name-in-english]/
├── Card.tsx
├── JokePage.tsx
└── styles.module.css
Card.tsx — minimal template:
import styles from "./styles.module.css";
import { CardProps } from "../../types";
export default function [YourName]Card({ student, onClick }: CardProps) {
return (
<div onClick={onClick} className={`joke-card ${styles.card}`}>
<h2 className={styles.title}>{student.jokeTitle}</h2>
<p className={styles.name}>{student.name}</p>
</div>
);
}JokePage.tsx — minimal template:
import styles from "./styles.module.css";
import { PageProps } from "../../types";
export default function [YourName]JokePage({ onBack }: PageProps) {
return (
<div className={`joke-page ${styles.jokePage}`}>
<p>joke setup</p>
<p>joke punchline</p>
<button onClick={onBack}>← Back</button>
</div>
);
}Important: CSS class names in
styles.module.cssmust be generic (.card,.title, etc.) — no student names in class names. Vite scopes them automatically at build time.
{
id: "[your-name-in-english]",
name: "[full name in Hebrew]",
jokeTitle: "[short title — shown in the browser tab]",
CardComponent: [YourName]Card,
JokePageComponent: [YourName]JokePage,
},git add src/students/[your-name-in-english]/
git add src/students/index.ts
git commit -m "add: [your name] joke"
git push -u origin [your-branch-name]main
└── team/[team-name] ← team branch (15 students)
└── sub/[subteam-name] ← subteam branch (3 students)
└── [student-name] ← personal branch
Merge order (bottom to top):
- Each student → her subteam branch (PR + resolve conflicts)
- Subteam → team branch (PR + resolve conflicts)
- Team →
main(PR + resolve conflicts)
The live branch graph is available inside the app — click "🌿 מפת הבראנצ'ים".
| Command | Description |
|---|---|
npm run dev |
Start dev server (also regenerates branch graph) |
npm run build |
Production build |
npm run gen-branches |
Manually regenerate the branch graph |
The gen-branches script reads local Git data and writes src/generated/branches.ts.
It detects the parent of each branch using git merge-base — independent of branch naming conventions.
npm run gen-branches # run after new branches are created- React 18 + TypeScript
- Vite 5 — build tool + HMR
- React Router 6 — client-side routing
- CSS Modules — scoped styles per student