Declarative navigation styling in CSS
The CSS Navigation spec draft aims to bring route-aware styling into the stylesheet. The core idea: let authors apply styles based on the specific page-to-page navigation that triggered a view transition, without hand-managing that state in JavaScript.
The proposal introduces two at-rules. A @location block defines a named set of URLs via descriptors, and a @navigation rule lets you query whether a navigation occurred between two of those named locations. Bramus has outlined the mechanics as the feature is being shaped:
- Declare
@locationto capture the URLs involved (earlier drafts called this@route, but there has since been a rename). - Use
@navigationto test for navigation between two declared locations. - Apply transition-related styles to elements on each side of the match.
A basic pairing looks like this:
/* Define the locations */
@location --contact-page {
pathname: ("/contact");
}
@location --contact-confirmation {
pathname: ("/contact/thanks");
}
That syntax depends on knowing both endpoints precisely. For cases where the destination is less exact, the spec offers url-pattern matching:
@location --article {
pattern: url-pattern("/article/:id");
}
/*
Where :id matches anything two levels deep like:
/article/25
/article/3785
/article/whatever
*/
Location descriptors aren't limited to pathname and url-pattern — the draft also anticipates matching on hash, port, hostname, protocol, and search. Concrete examples for when each is the right tool are still sparse, though.
Querying navigation
Within a @navigation rule, you connect two location idents with from and to keywords:
/* Fire a transition when navigating between these two pages */
@navigation (from: --contact) and (to: --contact-confirmation) {
/* Apply transition */
}
For tighter syntax, the same rule can be written more concisely:
/* Fire a transition when navigating between these two pages */
@navigation (between: --contact and --contact-confirmation) {
/* Apply transition */
}
The rule also supports a not keyword for styling whenever a navigation does not match a given pair:
/* Fire a transition, but NOT when navigating between these two pages */
@navigation not (between: --contact and --contact-confirmation) {
/* Apply transition */
}
An additional at keyword connects locations differently. The spec draft is still vague here, but Bramus demonstrates the intent:
@navigation (between: --home and --detail) {
@navigation (at: --home) {
/* Target the clicked link's image */
:nav-source img {
view-transition-name: image;
}
}
}
The read is that this applies to the moment navigation begins — you're at --home, heading toward --detail, and you style the element accordingly at the very start of that transition.
Pseudo-classes for sources and destinations
The spec also defines a :nav-source pseudo (possibly to be renamed :navigation-source) that matches the element that actually triggers the navigation — a link, image, or any other clickable. There's no corresponding pseudo yet for the element on the receiving end of the transition, though one could imagine a use for something akin to :target.
Separately, a :link-to() pseudo targets linked elements whose destination is a declared location. The spec definition:
@location --homepage {
pattern: url-pattern("/");
}
:link-to(--homepage) {
font-weight: bold;
}
Which would apply in HTML such as:
<a href="/">Back to Home</a>
The usefulness here is akin to styling links by destination, but it becomes a stronger convenience when a location is already formally declared elsewhere in the stylesheet.
Limitations and open questions
One significant gap: sites with flat URL structures. At something like CSS-Tricks, where nearly every page is a single path segment (/about, /article-url), you can't craft a location pattern that distinguishes "any article page" from "this one specific page." The spec may implicitly reward sites with more hierarchical URL schemes, and those are not trivial to retrofit. One suggestion is to append a distinguishing query parameter, such as ?blog, to unlock finer-grained pattern matching.
There's also the question of destination-page styling based on the referrer. For example, applying different styles to an About page depending on whether the visitor arrived from the homepage or from elsewhere:
@navigation (between: --home and --article) {
@navigation (at: --article) {
/* Target the .article-header element */
.article-header {
background-image: url('/path-to-image.webp');
}
}
}
Whether such cross-page context inheritance is safe from a privacy standpoint (CSS-based fingerprinting is a known concern) remains to be seen.
There's also a broader architectural critique worth weighing: rather than proliferating single-purpose at-rules like @location, @color-profile, and @position-try, a general-purpose rule — analogous to how @property covers property-value pairs — could have unified configuration for various data structures. Fewer bespoke rules would ease the learning curve.
The draft also covers much more, including navigation types (back, forward, reload) and navigation phases (loading, ready, committed). It's a substantial read, but so is the broader view-transitions model it plugs into.



