Mar 13, 2025

    css :focus :focus visible and :focus within | all you need to know

    Overview

    If you are working with CSS for a while now you must be familiar with the :focus pseudo class, You add the :focus class to your desired CSS selector and add styles to be displayed whenever that HTML element is focused, seems pretty straightforward right ?, But by only doing this you are missing out on some great features that comes with CSS.

    In this blog we will explore the subtle but important differences between all three pseudo classes :focus, :focus-within, and focus-visible, we will also talk about a selector that does not exist in CSS and we will create it ourselves.

    :focus

    When we add a :focus pseudo class to our CSS selector and add styles to it, those styles will be displayed whenever the user focuses on the HTML element, What's important to note is :

    The styles will be shown regardless if user focuses on the element using their mouse cursor or their keyboard.

    This is crucial to understand, the :focus pseudo class acts as a catch all class for all focus related events.

    :focus-visible

    :focus-visible differs from :focus in that :

    :focus-visible will only display the styles when the element does not have an implicit focus.

    but what does it mean ? For example if we have a button with :focus-visible class, it will have no effect on the styles when the button is clicked, because if the user is clicking on the button they already know what they want and don't need any additional styles to show what the current focus is on, but if user tabs over a button using their keyboard or in any other way, the focus-visible class will come into effect and the styles will be displayed.

    :focus-visible does not work the same way with all HTML elements. For example if the user tabs over an input element, the class will work as expected, BUT even if the user clicks on the input to type something, the class will come into effect and the styles will be displayed. But why ? There could be multiple reasons for it, for example :

    1. When the user clicks on an input element, they would like the element to be focused so they know where they are providing the input.
    2. If a user is on a mobile phone or other touchscreen device and they click on an input element, the on screen keyboard of their device will pop up and will move our HTML content around to make space for the keyboard. In this case as well user would like the input element to be in focus so they don't lose track of it.

    :focus-within

    This class is fundamentally different from the other two pseudo classes that we just discussed.

    :focus-within is added to the container which contains our elements instead of the elements itself.

    This class allows us to style the parent container based on the focus state of its children. Whenever any child of the element with this class is focused, this class will come into effect and all the styles for the container will be displayed. This might be useful in custom drop downs or any other custom element that you create.

    The only caveat is, If a child has :focus-visible class and it get implicit focus from a user action like click, the :focus-visible will not come into effect and no styles will be shown for that element, BUT the parent class :focus-within will come into effect and the parent container will be styled. This might not be your desired behavior, we will address this in the following section.

    :focus-visible-within OR :focus-within:visible

    Before we go any further, I just want to make one thing clear :

    This class does not exist !

    If this class existed, it would work something like this : whenever a container's child with a class of :focus-visible gets focused, this class would come into effect and we would have been able to style the parent container.

    But don't worry, we can implement this behavior ourselves with a very simple CSS selector :

    .container:has(:focus-visible)

    Here the .container is a class that we have added to our parent element, and using the 'has' selector we are checking if the parent element contains any child with focus-visible. By using this simple selector we can achieve our desired behavior.

    Example 1.

    If you still have any doubt with any of the pseudo classes we discussed above, hopefully this example will make it more clear.

    For this example we will create a simple HTML div tag with some children and a CSS file to show different focus states.

    index.html

        <div class="container">
            <button class="focus">Focus</button>
            <button class="focus-visible">Focus Visible</button>
            <input class="focus" type="text" placeholder="focus">
            <input class="focus-visible" type="text" placeholder="focus-visible">
        </div>
    

    styles.css

    /* Other basic positioning and spacing styles before this, not relevant for this example*/
    .focus:focus{
        border: 2px solid red;
        outline: none;
    }
    
    .focus-visible:focus-visible{
        border: 2px solid red;
        outline: none;
    }
    
    .container:focus-within{
        border-radius: 4px;
        box-shadow: 0 4px 4px 2px rgba(0, 0, 0, 0.3);
        background-color: rgba(73, 88, 61, 1);
    }
    
    

    Our example app looks like this :

    We have a div parent container, two buttons and two input elements. One of each button and input element is styled using :focus and the other is styled using :focus-visible. The container is also being styled differently and it's background color will be changed if any of its child is in a focused state.

    This is how our app will behave when we click on or use keyboard to navigate to any of our HTML element :

    There are a few things to note here :

    1. When we click on or navigate the button and input styled with :focus, both of them get styled as expected.
    2. When we click on or navigate the button second button styled with :focus-visible, the button does not get styled because it has implicit focus since we are clicking on it and not navigating to it from a keyboard or any other device.
    3. When we click on or navigate the input styled with :focus-visible it still gets styled even if it has implicit focus, because user needs to have track of the element that they are typing on.
    4. The container is styled using :focus-within and it gets styled when we click on the button and input styled with :focus as expected, but it also gets styled when we click on the button and input styled with :focus-visible.
    5. When we click outside the focused element it's styles get removed along with the container's styles.

    Example 2

    For this example our HTML will stay the same, the CSS will stay the same for all input and button elements. For the container we now use the has selector to simulate :focus-visible-within behavior for the container.
    Here is the updated CSS for the container :

    styles.css

    /* .container:focus-within{
        border-radius: 4px;
        box-shadow: 0 4px 4px 2px rgba(0, 0, 0, 0.3);
        background-color: rgba(73, 88, 61, 1);
    } */
    
    .container:has(:focus-visible){
        border-radius: 4px;
        box-shadow: 0 4px 4px 2px rgba(0, 0, 0, 0.3);
        background-color: rgba(73, 88, 61, 1);
    }
    

    Now the container's style behavior will be different depending on whether we clicked on a button or if we used our keyboard to navigate to it.

    If we use our keyboard and press tab to cycle between our elements, it will look like this :

    But if use our mouse to click on the elements, it will look something like this :

    Things to note here :

    1. In the first image, when we are using our keyboard to tab over the elements, container's styles are getting changed because :has(:focus-visible) selector comes into effect.
    2. In the second image, when we are using our mouse to click on elements, for the buttons the container's style does not change as expected, and for the inputs it does.

    Conclusion

    You should now have a clear understanding of all the focus states and pseudo classes. If you still have any doubts or if you have something to add, don't hesitate to comment it down below 😄.

    Share this:

    Category:

    frontend development

    Tags:

    0/3000