Skip to content

fix: white-space pre-wrap, break-spaces and pre-line wrap - #296

Merged
tannevaled merged 1 commit into
mainfrom
white-space-wrapping-values
Oct 18, 2026
Merged

tannevaled merged 1 commit into
mainfrom
white-space-wrapping-values

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Problem

white-space: pre-wrap and pre-line were parsed as pre (css/parse.go:789), so a block using them never wrapped. break-spaces was not parsed at all. Marp themes put code in pre with white-space: pre-wrap.

Spec read

  • CSS Text 3 §3 table (new lines / spaces / wrapping / end-of-line spaces):
    • pre-wrap: preserve, preserve, wrap, hang.
    • break-spaces: preserve, preserve, wrap, wrap (spaces take room).
    • pre-line: preserve, collapse, wrap, remove.
  • §4.1.2: preserved spaces at the end of a line hang under pre-wrap; under break-spaces they cannot hang or collapse.
  • §5: under pre-wrap the wrap opportunity is at the end of a space run; under break-spaces it is after every space.

Change

  • css.WSPreWrap, WSBreakSpaces, WSPreLine and WhiteSpace.PreservesSpaces().
  • appendWords now uses the text's own inherited white-space:
    • pre-wrap: each newline is a forced break. Leading indentation stays in the first word. Each inner space run becomes the next word's SpaceBefore at its real width, so it is a wrap opportunity and hangs at a line edge. A line of only spaces hangs entirely.
    • break-spaces: spaces stay in the previous word's width (they take room), and the next word gets a new InlineItem.BreakBefore opportunity that glueRun honours. Breaking between two spaces of one run is not modelled.
    • pre-line: normal collapsing per line, with newlines as forced breaks.
  • Side effect, spec-aligned: a white-space: normal span inside <pre> now collapses (its own value decides). The block's no-wrap placement for pre is unchanged.
  • Min-content treats only pre and nowrap as unbreakable.

Tests

  • TestWhiteSpaceWrappingValues (RenderHTML + script measuring boxes): pre-wrap, break-spaces and pre-line blocks wrap; pre-wrap keeps spaces and indentation; pre-line collapses spaces; newlines are kept for all three. Failed on main (no wrapping, pre-line kept spaces, break-spaces lost the newline). pre stays one line (control).
  • Layout unit test of the produced items (hanging, BreakBefore, tabs, blank lines, normal-in-pre); css test of the keywords. css 99.5%, layout 100%.

🤖 Generated with Claude Code

pre-wrap and pre-line were parsed as pre and break-spaces not at all, so a
block using them never wrapped - Marp themes set white-space:pre-wrap on
code blocks. Per the CSS Text 3 §3 table:

- pre-wrap: spaces and newlines preserved, wraps; a run of spaces is a
  soft wrap opportunity at its end and hangs at the end of a line
  (§4.1.2) - leading indentation stays part of the first word, inner runs
  become the next word's SpaceBefore at their real width;
- break-spaces: preserved and wraps, but spaces never hang: they stay in
  the previous word's width and the next word carries a new BreakBefore
  wrap opportunity;
- pre-line: spaces collapse as in normal, newlines are forced breaks.

The text's own element now decides (its inherited white-space), so a
white-space:normal span inside a <pre> collapses as in a browser. Min-content
treats only pre and nowrap as unbreakable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 00bfc34 into main Oct 18, 2026
7 checks passed
@tannevaled
tannevaled deleted the white-space-wrapping-values branch October 18, 2026 19:24
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.

1 participant