Skip to content

Lay out the organizers page, and cut the photos square - #199

Merged
DZPM merged 3 commits into
editionfrom
pr/6-organizers
Oct 7, 2026
Merged

DZPM merged 3 commits into
editionfrom
pr/6-organizers

Conversation

@DZPM

@DZPM DZPM commented Oct 5, 2026

Copy link
Copy Markdown
Member

Stacked on #196. Its base is work/check-rendered, so the diff shows only this pull request's own changes. GitHub retargets it as the ones below merge.

Everything about how a person appears on the site: the layout of the grid, the icon beside their name, and the photo itself.

Five to a row, because five is what the page asks for

people_per_line went into col-md-{{ div 12 N }}. Twelve divides by 1, 2, 3, 4, 6 and 12, so any other value was rounded without a word: people_per_line: 5 produced col-md-2, which is six cards to a row. The width now comes from a per_line_N class that takes a fraction of the row, so the number asked for is the number shown.

The count steps down on a narrow screen, because five cards at 768px leave 114px each. A row holds three from 768px, four from 992px, and the full count from 1200px.

One shape per card

The card is a flex column, so every card in a row ends at the same height and the social links sit on the bottom edge whatever the length of the role above them. They were positioned absolutely before, against an ancestor far up the page, because .person_item was never positioned itself. They were in the right place by luck.

The icons are a centred flex row. As inline-block they were separated by the whitespace between the tags, about 4px each, which was enough to push the fourth icon of a narrow card onto a second line. Measured in headless Chromium at seven viewport widths from 360 to 1920: no card wraps its icons at any of them.

The border takes the brand blue on hover and on focus-within.

The X mark

Font Awesome 5.8.2 predates the rename and carries no X icon. Upgrading the vendored set to a 6.x release for one glyph would change every other icon on the site, so the mark is drawn from the Font Awesome 6 Free path as a CSS mask instead. It takes its colour from currentColor, so it follows the same colour and hover rules as the glyphs beside it, and it costs no extra request.

Person cards, bio dialogs and the footer all use it, and every social link gains a title, so a pointer gets the name a screen reader already had.

The photos are cut square here, not on every build

The site serves every person photo as a square. The square was cut at build time, which costs twice: the crop is picked by a rule that does not know where the face is, and the repository carries sources far larger than anything the site ever serves.

bin/square-photos cuts it once, so the crop is reviewable in a pull request like any other change and the build only resizes. It finds the face with OpenCV, crops the largest square it can centre on that face, and resizes to at most 800px.

It never enlarges, so the 27 photos under 400px stay small and the check reports them. It does not zoom in either: a full-length shot on a beach stays a full-length shot on a beach, and the fix for those is a different photo.

Before After
Square photos 76 of 132 132 of 132
Source weight 38.7 MB 12.2 MB
Largest file 5.0 MB 755 KB

66 of the 132 photos changed.

Picking the right face took three attempts, and each wrong one was found by looking at the output. The cascade returns several boxes per portrait. Taking the largest put the crop on a subject's torso. Taking the most confident put it on a 59px patch of background while the real face, ten times wider, scored lower. Dropping every box wider than 60% of the short side fixed the first and broke a close selfie whose face really is 64% of it. Width alone cannot tell a selfie from a torso; the score can, 7.27 against 1.72. So a wide box survives when it is confident, and the largest survivor wins.

One photo carries EXIF Orientation 5. A browser honours that tag and shows the picture upright, so the site looked right while every tool reading the pixels saw it on its side. The rotation is now in the pixels and the tag is gone.

bin/check-content rejects a photo that is not square, one over 1200px, and one over 1000 KB. That last limit is for the pull request nobody reads closely: a photo straight off a phone is several megabytes and tens of megapixels, and it costs the repository that much for ever. Verified by planting a 900x600 photo and a 1500x1500 one.

How it was checked

The four checks pass. The layout was measured in headless Chromium rather than eyeballed, and every crop was reviewed one by one against the original before being applied.

