<div class="row">
  <h1>Use TAB or hover an element</h1>
</div>              

<div class="row">
  <div>General div: Not focusable</div>
  <div tabindex="0">Div with tabIndex > -1: focusable</div>
  <ul>
    <li>general li: not focusable</li>
    <li tabindex="0">li with tabIndex: focusable</li>
  </ul>
  <details>
    <summary>Details: Focusable</summary>
    some text
    <a href="#">anchor inside details: focucalbe</a>
  </details>
</div>
<div class="row">
  <input type="text" value="text input: focusable" />
  <select>
    <option selected>General Select: focusable</option>
    <option>Text 2</option>
    <option disabled>Disabled Option</option>
    <option>Text 3</option>
  </select>
  <input type="submit" disabled value="Disabled input[type=submit]: not focusable" />
  <div>
    <label>
       <input type="checkbox" checked /> checkbox
    </label>
  </div>
  <div>
    <button type="button">
      general button: focusable
    </button>
  </div>
  <div>
    <button disabled>       
      disabled button: not focusable
    </button>
  </div>
</div>
<div class="row">
  <label for="idArea">
    Label for Textarea
  </label>
  <textarea id="idArea">Testarea: focusable</textarea>
  <label>
    Textarea inside label
    <textarea>Testarea: focusable</textarea>
  </label>
  <a href="#">
    Geteral anchor: focusable
  </a>
  <a>
    Anchor without href: not focusable
  </a>
</div>

<div class="overlay" id="overlay">
  <div class="border border--leftTop" id="leftTop"></div>
  <div class="border border--leftBottom" id="leftBottom"></div>
  <div class="border border--rightTop" id="rightTop"></div>
  <div class="border border--rightBottom" id="rightBottom"></div>
</div>
body {
  background: #1B2B34;
  display: flex;
  flex-direction: column;
  justify-content: center;
  color: #f5f5f5;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

button:disabled,
input:disabled {
  color: #AFC8D5; 
}

a {
  color: #B9AED5;
}

*:not(#fake-id) {
  outline: 0;
}

.row {      
  display: flex;
  padding: 20px;
  justify-content: space-around;
  align-items: center;
  flex-wrap: wrap;
  & > * {
    margin: 5px;
  }
}

.overlay {
  pointer-events: none;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1000;
  transition: opacity 0.1s;
}

.border {
  position: absolute;
  top: -4px;
  left: -4px;
  transform: translate(-20px, -20px);
  transition: transform;
  transition-duration: 0.1s;
  border-style: solid;
  border-width: 1px;
  border-color: red;
  width: 8px;
  height: 8px;
  
  &--leftTop {
    transform: translate3d(-20px, -20px, 1px);
    border-bottom-color: transparent;
    border-right-color: transparent;
  }
  &--leftBottom {
    transform: translate3d(-20px, calc(100vh + 20px), 1px);
    border-top-color: transparent;
    border-right-color: transparent;
  }
  &--rightTop {
    transform: translate3d(calc(100vw + 20px), -20px, 1px);
    border-bottom-color: transparent;
    border-left-color: transparent;
  }
  &--rightBottom {
    transform: translate3d(calc(100vw + 20px), calc(100vh + 20px), 1px);
    border-top-color: transparent;
    border-left-color: transparent;
  }
}
View Compiled
let rect;
let current;

const overlay = document.querySelector('#overlay');
const leftTop = document.querySelector('#leftTop');
const leftBottom = document.querySelector('#leftBottom');
const rightTop = document.querySelector('#rightTop');
const rightBottom = document.querySelector('#rightBottom');

const translate = (x, y) => `transform: translate(${x}px, ${y}px)`;

const show = (element) => {
  const rect = element?.getBoundingClientRect();
  let opacity = 0;
  if (rect) {
    opacity = 1;
    
    leftTop.setAttribute('style', translate(rect.left, rect.top));
    leftBottom.setAttribute('style', translate(rect.left, rect.bottom));
    rightTop.setAttribute('style', translate(rect.right, rect.top));
    rightBottom.setAttribute('style', translate(rect.right, rect.bottom));
  }
  
  overlay.setAttribute('style', `opacity: ${opacity}`);
};

const hide = () => {
  show();
};

const checkHref = (element) => {
  const tag = element.tagName;
  return !(tag === 'A' || tag === 'AREA') || element.href;
};

const onFocus = (event) => {
  if (
    event?.target?.tabIndex > -1 
    && current !== event.target
  ) {
    // skip anchors and areas without href and disabled items
    if (!checkHref(event.target) || event.target.disabled) {
      return;
    }

    current = event.target;
    show(current);
  }
};

const onBlur = (event) => {
  if (event?.target?.tabIndex > -1) {
    // find parent item that can be focusable
    current = current?.parentNode;
    while (current && current.tabIndex < 0) {
      current = current.parentNode;
    }

    if (current === document) {
      current = null;
    }

    // return focus to activeElement
    if (!current && document.activeElement && document.activeElement !== document.body) {
      current = document.activeElement;
    }

    if (current) {
      show(current);
    } else {
      hide();
    }
  }
};

document.addEventListener('focus', onFocus, true);
document.addEventListener('blur', onBlur, true);
document.addEventListener('pointerenter', onFocus, true);
document.addEventListener('pointerleave', onBlur, true);

// don't forget to remove subscription
// document.removeEventListener('focus', onFocus, true);
// document.removeEventListener('blur', onBlur, true);
// document.removeEventListener('pointerenter', onFocus, true);
// document.removeEventListener('pointerleave', onBlur, true);
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.