Skip to content

fix(a11y): use figure/figcaption for media block captions#2717

Open
nperez0111 wants to merge 2 commits intomainfrom
feat/revisit-original-issue
Open

fix(a11y): use figure/figcaption for media block captions#2717
nperez0111 wants to merge 2 commits intomainfrom
feat/revisit-original-issue

Conversation

@nperez0111
Copy link
Copy Markdown
Contributor

@nperez0111 nperez0111 commented May 6, 2026

Summary

Render media blocks (image / video / audio / file) with <figure>+<figcaption> when a caption is set. Closes #2055, supersedes #2056.

Changes

  • createFileBlockWrapper.ts / FileBlockWrapper.tsx: wrapper element is <figure> when a caption is shown, <div> otherwise; caption uses <figcaption> instead of <p>.
  • Image/block.ts and React Image/block.tsx — alt text:
    • caption present → alt="" (figcaption is the accessible name; avoids double-announcement)
    • no caption, name present → alt={name}
    • neither → alt="" (decorative; no extra ARIA)
  • React Video/Audio previews: removed interim aria-describedby — figure/figcaption now provides the association.
  • Block.css: reset margin: 0 on .bn-file-block-content-wrapper so the figure version matches the previous div layout (figure default margin: 1em 40px was not covered by the existing .bn-default-styles p-reset).
  • Snapshots refreshed (core, react, server-util).

Notes

  • Parsing was already covered: Image/Video/Audio/File parsers handle <figure>+<figcaption> via parseFigureElement (the export path was already producing that structure).
  • Did not run a manual browser-side accessibility check — flagging explicitly.

Co-authored with @Ovgodd, whose original PR did the alt-text simplification preserved here.

🤖 Generated with Claude Code

Closes #2055. Supersedes #2056.

When a file/image/video/audio block has a caption, render the wrapper
as <figure> with a <figcaption> instead of a <div>+<p>. This matches
the WCAG-recommended semantic for caption-content association and
removes the need for ad-hoc ARIA fallbacks.

Image alt text logic is also tightened:
- caption present -> alt="" (the figcaption is the accessible name;
  this avoids screen readers double-announcing the caption)
- no caption, name present -> alt={name}
- neither -> alt="" (decorative; aria-hidden was dropped because it
  would have removed unintentionally-unlabeled images from the
  accessibility tree entirely)

Co-Authored-By: Cyril G <c.gromoff@gmail.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
blocknote Ready Ready Preview May 6, 2026 5:17pm
blocknote-website Ready Ready Preview May 6, 2026 5:17pm

Request Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

📝 Walkthrough

Walkthrough

The PR updates file and image block rendering to use semantic

/ when a caption and URL exist, and makes image alt text caption-aware (empty when caption present, otherwise uses name), with small CSS to reset figure margins.

Changes

File & Image Accessibility (semantic figures + caption-aware alt text)

Layer / File(s) Summary
Data / Condition
packages/core/src/blocks/File/helpers/render/createFileBlockWrapper.ts, packages/react/src/blocks/File/helpers/render/FileBlockWrapper.tsx
Introduce useFigure flag determined by block.props.url !== "" && !!block.props.caption (core) and equivalent URL/caption/loading checks (react).
Core Rendering
packages/core/src/blocks/File/helpers/render/createFileBlockWrapper.ts
Wrapper creation becomes conditional: render figure when useFigure true, otherwise div. Caption element changed from <p> to <figcaption>.
React Component Wiring
packages/react/src/blocks/File/helpers/render/FileBlockWrapper.tsx
Dynamic Wrapper (either figure or div) used; loader, AddFileButton and preview logic preserved; caption rendered inside <figcaption> when present.
Alt Text Logic
packages/core/src/blocks/Image/block.ts, packages/react/src/blocks/Image/block.tsx
Compute an alt that is empty when a caption exists; otherwise use the file/image name or "". Apply consistently in preview and external HTML paths with clarifying comments.
Styling
packages/core/src/editor/Block.css
Reset default browser margins for <figure> within .bn-file-block-content-wrapper when caption present (small CSS addition).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FileBlock as File Block
    participant Wrapper
    participant DOM

    User->>FileBlock: Add or view file with metadata (url, caption)
    FileBlock->>Wrapper: Evaluate url and caption (useFigure)
    alt caption exists and url present (useFigure)
        Wrapper->>DOM: Render <figure> container
        FileBlock->>DOM: Render file preview / loader inside figure
        FileBlock->>DOM: Render <figcaption> with caption text
    else no caption or missing url
        Wrapper->>DOM: Render <div> container
        FileBlock->>DOM: Render file preview / loader
        FileBlock->>DOM: Optionally render non-figure caption fallback
    end
    DOM-->>User: Display semantic structure
