Problem
Found in the consuming app on 0.16.0-fork.1. Positioned elements paint in tree order, and zIndex is not supported, so a positioned element can't be placed early in the tree without the content after it painting over it. Browsers apply CSS stacking order instead: positioned descendants paint above in-flow, non-positioned content regardless of source order, and z-index orders positioned elements among themselves.
In the app this blocks an accessibility fix. The dock is an absolutely positioned layer at the bottom of the window. To make it the first thing Tab reaches, it has to come before <main> in the tree. On the web that changes nothing visually. In GPU-IX the opaque <main> then paints over the dock and it disappears.
Minimal repro, a 200×100 root with position: relative, captured with renderer.captureScreenshot():
const overlay = <div style={{ position: "absolute", left: 20, top: 20, width: 100, height: 50, background: "#ff0000" }} />;
const page = <div style={{ width: 200, height: 100, background: "#0000ff" }} />;
// overlay first: [overlay, page] overlay last: [page, overlay]
| Tree order |
GPU-IX |
Chromium |
| overlay, then page |
all blue: overlay hidden |
red overlay over blue |
| page, then overlay |
red overlay over blue |
red overlay over blue |
zIndex is not a StyleDesc key, and at runtime it is rejected: property "zIndex" rejected value 1: unsupported style property, with the rest of the style applied.
Fix
- Paint in CSS stacking order: within a stacking context, in-flow non-positioned content first, then positioned descendants with
z-index: auto / 0 in tree order, then positive z-index in ascending order; negative z-index below in-flow content.
- Support
zIndex on positioned elements, creating a stacking context as CSS does. opacity < 1 and transform should create stacking contexts too, if they don't already.
- Hit-testing and pointer delivery follow the same order as painting, so a positioned overlay that paints on top also receives the click.
Done means
- In the repro, "overlay, then page" shows the red overlay, matching Chromium.
zIndex is typed on StyleDesc, accepted without a diagnostic, and orders sibling positioned elements, including a negative value painting below in-flow content.
- Pointer events reach the topmost painted element in both orders.
🤖 Generated with Claude Code
Problem
Found in the consuming app on 0.16.0-fork.1. Positioned elements paint in tree order, and
zIndexis not supported, so a positioned element can't be placed early in the tree without the content after it painting over it. Browsers apply CSS stacking order instead: positioned descendants paint above in-flow, non-positioned content regardless of source order, andz-indexorders positioned elements among themselves.In the app this blocks an accessibility fix. The dock is an absolutely positioned layer at the bottom of the window. To make it the first thing Tab reaches, it has to come before
<main>in the tree. On the web that changes nothing visually. In GPU-IX the opaque<main>then paints over the dock and it disappears.Minimal repro, a 200×100 root with
position: relative, captured withrenderer.captureScreenshot():zIndexis not aStyleDesckey, and at runtime it is rejected:property "zIndex" rejected value 1: unsupported style property, with the rest of the style applied.Fix
z-index: auto/0in tree order, then positivez-indexin ascending order; negativez-indexbelow in-flow content.zIndexon positioned elements, creating a stacking context as CSS does.opacity< 1 andtransformshould create stacking contexts too, if they don't already.Done means
zIndexis typed onStyleDesc, accepted without a diagnostic, and orders sibling positioned elements, including a negative value painting below in-flow content.🤖 Generated with Claude Code