The Power Of Dialog/ue - Part 2

Using powerful elements to enhance communication

In the first part of this blog series we discussed how important communication and dialogue is in work, life and websites. In this second part we will explore how the dialog element can be used in other creative ways and find parallels to how to communicate more effectively using tools like Nonviolent Communication (NVC).

Your client says to you, “this analytics interface is completely off from our wireframes. You’re missing the data visualization approach we discussed, and I can’t present this to the marketing team leads”. How do you respond?

Instinctively, you might quickly utter something defensive like “we are working really hard to get this right but it seems like you keep changing what you want”. Or maybe in frustration you might respond with “but we signed off on these requirements and designs just last week.”

Or maybe in fear of losing this big contract you surrender: “You are right. We will try harder. Please give us another chance.”

However, there is a very different option that reflective listening practices such as NVC offer as a different starting point. The goal is to first make sure that you both understand what is being expressed and more importantly that the other party trusts that they are being heard.

It might come out like: “It sounds like you are disappointed and upset and it is really important for you that we have a clearer shared vision for this application so that we can successfully demonstrate this to stakeholders. Am I hearing you correctly?”

We could have just repeated back the same words that the client chose, which would have been alright, but instead we translated some of the feelings and deeper values being expressed. Even if we don’t get it right the client will hopefully sense that we are trying to understand them and build a connection and shift the dynamic towards trust and collaboration. Now we are moving towards effective dialogue.


We often need to pivot in our code as well and the dialog element gives us that flexibility as well. In the project mentioned in part 1 of our series we also use the Jumpstart slideover component you can try out here. However, it turned out that our client wanted the rest of the page to remain interactive since the slideover often contained relevant information for the main user experience.

It was quite simple to override the stimulus controller with our slideout version:


import { Slideover } from "tailwindcss-stimulus-components"

export default class extends Slideover{
  open() {
    this.dialogTarget.show()
  }
}

We are using the show dialog method which “displays the dialog modelessly, i.e., still allowing interaction with content outside of the dialog.”, just exactly what we want.

Of course now we need to take responsibility for how we handle the context stacking which is another good reason to use one dialog for each type of component and to place it at the bottom of your html so that it will have precedence in your stacking contexts on the page.

It is also important to consider how and when to close your dialog components. When using the showModal method you typically will want it to close when clicking anywhere outside the dialog. The slideover stimulus controller has a backdropClose method just for that purpose. Of course, our dialog does not have a backdrop when opened with show so users will now need to resort to using the “x” or some similar explicit button to close it. Note that the ESC key won’t close it either since we now have a non-modal dialog open.

However, we noticed that often we wanted the slideout to close when users submitted a form; otherwise, they would be left wondering whether anything happened. We could just trigger a close action on the submit button but what if there are validation errors. We came up with a clever solution using a custom turbo stream action.

Turbo streams ship with 8 basic actions but starting with version 7.2 anyone could add their own custom actions. We added the following to the turbo_streams.js that ships with Jumpstart:

Turbo.StreamActions.click_target = function() {
  this.targetElements.forEach((element) => element.click())
}

Since our slideout dialog looks like this in our application layout:

    <dialog data-slideout-target="dialog"
            data-action="click->slideout#close"
            class="slideout fixed top-0 z-20 border-l border-base-variant
                   h-dvh max-h-dvh m-0 ml-auto max-w-md p-8"
            id="slideout">
      <div class="h-full w-full">
        <button type="button" class="absolute top-9 right-8" data-action="slideout#close medallion#unset">
          <%= render_svg("xmark", styles: "fill-current size-3") %>
          <span class="sr-only">Close</span>
        </button>

        <%= turbo_frame_tag "slide-panel" %>
      </div>
    </dialog>

We can do something like the following in our controllers:

  def create
    if product.save
      flash.now[:notice] = {title: t(".success")}
      render turbo_stream: [
        turbo_stream.click_target("slideout"),
        turbo_stream.replace("toasts", partial: "toasts")
      ]
    else
      render :new, status: :unprocessable_entity, layout: false
    end
  end

We could also have created an explicit turbo stream action to close the slideout but being able to click any element on the page with a matching “id” is a bit more versatile and does the job here just as well.


As you can see, slowing down and reflecting before choosing how to communicate is necessary for good dialog/ue, in websites and in the work place. There is usually more than one option for our interactions and we usually are more effective when we consider what others are needing before we choose how to respond.

In the third and final part in this series we look at what we learned from one more complicated dialog interaction on this same project and get more inspiration for how to build good dialogue connections in workplace relationships as well.

If you’re looking for a team to help you discover the right thing to build and help you build it, get in touch.

Published on August 15, 2025