Loading
sequenceDiagram
    participant User
    participant ImageBlock as Image Block
    participant AltLogic as Alt Logic
    participant DOM

    User->>ImageBlock: Render image (with/without caption)
    ImageBlock->>AltLogic: Compute alt (caption-aware)
    alt caption exists
        AltLogic-->>ImageBlock: alt = ""
        ImageBlock->>DOM: Render <img alt=""> inside <figure>
        ImageBlock->>DOM: Render <figcaption> visible caption
    else no caption
        AltLogic-->>ImageBlock: alt = image name or ""
        ImageBlock->>DOM: Render <img alt="name"> (or decorative)
    end
    DOM-->>User: Accessible announcement without duplication
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I stitched a figure, neat and true,
A caption nestled, snug in view,
Alt goes quiet when fig sings loud,
Screen readers smile, the audience proud —
Hopping onward, accessibility anew.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(a11y): use figure/figcaption for media block captions' clearly and specifically describes the main change—adopting semantic figure/figcaption elements for captioned media to fix accessibility.
Linked Issues check ✅ Passed The PR successfully implements the core requirement from #2055: using / for semantic association and tightening alt-text logic per WCAG H67 (caption→alt='', name→alt=name, neither→alt='').
Out of Scope Changes check ✅ Passed All changes—figure/figcaption wrappers, alt-text tightening, CSS margin resets, aria-describedby removal—directly address the linked issue and accessibility objectives; no unrelated refactoring or scope creep detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description is comprehensive and well-structured, covering all required sections from the template including Summary, Rationale (Changes), Impact (Checklist acknowledgments), and Additional Notes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/revisit-original-issue

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Two CI fixes for #2717:

- Block.css: add `margin: 0` to .bn-file-block-content-wrapper so the
  wrapper looks identical whether it renders as <figure> (captioned)
  or <div> (uncaptioned). Browser default <figure> margin is `1em 40px`,
  whereas the previous <div>+<p class="bn-file-caption"> structure had
  the <p> margins reset by .bn-default-styles. Without this reset the
  captioned-image visual snapshot grew by ~50px.
- server-util/ServerBlockNoteEditor.test.ts.snap: refresh — these
  snapshots cover the full HTML/markdown round-trip and were missed
  in the previous snapshot pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 6, 2026

Open in StackBlitz

@blocknote/ariakit

npm i https://pkg.pr.new/@blocknote/ariakit@2717

@blocknote/code-block

npm i https://pkg.pr.new/@blocknote/code-block@2717

@blocknote/core

npm i https://pkg.pr.new/@blocknote/core@2717

@blocknote/mantine

npm i https://pkg.pr.new/@blocknote/mantine@2717

@blocknote/react

npm i https://pkg.pr.new/@blocknote/react@2717

@blocknote/server-util

npm i https://pkg.pr.new/@blocknote/server-util@2717

@blocknote/shadcn

npm i https://pkg.pr.new/@blocknote/shadcn@2717

@blocknote/xl-ai

npm i https://pkg.pr.new/@blocknote/xl-ai@2717

@blocknote/xl-docx-exporter

npm i https://pkg.pr.new/@blocknote/xl-docx-exporter@2717

@blocknote/xl-email-exporter

npm i https://pkg.pr.new/@blocknote/xl-email-exporter@2717

@blocknote/xl-multi-column

npm i https://pkg.pr.new/@blocknote/xl-multi-column@2717

@blocknote/xl-odt-exporter

npm i https://pkg.pr.new/@blocknote/xl-odt-exporter@2717

@blocknote/xl-pdf-exporter

npm i https://pkg.pr.new/@blocknote/xl-pdf-exporter@2717

commit: d520572

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.

Image caption isn't associated to the image in an accessible way

1 participant