Skip to content

Describe the featured image without opening the media library - #197

Merged
ssavutu merged 2 commits into
mainfrom
feat/featured-image-alt
Aug 7, 2026
Merged

Describe the featured image without opening the media library#197
ssavutu merged 2 commits into
mainfrom
feat/featured-image-alt

Conversation

@ssavutu

@ssavutu ssavutu commented Aug 6, 2026

Copy link
Copy Markdown
Member

Closes the last gap in in-place alt-text editing. Body images and library records both got it in #190; the featured image did not.

What was wrong

The Featured Image block showed a thumbnail, Change image and Remove. It never displayed the alt text, never warned when there was none, and offered no way to set one. The only route was to click Change image, find the row in the picker, edit the alt there, and close without changing the image — a destructive-looking flow to make a non-destructive edit.

The alt was also discarded on the way in: onSelect kept item.url and dropped item.alt_text, so an image described once in the library arrived at the featured slot undescribed.

Why article-scoped

New photo_alt column rather than a lookup into media.alt_text:

  • The same photo can want a different description in a different story — the same argument that made body attachments article-scoped.
  • A featured image set via Use URL has no library record to read one from.
  • The featured image is stored as a bare URL, so a library-scoped edit would have to resolve that URL back to a row before it knew what to patch.

Nothing is lost by not reading the library. Scalene currently renders alt={normalizeText(post.title)} — the headline, repeated next to itself, which tells a screen-reader user nothing new. The public site can consume featured_image_alt in its own change; until then the CMS is the only place the real description exists.

Behaviour

  • Alt text is editable inline in the Featured Image block and saves with the article.
  • Picking an image adopts the library's description, but only when the image actually changes, so re-picking the same photo cannot overwrite a description written here.
  • Removing the image clears the alt — it belonged to the photo being removed.
  • Missing alt is called out in the destructive colour rather than left silent.

Testing

  • TestArticlePatchHTTP_PhotoAltRoundTrip — persists trimmed, clears on empty, rejects a non-string with 400. Run against a real MariaDB, not just skipped.
  • autosaves featured image alt text on its own — the field is in the autosave snapshot. This has been the failure mode twice: a field outside the body editor that is missing from the snapshot never marks the article dirty and the edit is silently discarded.
  • Full go test ./... with CMS_TEST_DSN set, tsc --noEmit, vitest run, eslint all clean. The one remaining lint warning (saveArticle dep) is pre-existing on main.
  • Verified the EnsureArticlesSchema ALTER applies to a table that predates the column.

Notes

The hint text sits outside its <label> with aria-describedby. Inside, it would have been read as part of the field's name rather than its description.

Adding an article column means touching four SELECT lists feeding one positional ScanArticle, plus the two integration-test schemas that build their own articles table — the full DB-backed suite is what caught those.

🤖 Generated with Claude Code

Body images and library records both got in-place alt editing; the featured
image was left behind. Its block in the editor showed a thumbnail, "Change
image" and "Remove" -- it never displayed the alt text, never warned when there
was none, and offered no way to set it. The only route was to click "Change
image", find the row in the picker, edit the alt there, and close without
changing the image: a destructive-looking flow to make a non-destructive edit.

The alt was also being discarded on the way in. `onSelect` kept `item.url` and
dropped `item.alt_text`, so an image described once in the library arrived at
the featured slot undescribed.

Article-scoped, in a new `photo_alt` column, rather than a lookup into the media
library. Three reasons: the same photo can want a different description in a
different story, which is the same argument that made body attachments
article-scoped; a featured image set by "Use URL" has no library record to read
one from; and the featured image is stored as a bare URL, so a library-scoped
edit would have to resolve that URL back to a row before it knew what to patch.

Nothing is lost by not reading the library: Scalene renders the featured image
as `alt={normalizeText(post.title)}` today, so the live behaviour this replaces
is an alt that repeats the headline sitting next to it -- which tells a screen
reader user nothing they have not already heard. The public site can consume
`featured_image_alt` in its own change; until then the CMS is simply the only
place the real description exists.

Picking an image now adopts the library's description, but only when the image
actually changes, so re-picking the same photo cannot overwrite a description
written here. Removing the image clears it, since the description belonged to
the photo being removed.

The field is in the editor's autosave snapshot. That has been the failure mode
twice now -- a field outside the body editor that is missing from the snapshot
never marks the article dirty, and the edit is silently discarded -- so there is
a regression test that types only alt text and asserts the PATCH.

The hint text sits outside its <label> and is wired up with aria-describedby.
Inside, it would have been read as part of the field's name rather than as its
description.

Adding an article column means touching four separate SELECT lists that feed one
positional ScanArticle, plus the two integration-test schemas that build their
own `articles` table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ssavutu
ssavutu marked this pull request as draft August 6, 2026 07:15
@ssavutu
ssavutu marked this pull request as ready for review August 7, 2026 00:35
Conflict was two tests added at the same point in the autosave describe
block: this branch's "alt text autosaves on its own" and main's three
regenerate-slug cases (#201). They test unrelated fields, so both are
kept.

editArticleView.tsx merged clean and correctly: the snapshot now carries
both photoAlt and main's isNew-scoped slugInput, so an alt-text edit
still autosaves and a regenerated slug still waits for an explicit save.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ssavutu
ssavutu merged commit e9477b8 into main Aug 7, 2026
6 checks passed
@ssavutu
ssavutu deleted the feat/featured-image-alt branch August 7, 2026 00:42
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.

1 participant