mirror of
https://github.com/bigskysoftware/htmx.git
synced 2025-10-02 15:25:26 +00:00
view transitions
This commit is contained in:
parent
9162262cdc
commit
60a4741bb3
@ -17,6 +17,7 @@ insert_anchor_links = "left"
|
|||||||
* [SPA Alternative](@/essays/spa-alternative.md)
|
* [SPA Alternative](@/essays/spa-alternative.md)
|
||||||
* [A Response To "Have SPAs Ruined The Web"](@/essays/a-response-to-rich-harris.md)
|
* [A Response To "Have SPAs Ruined The Web"](@/essays/a-response-to-rich-harris.md)
|
||||||
* [Template Fragments](@/essays/template-fragments.md)
|
* [Template Fragments](@/essays/template-fragments.md)
|
||||||
|
* [View Transitions](@/essays/view-transitions.md)
|
||||||
|
|
||||||
|
|
||||||
### Older [intercooler.js](https://intercoolerjs.org) Essays
|
### Older [intercooler.js](https://intercoolerjs.org) Essays
|
||||||
|
250
www/content/essays/view-transitions.md
Normal file
250
www/content/essays/view-transitions.md
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
+++
|
||||||
|
template = "demo.html"
|
||||||
|
title = "View Transitions"
|
||||||
|
date = 2023-04-11
|
||||||
|
[taxonomies]
|
||||||
|
tag = ["posts"]
|
||||||
|
+++
|
||||||
|
|
||||||
|
We have asserted, for a while now, that a major reason that many people have adopted the SPA architecture for web applications
|
||||||
|
is due to aesthetic considerations.
|
||||||
|
|
||||||
|
As we mention in our book [Hypermedia Systems](https://hypermedia.systems), when
|
||||||
|
discussing the Web 1.0-style contact management application we begin with, there are serious _aesthetic_ issues with
|
||||||
|
the application, even if it has feature-parity with an SPA version:
|
||||||
|
|
||||||
|
> From a user experience perspective: there is a noticeable refresh when you move between pages of the application, or when you create, update or
|
||||||
|
> delete a contact. This is because every user interaction (link click or form submission) requires a full page
|
||||||
|
> refresh, with a whole new HTML document to process after each action.
|
||||||
|
>
|
||||||
|
> *–Hypermedia Systems - [Chapter 5](https://hypermedia.systems/book/extending-html-as-hypermedia/)*
|
||||||
|
|
||||||
|
This jarring "ka-chunk" between webpages, often with a [Flash of Unstyled Content](https://webkit.org/blog/66/the-fouc-problem/)
|
||||||
|
has been with us forever and, while modern browsers have improved the situation somewhat (while, unfortunately, also making
|
||||||
|
it less obvious that a request is in flight) the situation is still bad, particularly when compared with what a well-crafted
|
||||||
|
SPA can achieve.
|
||||||
|
|
||||||
|
Now, early on in the life of the web, this wasn't such a big deal. We had stars flying around dinosaurs _in the browser's toolbar_,
|
||||||
|
flaming text, table-based layouts, dancing babies and so forth, and we were largely comparing the web with things like
|
||||||
|
ftp clients.
|
||||||
|
|
||||||
|
The bar was _low_ and the times were _good_.
|
||||||
|
|
||||||
|
Alas, the web has since put away such childish things, and now we are expected to present polished, attractive interfaces
|
||||||
|
to our users, _including_ smooth transitions from one view state to another.
|
||||||
|
|
||||||
|
Again, we feel this is why many teams default to the SPA approach: the old way just seems... clunky.
|
||||||
|
|
||||||
|
## CSS Transitions
|
||||||
|
|
||||||
|
The early web engineers realized that web developers would like to provide smooth transitions between different view states
|
||||||
|
and have offered various technologies for achieving this. A major one is [CSS Transitions](https://developer.mozilla.org/en-US/docs/Web/CSS/transition),
|
||||||
|
which allow you to specify a mathematical _transition_ from one state to another.
|
||||||
|
|
||||||
|
Unfortunately for HTML, CSS transitions are only available if you use JavaScript: you have to change elements dynamically
|
||||||
|
in order to trigger the transitions, which "vanilla" HTML can't do. In practice, this meant that only the cool kids
|
||||||
|
using JavaScript to build SPAs got to use these tools, further cementing the _aesthetic superiority_ of SPAs.
|
||||||
|
|
||||||
|
htmx, as you probably know, makes CSS Transitions [available in plain HTML](https://htmx.org/examples/animations/) via
|
||||||
|
a somewhat elaborate [swapping model](https://htmx.org/docs/#request-operations) where we take elements that are in both
|
||||||
|
the old and new content and "settle" attributes on them. It's a neat trick and can be used to make hypermedia-driven
|
||||||
|
application feel as buttery-smooth as well done SPA.
|
||||||
|
|
||||||
|
However, there is a new kid on the block: [The View Transition API](https://developer.chrome.com/docs/web-platform/view-transitions/)
|
||||||
|
|
||||||
|
## The View Transition API
|
||||||
|
|
||||||
|
The View Transition API is much more ambitious than CSS transitions in that it is attempting to provide a simple, intuitive
|
||||||
|
API for transitioning an _entire DOM_ from one state to another in a way that mere mortals can take advantage of.
|
||||||
|
|
||||||
|
Furthermore, this API is supposed to be available not only in JavaScript, but also for plain old links and forms in HTML as well,
|
||||||
|
making it possible to build _much nicer_ user interfaces using the Web 1.0 approach.
|
||||||
|
|
||||||
|
It will be fun to revisit the Contact application in "Hypermedia Systems" when this functionality is available!
|
||||||
|
|
||||||
|
As of this writing, however, the API is, like CSS Transitions, only available in JavaScript, and its only been just
|
||||||
|
released in Chrome 111+.
|
||||||
|
|
||||||
|
In JavaScript, The API could not be more simple:
|
||||||
|
|
||||||
|
```js
|
||||||
|
|
||||||
|
// this is all it takes to get a smooth transition from one
|
||||||
|
// state to another!
|
||||||
|
document.startViewTransition(() => updateTheDOMSomehow(data));
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, that's my kind of API.
|
||||||
|
|
||||||
|
As luck would have it, it's trivial to wrap this API around the regular htmx swapping model, which allows us to
|
||||||
|
start exploring View Transitions in htmx, even before it's generally available in HTML!
|
||||||
|
|
||||||
|
And, as of [htmx 1.9.0](https://unpkg.com/htmx.org@1.9.0), you can start experimenting with the API by adding the
|
||||||
|
`transition:true` attribute to an [`hx-swap`](/attributes/hx-swap) attribute.
|
||||||
|
|
||||||
|
## A Practical Example
|
||||||
|
|
||||||
|
So let's look at a simple example of this new shiny toy coupled with htmx.
|
||||||
|
|
||||||
|
Doing so will involve two parts:
|
||||||
|
|
||||||
|
* Defining our View Transition animation via CSS
|
||||||
|
* Adding a small annotation to an htmx-powered button
|
||||||
|
|
||||||
|
### The CSS
|
||||||
|
|
||||||
|
The first thing that we need to do is define the View Transition animation that we want.
|
||||||
|
|
||||||
|
* Define some animations using @keframes to slide and fade content
|
||||||
|
* Define a view transition with the name `slide-it` using the `:view-transition-old()` and `:view-transition-new()` pseudo-selectors
|
||||||
|
* Tie the `.sample-transition` class to the `slide-it` view transition that we just defined, so we can bind it to elements via a that CSS class name
|
||||||
|
|
||||||
|
(Fuller details on the View Transition API can be found on the [Chrome Developer Page](https://developer.chrome.com/docs/web-platform/view-transitions/)
|
||||||
|
documenting them.)
|
||||||
|
|
||||||
|
```html
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
to { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-from-right {
|
||||||
|
from { transform: translateX(90px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-to-left {
|
||||||
|
to { transform: translateX(-90px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* define animations for the old and new content */
|
||||||
|
::view-transition-old(slide-it) {
|
||||||
|
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
|
||||||
|
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
|
||||||
|
}
|
||||||
|
::view-transition-new(slide-it) {
|
||||||
|
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
|
||||||
|
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tie the view transition to a given CSS class */
|
||||||
|
.sample-transition {
|
||||||
|
view-transition-name: slide-it;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
This CSS sets it up such that content with the `.sample-transition` class on it will fade out and slide to the left when
|
||||||
|
it is removed, and new content will fade in and slide in from the right.
|
||||||
|
|
||||||
|
### The HTML
|
||||||
|
|
||||||
|
With our View Transition defined via CSS, the next thing to do is to tie this View Transition to an actual element that
|
||||||
|
htmx will mutate, and to specify that htmx should take advantage of the View Transition API:
|
||||||
|
|
||||||
|
```html
|
||||||
|
|
||||||
|
<div class="sample-transition">
|
||||||
|
<h1>Initial Content</h1>
|
||||||
|
<button hx-get="/new-content"
|
||||||
|
hx-swap="innerHTML transition:true"
|
||||||
|
hx-target="closest div">
|
||||||
|
Swap It!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we have a button that issues an `GET` to get some new content, and that replaces the closest div's inner HTML
|
||||||
|
with the response.
|
||||||
|
|
||||||
|
That div has the `sample-transition` class on it, so the View Transition defined above will apply to it.
|
||||||
|
|
||||||
|
Finally, the `hx-swap` attribute includes the option, `transition:true`, which is what tells htmx to use the
|
||||||
|
internal View Transition JavaScript API when swapping.
|
||||||
|
|
||||||
|
## Demo
|
||||||
|
|
||||||
|
With all that tied together, we are ready to start using the View Transition API with htmx. Here's a demo, which
|
||||||
|
should work in Chrome 111+ (other browsers will work fine, but won't get the nice animation):
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes fade-in {
|
||||||
|
from { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-out {
|
||||||
|
to { opacity: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-from-right {
|
||||||
|
from { transform: translateX(90px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slide-to-left {
|
||||||
|
to { transform: translateX(-90px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* define animations for the old and new content */
|
||||||
|
::view-transition-old(slide-it) {
|
||||||
|
animation: 180ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
|
||||||
|
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
|
||||||
|
}
|
||||||
|
::view-transition-new(slide-it) {
|
||||||
|
animation: 420ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
|
||||||
|
600ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tie the view transition to a given CSS class */
|
||||||
|
.sample-transition {
|
||||||
|
view-transition-name: slide-it;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="sample-transition" style="padding: 24px">
|
||||||
|
<h1>Initial Content</h1>
|
||||||
|
<button hx-get="/new-content" hx-swap="innerHTML transition:true" hx-target="closest div">
|
||||||
|
Swap It!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var originalContent = htmx.find(".sample-transition").innerHTML;
|
||||||
|
|
||||||
|
this.server.respondWith("GET", "/new-content", function(xhr){
|
||||||
|
xhr.respond(200, {}, "<h1>New Content</h1> <button hx-get='/original-content' hx-swap='innerHTML transition:true' hx-target='closest div'>Restore It! </button>")
|
||||||
|
});
|
||||||
|
|
||||||
|
this.server.respondWith("GET", "/original-content", function(xhr){
|
||||||
|
xhr.respond(200, {}, originalContent)
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
Assuming you are looking at this page in Chrome 111+, you should see the content above slide gracefully out to the
|
||||||
|
left and be replaced by new content sliding in from the right. Nice!
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Hey now, that's pretty neat, and, once you get your head around the concept, not all that much work! This new API
|
||||||
|
shows a lot of promise.
|
||||||
|
|
||||||
|
View Transitions are an exciting new technology that we feel can dramatically level the playing field between
|
||||||
|
[Hypermedia Driven Applications](https://htmx.org/essays/hypermedia-driven-applications/) and the more prevalent SPA
|
||||||
|
architecture used today.
|
||||||
|
|
||||||
|
By doing away with the ugly "ka-chunk" of Web 1.0 applications, the aesthetic advantages of the
|
||||||
|
SPA approach will be diminished, and we can make decisions less around "sizzle" and focus more on the actual [technical
|
||||||
|
tradeoffs](https://htmx.org/essays/when-to-use-hypermedia/) associated with various architectures.
|
||||||
|
|
||||||
|
We are looking forward to when View Transitions are available in vanilla HTML, but, until then, you can start playing
|
||||||
|
with them in htmx, today!
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user