Idea and Setup
The setup is pretty straight-forward. It’s just a turbo frame with a spinner inside of it.
However, when the frame is being loaded, it looks something like <turbo-frame id="loadable" busy="" aria-busy="true">
- it has a [busy]
attribute attached to it.
Turns out, it’s possible to leverage that [busy]
attribute. 💆
Adding Tailwind modifiers
This is where most of the 🪄🎩 magic 🎩🪄 happens. Tailwind allows to set up custom modifiers which is exactly what we need in this case.
// tailwind.config.js
let plugin = require("tailwindcss/plugin");
module.exports = {
// ...
plugins: [
plugin(({ addVariant }) => {
addVariant("busy", "&[busy]");
addVariant("group-busy", ":merge(.group)[busy] &");
}),
],
};
Here a busy
modifier is set up which is bound to the [busy]
attribute. Also, group-busy
is added to target the spinner inside of the parent turbo frame.
Finishing touches
Now we can just ✨sprinkle✨ some classes on some elements.
<turbo-frame id="loadable" class="group">
<svg
class="group-busy:inline hidden h-6 w-6 animate-spin"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
class="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
></circle>
<path
class="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
</turbo-frame>
In this instance, the spinner just appears while the turbo frame is being loaded. The frame element now has a group
class while the spinner now has group-busy:inline hidden
classes.
Obviously, you can now style it to your liking when using the respective modifiers. For example, busy:opacity-50
will make the turbo frame transparent while loading.
Wrapping up
The main goal was to once again shake off some unnecessary Javascript from the codebase. This turned out to be a rather tidy way of doing so while keeping the simplicity of Hotwire and the flexibility of Tailwind.