How I Created The Blog Category Filter

If you've visited my blog page, you've probably noticed the category filtering option, which highlights only blog posts relevant to a given topic. This feature uses only HTML and CSS without JavaScript, and I will share how I made it.


Update from February 2, 2026: The original way I made it was a bit more complicated, and warranted a whole blog post. I later realized I can do it much more simply using radio buttons, as seen in the first example here. I'll keep the original blog post anyway even though it doesn't have a real use case anymore.

Regarding A

Regarding B

Regarding A and B


First, let's talk about the requirements:

First, I created clickable components for each of my blogposts and categories. In this example, I will be working with 3 fake blog posts, titled "Regarding A", "Regarding B", and "Regarding A and B". The categories I wil be using are "A" and "B", and you can guess which blog posts are contained in each category.

<button class="filter-button">Show Me Everything</button>
<button class="filter-button">Show Me Category A</button>
<button class="filter-button">Show Me Category B</button>
<a href="/blog/a" class="blog"><div>
    <h3>Regarding A</h3>
</div> </a>
<a href="/blog/b" class="blog"><div>
    <h3>Regarding B</h3>
</div> </a>
<a href="/blog/ab" class="blog"><div>
    <h3>Regarding A and B</h3>
</div> </a>

The buttons don't yet do anything, but we'll add functionality later. So far we have the following:





Regarding A

Regarding B

Regarding A and B

There are not very many options for changing the appearance of a page following a button press in pure HTML/CSS. One option is with <details> and <summary> tags, such as I used in the expandable content tree on the main page of my website following this tutorial. While I think this option is capable of giving visually good results, I don't think there's a way to allow for blogs to appear in multiple different categories without repeating HTML content.

Instead, we will use the :focus and :focus-within features of CSS to alter the appearance of blogposts given the selected category. One minor hurdle here is that :focus-within looks for whether itself or any of its descendants are "focused", and only affects descendants, which means we need to wrap our categories, together with all our blogposts, in some additional <div>s: one for each category so we can differentiate between each of the categories.

We will also want to target only certain blogposts to hide/appear depending on the categories, so we create classes for each of the categories.

<button class="filter-button">Show Me Everything</button>
<div id="category-a-div">
    <button class="filter-button">Show Me Category A</button>
    <div id="category-b-div">
        <button class="filter-button">Show Me Category B</button>
        <a href="/blog/a" class="blog category-a"><div>
            <h3>Regarding A</h3>
        </div> </a>
        <a href="/blog/b" class="blog category-b"><div>
            <h3>Regarding B</h3>
        </div> </a>
        <a href="/blog/ab" class="blog category-a category-b"><div>
            <h3>Regarding A and B</h3>
        </div> </a>
    </div>
</div>

Here I add padding and different background colors to each of the divs to make it clear what the structure is.

Now, let's add some styling so that children of the category <div>s get affected by which button we click. We can do this by altering the appearance of the blog posts depending on which categories the focus is on. Note that if we click on category B, both category-a-div and category-b-div are focused, so the order of the CSS styling blocks is important, and it is also important that we add a "general" class for all the posts (element selectors like a are of lower specificity than class selectors; we need all of these styling blocks to be of equal specificity). We might optimistically think we can use the display:none; option in CSS to hide the blog posts that aren't in the selected category. But look at what happens when we do that:

<style>
#category-a-div:focus-within .blog {
    display:none;
}
#category-a-div:focus-within .category-a {
    display:inherit;
}
#category-b-div:focus-within .blog {
    display:none;
}
#category-b-div:focus-within .category-b {
    display:inherit;
}
</style>

Maybe this doesn't happen on all browsers, but at least on mine, sometimes when I click on a blog post, the focus gets shifted before a get sent to the blog post I clicked on, which means some of the previously hidden posts become unhidden. This causes the browser not to take me correctly to the page I want. The next best thing is to do what I did and just change the appearance of the blog posts that aren't of the selected category. In this example I used visibility:hidden;, but in my real blog I changed the text color.

<style>
#category-a-div:focus-within .blog {
     visibility:hidden;
}
#category-a-div:focus-within .category-a {
     visibility:inherit;
}
#category-b-div:focus-within .blog {
     visibility:hidden;
}
#category-b-div:focus-within .category-b {
     visibility:inherit;
}
</style>

This causes irrelevant blog posts to keep their position, but become invisible and unclickable, as seen below.

So that's that! From this point, I just added some standard CSS styling to all of the elements to get the working version. If you figure out a way to make a similar categorical filter that actually hides blog posts based on the selected category, and consitently works when clicking on a blog post, please reach out to me and let me know!