Darek Kay's picture Darek Kay

Focusing partially hidden elements in CSS

When combining overflow: hidden with position: absolute, it is possible to position content off-screen:

<nav>
<div>Awesome content</div>
<button>Click me</button>
</nav>
nav {
position: relative;
overflow: hidden;
height: 80px;
}

button {
position: absolute;
bottom: -30px;
height: 30px;
}

Button in an overflow hidden container, rendered outside the container

If the hidden element is focusable (like the button in this example), browsers will scroll this element into view on focus. This is great from accessibility perspective, as all focusable elements are reachable. However, other elements may be moved out of view:

Button moved into the view on focus, moving available content out of view

The goal

Let's imagine an element that is only partially visible:

Half of a button visible

On hover and focus, we want to move the element fully into view, without the "awesome content" moving:

Button fully visible on focus

If you're wondering about a practical application, look around this page 🕵️‍♂️

First solution: bottom

My first idea was to change the button's bottom position. The value is negative by default and 0 on focus/hover:

button {
bottom: -30px;
height: 50px;
transition: bottom 0.2s ease;
}

button:focus,
button:hover
{
bottom: 0;
}

Hover works just fine, but (keyboard) focus is broken due to the browser's default behavior described above. The browser doesn't know that my code will make the focusable element fully visible. Depending on the browser, the content is either moved out of view or there is a short jumping animation.

Not a solution: tabindex

I could make the button unfocusable:

<button tabindex="-1">Click me</button>

The hover effect still works, and the focus behavior isn't broken anymore. But this change would exclude all keyboard users from using my awesome button. That's no good.

Second solution: height

I almost considered a JavaScript workaround, as I came up with my final CSS-only solution: changing height instead of bottom:

button {
bottom: 0;
height: 20px;
transition: height 0.2s ease;
}

button:focus,
button:hover
{
height: 50px;
}

The button is not positioned off-screen anymore, so it doesn't mess around with the surrounding content on hover/focus.

However, there is a visual difference:

  • bottom: The button is moved and slides in on interaction.
  • height: The button is squeezed and expands on interaction.

I'm using an unstyled image button, and the difference isn't noticeable. With a regular button, you might need to put some more effort in making it look good.

Here's the full code sandbox: https://codepen.io/darekkay/pen/VwjqbMM


Want to leave a comment?

Join the discussion at Twitter or Mastodon. Feel free to drop me an email. 💌

Focusing partially hidden elements in CSS