@DZPM
DZPM requested a review from a team as a code owner October 5, 2026 18:34
@DZPM DZPM self-assigned this Oct 5, 2026
DZPM added 3 commits October 7, 2026 11:36
people_per_line went into a Bootstrap column class, col-md-{12/N}. Twelve
divides by 1, 2, 3, 4, 6 and 12, so any other value was rounded without a
word: five asked for col-md-2, which is six cards to a row. The width now
comes from a per_line_N class that takes a fraction of the row, so the
number asked for is the number shown.

The count steps up with the viewport. Five cards on a 768px screen leave
114px each, too narrow for a name, so a row holds three from 768px, four
from 992px, and the full count from 1200px.

The card is a flex column, so every card in a row ends at the same height
and the social links sit on the bottom edge whatever the length of the role
above them. They were positioned absolutely before, against an ancestor far
up the page, because this box was never positioned itself.

The icons are a centred flex row. As inline-block they were separated by
the whitespace between the tags, about 4px each, which was enough to push
the fourth icon of a narrow card onto a second line. Flex drops those text
nodes, so the spacing is the one set here and nothing else.

The border takes the brand blue on hover and on focus-within, so the card
the pointer or the keyboard is on is obvious.

The organizers page asks for five to a row in its first three sections, and
the section titles now name the year each board belongs to.
Font Awesome 5.8.2 predates the rename and carries no X icon. Upgrading the
vendored set to a 6.x release for one glyph would change every other icon
on the site, so the mark is drawn from the Font Awesome 6 Free path as a
CSS mask instead. It takes its colour from currentColor, so it follows the
same colour and hover rules as the glyphs beside it, and it costs no extra
request.

The person cards, the bio dialogs and the footer all use it. The labels now
say X rather than Twitter.

Every social link also gains a title, so a pointer gets the same name a
screen reader already had.

The twitter key keeps its name in the person front matter, and the links
still point at twitter.com, which redirects. Renaming the key would touch
every person file that has one, for no gain today.
The site serves every person photo as a square. The square was cut at build
time, which costs twice: the crop is picked by a rule that does not know where
the face is, and the repository carries sources far larger than anything the
site ever serves.

bin/square-photos cuts it once, here, so the crop is reviewable in a pull
request like any other change and the build only resizes. It finds the face
with OpenCV, crops the largest square it can centre on that face, and resizes
to at most 800px, which is what the 2x variant of a 400px grid slot needs.

It never enlarges. A 200px photo blown up to 800 is the same picture in four
times the bytes, so the 27 photos under 400px stay small and the check reports
them.

Picking the right face took three attempts, and each wrong one was found by
looking at the output rather than by reasoning about it, which is the argument
for cutting the crop here instead of in the build.

The cascade returns several boxes per portrait. Taking the largest put the
crop on a subject's torso. Taking the most confident put it on a 59px patch of
background while the real face, ten times wider, scored lower. Dropping every
box wider than 60% of the short side fixed the first and broke a close selfie
whose face really is 64% of it. Width alone cannot tell a selfie from a torso;
the score can, 7.27 against 1.72. So a wide box survives when it is confident,
and the largest survivor wins.

One photo carries EXIF Orientation 5. A browser honours that tag and shows the
picture upright, so the site looked right while every tool reading the pixels
saw it on its side. The rotation is now in the pixels and the tag is gone.

66 of the 132 photos changed. The sources go from 38.7 MB to 12.2 MB and the
largest file from 5.0 MB to 755 KB. All 132 are now square.

check-content rejects a photo that is not square, one over 1200px, and one
over 1000 KB. That last one is for the pull request nobody reads closely: a
photo straight off a phone is several megabytes and tens of megapixels, and it
costs the repository that much for ever.
@DZPM
DZPM force-pushed the pr/6-organizers branch from 7a3f301 to d08d219 Compare October 7, 2026 09:37
@DZPM
DZPM changed the base branch from work/check-rendered to edition October 7, 2026 09:37
@DZPM
DZPM merged commit 9c01ce7 into edition Oct 7, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants