Skip to content

fix(ui): wrap text at words, not mid-character - #27

Merged
canghai118 merged 1 commit into
linkcraftstudio:mainfrom
quadcom:fix/word-wrap
Aug 28, 2026
Merged

fix(ui): wrap text at words, not mid-character#27
canghai118 merged 1 commit into
linkcraftstudio:mainfrom
quadcom:fix/word-wrap

Conversation

@quadcom

@quadcom quadcom commented Aug 27, 2026

Copy link
Copy Markdown

What this PR does

Stops rendered markdown wrapping mid-word. Two changes:

  • app/assets/css/tailwind.css — reset the word-break: break-all that md-editor-v3 applies to its preview containers, restoring normal word wrapping everywhere markdown is displayed.
  • app/pages/index.vue — swap the board list excerpt's break-all for break-words.

Presentation only. No schema, API, env or dependency changes.

Why

No issue filed — reported by a self-hosting user, reproduced against main.

md-editor-v3's stylesheet sets word-break: break-all on .md-editor-preview, on .md-editor-html, and on .md-editor-preview h1h6. word-break is inherited, so the whole rendered subtree picks it up and the browser may break between any two characters — even when the word would have fitted on the next line:

Before                              After
──────────────────────             ──────────────────────
Characteristically the             Characteristically the
internationalisati                 internationalisation
on responsibilities were           responsibilities were
uncomfortabl                       uncomfortable.
e.

That affects every surface that renders markdown: post bodies (PostContent.vue), comments (CommentContent.vue), the portal welcome block (PortalWelcomeBlock.vue), changelog entries, and the editor's previewOnly pane. It also silently defeats the overflow-wrap: anywhere that PostContent.vue, CommentContent.vue and md-editor-preview.css already set — that rule alone breaks only a word that genuinely cannot fit, which is the intended behaviour.

app/pages/index.vue had the same break-all applied directly to the card excerpt, producing the effect on every summary on the board list.

The editor input is not affected and is unchanged here. CodeMirror already wraps at word boundaries; the reported "it breaks while I type" turned out to be the preview beside it. Worth knowing if the same report reaches you.

How to review

Three things in the reset are deliberate and would look arbitrary without explanation. All three are also written into the comment above the rule.

1. The doubled class. md-editor-v3's CSS is imported inside ThemedMdEditor.vue / ThemedMdPreview.vue, so it ships in a lazily-loaded chunk and its order relative to the global stylesheet is not guaranteed. The reset cannot rely on coming last, so it wins on specificity instead: two classes out-rank the library's one, without !important.

2. th/td are listed explicitly. The default preview theme sets the legacy word-wrap alias on cells:

div.default-theme table tr th, div.default-theme table tr td { word-wrap: break-word; … }

That overrides what cells would otherwise inherit from the container, so without naming them a table containing a long URL stops wrapping and pushes the table wider than its column.

3. anywhere rather than break-word. This one is easy to get wrong. The library renders links as display: inline-flex, so a bare URL becomes a flex item, and a flex item will not shrink below its min-content width. Only anywhere reduces min-content — under break-word a long link keeps its full width and overflows the column instead of wrapping. I had break-word first and it visibly regressed exactly this case.

Deliberately not changed: the word-break: break-all on the plain-text fallback link in server/utils/email-templates.ts, where breaking a bare URL anywhere is the point.

Verifying it

The change was checked against a live md-editor-v3 v6.4.2 instance rendering a document that mixes long ordinary words with unbreakable URLs in a heading, a paragraph, a list item, a table cell, a blockquote and an inline code span, in a ~300px column. Before: prose broken mid-character throughout. After: every word intact, every URL still broken (only where it cannot fit), and nothing overflowing its container in any of those contexts.

To reproduce, post a card whose body contains several long words plus a long URL in each of those constructs, then view it narrow on the board list, the post detail, a comment, a changelog entry, the welcome block, and the editor's preview toggle.

Checklist

  • Commits are signed off (git commit -s)
  • Tests pass locally (pnpm build)
  • If schema changed, migration was generated and committed — n/a, no schema change
  • If public API or env vars changed, docs updated — n/a, presentation only
  • No secrets, internal URLs, or team member names in the diff

md-editor-v3's stylesheet sets `word-break: break-all` on `.md-editor-preview`
and `.md-editor-html`. `word-break` is inherited, so every descendant of a
rendered-markdown block picked it up and split ordinary words down the middle —
card bodies, comments, changelog entries and the welcome block alike. It also
defeated the `overflow-wrap: anywhere` those components already set, which on
its own would have broken only words that genuinely cannot fit.

Reset it globally rather than per component, since the preview is rendered from
several call sites. Three details in the reset are load-bearing and are spelled
out in the comment above it: the doubled class (the library's CSS rides in a
lazy chunk, so source order cannot decide the winner); the explicit `th`/`td`
(the default theme sets the legacy `word-wrap` alias on cells, which overrides
what they would inherit); and `anywhere` rather than `break-word` (links render
as `display: inline-flex`, and a flex item will not shrink below its min-content
width, which only `anywhere` reduces — under `break-word` a long link overflows
its column instead of wrapping).

The board list's card excerpt carried the same `break-all` inline; it becomes
`break-words`, which still contains a runaway URL without chopping prose.

Left alone: the `break-all` on the bare fallback link in the e-mail templates,
where breaking anywhere is the point. The editor input is untouched — CodeMirror
already wraps at word boundaries.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EcBReeukM2hnDV9Pau1jyA
Signed-off-by: Quadcom <admin@quadcom.ca>
quadcom pushed a commit to quadcom/feedlog-token that referenced this pull request Aug 27, 2026
fix/word-wrap is cut from upstream and is open as PR linkcraftstudio#27 there. Merging rather
than cherry-picking keeps the same commit id on both sides, so when upstream
takes the fix this history reconciles instead of duplicating it.

The token PR branch is untouched: it does not descend from this merge. Note that
main is no longer a safe base for a new PR branch — cut those from upstream/main.
@canghai118
canghai118 merged commit 8ef559a into linkcraftstudio:main Aug 28, 2026
2 checks passed
@canghai118

Copy link
Copy Markdown
Contributor

Thanks for the thoughtful contribution—this significantly improves Markdown text wrapping across FeedLog!

@quadcom
quadcom deleted the fix/word-wrap branch August 28, 2026 14:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants