From 15ecd5f9115279b80fb799002917b6c50709a530 Mon Sep 17 00:00:00 2001 From: Nick the Sick Date: Wed, 6 May 2026 18:57:13 +0200 Subject: [PATCH 1/2] fix(a11y): use figure/figcaption for media block captions Closes #2055. Supersedes #2056. When a file/image/video/audio block has a caption, render the wrapper as
with a
instead of a
+

. 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 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../helpers/render/createFileBlockWrapper.ts | 9 +++++++-- packages/core/src/blocks/Image/block.ts | 11 +++++++++-- .../File/helpers/render/FileBlockWrapper.tsx | 14 +++++++++++--- packages/react/src/blocks/Image/block.tsx | 13 +++++++++---- .../text/html/basicBlocksWithProps.html | 2 +- .../copy/__snapshots__/text/html/image.html | 2 +- .../__snapshots__/text/html/nestedImage.html | 2 +- .../__snapshots__/blocknoteHTML/file/basic.html | 6 +++--- .../__snapshots__/blocknoteHTML/file/nested.html | 12 ++++++------ .../__snapshots__/blocknoteHTML/file/noName.html | 6 +++--- .../__snapshots__/blocknoteHTML/image.html | 2 +- .../__snapshots__/blocknoteHTML/image/basic.html | 8 ++++---- .../blocknoteHTML/image/nested.html | 16 ++++++++-------- .../blocknoteHTML/image/noName.html | 8 ++++---- .../blocknoteHTML/image/noPreview.html | 6 +++--- .../blocknoteHTML/image/withCaption.html | 8 ++++---- .../blocknoteHTML/video/withCaption.html | 6 +++--- .../export/__snapshots__/html/image.html | 2 +- .../export/__snapshots__/html/image/basic.html | 2 +- .../export/__snapshots__/html/image/nested.html | 4 ++-- .../export/__snapshots__/html/image/noName.html | 2 +- .../__snapshots__/html/image/withCaption.html | 2 +- .../export/__snapshots__/markdown/image.md | 2 +- .../blocknoteHTML/reactFile/basic.html | 6 +++--- .../blocknoteHTML/reactFile/nested.html | 12 ++++++------ .../blocknoteHTML/reactFile/noName.html | 6 +++--- .../blocknoteHTML/reactImage/basic.html | 8 ++++---- .../blocknoteHTML/reactImage/nested.html | 16 ++++++++-------- .../blocknoteHTML/reactImage/noName.html | 8 ++++---- .../blocknoteHTML/reactImage/noPreview.html | 6 +++--- .../__snapshots__/html/reactImage/basic.html | 2 +- .../__snapshots__/html/reactImage/nested.html | 4 ++-- .../__snapshots__/html/reactImage/noName.html | 2 +- 33 files changed, 120 insertions(+), 95 deletions(-) diff --git a/packages/core/src/blocks/File/helpers/render/createFileBlockWrapper.ts b/packages/core/src/blocks/File/helpers/render/createFileBlockWrapper.ts index cbb347acff..51de658d2c 100644 --- a/packages/core/src/blocks/File/helpers/render/createFileBlockWrapper.ts +++ b/packages/core/src/blocks/File/helpers/render/createFileBlockWrapper.ts @@ -26,7 +26,12 @@ export const createFileBlockWrapper = ( element?: { dom: HTMLElement; destroy?: () => void }, buttonIcon?: HTMLElement, ) => { - const wrapper = document.createElement("div"); + // Use a

/
when the block has a caption, so the caption + // is semantically associated with its content for assistive tech. Falls back + // to a plain
when there is no caption (or the file has not been + // uploaded yet, since the upload UI never shows the caption). + const useFigure = block.props.url !== "" && !!block.props.caption; + const wrapper = document.createElement(useFigure ? "figure" : "div"); wrapper.className = "bn-file-block-content-wrapper"; // Show the add file button if the file has not been uploaded yet. Change to @@ -73,7 +78,7 @@ export const createFileBlockWrapper = ( // Show the caption if there is one. if (block.props.caption) { - const caption = document.createElement("p"); + const caption = document.createElement("figcaption"); caption.className = "bn-file-caption"; caption.textContent = block.props.caption; wrapper.appendChild(caption); diff --git a/packages/core/src/blocks/Image/block.ts b/packages/core/src/blocks/Image/block.ts index 503c483527..fc6d4b3377 100644 --- a/packages/core/src/blocks/Image/block.ts +++ b/packages/core/src/blocks/Image/block.ts @@ -116,7 +116,11 @@ export const imageRender = image.src = block.props.url; } - image.alt = block.props.name || block.props.caption || "BlockNote image"; + // When a caption is set, the wrapper renders a
that serves as + // the image's accessible name, so an empty alt avoids double-announcement. + // Otherwise prefer the file name; fall back to "" (decorative) when neither + // is provided. + image.alt = block.props.caption ? "" : block.props.name || ""; image.contentEditable = "false"; image.draggable = false; imageWrapper.appendChild(image); @@ -150,7 +154,10 @@ export const imageToExternalHTML = if (block.props.showPreview) { image = document.createElement("img"); image.src = block.props.url; - image.alt = block.props.name || block.props.caption || "BlockNote image"; + // When a caption is set, createFigureWithCaption wraps the image in a + //
/
below, which serves as the accessible name. + // An empty alt avoids screen readers announcing the caption twice. + image.alt = block.props.caption ? "" : block.props.name || ""; if (block.props.previewWidth) { image.width = block.props.previewWidth; } diff --git a/packages/react/src/blocks/File/helpers/render/FileBlockWrapper.tsx b/packages/react/src/blocks/File/helpers/render/FileBlockWrapper.tsx index 6499b40ca0..f277db693f 100644 --- a/packages/react/src/blocks/File/helpers/render/FileBlockWrapper.tsx +++ b/packages/react/src/blocks/File/helpers/render/FileBlockWrapper.tsx @@ -29,8 +29,14 @@ export const FileBlockWrapper = ( ) => { const showLoader = useUploadLoading(props.block.id); + // Use a
/
when the block has a caption, so the caption + // is semantically associated with its content for assistive tech. + const useFigure = + props.block.props.url !== "" && !!props.block.props.caption && !showLoader; + const Wrapper = useFigure ? "figure" : "div"; + return ( -
{props.block.props.caption}

+
+ {props.block.props.caption} +
)} )} -
+ ); }; diff --git a/packages/react/src/blocks/Image/block.tsx b/packages/react/src/blocks/Image/block.tsx index 92de17d8cc..9718301b1d 100644 --- a/packages/react/src/blocks/Image/block.tsx +++ b/packages/react/src/blocks/Image/block.tsx @@ -18,6 +18,10 @@ export const ImagePreview = ( ) => { const resolved = useResolveUrl(props.block.props.url!); + // When a caption is set, FileBlockWrapper renders a
that serves + // as the image's accessible name; an empty alt avoids double-announcement. + const alt = props.block.props.caption ? "" : props.block.props.name || ""; + return ( {props.block.props.caption @@ -43,12 +47,13 @@ export const ImageToExternalHTML = ( return

Add image

; } + // When a caption is set, the image is wrapped in
/
+ // below; an empty alt avoids double-announcement of the caption text. + const alt = props.block.props.caption ? "" : props.block.props.name || ""; const image = props.block.props.showPreview ? ( { ) : ( diff --git a/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocksWithProps.html b/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocksWithProps.html index 782aab8b66..4cb72b3758 100644 --- a/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocksWithProps.html +++ b/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocksWithProps.html @@ -54,7 +54,7 @@

Heading 1

> 1280px-Placeholder_view_vector.svg.png
Placeholder
diff --git a/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/image.html b/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/image.html index 8b48774f5b..dbeb38ec30 100644 --- a/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/image.html +++ b/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/image.html @@ -1,5 +1,5 @@ BlockNote image \ No newline at end of file diff --git a/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/nestedImage.html b/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/nestedImage.html index ece8708071..45b2830cd7 100644 --- a/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/nestedImage.html +++ b/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/nestedImage.html @@ -1,7 +1,7 @@

Paragraph 1

BlockNote image diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/basic.html index 9974d8d975..d7802da3e3 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/basic.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/basic.html @@ -9,7 +9,7 @@ data-caption="Caption" data-file-block="" > -
+
@@ -20,8 +20,8 @@

example

-

Caption

-
+
Caption
+
diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/nested.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/nested.html index 6553a5c4a8..c62486d27d 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/nested.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/nested.html @@ -9,7 +9,7 @@ data-caption="Caption" data-file-block="" > -
+
@@ -20,8 +20,8 @@

example

-

Caption

-
+
Caption
+
@@ -34,7 +34,7 @@ data-caption="Caption" data-file-block="" > -
+
@@ -45,8 +45,8 @@

example

-

Caption

-
+
Caption
+
diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/noName.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/noName.html index 47ae5b3bf9..1ec20af747 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/noName.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/file/noName.html @@ -8,7 +8,7 @@ data-caption="Caption" data-file-block="" > -
+
@@ -19,8 +19,8 @@

