0044. A media block floats left or right, and breaks text by default¶
Status: Accepted
Date: 2026-11-02
Context¶
0018 gave the media block a textAlignment property with the values left, center and right and the default left, and left the item on a line of its own whatever its alignment. On 2026-11-01 the maintainer decided that on the page a media item aligned left or right floats, as core's system/base library floats an item core's Align images filter gives align-left or align-right (core/modules/system/css/components/align.module.css), so that a converted page keeps its wrap, and that in the editor nothing floats, since a float inside BlockNote's list of blocks breaks the side menu, the drag handle and selection. Only the right float shipped (436fb2f): left was the default stored on every media block, and the converter read an embedded item with no data-align as left too, so floating it would have floated every item at half width.
Core's own editor has a fourth value. Its media embed in CKEditor 5 offers Break text, the default, which writes no data-align, beside Align left and wrap text, Align center and break text and Align right and wrap text (core/modules/ckeditor5/ckeditor5.ckeditor5.yml, media_mediaAlign), in that order on its toolbar.
The shared alignment vocabulary is a text block's. left is a paragraph's default and is stored on every one, so StyleVocabulary emits a class only for center, right and justify; the validator, the render policy and the editor's load strip always let left through; and a format's left flag only takes a toolbar button away (0017).
Decision¶
The media block's textAlignment takes none, left, center and right, and none is its default (DrupalMedia::NO_ALIGNMENT). The block's contract version goes from 3 to 4. No update path is written before 1.0.0: a block stored with left now floats.
On the page, none emits no class, and left, center and right emit blocknote-align--left, --center and --right on the figure. DrupalMedia::blockAttributes() adds the left class itself, since the shared vocabulary leaves left out for the text blocks whose default it is. css/blocknote.content.css floats a left-aligned item to the start of the line and a right-aligned one to the end, each at most half as wide as its container, with a margin of 1em on the side the text flows past, and scales an image, video or audio element inside it to fit. The float is cleared by an ::after on the element holding the figure, a selector wrapped in :where() so that it has no specificity and a theme's own ::after on that element wins where the two disagree: the element is the theme's field item, a list item or a quote, and Claro gives a quote ::after { content: no-close-quote }, which a rule of higher specificity replaced with an empty string, so quotation depth was never lowered and later quotes drew inner-level marks.
In the editor nothing floats. none and left both show the item at the start of the line; the toolbar's pressed button tells them apart.
The formatting toolbar and the block menu offer the four in core's order, with core's labels: Break text, Align left and wrap text, Align center and break text and Align right and wrap text, each the accessible name of its button or menu item (0011). Break text draws Remix Icon's layout-row-line, since upstream has no such function to take a glyph from (0033); the other three keep upstream's text alignment glyphs. Left, center and right follow the format's alignment flags as before. Break text is no alignment of the format's, so no flag takes it away, but it is offered only beside at least one alignment the format permits, since alone it would be a choice of one.
none is the block's own value, not the format's. The validator already leaves a value outside the format's vocabulary to the block's schema, and none emits no class at render; the editor's load strip now leaves such a value alone too. When the load strip takes a forbidden alignment off a block, the block's own default takes its place, by the property being dropped, where the strip used to write left, which on a media block is now a float.
Inserting the block over an empty paragraph carries a centered or right-aligned paragraph's alignment, and gives it none otherwise, since a paragraph's left is its default and not a request to float.
The converter reads data-align, or an image's align, as the float it names, and anything else as none. An embed or an image with no data-align takes the block's default, none; data-align="left" is left. A left alignment read from the markup around the element, such as a container's text-align, set the item against the start of the line without floating it, so it is none too; a centered or right one is still carried.
Amended 2026-11-02. Left and right are physical, in a right-to-left document too. The stylesheet floated a left-aligned item to inline-start and a right-aligned one to inline-end, so in a right-to-left document Align left and wrap text floated the item to the right, and an item the converter read from data-align="left" moved to the other side of the text core had shown it beside. Core's align-left and align-right are float: left and float: right whatever the direction, and the labels name the sides, so the stylesheet now floats a left-aligned item with float: left and a margin on its right, and a right-aligned one with float: right and a margin on its left. In the editor, upstream places an aligned block with justify-content, which follows the writing direction, so modules/blocknote_media/js/drupal-media.css places a left-aligned item against the left edge and a right-aligned one against the right edge in a right-to-left document as well. none stays at the start of the line, which there is the right.
Alternatives considered¶
left stays the default and never floats, the other answer the backlog offered. Rejected: a converted item core showed floated left would lose its wrap, and core's own editor already has the fourth value.
A class for left on every block, through the shared vocabulary. Rejected. Every paragraph stores left, so every paragraph on every page would gain a class that means nothing for it.
Containing the float with display: flow-root on the module's own list items and quotes, in place of the ::after. Rejected: a figure at the top level of a document is held by the theme's own field item, which a rule on the module's elements does not reach, and a quote made a formatting context beside an earlier float narrows beside it instead of letting its text flow past.
Floating in the editor. Rejected on 2026-11-01, for the side menu, the drag handle and selection.
Consequences¶
The values and the default 0018 decided are superseded; its property name, the absence of justify and of a width, and the absence of an alignment check of the block's own stand.
A format whose left flag is off takes Align left and wrap text away, and still accepts a stored left on save, as it does for a text block.
An author tells none from left in the editor only by the pressed button, since neither floats there. Amended 2026-11-02. In a right-to-left document the two are also on opposite sides: none at the start of the line, the right, and left against the left edge.
A theme that wants the item floated differently, or not at all, styles blocknote-align--left and blocknote-align--right itself. A theme ::after on an element holding a floated figure keeps its own properties, and the stylesheet's display: block and clear: both apply wherever the theme sets neither.
References¶
Drupal core: core/modules/ckeditor5/ckeditor5.ckeditor5.yml (media_mediaAlign), core/modules/system/css/components/align.module.css, core/themes/claro/css/base/elements.css.
This module: modules/blocknote_media/src/Plugin/BlockNoteBlock/DrupalMedia.php, modules/blocknote_media/src/Convert/DrupalMediaRule.php, modules/blocknote_media/src/Convert/MediaConversion.php, modules/blocknote_media/js/settings.ts, modules/blocknote_media/js/toolbar.tsx, modules/blocknote_media/js/blockmenu.ts, modules/blocknote_media/js/library.ts, modules/blocknote_media/js/block.tsx, js/src/policy.ts, css/blocknote.content.css, modules/blocknote_media/js/drupal-media.css, docs/rendering.md, tests/src/Kernel/MediaRenderTest.php, tests/src/Kernel/MediaBlockSettingsTest.php, tests/fixtures/conversion/media/embeds.html.