Description
Add a caption field to the image properties dialog in the Block Editor (libs/new-block-editor), persist it as a new attribute on the dotImage node, and render it as a <figcaption> across every renderer.
Today the dialog exposes three fields — Image URL (src), Tooltip (title) and Alt text (alt). There is no way to attach a visible caption to an image. title is the browser tooltip and must stay that way; the caption is a distinct, visible piece of content.
Current state (verified)
- Dialog template:
core-web/libs/new-block-editor/src/lib/editor/components/image-popover/image-popover.component.html — three controls, all bound to form.controls.{src,title,alt}.
- Node:
core-web/libs/new-block-editor/src/lib/editor/extensions/nodes/image.extension.ts — DotImage already serializes to <figure> in both renderHTML() and addNodeView(), so <figcaption> has a natural place to live.
- VTL renderer:
dotCMS/src/main/webapp/WEB-INF/velocity/static/storyblock/dotImage.vtl — already emits a <figure> wrapper.
Design decision
A new caption node attribute, rendered as <figcaption> inside the existing <figure>. title keeps its current meaning (tooltip). No fallback from title to caption — they are independent fields.
Files in scope
| File |
Change |
.../components/image-popover/image-popover.component.html |
Add the Caption input |
.../components/image-popover/image-popover.component.ts |
Add caption to the form group; read/write the node attr |
.../extensions/nodes/image.extension.ts |
Add caption attribute with parseHTML / renderHTML; emit <figcaption> in renderHTML() and addNodeView() |
dotCMS/src/main/webapp/WEB-INF/messages/Language.properties |
New keys: ...field.caption.label, ...field.caption.hint, ...field.caption.placeholder |
dotCMS/src/main/webapp/WEB-INF/velocity/static/storyblock/dotImage.vtl |
Emit <figcaption> when caption is set |
dotCMS/src/main/java/com/dotcms/tiptap/TiptapHtml.java |
Emit <figcaption> |
dotCMS/src/main/java/com/dotcms/tiptap/TiptapMarkdown.java |
Emit the caption in Markdown output |
core-web/libs/sdk/react/.../blocks/Image.tsx |
Render <figcaption> |
core-web/libs/sdk/vue/.../blocks/DotImage.vue |
Render <figcaption> |
core-web/libs/sdk/angular/.../blocks/image.component.ts |
Render <figcaption> |
core-web/libs/sdk/angular/.../dotcms-block-editor-renderer-native.component.html |
Render <figcaption> |
core-web/libs/sdk/types/src/lib/components/block-editor-renderer/internal.ts |
Add caption to the image attrs type |
Acceptance Criteria
Editor — happy path
Editor — field independence
Persistence & round-trip
Renderers
Layout interaction
Backward compatibility
Escaping
Tests
Priority
Medium
Additional Context
Description
Add a caption field to the image properties dialog in the Block Editor (
libs/new-block-editor), persist it as a new attribute on thedotImagenode, and render it as a<figcaption>across every renderer.Today the dialog exposes three fields — Image URL (
src), Tooltip (title) and Alt text (alt). There is no way to attach a visible caption to an image.titleis the browser tooltip and must stay that way; the caption is a distinct, visible piece of content.Current state (verified)
core-web/libs/new-block-editor/src/lib/editor/components/image-popover/image-popover.component.html— three controls, all bound toform.controls.{src,title,alt}.core-web/libs/new-block-editor/src/lib/editor/extensions/nodes/image.extension.ts—DotImagealready serializes to<figure>in bothrenderHTML()andaddNodeView(), so<figcaption>has a natural place to live.dotCMS/src/main/webapp/WEB-INF/velocity/static/storyblock/dotImage.vtl— already emits a<figure>wrapper.Design decision
A new
captionnode attribute, rendered as<figcaption>inside the existing<figure>.titlekeeps its current meaning (tooltip). No fallback fromtitletocaption— they are independent fields.Files in scope
.../components/image-popover/image-popover.component.html.../components/image-popover/image-popover.component.tscaptionto the form group; read/write the node attr.../extensions/nodes/image.extension.tscaptionattribute withparseHTML/renderHTML; emit<figcaption>inrenderHTML()andaddNodeView()dotCMS/src/main/webapp/WEB-INF/messages/Language.properties...field.caption.label,...field.caption.hint,...field.caption.placeholderdotCMS/src/main/webapp/WEB-INF/velocity/static/storyblock/dotImage.vtl<figcaption>whencaptionis setdotCMS/src/main/java/com/dotcms/tiptap/TiptapHtml.java<figcaption>dotCMS/src/main/java/com/dotcms/tiptap/TiptapMarkdown.javacore-web/libs/sdk/react/.../blocks/Image.tsx<figcaption>core-web/libs/sdk/vue/.../blocks/DotImage.vue<figcaption>core-web/libs/sdk/angular/.../blocks/image.component.ts<figcaption>core-web/libs/sdk/angular/.../dotcms-block-editor-renderer-native.component.html<figcaption>core-web/libs/sdk/types/src/lib/components/block-editor-renderer/internal.tscaptionto the image attrs typeAcceptance Criteria
Editor — happy path
captionattribute on thedotImagenode<figcaption>element from the imageEditor — field independence
title(tooltip) attributecaptionattributesrcremains the only required fieldPersistence & round-trip
captionin thedotImagenode'sattrsparseHTMLreads a caption back from a<figure><img><figcaption>…</figcaption></figure>structureRenderers
dotImage.vtl) renders<figcaption>with the caption text whencaptionis set<figcaption>element whencaptionis unset, empty, or the literal string"null"<figcaption>with the caption text<figcaption>with the caption textimage.component.ts) renders<figcaption>with the caption text<figcaption>with the caption textTiptapHtmlserver-side HTML output includes the<figcaption>TiptapMarkdownoutput includes the caption textLayout interaction
textWrapset keeps theimage-wrap-*figure class and the caption stays inside the wrapped figuretextAlignset keeps theimage-align-*figure class<figcaption>outside the<a>, not inside itBackward compatibility
captionattribute render exactly as they do today — no<figcaption>, no layout shiftEscaping
<,>,&, or"is HTML-escaped in every renderer and does not inject markupTests
libs/new-block-editor/cover the dialog field, node attribute round-trip, and<figcaption>serializationPriority
Medium
Additional Context
libs/new-block-editoronly. The legacylibs/block-editoris not in scope.image.extension.tsanddotImage.vtl.