- Compatible XF 2.x versions
- 2.3
- Additional requirements
- None on the server. The animation plays in browsers that support cross-document View Transitions (Chrome, Edge, Opera 126+); other browsers open pages exactly as before.
- License
- Free to use and modify on any forum.
- Visible branding
- No
Recorded on a stock XenForo 2.3.13 install, default style, shown at one third of the real speed.
Make XenForo feel like an app
When a member opens a thread, its title rises out of the thread list and settles as the page title, while the starter's avatar grows into the first post. Forum titles do the same from the forum list. The header stays perfectly still and the rest of the page fades in. Press Back and it all plays in reverse.
This uses cross-document View Transitions, a browser feature built exactly for this. No add-on, no JavaScript, no file edits: five template modifications and one block of CSS. Ten minutes, fully reversible.
Browser support
- Chrome, Edge, Opera and other Chromium browsers (version 126 and later): full effect. Tested in Chrome.
- Safari 18.2 and later supports cross-document transitions too (not tested here).
- Firefox and older browsers: pages open exactly as they do today. Nothing breaks.
- Members who ask their system for reduced motion get no animation at all.
Step 1: add the CSS
Appearance > Styles > your style > Templates > extra.less, paste this at the end and save.
CSS:
/* Native page transitions for XenForo 2.3 */
@view-transition { navigation: auto; }
.p-header { view-transition-name: xfvt-header; }
.p-nav { view-transition-name: xfvt-nav; }
.p-sectionLinks { view-transition-name: xfvt-subnav; }
.structItem--thread .structItem-title
{
view-transition-name: var(--xfvt-title, none);
view-transition-class: xfvt-text;
}
.structItem--thread .structItem-cell--icon:not(.structItem-cell--iconEnd) .structItem-iconContainer > .avatar:first-child,
.message-avatar .avatar
{
view-transition-name: var(--xfvt-avatar, none);
view-transition-class: xfvt-avatar;
}
.node-title > a
{
display: inline-block;
view-transition-name: var(--xfvt-forum, none);
view-transition-class: xfvt-text;
}
.xfvt-h1
{
display: block;
view-transition-name: var(--xfvt-title, var(--xfvt-forum, none));
view-transition-class: xfvt-text;
}
::view-transition-group(*)
{
animation-duration: .5s;
animation-timing-function: cubic-bezier(.22, 1, .36, 1);
}
::view-transition-old(*.xfvt-text),
::view-transition-new(*.xfvt-text)
{
width: auto;
height: 100%;
}
::view-transition-old(*.xfvt-text) { animation: .2s ease-out both xfvt-out; }
::view-transition-new(*.xfvt-text) { animation: .3s ease-in .1s both xfvt-fade; }
@keyframes xfvt-fade { from { opacity: 0; } }
::view-transition-old(root),
::view-transition-old(*):only-child { animation: .2s ease-out both xfvt-out; }
::view-transition-new(root),
::view-transition-new(*):only-child { animation: .45s cubic-bezier(.22, 1, .36, 1) .05s both xfvt-in; }
@keyframes xfvt-out { to { opacity: 0; } }
@keyframes xfvt-in { from { opacity: 0; transform: translateY(12px); } }
@media (prefers-reduced-motion: reduce)
{
@view-transition { navigation: none; }
}
Step 2: add five template modifications
Appearance > Template modifications > Add template modification. For each one below: pick the template, type a modification key and a description, leave Search type on Simple replacement, paste Find and Replace, save. Each Find string appears exactly once in XenForo 2.3.
1. Thread list: name each title and avatar
Template:
thread_list_macros / key: xfvt_thread_listFind:
Code:
js-threadListItem-{$thread.thread_id}"
Code:
js-threadListItem-{$thread.thread_id}" style="--xfvt-title: xfvt-t{$thread.thread_id}; --xfvt-avatar: xfvt-a{$thread.thread_id}"
2. Thread page: give the title the same name
Template:
thread_view / key: xfvt_thread_viewFind:
Code:
<xf:h1>{{ prefix('thread', $thread) }}{$thread.title}</xf:h1>
Code:
<xf:h1><span class="xfvt-h1" style="--xfvt-title: xfvt-t{$thread.thread_id}">{{ prefix('thread', $thread) }}{$thread.title}</span></xf:h1>
3. First post: give the starter's avatar the same name
Template:
post_macros / key: xfvt_first_postFind:
Code:
id="js-post-{$post.post_id}"
Code:
id="js-post-{$post.post_id}" style="--xfvt-avatar: {{ $post.isFirstPost() ? 'xfvt-a' . $post.thread_id : 'none' }}"
4. Forum list: name each forum title
Template:
node_list_forum / key: xfvt_forum_listFind:
Code:
<a href="{{ link('forums', $node) }}" data-xf-init
Code:
<a href="{{ link('forums', $node) }}" style="--xfvt-forum: xfvt-f{$node.node_id}" data-xf-init
5. Forum page: give the forum title the same name
Template:
forum_view / key: xfvt_forum_viewFind:
Code:
<xf:title page="{$page}">{$forum.Node.title}</xf:title>
Code:
<xf:title page="{$page}">{$forum.Node.title}</xf:title>
<xf:h1><span class="xfvt-h1" style="--xfvt-forum: xfvt-f{$forum.node_id}">{$forum.Node.title}</span></xf:h1>
That's it. Open your forum in Chrome and click a thread.
How it works
The browser takes a snapshot of the page you leave and of the page you land on. Any element that carries the same
view-transition-name on both pages is treated as one object and animated from its old position and size to its new ones. Everything else cross-fades.The template modifications only write a name into a CSS variable, for example
--xfvt-title: xfvt-t42 on thread 42 in the list and on its page title. The CSS decides which element picks the variable up. The title is scaled by its height, so the small list title grows smoothly into the large heading instead of jumping between two font sizes.Good to know
- A name must be unique on a page. If two visible elements share one, the browser skips the animation for that click and simply opens the page. This is why the CSS leaves out the "last poster" avatar on the right of each thread row: it uses the same container class as the starter's avatar.
- Unread links work. Members are sent through
/unread, which redirects to the right post; the transition survives the redirect. - Custom styles: if your style renames
.p-header,.p-navor the thread list markup, adjust the selectors in the CSS. The template modifications do not depend on the style. - Make anything fly: give an element the same
view-transition-nameon two pages, for example a member's avatar on the member card and on the profile. - Slow it down to inspect it: Chrome DevTools > More tools > Animations, then set the speed to 10%.
- Uninstall: delete the five template modifications and the CSS block. Nothing else was touched.
Tested on
XenForo 2.3.13, default style, light and dark variations, desktop and 390 px phone widths, guests and logged-in members, Chrome. Checked for duplicate names on the forum index, forum, thread, What's new, New posts, Featured, Members, Search and Unanswered threads pages: none.