-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image.html index cf93dca684..a17b1d3982 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image.html @@ -15,7 +15,7 @@ BlockNote image diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html index 5c7411f3ed..1e6591a369 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html @@ -10,17 +10,17 @@ data-preview-width="256" data-file-block="" > -
- example +
-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/nested.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/nested.html index 7eac772896..a5b2647bec 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/nested.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/nested.html @@ -9,17 +9,17 @@ data-preview-width="256" data-file-block="" > -
- Caption +
-

Caption

-
+
Caption
+
@@ -32,17 +32,17 @@ data-preview-width="256" data-file-block="" > -
- Caption +
-

Caption

-
+
Caption
+
diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noName.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noName.html index 315d8db293..7032fc34e8 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noName.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noName.html @@ -9,17 +9,17 @@ data-preview-width="256" data-file-block="" > -
- Caption +
-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noPreview.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noPreview.html index 3e1f5a6264..59d65f82ae 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noPreview.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/noPreview.html @@ -11,7 +11,7 @@ data-preview-width="256" data-file-block="" > -
+
@@ -22,8 +22,8 @@

example

-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html index bca753588e..5292742600 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html @@ -9,7 +9,7 @@ data-caption="This is a caption" data-file-block="" > -
@@ -17,14 +17,14 @@ Example Image
-

This is a caption

- +
This is a caption
+ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html index 9de3da2aca..b85963a4e8 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html @@ -9,7 +9,7 @@ data-caption="Video caption" data-file-block="" > -
@@ -24,8 +24,8 @@
-

Video caption

- +
Video caption
+ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image.html index ef7342fe92..a9efaefefc 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image.html @@ -1,5 +1,5 @@ BlockNote image \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/basic.html index 4f365f8d88..0fa5bf338c 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/basic.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/basic.html @@ -4,6 +4,6 @@ data-caption="Caption" data-preview-width="256" > - example +
Caption
\ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/nested.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/nested.html index 04ccf17c56..667cef41ab 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/nested.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/nested.html @@ -1,5 +1,5 @@
- Caption +
Caption
- Caption +
Caption
\ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/noName.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/noName.html index 686fc7d4e5..47f0cbe255 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/noName.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/noName.html @@ -1,4 +1,4 @@
- Caption +
Caption
\ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html index 3ecba73103..3a322f85b0 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html @@ -3,6 +3,6 @@ data-name="Example Image" data-caption="This is a caption" > - Example Image +
This is a caption
\ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image.md index 3219bb9f00..58d07ff1a4 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image.md +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image.md @@ -1 +1 @@ -![BlockNote image](https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png) +![](https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png) diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/basic.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/basic.html index 9974d8d975..d7802da3e3 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/basic.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/basic.html @@ -9,7 +9,7 @@ data-caption="Caption" data-file-block="" > -
+
@@ -20,8 +20,8 @@

example

-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/nested.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/nested.html index 6553a5c4a8..c62486d27d 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/nested.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/nested.html @@ -9,7 +9,7 @@ data-caption="Caption" data-file-block="" > -
+
@@ -20,8 +20,8 @@

example

-

Caption

-
+
Caption
+
@@ -34,7 +34,7 @@ data-caption="Caption" data-file-block="" > -
+
@@ -45,8 +45,8 @@

example

-

Caption

-
+
Caption
+
diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/noName.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/noName.html index 47ae5b3bf9..1ec20af747 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/noName.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactFile/noName.html @@ -8,7 +8,7 @@ data-caption="Caption" data-file-block="" > -
+
@@ -19,8 +19,8 @@

-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html index 5c7411f3ed..1e6591a369 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html @@ -10,17 +10,17 @@ data-preview-width="256" data-file-block="" > -
- example +
-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html index 3a0d0d50b0..c26f90e9d4 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html @@ -10,17 +10,17 @@ data-preview-width="256" data-file-block="" > -
- example +
-

Caption

-
+
Caption
+
@@ -34,17 +34,17 @@ data-preview-width="256" data-file-block="" > -
- example +
-

Caption

