Skip to content

Commit e720d54

Browse files
committed
Document speaker photo conventions and browser QA in CLAUDE.md
The data-loading and grid-filtering notes described behaviour the code no longer has.
1 parent 37e7174 commit e720d54

1 file changed

Lines changed: 73 additions & 25 deletions

File tree

CLAUDE.md

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,36 @@ Production: https://dev-mtl.ca | Bilingual (FR/EN) conference site for /dev/mtl,
55
## Commands
66

77
```bash
8-
yarn dev # Vite dev server
9-
yarn build # Production build (copies index.html → 404.html for GH Pages SPA routing)
10-
yarn typecheck # tsc --noEmit (strict)
11-
yarn lint # ESLint, zero warnings allowed (--max-warnings=0)
12-
yarn format # Prettier --write
13-
yarn format-check # Prettier --list-different
14-
yarn test # Vitest (happy-dom environment)
8+
yarn dev # Vite dev server
9+
yarn build # Production build (copies index.html → 404.html for GH Pages SPA routing)
10+
yarn typecheck # tsc --noEmit (strict)
11+
yarn lint # ESLint, zero warnings allowed (--max-warnings=0)
12+
yarn format # Prettier --write
13+
yarn format-check # Prettier --list-different
14+
yarn test # Vitest (happy-dom environment)
1515
```
1616

1717
**CI runs on every push to main:** typecheck → lint → build → deploy to GitHub Pages.
18-
Always run `yarn typecheck` and `yarn lint` before committing.
18+
There is no staging — pushing to `main` publishes to https://dev-mtl.ca. Always run `yarn typecheck`
19+
and `yarn lint` before committing.
20+
21+
`yarn format-check` currently flags a dozen pre-existing files. Check that your own files are clean
22+
rather than expecting the whole command to pass.
23+
24+
### QA a content change in the browser
25+
26+
Content changes (speaker, sponsor, schedule) are only really verified by looking at the page. Start the
27+
dev server through the preview tooling — never `yarn dev` in a shell — using the `dev` config already in
28+
`.claude/launch.json`, then open the page the change affects:
29+
30+
| Change | Page to check |
31+
| ------------------------ | ---------------------------------------- |
32+
| Speaker bio, talk, photo | `/speaker/{slug}` and `/speakers/{year}` |
33+
| Talk time or track | `/schedule` |
34+
| Sponsor | `/sponsors` and the home page |
35+
36+
Read the page text and console, screenshot the result, and stop the server when done. Verify it yourself —
37+
don't hand the URL to Nicolas and ask him to look.
1938

2039
## Tech Stack
2140

@@ -54,13 +73,16 @@ public/ # Static assets (images referenced in JSON data)
5473

5574
All content is JSON files in `src/assets/`. Loaded via `loadData<T>(folder)` which uses `import.meta.glob`.
5675

57-
To add a new year of speakers: create `src/assets/speakers-{year}/`, add a glob entry in `src/utils/loadData.ts`, and update `SpeakersPage.tsx`.
76+
Speakers load through `loadSpeakers<T>(year)`, which globs `speakers-*` and filters by year prefix.
77+
**Adding a speaker — or a whole new year — needs no code change:** drop the JSON in `src/assets/speakers-{year}/`
78+
and it is picked up. `SpeakersPage` reads the year from the route param, and `scripts/export-schedule-to-markdown.js`
79+
does a `readdir` on the folder.
5880

5981
### Internationalization — inline, no library
6082

6183
```tsx
6284
const { t } = useTranslation();
63-
<h2>{t({ fr: "Conférenciers", en: "Speakers" })}</h2>
85+
<h2>{t({ fr: "Conférenciers", en: "Speakers" })}</h2>;
6486
```
6587

6688
All translations are inline using `t()`. No separate translation files. Every user-visible string must have both `fr` and `en`.
@@ -75,6 +97,20 @@ Rendered as: `src={`${import.meta.env.BASE_URL}${speaker.image}`}`.
7597

7698
SVGs imported as React components via svgr: `import Logo from "../../assets/logo.svg?react"`.
7799

