JSFiddle - React, Tailwind, and code Playground

HTML

<div class="contexto" id="A">
    A A A A A A A A A A A A A A A A A A A A A A A A A A A A A A
    <div id="dentroDeA"></div>
</div>
<div class="contexto" id="B">
    B B B B B B B B B B B B B B B B B B B B B B B B B B B B B B
    <div id="dentroDeB"></div>
</div>
<div id="semZ">
    <div id="dentroDeSemZ"></div>
</div>
<div id="semZ2"></div>

<table border="1">
</table>

CSS

.contexto { position: absolute; width: 100px; height: 100px; }

#A { background: red; z-index: 0; }
#B { left: 80px; top: 80px; background: green; z-index: 0; }

.contexto div { position: absolute; width: 40px; height: 40px; }

#A div { bottom: 5px; right: 5px; background: blue; z-index: 0; }
#B div { top: 5px; left: 0px; background: yellow; z-index: 0; }

#semZ { 
    position: absolute; width: 100px; height: 100px;
    top: 0; left: 75px; background: lightgray;
}

#semZ div { 
    position: absolute; width: 40px; height: 40px;
    top: 20px; left: 20px; background: cyan;
}

#semZ2 { 
    position: absolute; width: 100px; height: 100px;
    top: 40px; left: 85px; background: gray;
}

table {
    position:absolute;
    left: 200px;
}

JavaScript

var ctxA = document.getElementById('A');
var ctxB = document.getElementById('B');
var dentroDeA = document.getElementById('dentroDeA');
var dentroDeB = document.getElementById('dentroDeB');
var semZ = document.getElementById('semZ');
var semZ2 = document.getElementById('semZ2');
var DsemZ = document.getElementById('dentroDeSemZ');

function naFrente(a, b, memo) {
    // Guardando o elemento passado na chamada original,
    // em caso de recursão
    memo = memo || [[],[]];
    memo[0].push(a);
    memo[1].push(b);
    
    // Contextos de empilhamento dos elementos passados
    var ctxA = contexto(a);
    var ctxB = contexto(b);

    // Se a é descendente de b, considera que a está na frente
    if(descendente(a, b)) return a;

    // Se b é descendente de ba, considera que b está na frente
    if(descendente(b, a)) return b;    
    
    // Se no mesmo contexto, compara z-índices
    if(ctxA === ctxB) {
        var zA = +css(a, 'z-index');
        var zB = +css(b, 'z-index');
        
        // Comparando dois z-índices definidos
        if(!isNaN(zA) && !isNaN(zB)) {
           return zB > zA ? memo[1][0] : memo[0][0];
        
        // Primeiro termo não definido:
        // retorna o segundo se não for NaN
        } else if(isNaN(zA)) {
            return isNaN(zB) ? memo[0][0] : memo[1][0];
        
        // Ambos NaN, retorna o primeiro termo
        } else {
            return memo[0][0];
        }

    // Recursão no caso de contextos diferentes
    } else {
        // Se subiu até o body, restaura o contexto anterior
        // para achamada recursiva
        ctxA = ctxA === document.body ? a : ctxA;
        ctxB = ctxB === document.body ? b : ctxB;
        return naFrente(ctxA, ctxB, memo);
    }
}

// Determina o elemento que representa o contexto de empilhamento de outro
function contexto(el) {
    var ctx = el.parentElement;
    if(ctx && ctx !== document.body && el !== document.body) {
        // Verifica se o elemento está posicionado, 
      ...