-
+
Caption
+
diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noName.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noName.html index 315d8db293..7032fc34e8 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noName.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noName.html @@ -9,17 +9,17 @@ data-preview-width="256" data-file-block="" > -
- Caption +
-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noPreview.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noPreview.html index 3e1f5a6264..59d65f82ae 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noPreview.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/noPreview.html @@ -11,7 +11,7 @@ data-preview-width="256" data-file-block="" > -
+
@@ -22,8 +22,8 @@

example

-

Caption

-
+
Caption
+ diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/basic.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/basic.html index 4f365f8d88..0fa5bf338c 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/basic.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/basic.html @@ -4,6 +4,6 @@ data-caption="Caption" data-preview-width="256" > - example +
Caption
\ No newline at end of file diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/nested.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/nested.html index 8dfc8061ff..a9fa90407d 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/nested.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/nested.html @@ -4,7 +4,7 @@ data-caption="Caption" data-preview-width="256" > - example +
Caption
- example +
Caption
\ No newline at end of file diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/noName.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/noName.html index 686fc7d4e5..47f0cbe255 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/noName.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/noName.html @@ -1,4 +1,4 @@
- Caption +
Caption
\ No newline at end of file From 28991e20bc21c954d0803238015d0303117923e8 Mon Sep 17 00:00:00 2001 From: Nick the Sick Date: Wed, 6 May 2026 19:12:42 +0200 Subject: [PATCH 2/2] fix(a11y): reset figure default margins; refresh server-util snapshots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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
(captioned) or
(uncaptioned). Browser default
margin is `1em 40px`, whereas the previous
+

structure had the

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) --- packages/core/src/blocks/Image/block.ts | 14 +++++--------- packages/core/src/editor/Block.css | 3 +++ packages/react/src/blocks/Image/block.tsx | 11 +++++------ .../ServerBlockNoteEditor.test.ts.snap | 2 +- .../text/html/basicBlocksWithProps.html | 2 +- .../__snapshots__/blocknoteHTML/image/basic.html | 2 +- .../__snapshots__/blocknoteHTML/image/urlOnly.html | 7 +------ .../blocknoteHTML/image/withCaption.html | 2 +- .../export/__snapshots__/html/image/basic.html | 2 +- .../export/__snapshots__/html/image/urlOnly.html | 2 +- .../__snapshots__/html/image/withCaption.html | 2 +- .../export/__snapshots__/markdown/image/urlOnly.md | 2 +- .../blocknoteHTML/reactImage/basic.html | 2 +- .../blocknoteHTML/reactImage/nested.html | 4 ++-- .../__snapshots__/html/reactImage/basic.html | 2 +- .../__snapshots__/html/reactImage/nested.html | 4 ++-- 16 files changed, 28 insertions(+), 35 deletions(-) diff --git a/packages/core/src/blocks/Image/block.ts b/packages/core/src/blocks/Image/block.ts index fc6d4b3377..9de973bb49 100644 --- a/packages/core/src/blocks/Image/block.ts +++ b/packages/core/src/blocks/Image/block.ts @@ -116,11 +116,10 @@ export const imageRender = image.src = block.props.url; } - // When a caption is set, the wrapper renders a

that serves as - // the image's accessible name, so an empty alt avoids double-announcement. - // Otherwise prefer the file name; fall back to "" (decorative) when neither - // is provided. - image.alt = block.props.caption ? "" : block.props.name || ""; + // alt describes image content (per WCAG H86); figcaption (when present) + // is the contextual caption. Fall back to "" so unlabelled images are + // marked decorative rather than getting a noisy generic fallback. + image.alt = block.props.name || ""; image.contentEditable = "false"; image.draggable = false; imageWrapper.appendChild(image); @@ -154,10 +153,7 @@ export const imageToExternalHTML = if (block.props.showPreview) { image = document.createElement("img"); image.src = block.props.url; - // When a caption is set, createFigureWithCaption wraps the image in a - //
/
below, which serves as the accessible name. - // An empty alt avoids screen readers announcing the caption twice. - image.alt = block.props.caption ? "" : block.props.name || ""; + image.alt = block.props.name || ""; if (block.props.previewWidth) { image.width = block.props.previewWidth; } diff --git a/packages/core/src/editor/Block.css b/packages/core/src/editor/Block.css index bba397ee58..990a856504 100644 --- a/packages/core/src/editor/Block.css +++ b/packages/core/src/editor/Block.css @@ -468,6 +468,9 @@ NESTED BLOCKS cursor: pointer; display: flex; flex-direction: column; + /* Reset default
browser margins (the wrapper becomes a
+ when the block has a caption). */ + margin: 0; user-select: none; } diff --git a/packages/react/src/blocks/Image/block.tsx b/packages/react/src/blocks/Image/block.tsx index 9718301b1d..be682b47c2 100644 --- a/packages/react/src/blocks/Image/block.tsx +++ b/packages/react/src/blocks/Image/block.tsx @@ -18,9 +18,10 @@ export const ImagePreview = ( ) => { const resolved = useResolveUrl(props.block.props.url!); - // When a caption is set, FileBlockWrapper renders a
that serves - // as the image's accessible name; an empty alt avoids double-announcement. - const alt = props.block.props.caption ? "" : props.block.props.name || ""; + // alt describes image content (per WCAG H86); figcaption (when present) + // is the contextual caption. Fall back to "" so unlabelled images are + // marked decorative rather than getting a noisy generic fallback. + const alt = props.block.props.name || ""; return ( Add image

; } - // When a caption is set, the image is wrapped in
/
- // below; an empty alt avoids double-announcement of the caption text. - const alt = props.block.props.caption ? "" : props.block.props.name || ""; + const alt = props.block.props.name || ""; const image = props.block.props.showPreview ? ( converts to HTML (blocksToFullHTML) 1`] = `"

