From 0b684c9042760d6fd9af594824b749500ce3c83e Mon Sep 17 00:00:00 2001 From: Malou <158126425+kaitlyn-xie-jj@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:40:53 +0800 Subject: [PATCH 1/3] fix: apply image alignment independently in multi-image paragraphs --- src/plugins/remark-image-attributes.ts | 39 +++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/plugins/remark-image-attributes.ts b/src/plugins/remark-image-attributes.ts index 3974558e..03eb77b4 100644 --- a/src/plugins/remark-image-attributes.ts +++ b/src/plugins/remark-image-attributes.ts @@ -23,6 +23,17 @@ export function remarkImageAttributes() { const children = node.children; + // A shared wrapper cannot carry separate alignments for multiple images. + // In that case, give each image its own block-level alignment instead. + const multipleImages = + children.filter((child) => child.type === 'image').length > 1; + if (multipleImages) { + node.data = node.data || {}; + node.data.hName = 'div'; + node.data.hProperties = node.data.hProperties || {}; + node.data.hProperties.class = 'img-wrapper img-multiple'; + } + // Look for images with hash attributes in URL for (const child of children) { if (child.type === 'image') { @@ -54,6 +65,18 @@ export function remarkImageAttributes() { imgStyle += ` border: ${border};`; } + if (multipleImages) { + // Keep image styles isolated instead of aligning the entire + // paragraph (which would also align sibling images). + const margins = + align === 'left' + ? ' margin-left: 0; margin-right: auto;' + : align === 'right' + ? ' margin-left: auto; margin-right: 0;' + : ' margin-left: auto; margin-right: auto;'; + imgStyle += ` display: block;${margins}`; + } + // Set image properties child.data = child.data || {}; child.data.hProperties = child.data.hProperties || {}; @@ -74,10 +97,18 @@ export function remarkImageAttributes() { } // Wrap the paragraph to act as a container - node.data = node.data || {}; - node.data.hName = 'div'; - node.data.hProperties = node.data.hProperties || {}; - node.data.hProperties.class = `img-wrapper img-align-${align}`; + if (!multipleImages) { + node.data = node.data || {}; + node.data.hName = 'div'; + node.data.hProperties = node.data.hProperties || {}; + node.data.hProperties.class = `img-wrapper img-align-${align}`; + } + } else if (multipleImages) { + // Unattributed images retain their own default left alignment. + child.data = child.data || {}; + child.data.hProperties = child.data.hProperties || {}; + child.data.hProperties.style = + 'display: block; margin-left: 0; margin-right: auto;'; } else if (children.length === 1) { // Standalone image without attributes - still wrap and left-align node.data = node.data || {}; From 5e29f2ed09d2466ee18c629ecf11119f9bbe0a3a Mon Sep 17 00:00:00 2001 From: Malou <158126425+kaitlyn-xie-jj@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:41:27 +0800 Subject: [PATCH 2/3] test: cover isolated attributes for sibling images --- tests/remark-image-attributes.test.ts | 61 +++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/remark-image-attributes.test.ts diff --git a/tests/remark-image-attributes.test.ts b/tests/remark-image-attributes.test.ts new file mode 100644 index 00000000..df73cf26 --- /dev/null +++ b/tests/remark-image-attributes.test.ts @@ -0,0 +1,61 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; +import type { Image, Paragraph, Root } from 'mdast'; +import { remarkImageAttributes } from '../src/plugins/remark-image-attributes'; + +function image(url: string): Image { + return { type: 'image', url, alt: 'Example' }; +} + +function transform(...children: Image[]): Paragraph { + const paragraph: Paragraph = { type: 'paragraph', children }; + const tree: Root = { type: 'root', children: [paragraph] }; + remarkImageAttributes()(tree); + return paragraph; +} + +test('multiple images keep independent width, border and alignment', () => { + const first = image('./plain.webp'); + const second = image('./styled.webp#w=60&border&align=right'); + const paragraph = transform(first, second); + + assert.equal(paragraph.data?.hProperties?.class, 'img-wrapper img-multiple'); + assert.equal(paragraph.data?.hName, 'div'); + assert.equal(first.data?.hProperties?.style, + 'display: block; margin-left: 0; margin-right: auto;'); + assert.equal(first.data?.hProperties?.['data-slide-width'], undefined); + assert.equal(first.url, './plain.webp'); + assert.equal(second.url, './styled.webp'); + assert.match(String(second.data?.hProperties?.style), /width: 60%/); + assert.match(String(second.data?.hProperties?.style), /border: 5px solid #ADADAD/); + assert.match(String(second.data?.hProperties?.style), /margin-left: auto; margin-right: 0/); + assert.equal(second.data?.hProperties?.['data-slide-width'], '60'); + assert.equal(second.data?.hProperties?.['data-slide-align'], 'right'); +}); + +test('different alignments in one paragraph do not overwrite each other', () => { + const first = image('./one.webp#align=left'); + const second = image('./two.webp#align=center'); + transform(first, second); + + assert.match(String(first.data?.hProperties?.style), /margin-left: 0; margin-right: auto/); + assert.match(String(second.data?.hProperties?.style), /margin-left: auto; margin-right: auto/); +}); + +test('single-image paragraphs preserve existing wrapper behavior', () => { + const img = image('./single.webp#w=50&align=right'); + const paragraph = transform(img); + + assert.equal(paragraph.data?.hName, 'div'); + assert.equal(paragraph.data?.hProperties?.class, 'img-wrapper img-align-right'); + assert.equal(img.data?.hProperties?.style, 'width: 50%;'); + assert.equal(img.url, './single.webp'); +}); + +test('unattributed single image remains left aligned', () => { + const img = image('./plain.webp'); + const paragraph = transform(img); + + assert.equal(paragraph.data?.hProperties?.class, 'img-wrapper img-align-left'); + assert.equal(img.data, undefined); +}); From 79bf7119dbea0d9789afb4d80d964899829d5edf Mon Sep 17 00:00:00 2001 From: Malou <158126425+kaitlyn-xie-jj@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:42:03 +0800 Subject: [PATCH 3/3] ci: run image attribute regression tests before site build --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba55446e..fdde7b8a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,6 +26,9 @@ jobs: cache: true require-lockfile: true + - name: Test image attribute plugin + run: pnpm exec tsx --test tests/remark-image-attributes.test.ts + - name: Build site run: pnpm build