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.
) to enable cascading of additional styles from it to child elements;
- add a data attribute to the parent element (eg.
) 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.
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.
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.
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:
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:
And that’s how, by combining two—really developer-friendly—tools, you can create usable (Rails) applications with a minimal amount of code.