Skip to content

Improve view transitions extension point - #3918

Merged
1cg merged 3 commits into
bigskysoftware:four-devfrom
MichaelWest22:viewTransitionext
Jul 30, 2026
Merged

Improve view transitions extension point#3918
1cg merged 3 commits into
bigskysoftware:four-devfrom
MichaelWest22:viewTransitionext

Conversation

@MichaelWest22

Copy link
Copy Markdown
Collaborator

Description

Add an async extension point for view transitions by allowing the wrapping of the task object

Corresponding issue:

Testing

Please explain how you tested this change manually, and, if applicable, what new tests you added. If
you're making a change to just the website, you can omit this section.

Checklist

  • I have read the contribution guidelines
  • I have targeted this PR against the correct branch (master for website changes, dev for
    source changes)
  • This is either a bugfix, a documentation update, or a new feature that has been explicitly
    approved via an issue
  • I ran the test suite locally (npm run test) and verified that it succeeded

@bkuhlmann

Copy link
Copy Markdown
Contributor

Thanks for doing this. 🙇 I would need to test this with Terminus and htmx Slide to see if this finally allows me to dynamically swap out the desired View Transition at runtime. I'm still traveling back from Big Sky Dev Con but should be able to investigate this more at the end of this week.

@MichaelWest22

Copy link
Copy Markdown
Collaborator Author

@bkuhlmann Just updated my change to include a useful sourceElement to the triggered event and the full internal ctx object which you can use to make conditional decisions on what to change in the transition. You can perform an action before the transition starts and in the before event update the event.detail.task which is an async function to be your own async function that wraps the original task function allowing you to run some custom code before or after the swap inside the transition call. There is then an after event where you could do another action if needed. I think the after event is likely not needed in practice so we could probably remove this. Let me know if you can think of any use for the after event so we know if its worth keeping or not.

@1cg
1cg merged commit f7a9c1f into bigskysoftware:four-dev Jul 30, 2026
3 checks passed
@bkuhlmann

Copy link
Copy Markdown
Contributor

Michael, looks like Carson merged this so will switch to using htmx 4.0.0. Can log an issue if anything crops up.

@MichaelWest22

Copy link
Copy Markdown
Collaborator Author

@bkuhlmann I wonder if changing the events to add the viewTransition object into the first event would help. Then you could tap into .ready promise to customize the transition in JS or skip and create your own transition from scratch. We would just await the event details transition object afterwords which could be reassigned if needed. Be good to know if this was useful or not

@bkuhlmann

Copy link
Copy Markdown
Contributor

Could you provide an example of usage (or I guess I could use a feature branch)? I'd need to tinker with this via htmx Slide to see if what you suggest works.

@MichaelWest22

Copy link
Copy Markdown
Collaborator Author

Could you provide an example of usage (or I guess I could use a feature branch)? I'd need to tinker with this via htmx Slide to see if what you suggest works.
see the changes in #4000

https://github.com/MichaelWest22/htmx/blob/660a3996437f9b6f2c393f8fc9cefd112c9e0c66/src/htmx.js has this modified htmx.js you could test with.

                    let detail = {task, ctx, transition: document.startViewTransition(() => detail.task())};
                    this.__trigger(ctx.sourceElement, "htmx:before:viewTransition", detail);
                    await detail.transition?.finished;
                    this.__trigger(ctx.sourceElement, "htmx:after:viewTransition", detail);

is the new code change. you can still override detail.task as you did before to wrap it in a different before and after. but you can now also use the transistion object from the event with something like:

htmx.on("htmx:before:viewTransition", ({ detail }) => {
    detail.transition.ready.then(() => {
        const isMobile = window.matchMedia("(max-width: 768px)").matches;
        const isForward = navigation.currentEntry.index > navigation.entries().indexOf(navigation.currentEntry) - 1;

        document.documentElement.animate(
            {
                clipPath: isMobile
                    ? ['inset(0 0 100% 0)', 'inset(0 0 0% 0)']
                    : ['inset(0 100% 0 0)', 'inset(0 0% 0 0)'],
                easing: 'cubic-bezier(0.4, 0, 0.2, 1)'
            },
            {
                duration: 350,
                pseudoElement: isForward
                    ? '::view-transition-new(root)'
                    : '::view-transition-old(root)'
            }
        );
    });
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants