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;
}
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:
The goal
Let's imagine an element that is only partially visible:
On hover and focus, we want to move the element fully into view, without the "awesome content" moving:
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.
Šime Vidas found out that the jump glitch goes away when the transition
effect is removed. If you don't require any transition effect, that's a fine alternative.
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