I’ve made a reproduction project, which shows the issue. In summary, the issue is:
Layered UI objects will click through when they’re children of each other, but not when they’re siblings of each other, even if the UI layering appears identical.
I can’t tell if it’s a bug or by design (or by accident), but it’s definitely a surprise. Basically, the VisualElement hierarchy, and not the display order is being used to propagate events.
Here, I’ve created two setups. On the left, the red box is in front of the green box, but they’re siblings (added to the same root). When you click the red box in the overlapping area but don’t stop propagation, green nevertheless does not get click through events.
On the right, yellow is nested inside blue. When you click yellow, you get both and event for yellow and blue if you don’t stop propagation. This is in my mind the correct behavior for all visually layered objects.

Yes, it’s possible to hack around this, but is this the right behavior? It makes it impossible to load HUDs or panels or gizmos and give them the ability to click through. They will always eat the events, simply by being siblings. The display order is ignored.
It also forces HUDs into the awkward situation of having to be children of the objects they’re supposed to float on top of. And since absolute positioning is relative to the parent and not the screen, it forces you to always show overflow on the hierarchy you want to be on top of, and to compensate the coordinates in order to position the HUD absolutely. The alternative is that all HUDs eat all pointer events, or that you’re forced to unilaterally disable picking on HUD elements in order to start/stop them from reacting.
Here is the test code. You’ll also need to drop any PanelSettings configuration in Resources. Create an Empty game object in the scene and attach this script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class Main : MonoBehaviour
{
void Start()
{
var uiGameObj = new GameObject();
uiGameObj.name = "UI Container";
var rootDoc = uiGameObj.AddComponent<UIDocument>();
var root = rootDoc.rootVisualElement;
root.StretchToParentSize();
// Siblings, don't allow click through
var green = new VisualElement();
green.style.left = Pixels(200);
green.style.top = Pixels(200);
green.style.width = Pixels(200);
green.style.height = Pixels(200);
green.style.backgroundColor = new Color(0, 1, 0);
green.style.position = Position.Absolute;
green.RegisterCallback<PointerDownEvent>(GreenDown);
green.name = "Green";
root.Add(green);
var red = new VisualElement();
red.style.left = Pixels(300);
red.style.top = Pixels(300);
red.style.width = Pixels(200);
red.style.height = Pixels(200);
red.style.backgroundColor = new Color(1, 0, 0);
red.style.position = Position.Absolute;
red.RegisterCallback<PointerDownEvent>(RedDown);
red.name = "Red";
root.Add(red);
// Children, do allow clickthrough
var blue = new VisualElement();
blue.style.left = Pixels(600);
blue.style.top = Pixels(200);
blue.style.width = Pixels(200);
blue.style.height = Pixels(200);
blue.style.backgroundColor = new Color(0, 0, 1);
blue.style.position = Position.Absolute;
blue.RegisterCallback<PointerDownEvent>(BlueDown);
blue.name = "Blue";
root.Add(blue);
var yellow = new VisualElement();
yellow.style.left = Pixels(50);
yellow.style.top = Pixels(50);
yellow.style.width = Pixels(100);
yellow.style.height = Pixels(100);
yellow.style.backgroundColor = new Color(1, 1, 0);
yellow.style.position = Position.Absolute;
yellow.RegisterCallback<PointerDownEvent>(YellowDown);
yellow.name = "Yellow";
blue.Add(yellow);
// PanelSettings
var panelSettings = Resources.Load<PanelSettings>("PanelSettings");
if (panelSettings == null) {
Debug.Log("No panel settings found");
}
rootDoc.panelSettings = panelSettings;
}
public void GreenDown(PointerDownEvent evt)
{
Debug.Log("Green down");
}
public void RedDown(PointerDownEvent evt)
{
Debug.Log("Red down");
}
public void BlueDown(PointerDownEvent evt)
{
Debug.Log("Blue down");
}
public void YellowDown(PointerDownEvent evt)
{
Debug.Log("Yellow down");
}
public static Length Pixels(float pixels)
{
return new Length(pixels, LengthUnit.Pixel);
}
}
Desired Resolution: The same click-through rules should apply to siblings as they do to children, because the UI layering is identical.