Implement vertical-align via Parley - #832
Merged
Merged
Conversation
This was referenced Sep 3, 2026
staging-devin-ai-integration Bot
pushed a commit
that referenced
this pull request
Sep 3, 2026
…faces without U+0020)
staging-devin-ai-integration
Bot
force-pushed
the
devin/1788386630-vertical-align
branch
3 times, most recently
from
September 14, 2026 17:25
41b9df8 to
2b316d5
Compare
staging-devin-ai-integration
Bot
force-pushed
the
devin/1788386630-vertical-align
branch
2 times, most recently
from
September 21, 2026 16:47
75ab54e to
f113887
Compare
staging-devin-ai-integration
Bot
force-pushed
the
devin/1788386630-vertical-align
branch
2 times, most recently
from
September 24, 2026 19:54
6b7e7b5 to
5ee11fe
Compare
staging-devin-ai-integration
Bot
force-pushed
the
devin/1788386630-vertical-align
branch
from
September 26, 2026 02:48
5ee11fe to
9168ba6
Compare
Rebased onto main as a single commit: main now includes #914 (Parley upgrade to linebender main), which superseded the Parley repin commits that made up most of this branch's history. The previous history is preserved at backup/832-pre-rebase. - Map stylo vertical-align / alignment-baseline / baseline-shift to Parley's VerticalAlign on text styles and inline boxes - Drop Blitz's root line-height floor and per-span line-height pre-computation; Parley now owns line box sizing - Export inline-block / inline flex / grid baselines to Parley - Scale ch/ic linearly from font units to match shaped advances - Position glyph runs, inline boxes and decorations by baseline offset
Underlines/overlines/line-throughs of a decorating element are drawn at that element's inline-box baseline on each line (via Line::style_baseline through the parley style parent chain) instead of at each glyph run's shifted baseline, so vertically aligned descendants no longer carry the decoration with them. Pin parley to 1fd63fba72b38df9ec14892bd6f20e96fe2c1176, which adds Style::parent / Line::style_baseline and stops a trailing space from starting an extra empty line.
staging-devin-ai-integration
Bot
force-pushed
the
devin/1788386630-vertical-align
branch
from
September 28, 2026 13:12
8c94c04 to
33b10ca
Compare
tomcur
pushed a commit
to tomcur/parley
that referenced
this pull request
Sep 28, 2026
**Implements vertical alignment.** LLM Contributions: Generated with Fable 5.1 Low. Reviewed by GPT 5.6 Sol XHigh. This has also been manually reviewed, and gone through several rounds of iteration. I probably still need to do another round on the details of the the actual alignment, but I think this is architecturally in a good place. - Builds on top of linebender#762 - Blitz integration PR (runs WPT tests) DioxusLabs/blitz#832 This PR is designed to be reviewed commit-by-commit (4 commits): 1. **Introduce a minimal tree structure**: Adds `parent: u16` to every style, which allows for upwards tree-traversal. The `TreeBuilder` encodes it's tree with this new field. The `RangedBuilder` builds a trivial tree with one root, and where all style spans are direct children of that root. This builds on the existing convention that the 0th style is the root/paragraph level style. 2. **Adds `VerticalAlign` style**. `VerticalAlign` is struct which consists of `BaselineShift`, and `AlignmentBaseline` fields, and can be set both as a span style and on inline boxes. 3. **Introduces the concept of first available font and resolves a font + metrics for every style**. We thus end up with a tree of spans each with associated line metrics before we even begin layout. We do not use the actual resolved fonts for any run/atom/cluster for vertical alignment at all (this matches browsers, but there are other reasonable choices here, and we could bring back using run/cluster/atom metrics for this as an option if desired). Some level of alignment relative to the parent is already pre-computed at this stage. (Note: these style lookups are kinda expensive - I have a follow-up PR that eliminates that overhead with a global cache on the `FontContext` - DioxusLabs#19 for those who want a preview) 4. **Implements the actual vertical alignment logic** by building a list of "aligned subtrees" for each line, and computing the offsets of each span in each aligned subtree relative to each other. Most lines actually only have one "aligned subtree", so it is alignment within a tree doing most of the work. `vertical-align: top` and `bottom` introduce new "aligned subtrees" which are initially aligned independently of the other "aligned subtrees" on the line. There is then a final alignment step when completing each line that aligns the "aligned subtrees" to each other. Some notes: - The mental model for how spans interact with lines is that each span creates a box fragment in **every** line it intersects, and each a span's box fragment on a given line is always contained within (and aligned with) a box fragment for **every** ancestor span up to the root level, as well as with other spans (and their ancestors) that exist on the same line. - This is a generalisation of the "strut metric" concept: spans align with not just the root, but every span in between. `RangedBuilder` layouts fallback gracefully to "strut metric"-style layout (spans in ranged layouts only have one ancestor: the root). **Changelog** > ### Added > > - `vertical-align` style. > - TODO --------- Signed-off-by: Nico Burns <nico@nicoburns.com>
nicoburns
marked this pull request as ready for review
September 28, 2026 15:08
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Companion to linebender/parley#766 (now merged: Parley builds the CSS inline alignment tree itself: root strut, ancestor inline boxes, parent-relative baseline shifts,
top/bottomaligned subtrees).Cargo.tomlpins Parley to linebendermain63927df.stylo_to_parley::vertical_align: Stylo storesvertical-alignas the css-inline-3 longhands, and Parley'sVerticalAlign { alignment, shift }is the same compound, so both are mapped losslessly:alignment-baseline(baseline|text-top|text-bottom|middle) →parley::AlignmentBaseline,baseline-shift(sub|super|top|bottom|<length-percentage>) →parley::BaselineShift. Percentages resolve against the element's own line-height.baseline-shift: centerhas no Parley equivalent yet and is approximated asmiddle;baseline-sourceis ignored.TextStyle::vertical_alignon every span andInlineBox::vertical_alignon atomic inlines.InlineBox::baselineis now populated for inline-blocks from taffy'sbaselines.last.or(first)(+ top margin), orNone(bottom margin edge) whenoverflowisn't visible / there are no line boxes (CSS 2 §10.8.1). Inline layout now also reportsbaselines.lastupward.root_line_heightfloor on each span'sline-height— Parley adds the strut and every ancestor span's own box to the line, so the floor was double counting and broke shifted spans.append_inline_box_to_line(.., NEG_INFINITY, NEG_INFINITY)so they contribute no height.Cluster::style_index()replacesGlyph::style_index();Run::font_metrics(),run.font().font,NormalizedCoord::to_bits().Not included (follow-up once DioxusLabs/parley#19 / #23 land upstream): painting text decorations at the decorating box's baseline (needs
Style::parent/Line::style_baseline, fixestext-decoration-va-length-001) and theFontContext::from_partsadaptation.WPT (
css/CSS2/linebox css/css-inline css/CSS2/text)454 → 490 tests passing, 1198 → 1299 subtests, 0 crashes. Previously-passing tests that now fail are pre-existing gaps that the old always-tall strut happened to hide, e.g.:
linebox/baseline-block-with-overflow-001.html— taffy block layout propagates a child's baseline through anoverflow: hiddenblock; should synthesize from the bottom margin edge (taffy issue).css-inline/empty-span-size-002.html— Blitz doesn't model inline border/padding boxes, so an empty bordered span doesn't make the line visible.dominant-baseline-mixed-writing-modes-002.html— vertical writing modes /dominant-baselineunsupported.Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/da7669e341814cc7976e4ebf32b7eacb
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/da7669e341814cc7976e4ebf32b7eacb?variant=devin-insiders
Requested by: @nicoburns
WPT results
Subtests: 204 newly passing, 19 newly failing (net +185).
Full diff (179 changed tests)
Generated by the WPT workflow.