justify, which the site could always render

`rt-justify` has been in the sanitiser's allowlist all along, and styles.css gives it `text-align:
justify` with `hyphens: auto` — hyphenation being what stops justified text opening rivers of white
space between words. Only the toolbar was missing it.

The four are now a list beside the toolbar rather than a ternary chain, and the comment says what it has
to match: ALIGN_CLASSES in the site's rich-text.ts. A fifth offered here would be stripped on save,
silently, leaving somebody pressing a button that appears to do nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-31 05:54:44 +01:00
co-authored by Claude Opus 5
parent 43a447801d
commit 5c2d3baac0
+28 -13
View File
@@ -5,6 +5,7 @@ import Link from '@tiptap/extension-link';
import TextAlign from '@tiptap/extension-text-align';
import {
AlignCenter,
AlignJustify,
AlignLeft,
AlignRight,
Bold,
@@ -30,7 +31,7 @@ import {
*
* ── The toolbar is what the site can actually render ──
*
* Bold, italic, strike, a heading, two lists, three alignments, a link. Not because tiptap cannot do
* Bold, italic, strike, a heading, two lists, four alignments, a link. Not because tiptap cannot do
* more, but because anything offered here that the site's stylesheet has no rule for produces copy that
* looks right while editing and wrong when published — the worst kind of editor, since the person only
* finds out after saying it is done.
@@ -75,6 +76,19 @@ const Tool = ({
</button>
);
/**
* The alignments the toolbar offers.
*
* The same four the site's sanitiser accepts — ALIGN_CLASSES in src/rich-text.ts. A fifth offered here
* would be stripped on save, silently, leaving somebody pressing a button that appears to do nothing.
*/
const ALIGNMENTS = [
{ value: 'left' as const, Icon: AlignLeft },
{ value: 'center' as const, Icon: AlignCenter },
{ value: 'right' as const, Icon: AlignRight },
{ value: 'justify' as const, Icon: AlignJustify },
];
const Toolbar = ({ editor }: { editor: Editor }) => (
<div className="flex flex-wrap items-center gap-0.5 border-b border-border px-1 py-1">
<Tool label="Bold" onClick={() => editor.chain().focus().toggleBold().run()} active={editor.isActive('bold')}>
@@ -117,20 +131,21 @@ const Toolbar = ({ editor }: { editor: Editor }) => (
<span className="mx-1 h-4 w-px bg-border" />
{(['left', 'center', 'right'] as const).map((align) => (
{/*
Four alignments, justify included.
The site has always been able to render it: `rt-justify` is in the sanitiser's allowlist and
styles.css gives it `text-align: justify` with `hyphens: auto` — hyphenation being what stops
justified text opening rivers of white space between words. Only the toolbar was missing.
*/}
{ALIGNMENTS.map(({ value, Icon }) => (
<Tool
key={align}
label={`Align ${align}`}
onClick={() => editor.chain().focus().setTextAlign(align).run()}
active={editor.isActive({ textAlign: align })}
key={value}
label={`Align ${value}`}
onClick={() => editor.chain().focus().setTextAlign(value).run()}
active={editor.isActive({ textAlign: value })}
>
{align === 'left' ? (
<AlignLeft className="size-3.5" />
) : align === 'center' ? (
<AlignCenter className="size-3.5" />
) : (
<AlignRight className="size-3.5" />
)}
<Icon className="size-3.5" />
</Tool>
))}