DioxusLabs / DioxusLabs/blitz

1px borders sometimes not rendering

Open
#837 4 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
4.1k
Forks
203
Avg merge
8h 58m
Merged PRs (30d)
112

Description

I ran into situations where borders set to 1px sometimes don't render at all.

Example:
```css
.var-panel {
border: 1px solid #ff0000;
border-top-width: 0;
border-radius: 8px;
background: #212121;
overflow: hidden;
padding-bottom: 4px;
margin-bottom: 24px;
outline: none;
}
```

| Expected: red outline on the left, bottom and right side — top missing | Actual: the whole outline is gone, all four sides |
|---|---|
| Image | Image |

After some digging (with the help of AI), I found and verified two independent problems behind this:


**1. blitz: divide-by-zero → NaN erases the whole outline**

[`start_angle()`](https://github.com/DioxusLabs/blitz/blob/main/packages/blitz-paint/src/kurbo_css/css_box.rs#L616) in `kurbo_css/css_box.rs` computes each rounded corner's arc split from the ratio of the two adjacent border widths. If one width is zero the expression yields `inf / inf` = NaN. Corner arcs are shared between adjacent edges, and a box's four edge shapes are batched into a single fill command — so one zero-width side poisons the *whole* border outline (vello drops NaN segments silently).

The fix here could be a simple check for width like this:
```diff
fn start_angle(bt_width: f64, br_width: f64, radii: Vec2) -> f64 {
+ match (bt_width, br_width) {
+ (0.0, 0.0) => return FRAC_PI_4, // both zero: treat as uniform
+ (0.0, _) => return FRAC_PI_2, // horizontal split
+ (_, 0.0) => return 0.0, // vertical split
+ _ => {}
+ }
+
// slope of the border intersection split
let w = bt_width / br_width;
let x = radii.y / (w * radii.x);
```

**2. taffy: layout rounding zeroes device-pixel-snapped border widths at fractional scales**

blitz calls taffy's [`taffy::round_layout`](https://github.com/DioxusLabs/blitz/blob/main/packages/blitz-dom/src/resolve.rs#L394) and hands the result straight to the painter. So whatever that rounding does to border widths is what gets painted.

With fractional scaling (e.g. 1.25) a `1px` border arrives in taffy as `0.8` CSS px (DPI-aware CSS engines snap lengths to whole device pixels).
[`round_layout`](https://github.com/DioxusLabs/taffy/blob/main/src/compute/mod.rs#L219) then rounds each box edge to a whole *CSS* pixel independently, and the two edges of such a border can land on the same number:

```
left edge: 63.6 -> 64
edge + 0.8: 64.4 -> 64
rounded border width: 0
```

I let AI write a fix, which fixes the issue, but I don't fully understand it
and performance could be worse than now — that's why no PR, patch attached.

[round_layout_with_grid.patch](https://github.com/user-attachments/files/31863757/round_layout_with_grid.patch)

On the blitz side we could then pass the scale to use for rounding:

```rust
taffy::round_layout_with_grid(self, root_element_id, self.viewport.scale());
```

Contributor guide

Open the contributing guide

Research direction

Reproduce the border case, then inspect start_angle() in packages/blitz-paint/src/kurbo_css/css_box.rs and round_layout in packages/blitz-dom/src/resolve.rs, along with taffy's src/compute/mod.rs. Check both zero-width corners and fractional-scale rounding, using the attached patch as context. Done means 1px borders remain visible in both cases without introducing NaN geometry.

Written by the indexing model from the issue text.

Assessment

Tech stack
css, rust
Domain
computer-graphics, frontend, web-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.