100+
#### Speaker & organizer photos
101+
102+
Square **400×400**, `.jpg` or `.png`. This is the 2026 convention and it is applied consistently — 2024/2025
103+
are a mess (200×200 up to 4418×6627), don't copy them. Filename must match the speaker slug.
104+
105+
Speakers send their photo by email, so it usually lands in `~/Downloads`. Resize and convert in one step:
106+
107+
```bash
108+
sips -s format jpeg -s formatOptions 80 -Z 400 ~/Downloads/photo.png --out public/speakers-2026/hugues-lamy.jpg
109+
```
110+
111+
`speakers-{year}/user.png` is the placeholder for a speaker we have no photo of yet. `hasPhoto()` in
112+
`src/utils/speakerFilters.ts` uses it to keep those out of the home page showcases.
113+
78114
### Speaker URL slugs
79115

80116
Generated at navigation time from the `name` field: lowercase → NFD decomposition (strip accents) → replace spaces with hyphens. Keep this logic consistent between `SpeakersPage.tsx` and `SpeakerPage.tsx`.
@@ -95,25 +131,36 @@ Centralized in `src/constants/navlinks.ts` with bilingual names. Do not hardcode
95131
## Adding Content
96132

97133
### Speaker (`src/assets/speakers-{year}/{name}.json`)
134+
98135
```json
99136
{
100137
"name": "Full Name",
101138
"bio": "Bio or null",
102139
"position": "Title or null",
103-
"image": "speakers-2025/photo.jpg",
104-
"time": "2025-11-28T09:00:00",
140+
"community": "Montréal Ruby",
141+
"image": "speakers-2026/photo.jpg",
142+
"time": "2026-11-27T09:00:00",
105143
"track": 1,
106144
"title": "Talk Title",
107145
"description": "Talk description",
108146
"github": "username or null",
109147
"linkedin": "URL or null",
110-
"website": "URL or null"
148+
"website": "URL or null",
149+
"videoLink": "URL"
111150
}
112151
```
113152

114-
Speakers with `title === "Intro"` are filtered out from the speakers grid.
153+
`community` (partner meetup hosting the session) and `videoLink` are optional — omit them rather than
154+
setting `null`. Every other field is required.
155+
156+
Multi-paragraph `bio` and `description` are plain strings with `\n\n` between paragraphs — no markdown.
157+
158+
`hasAnnouncedTalk()` in `src/utils/speakerFilters.ts` keeps a session out of the speakers grid when `name`
159+
is empty, `title` is empty, or `title === "Intro"`. Such entries still show on the schedule — that is how
160+
unannounced slots (keynote, partner meetups) are placeholdered.
115161

116162
### Sponsor (`src/assets/sponsors/{name}.json`)
163+
117164
```json
118165
{
119166
"name": "Company",
@@ -126,6 +173,7 @@ Speakers with `title === "Intro"` are filtered out from the speakers grid.
126173
```
127174

128175
### Organizer (`src/assets/organizers/{name}.json`)
176+
129177
```json
130178
{
131179
"name": "Full Name",
@@ -139,17 +187,17 @@ Speakers with `title === "Intro"` are filtered out from the speakers grid.
139187

140188
## Routing
141189

142-
| Path | Page | Layout |
143-
|---|---|---|
144-
| `/` | HomePage | Own Navbar + Footer |
145-
| `/about` | AboutPage | Shared Layout |
146-
| `/venue` | VenuePage | Shared Layout |
147-
| `/schedule` | SchedulePage | Shared Layout |
148-
| `/speakers/:year` | SpeakersPage | Shared Layout |
149-
| `/speaker/:name` | SpeakerPage | Shared Layout |
150-
| `/sponsors` | SponsorsPage | Shared Layout |
151-
| `/transparency` | TransparencyPage | Shared Layout |
152-
| `/conduct` | ConductPage | Shared Layout |
190+
| Path | Page | Layout |
191+
| ----------------- | ---------------- | ------------------- |
192+
| `/` | HomePage | Own Navbar + Footer |
193+
| `/about` | AboutPage | Shared Layout |
194+
| `/venue` | VenuePage | Shared Layout |
195+
| `/schedule` | SchedulePage | Shared Layout |
196+
| `/speakers/:year` | SpeakersPage | Shared Layout |
197+
| `/speaker/:name` | SpeakerPage | Shared Layout |
198+
| `/sponsors` | SponsorsPage | Shared Layout |
199+
| `/transparency` | TransparencyPage | Shared Layout |
200+
| `/conduct` | ConductPage | Shared Layout |
153201

154202
`Layout` uses `<Outlet />` and derives the page title from `navLinks` based on the current path.
155203

0 commit comments

Comments
 (0)