Data-Attributes Magic with Tailwind CSS & Stimulus
Rails Designer
Posted on March 28, 2024
This article was originally published on Rails Designer
Changing the way some, or all, UI elements on an user interaction (click, hover, etc.) is really common. So you better have a good process to use this common flow.
There are basically three ways to go about this:
- add the CSS class(es) to every element that needs changing;
- add CSS classes to the parent element (eg.
<span class="nav-opened">
) to enable cascading of additional styles from it to child elements; - add a data attribute to the parent element (eg.
<span data-nav-opened>
) to enable cascading of additional styles from it to child elements.
I don't think I need to go over option 1, as that would never be a maintainable option. So let's check out option 2 first, followed by option 3. As I use Tailwind CSS exclusively that's what these examples use.
<nav class="group/nav nav-opened">
<ul class="hidden group-[.nav-opened]/nav:block">
<li>Item here</li>
</ul>
</nav>
I'm using named grouping, opposed to just group
(which is a good thing to do any way).
I don't think this solution is really bad, especially if you name the CSS class (.nav-opened
) well. But now check out the option with a data attribute.
<nav data-nav-opened class="group/nav">
<ul class="hidden group-data-[nav-opened]/nav:block">
<li>Item here</li>
</ul>
</nav>
Quite similar, right? Actually it is a bit longer than the CSS class option. But the more important point is that it keeps concerns separated (CSS classes for aesthetics and data-attributes for states and behavior).
Quick pro-tip. The following works just as well with Tailwind CSS. It uses the open
-modifier.
<nav open class="group/nav">
<ul class="hidden group-open/nav:block">
<li class="text-orange-500">Item here</li>
</ul>
</nav>
So far, the examples shown were really simple. But there's no reason you cannot expand what happens when the parent's “state” changes. A common thing you see is a chevron that changes rotation. Something like this:
<nav data-nav-opened class="group/nav">
<button class="flex items-center gap-1">
Open
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" data-slot="icon" class="w-3 h-3 transition group-data-[nav-opened]/nav:rotate-180">
<path fill-rule="evenodd" d="M12.53 16.28a.75.75 0 0 1-1.06 0l-7.5-7.5a.75.75 0 0 1 1.06-1.06L12 14.69l6.97-6.97a.75.75 0 1 1 1.06 1.06l-7.5 7.5Z" clip-rule="evenodd"/>
</svg>
</button>
<ul class="hidden group-data-[nav-opened]/nav:block">
<li>Item here</li>
</ul>
</nav>
Notice the svg
inside the button that flips 180º when data-nav-opened
is added? From here on out, it's only your imagination that is the limiting factor.
How can we combine this with Stimulus?
Stimulus is a great choice for user interactions like this as it's really declarative, this works great together with the Tailwind CSS setup explained above.
The following Stimulus controller is one I use often (and comes together with some of the Rails Designer Components).
// app/javascript/controllers/data_attribute_controller.js
import { Controller } from "@hotwired/stimulus";
export default class extends Controller {
static values = { key: String };
disconnect() {
this.element.removeAttribute(this.keyValue);
}
toggle() {
this.element.toggleAttribute(`data-${this.keyValue}`);
}
}
That's the whole controller! With the above example, this is how it's used:
<nav data-controller="data-attribute" data-attribute-key-value="nav-opened" class="group/nav">
<button data-action="data-attribute#toggle" class="flex items-center gap-1">
Open
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" data-slot="icon" class="w-3 h-3 transition group-data-[nav-opened]/nav:rotate-180">
<path fill-rule="evenodd" d="M12.53 16.28a.75.75 0 0 1-1.06 0l-7.5-7.5a.75.75 0 0 1 1.06-1.06L12 14.69l6.97-6.97a.75.75 0 1 1 1.06 1.06l-7.5 7.5Z" clip-rule="evenodd"/>
</svg>
</button>
<ul class="hidden group-data-[nav-opened]/nav:block">
<li>Item here</li>
</ul>
</nav>
And that's how, by combining two—really developer-friendly—tools, you can create usable (Rails) applications with a minimal amount of code.
Posted on March 28, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.