Fix 1px borders vanishing when one side is zero-width or at fractional scales - #902
Draft
Shinyaigeek wants to merge 1 commit into
Draft
Shinyaigeek wants to merge 1 commit into
Shinyaigeek wants to merge 1 commit into
Conversation
…l scales Fixes DioxusLabs#837. Two independent bugs each erased `border: 1px solid; border-top-width: 0; border-radius: 8px` entirely: blitz-paint: `start_angle()` splits a corner arc between its two adjacent sides from the ratio of their widths. A zero-width side made that `inf / inf = NaN`, and since all same-coloured edges are filled as one path the NaN corner dropped the whole outline. Return the limiting angle for a zero-width side instead (the arc belongs entirely to the other side). blitz-dom: `taffy::round_layout` snaps edges to whole CSS pixels, but stylo already snaps lengths to device pixels, so at a fractional scale a `1px` border is `1 / scale` CSS px wide and its two edges could round to the same value, leaving a zero-width border to paint. Replace it with a rounding pass on the device pixel grid (`round(v * scale) / scale`), identical to taffy's at scale 1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SCkGi9MbYao9rnHMLiGq1D
This branch has not been deployed
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
Fixes #837.
should draw the left, right and bottom sides, but Blitz drew nothing at all. Two independent bugs were behind it, in different layers; the first one alone erases the outline at every scale, the second one erases it at fractional device pixel ratios even once the first is fixed.
1.
blitz-paint: a zero-width side made the corner split angleNaNstart_angle()inkurbo_css/css_box.rsdecides where a rounded corner's arc is split between its two adjacent sides, from the ratio of their border widths. Withbt_width == 0the closed form evaluatesw = 0,k = ry / (w·rx) = inf,√k / (√k + √2) = inf / inf = NaN. The arc then contains NaN segments, and becausedraw_borderbatches all same-coloured edges into a single fill (to avoid AA seams), Vello silently drops the whole outline — not just the one edge.Fix: return the limiting angle for a zero-width side up front (
bt == 0 → π/2,br == 0 → 0, both zero→ π/4), i.e. the whole arc belongs to the side that has a width. This is the limit of the existing formula, not a different geometry.2.
blitz-dom: layout rounding zeroed one-device-pixel borders at fractional scalesStylo already snaps lengths to whole device pixels, so at scale 1.5 a
1pxborder reaches layout as0.667CSS px.taffy::round_layoutthen rounds each edge to a whole CSS pixel from its cumulative position and takes the difference — which can put both edges of such a border on the same integer:Which side gets hit depends on where the fractional part lands (1.25× for the reporter, 1.5× in the test here).
Fix:
resolve_layoutnow calls around_layoutinblitz-domthat is taffy's algorithm verbatim (cumulative-position rounding, widths as the difference of two rounded edges, so no gaps open between boxes) but snaps to the device pixel grid:round(v · scale) / scalewithscale = viewport.scale(), the same value handed to stylo asdevice_pixel_ratio. A border that stylo made exactly one device pixel wide therefore stays one device pixel wide. At scale 1 this is bit-for-bitv.round(), so WPT and existing tests are unaffected; at scale 2 layout now lands on integer device pixels rather than even ones.Taffy is a git dependency here, so this lives in
blitz-domrather than changingtaffy::round_layout's signature.Tests
css_box.rs:handles_zero_width_sidesunit test forstart_angle.tests/blitz-tests/tests/thin_border_one_side_zero.rs:final_layout().borderis a whole, non-zero number of device pixels at 1.0 / 1.25 / 1.5 / 1.75 / 2.0 without going through paint.All three fail on
mainand pass with this change.cargo testforblitz-tests,blitz-dom,blitz-paint,stylo_taffy, pluscargo fmt --checkandclippy, are clean.Before / after (scale 1.5,
border-top-width: 0+border-radius: 8px):🤖 Generated with Claude Code
https://claude.ai/code/session_01SCkGi9MbYao9rnHMLiGq1D