From bc3cd6a8426844433779f7ed4416895dc6418354 Mon Sep 17 00:00:00 2001 From: Dmitrii Troitskii Date: Mon, 5 Oct 2026 06:08:14 +0000 Subject: [PATCH 1/2] fix: use logical Tailwind classes so layout mirrors in RTL --- .changeset/logical-rtl-classes.md | 5 +++++ packages/streamdown/__tests__/components.test.tsx | 6 +++--- .../__tests__/pan-zoom-interaction.test.tsx | 8 ++++---- packages/streamdown/lib/code-block/body.tsx | 4 ++-- packages/streamdown/lib/code-block/header.tsx | 2 +- packages/streamdown/lib/code-block/index.tsx | 2 +- packages/streamdown/lib/components.tsx | 14 +++++++------- packages/streamdown/lib/image.tsx | 2 +- packages/streamdown/lib/link-modal.tsx | 2 +- .../streamdown/lib/mermaid/download-button.tsx | 8 ++++---- .../streamdown/lib/mermaid/fullscreen-button.tsx | 2 +- packages/streamdown/lib/mermaid/pan-zoom.tsx | 2 +- packages/streamdown/lib/table/copy-dropdown.tsx | 8 ++++---- .../streamdown/lib/table/download-dropdown.tsx | 6 +++--- 14 files changed, 38 insertions(+), 33 deletions(-) create mode 100644 .changeset/logical-rtl-classes.md diff --git a/.changeset/logical-rtl-classes.md b/.changeset/logical-rtl-classes.md new file mode 100644 index 000000000..b9d241d55 --- /dev/null +++ b/.changeset/logical-rtl-classes.md @@ -0,0 +1,5 @@ +--- +"streamdown": patch +--- + +fix: use logical Tailwind classes (`ps-*`, `border-s-*`, `text-start`, `start-*`, `end-*`, `ms-*`, `me-*`) instead of physical left/right classes so layout mirrors in RTL text diff --git a/packages/streamdown/__tests__/components.test.tsx b/packages/streamdown/__tests__/components.test.tsx index 03ba1b075..f96d076cc 100644 --- a/packages/streamdown/__tests__/components.test.tsx +++ b/packages/streamdown/__tests__/components.test.tsx @@ -236,8 +236,8 @@ describe("Markdown Components", () => { const blockquote = container.querySelector("blockquote"); expect(blockquote).toBeTruthy(); expect(blockquote?.className).toContain("my-4"); - expect(blockquote?.className).toContain("border-l-4"); - expect(blockquote?.className).toContain("pl-4"); + expect(blockquote?.className).toContain("border-s-4"); + expect(blockquote?.className).toContain("ps-4"); expect(blockquote?.className).toContain("italic"); }); }); @@ -525,7 +525,7 @@ describe("Markdown Components", () => { expect(th?.className).toContain("whitespace-nowrap"); expect(th?.className).toContain("px-4"); expect(th?.className).toContain("py-2"); - expect(th?.className).toContain("text-left"); + expect(th?.className).toContain("text-start"); expect(th?.className).toContain("font-semibold"); expect(th?.className).toContain("text-sm"); }); diff --git a/packages/streamdown/__tests__/pan-zoom-interaction.test.tsx b/packages/streamdown/__tests__/pan-zoom-interaction.test.tsx index 54239915f..5828bc66d 100644 --- a/packages/streamdown/__tests__/pan-zoom-interaction.test.tsx +++ b/packages/streamdown/__tests__/pan-zoom-interaction.test.tsx @@ -233,13 +233,13 @@ describe("PanZoom pointer interactions", () => { ); - // Fullscreen controls should use bottom-4 left-4 + // Fullscreen controls should use bottom-4 start-4 const controls = container.querySelector(".absolute.z-10"); expect(controls?.className).toContain("bottom-4"); - expect(controls?.className).toContain("left-4"); + expect(controls?.className).toContain("start-4"); }); - it("should position controls at bottom-2 left-2 when not fullscreen", () => { + it("should position controls at bottom-2 start-2 when not fullscreen", () => { const { container } = render(
Content
@@ -248,6 +248,6 @@ describe("PanZoom pointer interactions", () => { const controls = container.querySelector(".absolute.z-10"); expect(controls?.className).toContain("bottom-2"); - expect(controls?.className).toContain("left-2"); + expect(controls?.className).toContain("start-2"); }); }); diff --git a/packages/streamdown/lib/code-block/body.tsx b/packages/streamdown/lib/code-block/body.tsx index 8a51c9954..79e7fae28 100644 --- a/packages/streamdown/lib/code-block/body.tsx +++ b/packages/streamdown/lib/code-block/body.tsx @@ -30,9 +30,9 @@ const LINE_NUMBER_CLASSES_BASE = baseCn( "before:inline-block", "before:[counter-increment:line]", "before:w-6", - "before:mr-4", + "before:me-4", "before:text-[13px]", - "before:text-right", + "before:text-end", "before:text-muted-foreground/50", "before:font-mono", "before:select-none" diff --git a/packages/streamdown/lib/code-block/header.tsx b/packages/streamdown/lib/code-block/header.tsx index e6021cdb3..bcad9f79e 100644 --- a/packages/streamdown/lib/code-block/header.tsx +++ b/packages/streamdown/lib/code-block/header.tsx @@ -12,7 +12,7 @@ export const CodeBlockHeader = ({ language }: CodeBlockHeaderProps) => { data-language={language} data-streamdown="code-block-header" > - {language} + {language} ); }; diff --git a/packages/streamdown/lib/code-block/index.tsx b/packages/streamdown/lib/code-block/index.tsx index 5b313b3de..2d2e784cf 100644 --- a/packages/streamdown/lib/code-block/index.tsx +++ b/packages/streamdown/lib/code-block/index.tsx @@ -82,7 +82,7 @@ export const CodeBlock = ({ {children ? (
( return (
    ( return (
{shouldShowMermaidControls ? (
); }; diff --git a/packages/streamdown/lib/code-block/index.tsx b/packages/streamdown/lib/code-block/index.tsx index 2d2e784cf..5b313b3de 100644 --- a/packages/streamdown/lib/code-block/index.tsx +++ b/packages/streamdown/lib/code-block/index.tsx @@ -82,7 +82,7 @@ export const CodeBlock = ({ {children ? (
twMerge(clsx(inputs));