The :target pseudo-class refers to an element within the document that the URL’s fragment points to. For example, this piece of text is wrapped in a <mark> element which has an ID of #target-test. If you went to the url, https://bitsofco.de/the-target-trick#target-test, then that element will be the target and and styles applied to the :target pseudo-class for that element will take effect.

Last year, I wrote about the :target pseudo-class in my article on 5 Lesser Used CSS Selectors (and some use cases for them). The example I mentioned for the :target pseudo-class was to highlight the section of the page that is being referenced. For example, adding a background colour or border to it like in the example above.

But it recently occurred to me that we can use the :target pseudo-element in a much more useful way, to create interactive elements on the page without needing javascript.

Example 1 - Hiding & Showing Content

A simple use case for the :target pseudo-class is just to hide and show the content targeted. In a blog, for example, we may not want to display the comments section until the user clicks to show them. To achieve this we can simply hide the element unless it is a :target.

<a href="#comments">Show Comments</a>

<section id="comments">  
    <h3>Comments</h3>
    <!-- Comments here... -->
    <a href="#">Hide Comments</a>
</section>  
#comments:not(:target) {
    display: none;
}
#comments:target {
    display: block;
}

Demo of Hiding & Showing Content using :target

View Live Demo

Example 2 - A Slide-Out Navigation Drawer

Another use case is in creating a slide-out navigation drawer. We can fix the navigation drawer relative to the viewport to ensure that there will be no jumping around when the user clicks to open.

#nav {
    position: fixed;
    top: 0;
    height: 100%;
    width: 80%;
    max-width: 400px;
}

#nav:not(:target) {
    right: -100%;
    transition: right 1.5s;
}

#nav:target {
    right: 0;
    transition: right 1s;
}

Demo of A Slide-Out Navigation Drawer using :target

View Live Demo

Example 3 - A Pop-Up Modal

Taking this further, we can create a modal that will fill the entire page.

#modal-container {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.8);
    display: flex;
    justify-content: center;
    align-items: center;
}

.modal {
    width: 70%;
    background: #fff;
    padding: 20px;
    text-align: center;
}

#modal-container:not(:target) {
    opacity: 0;
    visibility: hidden;
    transition: opacity 1s, visibility 1s;
}

#modal-container:target {
    opacity: 1;
    visibility: visible;
    transition: opacity 1s, visibility 1s;
}

Demo of A Pop-Up Modal using :target

View Live Demo

Example 4 - Changing Global Styles

A final use case, although a less semantically-proper one, could be to apply :target to the <body> element itself, and completely restyle the page or alter its layout.

#body:not(:target) {
    main { width: 60%; }
    aside { width: 30%; }
    .show-sidebar-link { display: none; }
}

#body:target {
    main { width: 100%; }
    aside { display: none; }
    .hide-sidebar-link { display: none; }
}

Demo of Changing Global Styles using :target

View Live Demo

Is this Semantic and Accessible?

As I mentioned in Anchors vs Buttons, when an <a> element is used, the browser expects that the user be navigated to a different page or a different section within the page. In these examples (except the last one), that is essentially what is happening. The only trick is that the target element is being styled so it is hidden and shown dynamically.

As far as I can tell, there are two potential issues with this method -

  1. There is a change in the URL, which affects the user's browsing history. This means that if the user goes "back", they may be unintentionally navigated to the target element.
  2. To "close" the target element, the user needs to be navigated to either another element or just a #. The latter, which I have used in my examples, is not the most semantic and will cause the user to jump to the top of the page if they aren't there already.

Nonetheless, if used correctly, I think this method can still be at least a fallback to provide interaction for users if JavaScript is not an option. In some cases, like in my first example, it can even be preferable to and simpler than using JavaScript. As always, it depends on the scenario.