You’re probably a busy person, so here’s the CSS:
section:not(:target) {
display: none;
}
Demo: Open in a new tab
Open in a new tab
Explanation
The :target
CSS selector selects the element that is targeted by the URL fragment.
Combined with :not
, we can hide sections that are not referenced by the URL fragment.
Just as JS routers use the fragment to hide/show sections in the DOM, this “CSS router” uses the same fragment to hide/show sections in the DOM.
Experiment: Default section
Notice that the example above doesn’t start with the Home section. The content is blank initially. This is because on initial page load we don’t have a URL fragment to begin with.
We need to make an exception for the Home section.
Let’s start by not hiding the #home
section by default. Only hide #home
if there’s a specific :target
section.
- section:not(:target) {
+ section:not(#home, :target),
+ :root:has(:target) #home {
display: none;
}
Demo v2: Open in a new tab
Open in a new tab
Experiment: Nested routes
One thing that makes most client-side routers modular is the ability to nest routes. We can do the same with CSS.
- section:not(:target) {
+ section:not(:target, :has(:target)) {
display: none;
}
Demo v3: This demo is best when you view it in a separate tab
Open in a new tab
Parameterised routes?
The ultimate feature for client-side routers is to dynamically catch routes with parameters like for example /post/:id
.
Since HTML is static, there’s no real way to do this with CSS. ☹
Unless… you could render all possible :id
values in the markup and use it like you would nested routes.
<!-- ... -->
<section id="post/128"><!-- ... --></section>
<section id="post/129"><!-- ... --></section>
<section id="post/130"><!-- ... --></section>
<!-- ... -->
But that’d be like putting the entire database in HTML. And if you had multiple parameters in the route, it would be combinatorial explosion. So, nope. 👋