DEV Community

Andy Leverenz
Andy Leverenz

Posted on • Originally published at webcrunch.com on

Digging into View Transitions with Turbo 8 and Rails

This is a super quick tutorial to show you the new ViewTransition API that ships with Turbo 8, released recently.

View Transitions are a neat way to add more fluidity between views in your app. With Rails and Turbo 8, adding this effect is pretty straightforward. The most challenging obstacle is understanding the CSS side of the equation.

Let’s dive in and start experimenting.

Create a new rails app

I’ll use a basic Rails app with esbuild for JavaScript. We won’t use JavaScript in this tutorial, so feel free to use the defaults.

rails new turbo_view_transitions -j esbuild
Enter fullscreen mode Exit fullscreen mode

To save time, I’ll add the tailwind-rails gem to get scaffold UI out of the box.

bundle add tailwindcss-rails
rails tailwindcss:install
Enter fullscreen mode Exit fullscreen mode

With this out of the way we can generate a new Post scaffold with some demo columns.

rails g scaffold Post title:string body:text
Enter fullscreen mode Exit fullscreen mode

Update your root route to posts#index

# config/routes.rb
Rails.application.routes.draw do
  resources :posts  
  get "up" => "rails/health#show", as: :rails_health_check
  root "posts#index"
end
Enter fullscreen mode Exit fullscreen mode

Modifying the Rails app main layout

To use view transitions with Turbo 8, add a new meta tag to your layout’s <head></head> tags. <meta name="view-transition" content="same-origin" />

<!-- app/views/layouts/application.html.erb-->
  <!DOCTYPE html>
<html>
  <head>
    <title>TurboViewTransitions</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <meta name="view-transition" content="same-origin" />

    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_include_tag "application", "data-turbo-track": "reload", type: "module" %>
  </head>

  <body>
    <main class="container mx-auto mt-10 px-5 flex">
      <%= yield %>
    </main>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Here’s my modified layout. The main change is the new meta tag <meta name="view-transition" content="same-origin" />. I also changed the main tag to use a smaller margin at the top of the page.

Modifying the posts UI

In the app/views/posts/index.html.erb I added some new markup to help make this demo more visually appealing.

<div class="w-full">
  <% if notice.present? %>
    <p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
  <% end %>

  <div class="grid grid-cols-12 gap-16">
    <div class="col-span-8">
      <div class="flex justify-between items-center">
        <h1 class="font-bold text-4xl">Posts</h1>
        <%= link_to "New post", new_post_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
      </div>

      <div id="posts">
        <%= render @posts %>
      </div>
    </div>

    <aside class="col-span-4 bg-blue-600 rounded-lg text-white p-10">
      <h3 class="font-semibold text-3xl sidebar-title">Sidebar title</h3>
      <p>This is my sidebar</p>
    </aside>
  </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Consider this a basic blog with a sidebar. We’ll leverage view transitions to make it snazzy.

Add custom view transition CSS

Now for the exciting part. Let's add some view transition effects starting from simple to more complex.

Simple global transitions (easy)

You may be interested in a slight animation on every view transition. To do that, you could add a couple of lines of CSS to app/assets/stylesheets/application.tailwind.css

@tailwind base;
@tailwind components;
@tailwind utilities;

/* Global slight fade */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.5s;
}
Enter fullscreen mode Exit fullscreen mode

Target the root of the page directly and add a global transition.

How about different types of animations?

Want to get a little more sophisticated? Try these styles that offer some slide and fade effects all in one.

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes fade-out {
  to {
    opacity: 0;
  }
}

@keyframes slide-from-right {
  from {
    transform: translateX(60px);
  }
}

@keyframes slide-up {
  from {
    transform: translateY(130px);
  }
}

@keyframes slide-to-left {
  to {
    transform: translateX(-30px);
  }
}

/* Global but more fancy */
::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}

::view-transition-new(root) {
  animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}
Enter fullscreen mode Exit fullscreen mode

Singling out elements and combining transitions

Say you want to target independent elements directly but also have global transition effects. That’s possible. Here’s my code thus far.

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes fade-out {
  to {
    opacity: 0;
  }
}

@keyframes slide-from-right {
  from {
    transform: translateX(60px);
  }
}

@keyframes slide-up {
  from {
    transform: translateY(130px);
  }
}

@keyframes slide-to-left {
  to {
    transform: translateX(-30px);
  }
}

/* Global but more fancy */
::view-transition-old(root) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-out,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-to-left;
}

::view-transition-new(root) {
  animation: 210ms cubic-bezier(0, 0, 0.2, 1) 90ms both fade-in,
    300ms cubic-bezier(0.4, 0, 0.2, 1) both slide-from-right;
}

/* Sidebar */
aside {
  view-transition-name: sidebar;
}

html[data-turbo-visit-direction="forward"]::view-transition-new(sidebar) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-in,
    210ms cubic-bezier(0.4, 0, 0.2, 1) both slide-up;
}
Enter fullscreen mode Exit fullscreen mode

With turbo, we’re able to target directions. And in doing so, you can even tie into either the ::view-transition-new or ::view-transition-old states of the transition. With some gnarly CSS, you can target elements independently of one another and perform some wacky animations.

Notice how, much like CSS Grid, you can name “areas” to define layouts. In this specific case, you can use view-transition-name to name an element in the dom for use in other targeted CSS.

So I used

/* Sidebar */
aside {
  view-transition-name: sidebar;
}
Enter fullscreen mode Exit fullscreen mode

This targets the aside element in app/views/posts/index.html.erb and gives us a way to define a new transitional style based on the turbo directions. Here's the example from before with that in action. Notice how our named view transition is "sidebar.”

html[data-turbo-visit-direction="forward"]::view-transition-new(sidebar) {
  animation: 90ms cubic-bezier(0.4, 0, 1, 1) both fade-in,
    210ms cubic-bezier(0.4, 0, 0.2, 1) both slide-up;
}
Enter fullscreen mode Exit fullscreen mode

Hooking into Turbo

Turbo adds a data-turbo-visit-direction attribute to the <html> element to indicate the direction of the transition. The attribute can have one of the following values:

  • forward in advance visits.
  • back in restoration visits.
  • none in replace visits.

Depending on the request, if you view the source when clicking a link or button, the HTML element will quickly update with the new data attributes. As a result, your CSS can capture the forward, backward, and none directions to manipulate the elements on the page.

Browser Compatibility

Only Chrome and Microsoft Edge are currently supported, but I anticipate Safari and Firefox to follow soon. Keep this in mind as you add this to your apps.

What's the real-world use case for view transitions?

Having toyed with View Transitions, these will be extremely popular for folks leveraging Rails to build mobile apps. It allows you to do less with more native versus non-native apps. We see Turbo in use on the Basecamp team's apps already today, and it's about to get more familiar with other apps.

This is all not to say web apps or progressive web apps shouldn't leverage this feature. It will be overused to some degree. I remember when the trend of animating everything on a marketing website was popular. You couldn't scroll without something moving in or out of view. I worry we're in for that again...

We’ve only scratched the surface

This tutorial is super basic, but I hope it sheds some light on what’s possible with a little dash of CSS and tools you’re already using with Ruby on Rails. Among many new features, Turbo 8 brings a dash of life with View Transitions. Rails apps are about to get more pleasing, and I’m excited and here for it!

Links

Top comments (0)