You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
*[current specification issues (open and closed)](https://github.com/w3c/csswg-drafts/issues?q=label%3Acss-navigation-1%20is%3Aissue)
11
9
* initial syntax discussion for HTML route matching: https://github.com/WICG/declarative-partial-updates/issues/46
12
-
*initial discussion for CSS route matching: https://github.com/w3c/csswg-drafts/issues/12594
10
+
*Discussion for CSS route matching: https://github.com/w3c/csswg-drafts/issues/12594
13
11
14
12
# Motivation and Use Cases
15
13
@@ -38,6 +36,7 @@ This means adding features that:
38
36
* match patterns of URLs by exposing [URL Patterns](https://urlpattern.spec.whatwg.org/) in CSS
39
37
* apply styles conditionally based on the origin and destination of the current navigation, so that transitions between particular sets of URLs (whether separate documents or separate states/routes within a single page app) can be styled
40
38
* style an HTML link based on its target matching both the origin or destination of the current navigation *and* matching a particular URL pattern, which allows matching the correct item within the list in a list-to-details or details-to-list transition.
39
+
* apply styles based on the history type (e.g. back/forward) and phase (e.g. loading/committed) of the navigaiton
@@ -106,20 +102,7 @@ navigation.addEventListener("navigate", e => {
106
102
107
103
## Two-phase preview view transitions
108
104
109
-
See https://github.com/w3c/csswg-drafts/issues/12829
110
-
111
-
When performing a cross-document view-transition, the transition often has to delay until the next document is ready.
112
-
By using route-matching, we can render a "preview" of the new state using style only, and transition to that instantly, before continuing to the final content.
113
-
114
-
```css
115
-
@route (to: article) {
116
-
.article-skeleton { display: block }
117
-
}
118
-
119
-
@navigation {
120
-
view-transition: with-preview;
121
-
}
122
-
```
105
+
See [two-phase view transitions explainer](https://github.com/w3c/csswg-drafts/blob/main/css-view-transitions-2/two-phase-transition-explainer.md#solution-2-declarative-preview-view-transitions--navigation-preview-state)
123
106
124
107
## Declarative same-documentview transitions
125
108
@@ -146,13 +129,193 @@ navigation.addEventListener("navigate", e => {
146
129
});
147
130
```
148
131
149
-
## Style based on current route
132
+
<h1>CSS Navigations 1</h1>
133
+
134
+
135
+
The scope of `css-navigation-1` is to:
136
+
* change style conditionally (importantly including view transition) based on current navigation state
137
+
* apply style to links that participate in navigations
138
+
139
+
<h2>URL patterns</h2>
140
+
141
+
One main issue with URL matching is that URL comparison is notoriously finicky.
142
+
For example, alink to the `/about` page might be written as `/about`, `/about/`, or even `/about?utm_source=something`.
143
+
144
+
This has been addressed by the [URL Pattern API](https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API), by allowing
145
+
a `URLPattern` to act as a "matcher" - aset of rules to extract parameters from a URL or match it against another URL.
146
+
147
+
The `css-navigation-1` happily adopts the concept of URL patterns.
148
+
149
+
<h2>Routes</h2>
150
+
151
+
The `css-navigation-1` spec exposes a `@route` rule, which allows naming a URL or url pattern for use by conditional `@navigation` rules or link styling.
152
+
```css
153
+
@route --home {
154
+
pathname: "/";
155
+
base-url: document;
156
+
}
157
+
```
158
+
159
+
A `@route` can be a URL pattern or a URL.
160
+
161
+
In addition, the `url-pattern(string)` function can be used to define a quick URL pattern without having to name it.
162
+
163
+
<h2>Active navigation state</h2>
164
+
165
+
Both link matching and conditional navigation styling rely on the concept of "active navigation state".
166
+
The active navigation state is defined in terms of the HTML standard, and contains:
167
+
* The old & new URLs
168
+
* The old & new history index
169
+
* type (push, replace, traverse, reload)
170
+
* phase (loading, ready, committed)
171
+
* The sourceelement (clicked link, form, or submit button)
172
+
173
+
The CSS exposed definitions all rely on this state and reflect it.
174
+
175
+
This state is processed differently based on one of the following scenarios:
176
+
* For asame-document navigation, it relies on the navigation API and on its definition of whether a navigation is "committed".
177
+
* For across-document navigation, it starts when the navigation is initiated, and ends in the first frame of the new page.
178
+
179
+
When same-document navigations occur before the new page is rendered, the cross-document navigation takes precedence,
180
+
as the same-document navigation is "non-visual" so cannot be styled.
181
+
182
+
183
+
<h2>Conditional navigation styling</h2>
184
+
185
+
The `@navigation` at rule, as well as its corresponding `if (navigation())` clause, matches the rule with the above active navigation state.
186
+
187
+
188
+
### URL matching
189
+
190
+
```css
191
+
@navigation (from: --home) { ... }
192
+
@navigation (to: --about) { ... }
193
+
@navigation (between: --home and --about) { ... }
194
+
@navigation (with: url-pattern("/*")) { ... }
195
+
@navigation (at: url("/exact/?a=b")) { ... }
196
+
```
197
+
198
+
The `from`, `to`, `with`, and `at` queries define which URL to match in the active navigation state.
199
+
The `from` and `to` URLs are always the old & new ones, while `with` and `at` start as being equivalent to `from` and `to`, and swap when the navigation is committed, either by asame-document navigation API commit, or by the pages swapping in across-document navigation.
200
+
201
+
202
+
### Phase matching
203
+
204
+
```css
205
+
@navigation (phase: loading) { ... }
206
+
@navigation (phase: ready) { ... }
207
+
@navigation (phase: committed) { ... }
208
+
```
209
+
210
+
The `phase` query represents the phase of the active navigation state.
211
+
The `ready` phase is a bit special, and is only active in a (same-origin) cross-document navigation when the new document is ready but not swapped yet, e.g. for the purpose of showing a [preview](https://github.com/w3c/csswg-drafts/blob/main/css-view-transitions-2/two-phase-transition-explainer.md#solution-2-declarative-preview-view-transitions--navigation-preview-state) or for capturing the old state of across-documentview transition.
212
+
213
+
214
+
<h3>Navigation type matching</h3>
215
+
216
+
```css
217
+
@navigation (history: navigate) { ... }
218
+
@navigation (history: reload) { ... }
219
+
@navigation (history: traverse) { ... }
220
+
@navigation (history: back) { ... }
221
+
@navigation (history: forward) { ... }
222
+
```
223
+
224
+
The `history` query matches with the active navigation state's navigation type and session history indices.
225
+
It can help style different view transitions (or any other navigation-basedstyle) differently based on the type of navigation.
226
+
e.g.:
227
+
228
+
```css
229
+
@view-transition {
230
+
navigation: auto;
231
+
types: slide-from-right;
232
+
}
233
+
234
+
@navigation (history: back) {
235
+
@view-transition {
236
+
navigation: auto;
237
+
types: slide-from-left;
238
+
}
239
+
}
240
+
```
241
+
242
+
## Link matching
243
+
244
+
Apart from conditionally styling based on a navigation, links can be styled if they participate in a navigation.
245
+
246
+
### Link matching by route
247
+
248
+
The `:link-to` pseudo-class doesn't take navigation into account, but allows for matching alink URL without resorting to string matching of the `href` attribute:
249
+
250
+
```css
251
+
a:link-to(--home) { ... }
252
+
a:link-to(url-pattern("/about")) { ... }
253
+
a:link-to(url("/exact?a=1")) { ... }
254
+
```
255
+
256
+
### Matching the navigation'ssourceelement
257
+
258
+
The navigation's [source element](https://html.spec.whatwg.org/multipage/#navigation-source-element) is alink, form, or submit button.
259
+
260
+
The `:trigger-link` pseudo class allows styling that particular link, for the course of the navigation:
261
+
262
+
```css
263
+
:trigger-link { animation-name: blink; }
264
+
```
265
+
266
+
Note that `:trigger-link` only matches link elements (similar to `:any-link`).
267
+
268
+
### Matching alink that matches the active navigation
269
+
270
+
While `:trigger-link` is useful for matching the actual link that was clicked, for some use cases it is not sufficient.
271
+
For example, when going from movie details to a movie list full of thumbnails, the particular thumbnail of the movie should be style,
272
+
however it was not clicked!
273
+
274
+
The `:active-navigation` pseudo-class uses route-matching, similar to `@navigation`, to match alink with an active navigation:
275
+
276
+
```css
277
+
@route --movie-list {
278
+
pathname: "/*/movies";
279
+
base-url: document;
280
+
}
281
+
282
+
@route --movie-details {
283
+
pathname: "/*/movie/:id";
284
+
base-url: document;
285
+
}
286
+
287
+
@navigation ((at: --movie-details) and (with: --movie-list)) {
288
+
.hero { view-transition-name: hero-or-thumb }
289
+
}
290
+
291
+
@navigation (at: --movie-list) {
292
+
a:active-navigation(with --movie-details) {
293
+
.thumb { view-transition-name: hero-or-thumb }
294
+
}
295
+
}
296
+
```
297
+
298
+
When matching alink with an active navigation, by default the link's `href` URL is compared against the navigation URL.
299
+
However, when a URL pattern route is given like in the above example, each of these links are processed by the given URL pattern
300
+
and the resulting named groups are matched. This allows comparing a navigation URL with alink in a "lossy" way that allows ignoring
301
+
some URL parameters while respecting others.
302
+
303
+
To illustrate, the above `:active-navigation` rule would match the following:
304
+
*alink to `/en/movie/123` when navigating to or from `/en/movie/123`
305
+
*alink to `/en/movie/123` when navigating to or from `/fr/movie/123`
306
+
*alink to `/en/movie/123?from=list` when navigating to or from `/es/movie/123#scroll-here`
307
+
308
+
This might seem confusing at first but this "non-exact" match of URLs is essential given how URLs can carry multiple bits of information.
309
+
310
+
# Potential future enhancements
311
+
312
+
## Future enhancement: Style based on current route
150
313
In addition to the curation of the navigation itself, it is a common technique in modern web apps to have a "shell" that is common between pages and mostly static, and an "outlet" area for the dynamic content.
151
314
However, some parts of the shell often still have some dynamic parts that appear on "some" pages or in some scenarios, or appear different based on the current page.
152
315
153
316
For example, a chat widget or members area might only appear in certain pages. A "related" `<aside>` element might only appear in article pages.
154
317
155
-
#The initial proposed solution: HTML route map with CSS reflection
318
+
## Future enhancement: HTML route map with CSS reflection
156
319
- Routes are declared in HTML, to avoid requiring all the stylesheets to know the different route URLs or leak those URLs directly, and also to allow future enhancements that are not necessarily style-based.
157
320
- A route at is core is a named `URLPattern`.
158
321
- Matching a route can be toggle-like event target, similar to media-query matching. It can help responding to specific route changes without having to intercept *all* navigations.
@@ -181,36 +344,6 @@ For example, a chat widget or members area might only appear in certain pages. A
181
344
</body>
182
345
```
183
346
184
-
## CSS reflection
185
-
186
-
Naming a set of these rules in HTML already gives us something that CSS can build on:
187
-
```css
188
-
@route (home) {
189
-
#chat-widget { display: none; }
190
-
}
191
-
192
-
nav {
193
-
a:remote-link(pending) .spinner {
194
-
animation: spin;
195
-
}
196
-
}
197
-
198
-
/* navigation-based view-transition can work out of the box
199
-
because we can count on the final CSS state */
200
-
@view-transition {
201
-
navigation: auto;
202
-
}
203
-
204
-
/* or be route-specific */
205
-
@route (to: article) {
206
-
@view-transition {
207
-
navigation: auto;
208
-
types: slide-3d;
209
-
}
210
-
}
211
-
```
212
-
213
-
# Potential future enhancements
214
347
## Declarative interception & history-handling
215
348
216
349
In addition to CSS reflection, some basic navigation interception capabilities can be provided out of the box:
0 commit comments