Heading 2

Paragraph

list item

Example

Caption

Example

Caption

"`; +exports[`Test ServerBlockNoteEditor > converts to HTML (blocksToFullHTML) 1`] = `"

Heading 2

Paragraph

list item

Example
Caption

Example

Caption
"`; exports[`Test ServerBlockNoteEditor > converts to and from HTML (blocksToHTMLLossy) 1`] = `"

Heading 2

Paragraph

  • list item

Example
Caption
Example

Caption

"`; diff --git a/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocksWithProps.html b/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocksWithProps.html index 4cb72b3758..782aab8b66 100644 --- a/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocksWithProps.html +++ b/tests/src/unit/core/clipboard/copy/__snapshots__/text/html/basicBlocksWithProps.html @@ -54,7 +54,7 @@

Heading 1

>
Placeholder
diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html index 1e6591a369..55c9555506 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/basic.html @@ -15,7 +15,7 @@ style="position: relative; width: 256px;" >
- + example
diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/urlOnly.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/urlOnly.html index f529f9e2bb..6940729386 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/urlOnly.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/urlOnly.html @@ -12,12 +12,7 @@ style="position: relative; width: fit-content;" >
- BlockNote image +
diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html index 5292742600..5844c98607 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html @@ -17,7 +17,7 @@ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/basic.html index 0fa5bf338c..4f365f8d88 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/basic.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/basic.html @@ -4,6 +4,6 @@ data-caption="Caption" data-preview-width="256" > - + example
Caption
\ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/urlOnly.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/urlOnly.html index 80685a3c89..41960a99f8 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/urlOnly.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/urlOnly.html @@ -1 +1 @@ -BlockNote image \ No newline at end of file + \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html index 3a322f85b0..3ecba73103 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html @@ -3,6 +3,6 @@ data-name="Example Image" data-caption="This is a caption" > - + Example Image
This is a caption
\ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image/urlOnly.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image/urlOnly.md index dcf874d74c..f667f8e031 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image/urlOnly.md +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image/urlOnly.md @@ -1 +1 @@ -![BlockNote image](exampleURL) +![](exampleURL) diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html index 1e6591a369..55c9555506 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/basic.html @@ -15,7 +15,7 @@ style="position: relative; width: 256px;" >
- + example
diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html index c26f90e9d4..a6296f823b 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/blocknoteHTML/reactImage/nested.html @@ -15,7 +15,7 @@ style="position: relative; width: 256px;" >
- + example
@@ -39,7 +39,7 @@ style="position: relative; width: 256px;" >
- + example
diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/basic.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/basic.html index 0fa5bf338c..4f365f8d88 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/basic.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/basic.html @@ -4,6 +4,6 @@ data-caption="Caption" data-preview-width="256" > - + example
Caption
\ No newline at end of file diff --git a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/nested.html b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/nested.html index a9fa90407d..8dfc8061ff 100644 --- a/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/nested.html +++ b/tests/src/unit/react/formatConversion/export/__snapshots__/html/reactImage/nested.html @@ -4,7 +4,7 @@ data-caption="Caption" data-preview-width="256" > - + example
Caption
- + example
Caption
\ No newline at end of file